信号量的概念参见这里。
与消息队列和共享内存一样,信号量集也有自己的数据结构:
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
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
#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。
使用方法。
// 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]
key 已存在时返回错误,不再创建信号量集,而我们使用了ftok 函数产生一个唯一的key,传入的参数一定,则每次产生的key 值
一样,当第二次次执行./semtool -c ,会返回file exist 的错误,当然先删除当前信号量集,再create 是可以的,此时虽然key 还是一样
的,但返回的semid 是不同的。
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 -c
semtool -d
semtool -p
semtool -v
semtool -s <val>
semtool -g
semtool -f
semtool -m <mode>
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -c
simba@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 -v
current val is 1
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -v
current val is 1
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -s 3
value updated...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -g
current val is 3
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -p
current val is 2
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -m 600
current permissions is 666
permissions updated...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./semtool -d
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
可以设置信号量的资源数。ipcs -s 输出中的nsems 表示信号量的个数,当前只有一个;./semtool -v 输出中的current value 表示这个信号量的资源数。
参考:《UNP》