0%

OS-3 实验记录

This is os_3 code

Pipe community

Q

实现一个管道通信程序:
由父进程创建一个管道,然后再创建 3 个子进程,并由这三个子进程利用管道与父进程之间进行通信:子进程发送信息,父进程等三个子进程全部发完消息后再接收信息。通信的具体内容可根据自己的需要随意设计,要求能试验阻塞型读写过程中的各种情况,测试管道的默认大小,并且要实现进程间对管道的互斥访问。运行程序,观察各种情况下,进程实际读写的字节数以及进程阻塞唤醒的情况

在这个实验开始前 我们先清楚几个函数:

sem_init

用于创建信号量,其原型如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
该函数初始化由 sem 指向的信号对象,并给它一个初始的整数值 value
pshared 控制信号量的类型,值为 0 代表该信号量用于多线程间的同步,值如果大于 0 表示可以共享,用于多个相关进程间的同步

sem_wait

sem_wait 是一个阻塞的函数,测试所指定信号量的值,它的操作是原子的。若 sem value > 0,则该信号量值减去 1 并立即返回。若sem value = 0,则阻塞直到 sem value > 0,此时立即减去 1,然后返回

sem_post

把指定的信号量 sem 的值加 1,唤醒正在等待该信号量的任意线程。

sem_getvalue

获取信号量 sem 的当前值,把该值保存在 sval,若有 1 个或者多个线程正在调用 sem_wait 阻塞在该信号量上,该函数返回阻塞在该信号量上进程或线程个数。

sem_destroy

顾名思义,该函数用于对用完的信号量的清理,成功返回1,失败返回0

pipe.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
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>

#define SEM_W "sem_write"
#define SEM_R "sem_read"
#define ENABLE_NUM 3

void write_to_pipe(int fd[2], int sid);
int read_from_pipe(int fd[2]);
void P(sem_t *sem_ptr); // 原语操作P(S) = down() = wait() S=S-1; 若S<0, 进程暂停执行,放入信号量的等待队列
void V(sem_t *sem_ptr); // 原语操作V(S) = up() = signal() S=S+1; 若S<=0,唤醒等待队列中的一个进程
void catch_INT(int sig); // 捕获 Ctrl_c 退出父进程
void destroy_sem(); //销毁

int main(void)
{
int fd[2];
pid_t pid; // Process ID Type 宏定义 unsigined int
int ret = -1, child_num = ENABLE_NUM, enable_val = -1, sid = 0, i;
sem_t *write_psx, *read_psx; // sem_t 信号量数据类型 unsigined int
write_psx = sem_open(SEM_W, O_CREAT, 0666, 1); // 创建并初始化有名信号量 sem_open(*name, oflag, .../*mode_t mode,unsinged int value) //sem_open在不同进程中共享,oflag数据标志,O_CREAT创建,若文件不存在则创建他,mode访问权限,value初始化为value值
//name通常是文件系统中的某个文件。基于内存的信号量不需要指定名称
read_psx = sem_open(SEM_R, O_CREAT, 0666, 0);

if (pipe(fd) < 0) { // pipe()创建管道,无名管道创建,成功返回 0,失败返回 -1
printf("pipe error\n");
exit(1);
}
for (i = 0; i < child_num; i++) {
pid = fork(); //创建三个子进程
sid++;
if (pid < 0) {
printf("fork error\n");
destroy_sem(); //创建子进程失败,销毁信号
exit(0);
} else if (pid == 0) { // 创建成功 child process 跳出 for 循环
break;
}
}
if (pid == 0) { // only children process could reach here
P(write_psx); //write-1=0,进程执行
write_to_pipe(fd, sid); //若成功则返回写入的字节数,若出错则返回-1

V(read_psx); //read+1=1
V(write_psx); //write+1=1
exit(0);

} else { // only father process could reach here
signal(SIGINT, catch_INT);

while (1)
{
P(read_psx); //read=1
P(read_psx); //read=2
P(read_psx); //read=3
//读三个子进程
read_from_pipe(fd); //若成功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1
}
}
return 0;
}

void write_to_pipe(int fd[2], int sid)
{
int value;
char buf[10];
close(fd[0]);
memset(buf, sid + '0', sizeof(char)*10);
printf("[*] Children process %d write %d bytes data\n", sid, (int)sizeof(buf));
write(fd[1], buf, sizeof(buf)); // 若成功则返回写入的字节数,若出错则返回-1
}

