System V 信号量

信号量即能够执行互斥操作,也可以进行同步操作。本文介绍System V信号量的概念以及使用。

信号量

  • 信号量和P、V是由Dijsktra(迪杰斯特拉)提出的
  • 信号量
    • 互斥:P、V在同一个进程中
    • 同步:P、V在不同进程中
  • 信号量值含义
    • S>0:S表示可用资源的个数
    • S=0:表示无可用资源,无等待进程
    • S<0:|S|表示当前等待进程的个数
      1
      2
      3
      4
      struct semphore {
      int value;
      pointer_PCB queue;
      }

P原语

1
2
3
4
5
6
7
P(s) {
s.value--;
if (s.value < 0) {
该进程状态置为等待状态
将该进程的PCB插入相应的等待队列s.queue末尾
}
}

V原语

1
2
3
4
5
6
7
8
V(s) {
s.value++;
if (s.value<=0) {
唤醒相应等待进程队列s.queue中等待的一个进程
改变其状态为就绪态
并将其插入到就绪状态
}
}

信号量集数据结构

1
2
3
4
5
6
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
2
3
4
5
6
7
#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函数

  • 功能:用来创建和访问一个信号量集
  • 原型:

    1
    int semget(key_t key, int nsems, int semflg);
  • 参数:

    • key:信号量集的名字
    • nsems,信号量集中信号量的个数
    • smflg:由九个权限标志构成,他们的用法和创建文件时使用的mode模式标志是一样的
  • 返回值:成功返回一个非负整数,即该信号集的标识码,失败返回-1

semctl函数

  • 功能:用于控制信号量集
  • 原型:

    1
    int semctl(int semid, int semnum, int cmd, ...);
  • 参数:

    • semid:由semget返回的信号量标识码
    • semnum:信号集中信号量的序号
    • cmd:将要采取的动作(有三个可取值)
    • 最后一个参数根据命令的不同而不同
  • 返回值:成功返回0,失败返回-1

cmd:
| 命令 | 说明 |
| — | — |
| SETVAL | 设置信号量集中的信号量的计数值 |
| GETVAL | 获取信号量集中的信号量的计数值 |
| IPC_STAT | 把semid_ds结构中的数据设置为信号集的当前关联值 |
| IPC_SET | 在进程有足够权限的情况下,把信号量集的当前关联值设置为semid_ds数据结构体中给出的值 |
| IPC_RMID | 删除信号量集 |


semop函数

  • 功能:用来创建和访问一个信号量集
  • 原型:

    1
    int semop(int semid, struct sembuf *sops, unsigned nsops);
  • 参数:

    • semid:信号量集的标识码
    • sops:是指向一个结构数值的指针
    • nsops:信号量的个数
  • 返回值:成功返回0,失败返回-1

sembuf结构体:

1
2
3
4
5
struct sembuf {
shor sem_snum;
short sem_op;
short sem_flg;
};

  • sem_num是信号量的编号
  • sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用
  • sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO

