Linux IPC实践(11) --System V信号量(1)
信号量API
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); int semctl(int semid, int semnum, int cmd, ...); int semop(int semid, struct sembuf *sops, unsigned nsops);
semget
int semget(key_t key, int nsems, int semflg);
创建/访问一个信号量集
参数:
key: 信号集键(key)
nsems:信号集中信号量的个数
semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志一致
返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1;
此时创建的信号量集中的每一个信号量都会有一个默认值: 0, 如果需要更改该值, 则需要调用semctl函数->更改初始值;
/** 示例1: 封装一个创建一个信号量集函数
该信号量集包含1个信号量;
权限为0666
**/
int sem_create(key_t key)
{
int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
if (semid == -1)
err_exit("sem_create error");
return semid;
}
/** 示例2: 打开一个信号量集
nsems(信号量数量)可以填0,
semflg(信号量权限)也可以填0, 表示使用默认的权限打开
**/
int sem_open(key_t key)
{
int semid = semget(key, 0, 0);
if (semid == -1)
err_exit("sem_open error");
return semid;
}
shmctl
int semctl(int semid, int semnum, int cmd, ...);
控制信号量集
参数
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号(注意: 从0开始The semaphores in a set are numbered starting at 0.)
cmd:将要采取的动作(常用取值如下)
如果该函数需要第四个参数(有时是不需要第四个参数的, 取决于cmd的取值), 则程序中必须定义如下的联合体:
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific)*/
};
//struct semid_ds : Linux内核为System V信号量维护的数据结构
struct semid_ds
{
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* No. of semaphores in set */
};
/** 示例1: 将信号量集semid中的第一个信号量的值设置成为value(SETVAL)
注意: semun联合体需要自己给出(从man-page中拷贝出来即可)
**/
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
int sem_setval(int semid, int value)
{
union semun su;
su.val = value;
if (semctl(semid, 0, SETVAL, su) == -1)
err_exit("sem_setval error");
return 0;
}
/** 示例2: 获取信号量集中第一个信号所关联的值(GETVAL)
注意: 此时第四个参数可以不填, 而信号量所关联的值可以通过semctl的返回值返回(the value of semval.)
**/
int sem_getval(int semid)
{
int value = semctl(semid, 0, GETVAL);
if (value == -1)
err_exit("sem_getval error");
return value;
return 0;
}
/** 示例3: 删除一个信号量集(注意是删除整个集合)
IPC_RMID Immediately remove(立刻删除) the semaphore set, awakening all processes blocked in semop(2) calls on the set (with an error return and errno set to EIDRM)[然后唤醒所有阻塞在该信号量上的进程]. The argument semnum is ignored[忽略第二个参数].
**/
int sem_delete(int semid)
{
if (semctl(semid, 0, IPC_RMID) == -1)
err_exit("sem_delete error");
return 0;
}
//测试代码
int main(int argc,char *argv[])
{
int semid = sem_create(0x1234); //创建一个信号量集
sem_setval(semid, 500); //设置值
cout << sem_getval(semid) << endl; //获取值
sleep(10);
sem_delete(semid); //删除该集合
}
/**示例4: 获取/设置信号量的权限
注意:一定要设定struct semid_ds结构体, 以指定使用semun的哪个字段
**/
int sem_getmode(int semid)
{
union semun su;
// 注意: 下面这两行语句一定要设定.
// (告诉内核使用的semun的哪个字段)
struct semid_ds sd;
su.buf = &sd;
//
if (semctl(semid, 0, IPC_STAT, su) == -1)
err_exit("sem_getmode error");
printf("current permissions is: %o\n", su.buf->sem_perm.mode);
return 0;
}
int sem_setmode(int semid, char *mode)
{
union semun su;
// 注意: 下面这两行语句一定要设定.
// (告诉内核使用的semun的哪个字段)
struct semid_ds sd;
su.buf = &sd;
//
sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);
if (semctl(semid, 0, IPC_SET, su) == -1)
err_exit("sem_setmode error");
return 0;
}
semop
int semop(int semid, struct sembuf *sops, unsigned nsops);
用来操纵一个信号量集, 以实现P,V操作
参数:
semid:是该信号量的标识码,也就是semget函数的返回值
sops:是个指向一个结构数组(如果信号量集中只有一个信号量的话, 只有一个结构体也可)的指针
nsops:所设置的信号量个数(如果nsops>1话, 需要将sops[第二个参数]设置成为一个结构数组, 具体参考Man-Page给出的示例代码), 第三个参数其实也指出第二个参数所表示对象的个数;
//sembuf结构体
struct sembuf
{
unsigned short sem_num; /*semaphore number:信号量的编号(从0开始)*/
short sem_op; /* semaphore operation(+1, 0, -1) */
short sem_flg; /* operation flags: 常用取值为SEM_UNDO(解释见下) */
};
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是V操作,发出信号量已经变得可用, 该参数还可以等于0, 表示进程将阻塞直到信号量的值等于0;
sem_flg有三个取值: SEM_UNDO(在进程结束时, 将该进程对信号量的操作复原[即:取消该进程对信号量所有的操作], 推荐使用), IPC_NOWAIT(非阻塞)或0(默认操作, 并不撤销操作);
/** 示例: P,V操作封装
**可以将sembuf的第三个参数设置为IPC_NOWAIT/0, 以查看程序的状态的变化
**/
int sem_P(int semid)
{
struct sembuf sops = {0, -1, SEM_UNDO};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_P error");
return 0;
}
int sem_V(int semid)
{
struct sembuf sops = {0, +1, SEM_UNDO};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_V error");
return 0;
}
/** 信号量综合运用示例:
编译完成之后, 直接运行./semtool, 程序将打印该工具的用法;
下面的这些函数调用, 只不过是对上面所封装函数的稍稍改动, 理解起来并不困难;
**/
//semtool.cpp
#include "Usage.h"
int main(int argc,char *argv[])
{
int opt = getopt(argc, argv, "cdpvs:gfm:");
if (opt == '?')
exit(EXIT_FAILURE);
else if (opt == -1)
{
usage();
exit(EXIT_FAILURE);
}
key_t key = ftok(".", 's');
int semid;
switch (opt)
{
case 'c':
sem_create(key);
break;
case 'd':
semid = sem_open(key);
sem_delete(semid);
break;
case 'p':
semid = sem_open(key);
sem_P(semid);
sem_getval(semid);
break;
case 'v':
semid = sem_open(key);
sem_V(semid);
sem_getval(semid);
break;
case 's':
semid = sem_open(key);
sem_setval(semid, atoi(optarg));
sem_getval(semid);
break;
case 'g':
semid = sem_open(key);
sem_getval(semid);
break;
case 'f':
semid = sem_open(key);
sem_getmode(semid);
break;
case 'm':
semid = sem_open(key);
sem_setmode(semid, argv[2]);
sem_getmode(semid);
break;
default:
break;
}
return 0;
}
//Usage.h
#ifndef USAGE_H_INCLUDED
#define USAGE_H_INCLUDED
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <time.h>
#include <errno.h>
#include <mqueue.h>
using namespace std;
inline void err_quit(std::string message);
inline void err_exit(std::string message);
void usage()
{
cerr << "Usage:" << endl;
cerr << "./semtool -c #create" << endl;
cerr << "./semtool -d #delte" << endl;
cerr << "./semtool -p #signal" << endl;
cerr << "./semtool -v #wait" << endl;
cerr << "./semtool -s <val> #set-value" << endl;
cerr << "./semtool -g #get-value" << endl;
cerr << "./semtool -f #print-mode" << endl;
cerr << "./semtool -m <mode> #set-mode" << endl;
}
int sem_create(key_t key)
{
int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
if (semid == -1)
err_exit("sem_create error");
return semid;
}
int sem_open(key_t key)
{
int semid = semget(key, 0, 0);
if (semid == -1)
err_exit("sem_open error");
return semid;
}
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int sem_getmode(int semid)
{
union semun su;
// 注意: 下面这两行语句一定要设定.
// (告诉内核使用的semun的哪个字段)
struct semid_ds sd;
su.buf = &sd;
//
if (semctl(semid, 0, IPC_STAT, su) == -1)
err_exit("sem_getmode error");
printf("current permissions is: %o\n", su.buf->sem_perm.mode);
return 0;
}
int sem_setmode(int semid, char *mode)
{
union semun su;
// 注意: 下面这两行语句一定要设定.
// (告诉内核使用的semun的哪个字段)
struct semid_ds sd;
su.buf = &sd;
//
sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);
if (semctl(semid, 0, IPC_SET, su) == -1)
err_exit("sem_setmode error");
return 0;
}
int sem_getval(int semid)
{
int value = semctl(semid, 0, GETVAL);
if (value == -1)
err_exit("sem_getval error");
cout << "current value: " << value << endl;
return value;
}
int sem_setval(int semid, int value)
{
union semun su;
su.val = value;
if (semctl(semid, 0, SETVAL, su) == -1)
err_exit("sem_setval error");
return 0;
}
int sem_delete(int semid)
{
if (semctl(semid, 0, IPC_RMID) == -1)
err_exit("sem_delete error");
return 0;
}
// 为了能够打印信号量的持续变化, 因此sem_flg我们并没用SEM_UNDO
// 但是我们推荐使用SEM_UNDO
int sem_P(int semid)
{
struct sembuf sops = {0, -1, 0};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_P error");
return 0;
}
int sem_V(int semid)
{
struct sembuf sops = {0, +1, 0};
if (semop(semid, &sops, 1) == -1)
err_exit("sem_V error");
return 0;
}
inline void err_quit(std::string message)
{
std::cerr << message << std::endl;
exit(EXIT_FAILURE);
}
inline void err_exit(std::string message)
{
perror(message.c_str());
exit(EXIT_FAILURE);
}
#endif // USAGE_H_INCLUDED
附-Makefile
.PHONY: clean all
CC = g++
CPPFLAGS = -Wall -g
BIN = semtool
SOURCES = $(BIN.=.cpp)
all: $(BIN)
%.o: %.c
$(CC) $(CPPFLAGS) -c $^ -o $@
main: main.o
$(CC) $(CPPFLAGS) $^ -lrt -o $@
clean:
-rm -rf $(BIN) *.o bin/ obj/ core
Linux IPC实践(11) --System V信号量(1)的更多相关文章
- Linux IPC实践(12) --System V信号量(2)
实践1:信号量实现进程互斥 父子进程执行流程如下: 父进程 子进程 P P O(print) X(print) sleep sleep O(print) X(print) V V sleep slee ...
- Linux IPC实践(9) --System V共享内存
共享内存API #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int ...
- Linux IPC实践(6) --System V消息队列(3)
消息队列综合案例 消息队列实现回射客户/服务器 server进程接收时, 指定msgtyp为0, 从队首不断接收消息 server进程发送时, 将mtype指定为接收到的client进程的pid ...
- Linux IPC实践(13) --System V IPC综合实践
实践:实现一个先进先出的共享内存shmfifo 使用消息队列即可实现消息的先进先出(FIFO), 但是使用共享内存实现消息的先进先出则更加快速; 我们首先完成C语言版本的shmfifo(基于过程调用) ...
- Linux IPC实践(4) --System V消息队列(1)
消息队列概述 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法(仅局限于本机); 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值. 消息队列也有管道一样的不足: ...
- Linux IPC实践(5) --System V消息队列(2)
消息发送/接收API msgsnd函数 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 参数 msgid: 由ms ...
- linux网络编程之system v信号量(一)
今天起,学习信号量相关的知识,下面开始: 关于信号量,在前面已经介绍过了,这里回顾一下: 通过上面的描述,很容易就能想到信号量的一上数据结构: 下面再来回顾一下P.V原语: 所谓的原语就是指这段代码是 ...
- linux网络编程之system v信号量(二)
今天迎来元旦假期的最后一天了,过得好快~昨天跟小伙伴们在军都滑雪陪儿爽,虽说上了两回中级道都摔得异常的惨烈,但是在初级道上学习"s"转弯还是有一些小心得,可以在要往高手迈进的前提, ...
- Linux中的System V信号量
在进程同步,并发运行时,保证按序地访问共享资源是十分重要的.因此引入了临界区的概念,一次只能有一个线程进入临界区完成他的指令.而信号量(semaphore)的作用,类似于一个交通信号灯,它负责进程协作 ...
随机推荐
- 数据挖掘_requests模块的post方法
前面已经跟大家讲了requests模块的get方法,这一篇文章我们要介绍的是requests模块中的另一个比较常用的方法,post方法 post方法的形式相比于get要复杂一些,这时因为post在提交 ...
- XML相关知识
XML的定义: XML即可扩展标记语言标记是指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等.如何定义这些标记,既可以选择国际通用的标记语言,比如HTML,也可以使用 ...
- Spring-cloud (七)自定义HystrixCommand
前提 1.在继续学习Hystrix之前,向关注本人博客的各位致歉 由于之前的项目起名以及服务之间的名称不是很规范,所以我修改了这些名称方便后来的代码管理,这些代码可以在本人github中找到,这里贴出 ...
- PHP echo和print 语句
PHP echo 和 print 语句 在 PHP 中有两个基本的输出方式: echo 和 print. 本章节中我们会详细讨论两个语句的用法,并在实例中演示如何使用 echo 和 print. P ...
- 实验与作业(Python)-03 Python程序实例解析
截止日期: 要求: 下周实验课前上交,做好后在实验课上检查可获取平时分. 做出进阶或选做的的请用清晰的标致标识出来,方便老师批改 本次作业:可提交也可不提交.作业算平时成绩. 本次作业内容量较大,请组 ...
- hadoop入门级总结三:hive
认识hive Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供完整的SQL查询功能,可以将SQL语句转换为MapReduce任务运行 Hive是建立在 ...
- Swift3中方法可变参数语法的一些改变
我们知道在Swift2中,默认情况下方法的参数是let值,也就是不可改变的. 不过我们可以在参数前添加var关键字改变其不变性: func foo(var i:Int){ i += 1 print(i ...
- 剑指Offer——知识点储备-常用算法
剑指Offer--知识点储备-常用算法 快速排序 注:若排序是有序的,采用快排,则退化为冒泡排序. 解决这个问题,采用两个选取基准的方法 (1)随机选取基数(在这个区间内随机取一个数) 出现的恶劣情况 ...
- [Python]多个装饰器合并
django程序,需要写很多api,每个函数都需要几个装饰器,例如 @csrf_exempt @require_POST def foo(request): pass 既然那么多个方法都需要写2个装饰 ...
- Struts1应用、实现简单计算器、使用DispatchAction、显示友好的报错信息、使用动态Form简化开发
实现简单的支持加.减.乘.除的计算器 复制一份Struts1Demo修改:Struts1Calc 方案1: Struts1Calc 创建ActionForm: CalcForm extends Act ...