int read_from_pipe(int fd[2])
{
int ret;
char buf[1024];
close(fd[1]);
memset(buf, '\0', sizeof(char)*1024);
ret = read(fd[0], buf, 1024); // 若成功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1
while (ret > 0) {
printf("[*] Father process read %d bytes data: %s\n", ret, buf);
memset(buf, '\0', 1024);
ret = read(fd[0], buf, 1024);
}
return ret;
}

void P(sem_t *sem_ptr)
{
sem_wait(sem_ptr); // 信号量 sem<=0 则阻塞当前线程,sem>0 解除阻塞后将sem的值减一,表明公共资源经使用后减少
}

void V(sem_t *sem_ptr)
{
sem_post(sem_ptr); // 增加信号量的值,有线程阻塞信号量时,会使其中一个线程不再阻塞
}

void catch_INT(int sig)
{
printf("[*] Catch ctrl_c and exit\n");
destroy_sem();
exit(0);
}

void destroy_sem()
{
sem_unlink(SEM_W); // 从系统中删除有名信号量
sem_unlink(SEM_R);
}

pipe_max.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
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

void write_to_pipe(int fd[2]);

int main(void)
{
int fd[2];
pid_t pid;
int ret = -1;

if (pipe(fd) < 0) {
printf("[*] Error: pipe() return error\n");
exit(1);
}

pid = fork();
if (pid < 0) {
printf("[*] Error: fork() return error\n");
exit(1);
} else if (pid == 0) {
write_to_pipe(fd);
} else {
wait(NULL);
}

}

void write_to_pipe(int fd[2])
{
int count, ret;
char buffer[1024];
close(fd[0]);
memset(buffer, '*', 1024);

ret = write(fd[1], buffer, sizeof(buffer));
count = 1;
printf("[*] Pipe could write: %d bytes data\n", count * 1024);
while (1) {
ret = write(fd[1], buffer, sizeof(buffer)); // 管道被写满,则阻塞当前进程,等待数据被取走
if(ret == -1){
break; // 阻塞状态下无法自动跳出循环,可自行添加捕获 Ctrl_c 后跳出的代码
}
count++;
printf("[*] Pipe could write: %d bytes data\n", count * 1024);
}
}

Message queue

Q

利用 linux 的消息队列通信机制实现两个线程间的通信:
编写程序创建三个线程:sender1 线程、sender2 线程和 receive 线程,三个线程的功能描述如下:
① sender1 线程:运行函数 sender1(),它创建一个消息队列,然后,等待用户通过终端输入一串字符,将这串字符通过消息队列发送给 receiver 线程;可循环发送多个消息,直到用户输入 exit 为止,表示它不再发消息,最后向 receiver 线程发送消息 end1 ,并且等待 receiver 的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,结束程序的运行。
② sender2 线程:运行函数 sender2(),它创建一个消息队列,然后,等待用户通过终端输入一串字符,将这串字符通过消息队列发送给 receiver 线程;可循环发送多个消息,直到用户输入 exit 为止,表示它不再发消息,最后向 receiver 线程发送消息 end2 ,并且等待 receiver 的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,结束程序的运行。
③ receiver 线程运行 receive(),它通过消息队列接收来自 sender1 和 sender2 两个线程的消息,将消息显示在终端屏幕上,当收到内容为 end1 的消息时,就向 sender1 发送一个应答消息 over1 ;当收到内容为 end2 的消息时,就向 sender2 发送一个应答消息 over2 ;消息收完后删除消息队列。使用合适的信号量机制实现三个线程之间的同步与互斥。

!

pthread_join()函数原型:
int pthread_join(pthread_t thread, void **retval);

args:
pthread_t thread: 被连接线程的线程号
void **retval : 指向一个指向被连接线程的返回码的指针的指针

return:
线程连接的状态,0是成功,非0是失败

当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。

msg_queue.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define send_type 1 // 2种 消息类型
#define recv_type 2

#define send_1_to_recv 1 // 4种 消息走向
#define send_2_to_recv 2
#define recv_to_send_1 3
#define recv_to_send_2 4

#define bool int
#define false 0
#define true 1


void *send_thread_1(void *arg);
void *send_thread_2(void *arg);
void *recv_thread(void *arg);
void P(sem_t *sem_ptr); //p,v操作
void V(sem_t *sem_ptr);