信号量实例

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int sem_create(key_t key) {
int semid;
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
if (semid == -1) {
ERR_EXIT("semget");
}
return semid;
}
int sem_open(key_t key) {
int semid;
semid = semget(key, 0, 0);
if (semid == -1) {
ERR_EXIT("semget");
}
return semid;
}
int sem_setval(int semid, int val) {
union semun su;
su.val = val;
if (semctl(semid, 0, SETVAL, su)) {
ERR_EXIT("sem_setval");
}
return 0;
}
int sem_getval(int semid) {
int ret;
if ((ret = semctl(semid, 0, GETVAL, 0)) == -1) {
ERR_EXIT("sem_getval");
}
return ret;
}
int sem_d(int semid) {
int ret;
if (semctl(semid, 0, IPC_RMID, 0)) {
ERR_EXIT("sem_d");
}
return 0;
}
int sem_p(int semid) {
struct sembuf sb = {0, -1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_p");
}
return 0;
}
int sem_v(int semid) {
struct sembuf sb = {0, +1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_v");
}
return 0;
}
int sem_getmode(int semid) {
union semun su;
struct semid_ds sem;
su.buf = &sem;
if (semctl(semid, 0, IPC_STAT, su) < 0) {
ERR_EXIT("sem_getmode");
}
printf("current permissions is %o\n", su.buf->sem_perm.mode);
return 0;
}
int sem_setmode(int semid, char *mode) {
union semun su;
struct semid_ds sem;
su.buf = &sem;
if (semctl(semid, 0, IPC_STAT, su) < 0) {
ERR_EXIT("sem_getmode");
}
sscanf(mode, "%o", (unsigned int*)&su.buf->sem_perm.mode);
if (semctl(semid, 0, IPC_SET, su) < 0) {
ERR_EXIT("sem_setmode");
}
printf("current permissions is %o\n", su.buf->sem_perm.mode);
return 0;
}
void usage() {
printf("usage: semtool [option] [value]\n");
printf("\t-c: create a semphore\n");
printf("\t-d: delete a semphore\n");
printf("\t-s: set semphore value\n");
printf("\t-g: get semphore value\n");
printf("\t-p: p operation\n");
printf("\t-v: v operation\n");
printf("\t-m: set permissions mode\n");
printf("\t-o: get permissions mode\n");
}
int main(int argc, char *argv[]) {
int opt = getopt(argc, argv, "cds:gdpvm:o");
if (opt == '?') {
exit(EXIT_FAILURE);
}
if (opt == -1) {
usage();
exit(EXIT_FAILURE);
}
key_t key = ftok(".", 's');
int semid;
switch(opt) {
case 'c':
semid = sem_create(key);
break;
case 'd':
semid = sem_open(key);
sem_d(semid);
break;
case 's':
semid = sem_open(key);
sem_setval(semid, atoi(optarg));
printf("value updated\n");
break;
case 'g':
semid = sem_open(key);
printf("current value: %d\n", sem_getval(semid));
break;
case 'p':
semid = sem_open(key);
sem_p(semid);
printf("current value: %d\n", sem_getval(semid));
break;
case 'v':
semid = sem_open(key);
sem_v(semid);
printf("current value: %d\n", sem_getval(semid));
break;
case 'm':
semid = sem_open(key);
sem_setmode(semid, optarg);
break;
case 'o':
semid = sem_open(key);
sem_getmode(semid);
break;
}
return 0;
}

用信号量实现进程互斥实例

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int sem_create(key_t key) {
int semid;
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
if (semid == -1) {
ERR_EXIT("semget");
}
return semid;
}
int sem_open(key_t key) {
int semid;
semid = semget(key, 0, 0);
if (semid == -1) {
ERR_EXIT("semget");
}
return semid;
}
int sem_setval(int semid, int val) {
union semun su;
su.val = val;
if (semctl(semid, 0, SETVAL, su)) {
ERR_EXIT("sem_setval");
}
return 0;
}
int sem_getval(int semid) {
int ret;
if ((ret = semctl(semid, 0, GETVAL, 0)) == -1) {
ERR_EXIT("sem_getval");
}
return ret;
}
int sem_d(int semid) {
int ret;
if (semctl(semid, 0, IPC_RMID, 0)) {
ERR_EXIT("sem_d");
}
return 0;
}
int sem_p(int semid) {
struct sembuf sb = {0, -1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_p");
}
return 0;
}
int sem_v(int semid) {
struct sembuf sb = {0, +1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_v");
}
return 0;
}
int sem_getmode(int semid) {
union semun su;
struct semid_ds sem;
su.buf = &sem;
if (semctl(semid, 0, IPC_STAT, su) < 0) {
ERR_EXIT("sem_getmode");
}
printf("current permissions is %o\n", su.buf->sem_perm.mode);
return 0;
}
int sem_setmode(int semid, char *mode) {
union semun su;
struct semid_ds sem;
su.buf = &sem;
if (semctl(semid, 0, IPC_STAT, su) < 0) {
ERR_EXIT("sem_getmode");
}
sscanf(mode, "%o", (unsigned int*)&su.buf->sem_perm.mode);
if (semctl(semid, 0, IPC_SET, su) < 0) {
ERR_EXIT("sem_setmode");
}
printf("current permissions is %o\n", su.buf->sem_perm.mode);
return 0;
}
int semid;
void print(char op_char) {
int pause_time;
srand(getpid());
int i;
for (i=0; i<10; ++i) {
sem_p(semid);
printf("%c", op_char);
fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c", op_char);
fflush(stdout);
sem_v(semid);
pause_time = rand() % 2;
sleep(pause_time);
}
}
int main(int argc, char *argv[]) {
semid = sem_create(IPC_PRIVATE);
sem_setval(semid, 0);
pid_t pid;
pid = fork();
if (pid == -1) {
ERR_EXIT("fork");
}
if (pid > 0) {
sem_setval(semid, 1);
print('o');
wait(NULL);
sem_d(semid);
} else if (pid == 0) {
print('x');
}
return 0;
}

