cpp-doc-信号量

概念

信号量,是一种特殊的变量。
只能对信号量执行P操作和V操作,P操作、V操作都是原子操作,即其在执行时,不会被中断。

  • P操作

    如果信号量的值 > 0, 则把该信号量减1

    如果信号量的值 ==0, 则挂起该进程。

  • V操作

    如果有进程因该信号量而被挂起,则恢复该进程运行

    如果没有进程因该信号量而挂起,则把该信号量加1

system v信号量

用于进程间通信

相关函数

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
// 用来创建和访问一个消息队列
// @key:信号集的名称
// @nsems:信号集中信号量的个数
// @semflg:由九个权限标志构成,他们的用法和创建文件时使用的mode模式标志是一样的
// return:成功返回一个非负整数,即该信号集的标识码;失败返回-1
int semget(key_t key, int nsems, int semflg)

// 用于控制信号量集
// @semdi:由semget返回的信号集标识码
// @semnum:信号集中信号量的序号
// @cmd:将要采取的动作(有三个可取值)
// SETVAL:设置信号量集中的信号量的计数值
// GETVAL:获取信号量集中的信号量的计数值
// IPC_STAT:把semid_ds结构中的数据设置为信号集的当前关联值
// IPC_SET:在进程有足够权限的前提下,把信号集中的当前关联值设置为semid_ds结构中给出的值
// IPC_RMID:删除信号集
// @...:不同命令不同
// return:成功返回0;失败返回-1
int semctl(int semid, int semnum, int cmd, ...)


struct sembuf
{
short sem_num; // 信号量的编号
short sem_op; // 信号量移除PV操作时加减的数值,一般只会用到两个值,一个是"-1",也就是P操作,等待信号量变得可用;另一个是"+1",也就是我们的V操作,发出信号量已经变得可用
short sem_flg; // IPC_NOWAIT或SEM_UNDO
}

// 用来创建和访问一个信号量集
// @semid:是该信号量的标识码,也就是semget函数返回值
// @sops:是个指向一个结构数值的指针
// @nsops:信号量的个数
// return:成功返回0;失败返回-1
int semdop(int semid, struct sembuf* sops, unsigned nsops)

多进程互斥

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
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <unistd.h>
#include <math.h>

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) */
};


#define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

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;
int ret;
ret = semctl(semid, 0, SETVAL, su);
if(ret == -1)
ERR_EXIT("sem_setval");

return 0;
}

int sem_getval(int semid)
{
int ret;
ret = semctl(semid, 0, GETVAL, 0);
if(ret == -1)
ERR_EXIT("sem_getval");

return ret;
}

int sem_d(int semid)
{
int ret;
ret = semctl(semid, 0, IPC_RMID, 0);
if(ret == -1)
ERR_EXIT("sem_d");

return 0;
}

int sem_p(int semid)
{
struct sembuf sb = {0, -1, 0};
int ret;
ret = semop(semid, &sb, 1);
if(ret == -1)
ERR_EXIT("semop");

return ret;
}

int sem_v(int semid)
{
struct sembuf sb = {0, 1, 0};
int ret;
ret = semop(semid, &sb, 1);
if(ret == -1)
ERR_EXIT("semop");

return ret;
}

int semid;
void print(const char *str)
{
int pause_time;
srand(getpid());
int i;
for(int i = 0; i < 10; ++i)
{
sem_p(semid);
printf("%s\n", str);
fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%s\n", str);
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("parent");
wait(NULL);
sem_d(semid);
} else {
// 子进程
print("child");
}


return 0;
}

Posix 信号量

用于线程间通信

相关函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 对信号量进行初始化
// @sem:指向被初始化的信号量
// @pshared
// 0:表示该信号量是该进程内使用的“局部信号量”, 不再被其它进程共享。
// 非0:该信号量可被其他进程共享,Linux不支持这种信号量
// @value:信号量的初值。>= 0
// 返回值:成功,返回0;失败, 返回错误码
int sem_init (sem_t *sem, int pshared, unsigned int value);

// 信号量的P操作
// @sem:信号量
// 返回值:成功,返回0;失败, 返回错误码
int sem_wait (sem_t *sem);

// 信号量的V操作
// @sem:信号量
// 返回值:成功,返回0;失败, 返回错误码
int sem_post (sem_t *sem);

// 信号量的删除
// @sem:信号量
// 返回值:成功,返回0;失败, 返回错误码
int sem_destroy (sem_t *sem);

信号量处理多线程互斥

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
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define BUFF_SIZE 80

char buff[BUFF_SIZE];
sem_t sem;

static void* str_thread_handle(void *arg)
{
while(1) {
//P(sem)
if (sem_wait(&sem) != 0) {
printf("sem_wait failed!\n");
exit(1);
}

printf("string is: %slen=%d\n", buff, strlen(buff));
if (strncmp(buff, "end", 3) == 0) {
break;
}
}

return NULL;
}

int main(void)
{
int ret;
pthread_t str_thread;
void *thread_return;


ret = sem_init(&sem, 0, 0);
if (ret != 0) {
printf("sem_init failed!\n");
exit(1);
}

ret = pthread_create(&str_thread, 0, str_thread_handle, 0);
if (ret != 0) {
printf("pthread_create failed!\n");
exit(1);
}

while (1) {
fgets(buff, sizeof(buff), stdin);

//V(sem)
if (sem_post(&sem) != 0) {
printf("sem_post failed!\n");
exit(1);
}

if (strncmp(buff, "end", 3) == 0) {
break;
}
}

ret = pthread_join(str_thread, &thread_return);
if (ret != 0) {
printf("pthread_join failed!\n");
exit(1);
}

ret = sem_destroy(&sem);
if (ret != 0) {
printf("sem_destroy failed!\n");
exit(1);
}

return 0;
}

编译命令 g++ main1.cpp -o main1 -D_REENTRANT -lpthread