sem_t send_psx, recv_psx, final_recv_1, final_recv_2; // 定义4个 信号量类型
pthread_t send_pid_1, send_pid_2, recv_pid; // pthread_t 实际为 unsigned long int
int count = 1; // 计数器,声明为全局
bool send_1_over = false; // sender1线程 是否已经结束
bool send_2_over = false; // sender2线程 是否已经结束



struct msgbuf //消息缓存
{
long mtype;
char mtext[256];
int mw2w; // message where to where 表示消息的走向
};
int msgid; //messageID

int main(void)
{
sem_init(&send_psx, 0, 1); // pshared = 0,同一进程中多线程的同步
sem_init(&recv_psx, 0, 0);
sem_init(&final_recv_1, 0, 0);
sem_init(&final_recv_2, 0, 0);

msgid = msgget(IPC_PRIVATE, 0666|IPC_CREAT); // 创建消息队列
//创建消息队列, msgget(key_t, key, int msgflg);msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。它返回一个以key命名的消息队列的标识符(非零整数),失败时返回-1.
if (msgid < 0) {
printf("[*] Error: msgget() return error\n");
exit(1);
}
pthread_create(&send_pid_1, NULL, send_thread_1, NULL); // 创建 线程,并将线程加入当前 进程
pthread_create(&send_pid_2, NULL, send_thread_2, NULL);
pthread_create(&recv_pid, NULL, recv_thread, NULL);

pthread_join(send_pid_1, NULL); // 阻塞调用 send / receive 线程,功能相反于设置守护线程的 SetDaemon()
pthread_join(send_pid_2, NULL);
pthread_join(recv_pid, NULL);

return 0;
}

void *send_thread_1(void *arg)
{
char info[256]; // 消息发送区
struct msgbuf s_msg; // 消息缓存区
s_msg.mtype = send_type;
s_msg.mw2w = send_1_to_recv;
while (1) {
P(&send_psx);
printf("[%d]\n", count);
printf("[*] Send_thread_1 send: ");
scanf("%s", info);

if ((strcmp(info, "exit") == 0) || (strcmp(info, "end1") == 0)) {
strcpy(s_msg.mtext, "end1");
msgsnd(msgid, &s_msg, sizeof(struct msgbuf), 0);
//将msgp消息写入到标识符为msqid的消息队列
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
//消息队列标识符;发送给队列的消息; 要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度; 0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
V(&recv_psx); //recv+1=0, 进程继续执行
break; //跳出循环
}
strcpy(s_msg.mtext, info);
count++;
msgsnd(msgid, &s_msg, sizeof(struct msgbuf), 0);// 追加一条消息到消息队列中
V(&recv_psx);//保证了阻塞接受

}
P(&final_recv_1); // final_recv_1 处理 send_thread_1 最后一次接受消息的问题

msgrcv(msgid, &s_msg, sizeof(struct msgbuf), recv_type, 0); // 从消息队列中读一条消息
printf("[*] Send_thread_1 receive: %s\n", s_msg.mtext);
count++;

V(&send_psx);//保证了阻塞发送

if (send_1_over && send_2_over){ // 2个 sender线程 都发送过 'end' 且收到过 'over' 后,将移除消息队列
msgctl(msgid, IPC_RMID, 0); // 移除消息队列
}
pthread_exit(NULL); // 类比进程的终止 exit()
}

void *send_thread_2(void *arg)//本函数同上 不再赘述
{
char info[256]; // 消息发送区
struct msgbuf s_msg; // 消息缓存区
s_msg.mtype = send_type;
s_msg.mw2w = send_2_to_recv;
while (1) {
P(&send_psx);

printf("[%d]\n", count);
printf("[*] Send_thread_2 send: ");
scanf("%s", info);

if ((strcmp(info, "exit") == 0) || (strcmp(info, "end2") == 0)) {
strcpy(s_msg.mtext, "end2");
msgsnd(msgid, &s_msg, sizeof(struct msgbuf), 0);
V(&recv_psx);
break;
}
strcpy(s_msg.mtext, info);
count++;
msgsnd(msgid, &s_msg, sizeof(struct msgbuf), 0);// 追加一条消息到消息队列中
V(&recv_psx);

}
P(&final_recv_2); // final_recv_2 处理 send_thread_2 最后一次接受消息的问题

count++;
msgrcv(msgid, &s_msg, sizeof(struct msgbuf), recv_type, 0); // 从消息队列中读一条消息
printf("[*] Send_thread_2 receive: %s\n", s_msg.mtext);

V(&send_psx);

if (send_1_over && send_2_over){ // 2个 sender 线程 都发送过 'end' 且收到过 'over' 后,将移除消息队列
msgctl(msgid, IPC_RMID, 0); // 移除消息队列
}
pthread_exit(NULL); // 类比进程的终止 exit()
}