用信号集解决哲学家就餐问题

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int sem_p(int semid) {
struct sembuf sb = {0, -1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_p");
}
return 0;
}
int sem_v(int semid) {
struct sembuf sb = {0, +1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_v");
}
return 0;
}
#define DELAY ((rand() % 5) + 1)
int semid;
void wait_for_2fork(int no) {
int left = no;
int right = (no + 1) % 5;
struct sembuf buf[2] = {
{left, -1, 0},
{right, -1, 0}
};
semop(semid, buf, 2);
}
void free_2fork(int no) {
int left = no;
int right = (no + 1) % 5;
struct sembuf buf[2] = {
{left, 1, 0},
{right, 1, 0}
};
semop(semid, buf, 2);
}
void philosophere(int no) {
srand(getpid());
for (;;) {
printf("%d is thinking\n", no);
sleep(DELAY);
printf("%d is hungry\n", no);
wait_for_2fork(no);
sleep(DELAY);
printf("%d is eating \n", no);
free_2fork(no);
}
}
int main(int argc, char *argv[]) {
semid = semget(IPC_PRIVATE, 5, IPC_CREAT | 0666);
if (semid == -1) {
ERR_EXIT("semget");
}
union semun su;
su.val = 1;
int i;
for (i=0; i<5; i++) {
semctl(semid, i, SETVAL, su);
}
int no = 0;
pid_t pid;
for (i=1; i<=5; ++i) {
pid = fork();
if (pid == -1) {
ERR_EXIT("fork");
}
if (pid == 0) {
no = i;
break;
}
}
philosophere(no);
return 0;
}

