Linux进程间通信(IPC)编程实践(九)System V信号量 -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

   

System信号量集主要API

    #include

#include

    #include

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);

    /** 示例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;}int semctl(int semid, int semnum, int 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;}[cpp] view plaincopy在CODE上查看代码片派生到我的代码片int semop(int semid, struct sembuf *sops, unsigned nsops);[cpp] view plaincopy在CODE上查看代码片派生到我的代码片//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(解释见下) */};

   

/** 示例: 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;}

    下面我们封装一个信号量操作函数工具,将主要的操作封装起来,可以像命令一样使用,

Linux进程间通信(IPC)编程实践(九)System V信号量

电脑资料

Linux进程间通信(IPC)编程实践(九)System V信号量》(https://www.unjs.com)。

    /** 信号量综合运用示例:

编译完成之后, 直接运行./semtool, 程序将打印该工具的用法;

    下面的这些函数调用, 只不过是对上面所封装函数的稍稍改动, 理解起来并不困难;

**///semtool.cpp#include "Usage.h"int main(int argc,char *argv[]){int pt = 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#include #include #include#include #include#include#include #include#include #include#include #include#include #include#include #include#include #include#include #include#include #includeusing 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 #set-value" << endl;cerr << "./semtool -g #get-value" << endl;cerr << "./semtool -f #print-mode" << endl;cerr << "./semtool -m #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_UNDOint 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

    附:ftok函数

    系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

    ftok原型如下:

key_t ftok( char * fname, int id )
fname就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。

    返回值:

    当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。

    在我们获取到key之后,就可以使用该key作为某种方法的进程间通信的key值。

最新文章