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 short  sem_nsems; /* No. of semaphores in set */
};

同样地,第一个条目也是共有的ipc 对象内核结构,剩下的是私有成员。

以下是几个信号量集操作函数:

#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 都是允许的。
sem_flag:的两个取值是IPC_NOWAIT或SEM_UNDO,设为前者如果当某个信号量的资源为0时进行P操作,此时不会阻塞等待,而是直接返回资源不可用的错误;设为后者,当退出进程时对信号量资源的操作撤销;不关心时设置为0即可。

下面来封装一个信号量集操作函数的工具:

semtool.c

 C++ Code 
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,  10666  | IPC_CREAT | IPC_EXCL);
     if  (semid == - 1 )
        ERR_EXIT( "semget" );

     return  semid;
}

int  sem_open(key_t key)
{
     int  semid = semget(key,  00 );
     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 = { 01/*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 循环去解析命令行参数,即这些选项只能同时出现一个,当未使用选项时打印输出

使用方法。

根据解析到的选项来调用不同的函数,这些函数内部都调用了原始的信号量集操作函数,参照函数解释都不难理解。

需要注意一点是,这里为了只创建一个信号量集,只对这个信号量集的信号量进行操作,在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 -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     

因为我们在PV操作中指定了SEM_UNDO 选项,当进程退出时撤销操作,所以连续执行两次V操作后信号量的资源还是为0。通过-s

 可以设置信号量的资源数。 ipcs -s 输出中的nsems 表示信号量的个数,当前只有一个;./semtool -v 输出中的current value 表示这个信号量的资源数。

参考:《UNP》

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章