linux网络编程之System V 信号量(一):封装一个信号量集操作函数的工具
来源:互联网 发布:中小学辅导网络 编辑:程序博客网 时间:2024/05/09 06:34
信号量的概念参见这里。
与消息队列和共享内存一样,信号量集也有自己的数据结构:
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 short sem_nsems; /* No. of semaphores in set */};
同样地,第一个条目也是共有的ipc 对象内核结构,剩下的是私有成员。
Each semaphore in a semaphore set has the following associated values:
unsigned short semval; /* semaphore value */unsigned short semzcnt; /* # waiting for zero */unsigned short semncnt; /* # waiting for increase */pid_t sempid; /* process that did last op */
即每一个在信号量集中的信号量都有上述4个相关的变量。
1、semval :当前某信号量的资源数目
2、semzcnt:当sem_op(见 struct sembuf)为0,且semop 函数没有设置IPC_NOWAIT 标志,且当前semval 不为0,此时semzcnt 会加1,表示等待这个信号量的资源变为0的进程数加1,且进程会阻塞等待直到4个事件其中一个发生,具体可man 2 semop 一下。
3、semncnt:当sem_op(见 struct sembuf)< 0,且semop 函数没有设置IPC_NOWAIT 标志,且当前semval < |sem_op| ,此时semncnt 会加1,表示等待这个信号量的资源增加的进程数加1,且进程会阻塞等待直到4个事件其中一个发生,具体可man 2 semop 一下。
4、当正确执行了semop 函数,则信号量集中的每个信号量的sempid 参数都被设置为改变此信号量的进程pid。
以下是几个信号量集操作函数:
#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);
功能:用来创建和访问一个信号量集
原型 int semget(key_t key, int nsems, int semflg);
参数
key: 信号量集的名字
nsems:信号量集中信号量的个数
semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该信号量集的标识码;失败返回-1
功能:用于控制信号量集
原型 int semctl(int semid, int semnum, int cmd, …);
参数
semid:由semget返回的信号量集标识码
semnum:信号量集中信号量的序号,从0开始编号
cmd:将要采取的动作(有三个可取值)
最后一个参数是 union semun,具体成员根据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) */};
返回值:成功返回0;失败返回-1
cmd 取值如下:
SETVAL 设置信号量集中的信号量的计数值
GETVAL 获取信号量集中的信号量的计数值
IPC_STAT 把semid_ds结构中的数据设置为信号量集的当前关联值
IPC_SET 在进程有足够权限的前提下,把信号量集的当前关联值设置为semid_ds数据结构中给出的值
IPC_RMID 删除信号量集
功能:用来创建和访问一个信号量集
原型 int semop(int semid, struct sembuf *sops, unsigned nsops);
参数
semid:是该信号量集的标识码,也就是semget函数的返回值
sops:是个指向一个结构体的指针
nsops:信号量的个数
返回值:成功返回0;失败返回-1
struct sembuf{ unsigned short sem_num; /* semaphore number */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */};
sem_num:是信号量的编号。
sem_op:是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用。当然+-n 和0 都是允许的。需要注意的是只有+n 才确保将semval +n 后马上返回,而-n 和 0 很可能是会阻塞的,见文章上面的分析,+-n 需要进程对信号量集有写的权限,而0 只需要读的权限。
sem_flag:的两个取值是IPC_NOWAIT或SEM_UNDO,设为前者如果当某个信号量的资源为0时进行P操作,此时不会阻塞等待,而是直接返回资源不可用的错误;设为后者,当退出进程时对信号量资源的操作撤销;不关心时设置为0即可。
当要对一个信号量集中的多个信号量进行操作时,sops 是结构体数组的指针,此时nsops 不为1。此时对多个信号量的操作是作为一个单元原子操作,要么全部执行,要么全部不执行。
下面来封装一个信号量集操作函数的工具:
semtool.c
#include <sys/types.h>#include <unistd.h>#include <sys/ipc.h>#include <sys/sem.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>#define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0)union semun{ int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* array for GETALL, SETALL */ /* Linux specific part: */ struct seminfo *__buf; /* buffer for IPC_INFO */};int sem_create(key_t key){ int semid = semget(key, 1, 0666 | IPC_CREAT | IPC_EXCL); if (semid == -1) ERR_EXIT("semget"); return semid;}int sem_open(key_t key){ int semid = semget(key, 0, 0); if (semid == -1) ERR_EXIT("semget"); return semid;}int sem_p(int semid){ struct sembuf sb = {0, -1, /*IPC_NOWAIT*/SEM_UNDO}; int ret = semop(semid, &sb, 1); if (ret == -1) ERR_EXIT("semop"); return ret;}int sem_v(int semid){ struct sembuf sb = {0, 1, /*0*/SEM_UNDO}; int ret = semop(semid, &sb, 1); if (ret == -1) ERR_EXIT("semop"); return ret;}int sem_d(int semid){ int ret = semctl(semid, 0, IPC_RMID, 0); if (ret == -1) ERR_EXIT("semctl"); return ret;}int sem_setval(int semid, int val){ union semun su; su.val = val; int ret = semctl(semid, 0, SETVAL, su); if (ret == -1) ERR_EXIT("semctl"); printf("value updated...\n"); return ret;}int sem_getval(int semid){ int ret = semctl(semid, 0, GETVAL, 0); if (ret == -1) ERR_EXIT("semctl"); printf("current val is %d\n", ret); return ret;}int sem_getmode(int semid){ union semun su; struct semid_ds sem; su.buf = &sem; int ret = semctl(semid, 0, IPC_STAT, su); if (ret == -1) ERR_EXIT("semctl"); printf("current permissions is %o\n", su.buf->sem_perm.mode); return ret;}int sem_setmode(int semid, char *mode){ union semun su; struct semid_ds sem; su.buf = &sem; int ret = semctl(semid, 0, IPC_STAT, su); if (ret == -1) ERR_EXIT("semctl"); printf("current permissions is %o\n", su.buf->sem_perm.mode); sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode); ret = semctl(semid, 0, IPC_SET, su); if (ret == -1) ERR_EXIT("semctl"); printf("permissions updated...\n"); return ret;}void usage(void){ fprintf(stderr, "usage:\n"); fprintf(stderr, "semtool -c\n"); fprintf(stderr, "semtool -d\n"); fprintf(stderr, "semtool -p\n"); fprintf(stderr, "semtool -v\n"); fprintf(stderr, "semtool -s <val>\n"); fprintf(stderr, "semtool -g\n"); fprintf(stderr, "semtool -f\n"); fprintf(stderr, "semtool -m <mode>\n");}int main(int argc, char *argv[]){ int opt; opt = getopt(argc, argv, "cdpvs:gfm:"); if (opt == '?') exit(EXIT_FAILURE); if (opt == -1) { usage(); exit(EXIT_FAILURE); } key_t key = ftok(".", 's'); int semid; switch (opt) { case 'c': sem_create(key); 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 'd': semid = sem_open(key); sem_d(semid); break; case 's': semid = sem_open(key); sem_setval(semid, atoi(optarg)); 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]); break; } return 0;}
首先来介绍一个getopt 函数, int getopt(int argc, char * const argv[],const char *optstring);
可以解析命令行选项参数,前两个参数由main 函数传递,第三个参数是一个字符串集,即解析命令行参数看是否存在这些字符。如./semtool -s 3 则s
为选项,3为选项参数,optarg 是一个全局指针变量 extern char *optarg; 通过atoi(optarg) 可以获取数字3。
“cdpvs:gfm:” 表示选项s 和 m 后面可接参数,我们未使用一个while 循环去解析命令行参数,即这些选项只能同时出现一个,当未使用选项时打印输出
使用方法。
// optind: the index of first argument which has no option
// after getopt loop end: ./main -a xxx -b xxx ip port cnt optind points to ip
// usually optind+1 <= argc when no option arguments needed.
// argc: the count of arguments include exe; agrv[0 ~ argc-1]
根据解析到的选项来调用不同的函数,这些函数内部都调用了原始的信号量集操作函数,参照函数解释都不难理解。
需要注意一点是,这里为了只创建一个信号量集,只对这个信号量集的信号量进行操作,在sem_create 中指定了IPC_EXCL 选项,
即当key 已存在时返回错误,不再创建信号量集,而我们使用了ftok 函数产生一个唯一的key,传入的参数一定,则每次产生的key 值
一样,当第二次次执行./semtool -c ,会返回file exist 的错误,当然先删除当前信号量集,再create 是可以的,此时虽然key 还是一样
的,但返回的semid 是不同的。
且这个唯一的信号量集中只有唯一的一个信号量,即0号信号量,我们只对这个信号量进行PV操作。
使用举例如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -s------ Semaphore Arrays --------key semid owner perms nsems simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool usage:semtool -csemtool -dsemtool -psemtool -vsemtool -s <val>semtool -gsemtool -fsemtool -m <mode>simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -csimba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -s------ Semaphore Arrays --------key semid owner perms nsems 0x730135db 98304 simba 666 1 simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -vcurrent val is 1simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -vcurrent val is 1simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -s 3value updated...simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -gcurrent val is 3simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -pcurrent val is 2simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -m 600current permissions is 666permissions updated...simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -dsimba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -s------ Semaphore Arrays --------key semid owner perms nsems
因为我们在PV操作中指定了SEM_UNDO 选项,当进程退出时撤销操作,所以连续执行两次V操作后信号量的资源还是为0(创建后信号量默认资源为
0,不一定所有系统实现都会如此,应该显式地初始化为0)。通过-s 可以设置信号量的资源数。ipcs -s 输出中的nsems 表示信号量的个数,当前只有一个;./semtool -v 输出中的current value 表示这个信号量的资源数。
参考:《UNP》
转载自http://blog.csdn.net/jnu_simba/article/details/9099969
- linux网络编程之System V 信号量(一):封装一个信号量集操作函数的工具
- linux网络编程之System V 信号量(一):封装一个信号量集操作函数的工具
- Linux进程间通信(IPC)编程实践(九)System V信号量---封装一个信号量操作的工具集
- linux网络编程之System V信号量
- Linux 信号量(system V)
- System V 信号量(一)
- Linux System V 信号量
- linux网络编程之System V 信号量(二):用信号量实现进程互斥示例和解决哲学家就餐问题
- linux网络编程之System V 信号量(二):用信号量实现进程互斥示例和解决哲学家就餐问题
- linux 程序设计 System V 信号量编程
- <linux进程>system-V信号量的基本操作
- linux网络编程之System V 信号量(三):基于生产者-消费者模型实现先进先出的共享内存段
- linux网络编程之System V 信号量(三):基于生产者-消费者模型实现先进先出的共享内存段 .
- linux网络编程之System V 信号量(三):基于生产者-消费者模型实现先进先出的共享内存段
- Linux进程同步之System V 信号量
- linux进程通信之SYSTEM V信号量
- Linux进程同步之System V 信号量
- Linux进程间通信(IPC)编程实践(十)System V信号量---PV操作经典题目
- 分布式互联网架构——消息
- iOS10 Info.plist权限设置
- git pull 或者执行一些其他git操作 Auto packing the repository for optimum performance. You may also run "git gc
- Maven, Spring MVC, Mybatis, Mysql and Java_1
- 有意思的DCDC工作原理
- linux网络编程之System V 信号量(一):封装一个信号量集操作函数的工具
- JAVA环境变量配置
- 正在加载......
- vmware vsphere:添加虚拟机vnc访问
- (OK) error: code model kernel does not support PIC mode
- Neo4J(Cypher语句)初识
- JAVA校验和算法的实现
- 最新activiti测试版,流程图设计,bpm设计
- 日程管理的三款软件