void *recv_thread(void *arg)
{
struct msgbuf r_msg; // 消息缓存区
while (1) {
P(&recv_psx);
msgrcv(msgid, &r_msg, sizeof(struct msgbuf), send_type, 0);
if (r_msg.mw2w == send_1_to_recv){ // 根据 消息走向 判断来源
if (strcmp(r_msg.mtext, "end1") == 0) {
strcpy(r_msg.mtext, "over1");
r_msg.mtype = recv_type;
r_msg.mw2w = recv_to_send_1;
msgsnd(msgid, &r_msg, sizeof(struct msgbuf), 0);
printf("[*] Recv_thread receive 'end1' from Send_thread_1, return 'over1'\n");

V(&final_recv_1);
send_1_over = true;
}
else {
printf("[*] Recv_thread receive: %s from Send_thread_1\n", r_msg.mtext);
V(&send_psx);
}
}
else if (r_msg.mw2w == send_2_to_recv) { // 根据 消息走向 判断来源
if (strcmp(r_msg.mtext, "end2") == 0) {
strcpy(r_msg.mtext, "over2");
r_msg.mtype = recv_type;
r_msg.mw2w = recv_to_send_2;
msgsnd(msgid, &r_msg, sizeof(struct msgbuf), 0);
printf("[*] Recv_thread receive 'end2' from Send_thread_2, return 'over2'\n");

V(&final_recv_2);
send_2_over = true;

}
else {
printf("[*] Recv_thread receive: %s from Send_thread_2\n", r_msg.mtext);
V(&send_psx);
}
}


if (send_1_over && send_2_over){ // 2个 sender线程 都发送过 'end' 且收到过 'over' 后,将跳出循环,结束当前线程
break;
}
}
pthread_exit(NULL);
}

void P(sem_t *sem_ptr)
{
sem_wait(sem_ptr);
}

void V(sem_t *sem_ptr)
{
sem_post(sem_ptr);
}

in the end

其实我觉得这个核心要明白为啥要阻塞发送和阻塞接收,以及怎样阻塞的 这样就达到了这个实验的目的

Memory share

Q

利用 linux 的共享内存通信机制实现两个进程间的通信:
编写程序 sender,它创建一个共享内存,然后等待用户通过终端输入一串字符,并将这串字符通过共享内存发送给 receiver;最后,它等待 receiver 的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,删除共享内存,结束程序的运行。编写 receiver 程序,它通过共享内存接收来自 sender 的消息,将消息显示在终端屏幕上,然后再通过该共享内存向 sender 发送一个应答消息 over ,结束程序的运行。使用 有名信号量 或 System V 信号量实现两个进程对共享内存的互斥及同步使用。

init.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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024
#define KEY_NUM 1111

#define SEM_SEND "sem_send"
#define SEM_RECV "sem_recv"
#define SEM_FIN "sem_final"

void P(sem_t *semp);
void V(sem_t *semp);

void P(sem_t *semp)
{
sem_wait(semp);
}
void V(sem_t *semp)
{
sem_post(semp);
}

sender.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
key_t key;
int shmid;
void *shmp;

sem_t *send_psx;
sem_t *recv_psx;
sem_t *final_psx;

void init();
void destroy();
void catch_INT(int sig);
int check_value(sem_t *semtmp, int style);
int main(void)
{
int ret = -1;
char input[SHM_SIZE];
char info[SHM_SIZE];
signal(SIGINT, catch_INT);
init();

memset(input, '\0', sizeof(input));//清空输入
// send msg to receiver
while (1) {
printf("[*] Send message: ");
scanf("%s", input);
P(send_psx);
strcpy(info, (char *)shmp);
strcat(info, input);
strcpy((char *)shmp, info);
if (strcmp(input, "exit") == 0) {
ret = check_value(recv_psx, 0); //检查是否有线程在读取,保证了同时只能有一个线程/进程在 读取。如果可以,那么返回1
if (ret == 1) {
V(recv_psx);
}
break;
}
memset(input, '\0', sizeof(input));
ret = check_value(recv_psx, 0);
if (ret == 1) {
V(recv_psx);
}
V(send_psx);
}

// recv end signal
P(final_psx);
strcpy(input, (char *)shmp);
printf("[*] Recv messsge: %s\n", input);
destroy();
printf("[*] Sender end\n");
return 0;
}