用信号量解决生产者与消费者问题

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
/*semfifo.h*/
#ifndef SEMFIFO_H
#define SEMFIFO_H
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
typedef struct shmhead shmhead_t;
typedef struct shmfifo shmfifo_t;
struct shmhead {
unsigned int blksize; // 块大小
unsigned int blocks; // 总快数
unsigned int rd_index; // 读索引
unsigned int wr_index; // 写索引
};
struct shmfifo {
shmhead_t *p_shm; // 共享内存头部指针
char *p_payload; // 有效负载的其实地址
int shmid; // 共享内存ID
int sem_mutex; // 用来互斥使用的信号量
int sem_full; // 用来控制共享内存是否满的信号量
int sem_empty; // 用来控制信号量是否空的信号量
};
shmfifo_t *semfifo_init(int key, int blksize, int blocks);
void semfifo_pust(shmfifo_t *fifo, const void *buf);
void semfifo_get(shmfifo_t *fifo, void *buf);
void semfifo_destroy(shmfifo_t *fifo);
#endif
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
/*semfifo.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "semfifo.h"
shmfifo_t *semfifo_init(int key, int blksize, int blocks) {
shmfifo_t *fifo = (shmfifo_t *)malloc(sizeof(shmfifo_t));
assert(fifo != NULL);
memset(fifo, 0, sizeof(shmfifo_t));
int shmid;
shmid = shmget(key, 0, 0);
int size = sizeof(shmhead_t) + blksize * blocks;
if (shmid == -1) {
fifo->shmid = shmget(key, size, IPC_CREAT | 0666);
if (fifo->shmid == -1) {
ERR_EXIT("shmget");
}
fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);
if (fifo->p_shm == (shmhead_t *)-1) {
ERR_EXIT("shmat");
}
fifo->p_payload = (char *)(fifo->p_shm+1);
fifo->p_shm->blksize = blksize;
fifo->p_shm->blocks = blocks;
fifo->p_shm->rd_index = 0;
fifo->p_shm->wr_index = 0;
fifo->sem_mutex = sem_create(key);
fifo->sem_full = sem_create(key+1);
fifo->sem_empty = sem_create(key+2);
sem_setval(fifo->sem_mutex, 1);
sem_setval(fifo->sem_full, blocks);
sem_setval(fifo->sem_empty, 0);
} else {
fifo->shmid = shmid;
fifo->p_shm = (shmhead_t *)shmat(fifo->shmid, NULL, 0);
if (fifo->p_shm == (shmhead_t *)-1) {
ERR_EXIT("shmat");
}
fifo->p_payload = (char *)(fifo->p_shm+1);
fifo->sem_mutex = sem_open(key);
fifo->sem_full = sem_open(key+1);
fifo->sem_empty = sem_open(key+2);
}
return fifo;
}
void semfifo_put(shmfifo_t *fifo, void *buf) {
sem_p(fifo->sem_full);
sem_p(fifo->sem_mutex);
memcpy(fifo->p_payload + fifo->p_shm->blksize*fifo->p_shm->wr_index, buf, fifo->p_shm->blksize);
fifo->p_shm->wr_index = (fifo->p_shm->wr_index + 1) % fifo->p_shm->blocks;
sem_v(fifo->sem_mutex);
sem_v(fifo->sem_empty);
}
void semfifo_get(shmfifo_t *fifo, void *buf) {
sem_p(fifo->sem_empty);
sem_p(fifo->sem_mutex);
memcpy(buf, fifo->p_payload + fifo->p_shm->blksize*fifo->p_shm->rd_index, fifo->p_shm->blksize);
fifo->p_shm->rd_index = (fifo->p_shm->rd_index + 1) % fifo->p_shm->blocks;
sem_v(fifo->sem_mutex);
sem_v(fifo->sem_full);
}
void semfifo_destroy(shmfifo_t *fifo) {
sem_d(fifo->sem_mutex);
sem_d(fifo->sem_full);
sem_d(fifo->sem_empty);
shmdt(fifo->p_shm);
shmctl(fifo->shmid, IPC_RMID, 0);
free(fifo);
}
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int sem_create(key_t key) {
int semid;
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
if (semid == -1) {
ERR_EXIT("semget");
}
return semid;
}
int sem_open(key_t key) {
int semid;
semid = semget(key, 0, 0);
if (semid == -1) {
ERR_EXIT("semget");
}
return semid;
}
int sem_setval(int semid, int val) {
union semun su;
su.val = val;
if (semctl(semid, 0, SETVAL, su)) {
ERR_EXIT("sem_setval");
}
return 0;
}
int sem_getval(int semid) {
int ret;
if ((ret = semctl(semid, 0, GETVAL, 0)) == -1) {
ERR_EXIT("sem_getval");
}
return ret;
}
int sem_d(int semid) {
int ret;
if (semctl(semid, 0, IPC_RMID, 0)) {
ERR_EXIT("sem_d");
}
return 0;
}
int sem_p(int semid) {
struct sembuf sb = {0, -1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_p");
}
return 0;
}
int sem_v(int semid) {
struct sembuf sb = {0, +1, 0};
if (semop(semid, &sb, 1) < 0) {
ERR_EXIT("sem_v");
}
return 0;
}
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
/*sem_send.c*/
#include "semfifo.h"
#include <stdio.h>
#include <string.h>
typedef struct stu {
char name[32];
int age;
} STU;
int main() {
shmfifo_t *fifo = semfifo_init(1234, sizeof(STU), 3);
STU s;
memset(&s, 0, sizeof(STU));
s.name[0] = 'A';
s.age = 20;
int i;
for (i=0; i<5; ++i) {
semfifo_put(fifo, &s);
s.name[0] = s.name[0] + 1;
s.age = s.age + i + 1;
printf("SEND OK\n");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*semfifo_recv.c*/
#include "semfifo.h"
#include <stdio.h>
#include <string.h>
typedef struct stu {
char name[32];
int age;
} STU;
int main() {
shmfifo_t *fifo = semfifo_init(1234, sizeof(STU), 3);
STU s;
memset(&s, 0, sizeof(STU));
int i;
for (i=0; i<5; ++i) {
semfifo_get(fifo, &s);
printf("name = %s age = %d\n", s.name, s.age);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/*semfifo_free.c*/
#include "semfifo.h"
typedef struct stu {
char name[32];
int age;
} STU;
int main() {
shmfifo_t *fifo = semfifo_init(1234, sizeof(STU), 3);
semfifo_destroy(fifo);
}