void init()
{
key = KEY_NUM;
shmid = shmget(key, SHM_SIZE, 0666|IPC_CREAT); // 创建 share memory,成功则返回id (一个与key相关的标识符)
/*******
0666表示权限,跟文件的权限设置是一样的。
4 2 1 分别表示,读 写 执行,3种权限。
比如,上面的 6 = 4 + 2 ,表示 读+写。
如果是 7 = 4 + 2 + 1 ,表示 读+写+执行。

另,0666 每一位表示一种类型的权限,比如,第一个0是UID
第一个6表示拥有者的权限
第二个6表示同组权限
第三个6表示他人的权限
其实这个0666就是赋予所有用户以读写权限
而互斥则是由后面实现的
*******/
if (shmid < 0) {
printf("{*] Error: shmget() return error\n");
exit(1);
}
shmp = shmat(shmid, NULL, 0); // shared memory attach,连接到当前进程地址空间,返回指向 share memory 的指针
//shmat调用成功时返回一个指向共享内存第一个字节的指针
/********
第一个参数,shm_id是由shmget()函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。
********/

memset((char *)shmp, '\0', SHM_SIZE);
send_psx = sem_open(SEM_SEND, O_CREAT, 0666, 1); // 有名信号量,适用于不同进程间的同步互斥

recv_psx = sem_open(SEM_RECV, O_CREAT, 0666, 0);
final_psx = sem_open(SEM_FIN, O_CREAT, 0666, 0);
}
void destroy()
{
shmdt(shmp); // share memory detach attach 断开共享内存与当前进程地址空间的连接,使该共享内存对当前进程不再可用
shmctl(shmid, IPC_RMID, NULL); // 撤销 share memory
//IPC_RMID:删除共享内存段
sem_unlink(SEM_SEND);
sem_unlink(SEM_RECV);
sem_unlink(SEM_FIN);
}
int check_value(sem_t *semtmp, int style)
{
// return 0: can not release this sem
// return 1: you should release it
// style means we check value with different ways
int ret = -1, sem_vl;
ret = sem_getvalue(semtmp, &sem_vl);
if (ret == -1) {
printf("[*] Error:get sem value error\n");
destroy();
exit(0);
}

if (sem_vl == style)
return 1;
else
return 0;

}

void catch_INT(int sig)
{
printf("[*] Catch SIGINT\n");
destroy();
exit(0);
}

receiver.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
key_t key;
int shmid;
void *shmp;

sem_t *send_psx; // 信号量结构类型
sem_t *recv_psx;
sem_t *final_psx;

void init();
void close_all();
void catch_INT(int sig);

int main(void)
{
char input[SHM_SIZE];
signal(SIGINT, catch_INT);
init();
// recv msg from sender
while (1) {
P(recv_psx);
strcpy(input, (char *)shmp);
memset((char *)shmp, '\0', SHM_SIZE);//清空shmp
printf("[*] Recv message: %s\n", input);
if (strcmp(input, "exit") == 0) {
strcpy((char *)shmp, "over");//收到over跳出循环
printf("[*] Send message: over\n");
V(final_psx);
break;
}
}
close_all();
printf("[*] Receiver end\n");
return 0;
}
//下面这些函数同上
void init()
{
key = KEY_NUM;
shmid = shmget(key, SHM_SIZE, 0666|IPC_CREAT);
if (shmid < 0) {
printf("[*] Error: shmget() return error\n");
exit(1);
}
shmp = shmat(shmid, NULL, 0);
send_psx = sem_open(SEM_SEND, O_CREAT, 0666, 1);
recv_psx = sem_open(SEM_RECV, O_CREAT, 0666, 0);
final_psx = sem_open(SEM_FIN, O_CREAT, 0666, 0);
}

void close_all()
{
sem_close(send_psx);
sem_close(recv_psx);
sem_close(final_psx);
shmdt(shmp);
}

void catch_INT(int sig)
{
printf("[*] Catch SIGINT\n");
close_all();
exit(0);
}

函数check_value()的必要性?
check_value(sem_t *semtmp, int style) 可以判断是否有 线程/进程 阻塞在 sem_wait(semtmp) 上,即 check_value(recv_psx,0) 可以判断是否有线程/进程在 读取 ,若没有,才对 recv_psx 进行 V原语操作 ,释放 读取 的资源,允许 读取 。保证了同时只能有一个线程/进程在 读取 。