进程通信
1、管道
1. 匿名管道
匿名管道供具有血缘关系的进程进行通信
匿名管道通信步骤:
1、一个进程分别以读写方式打开同一个文件,这样就有两个文件描述符,一个以read方式指向该文件,一个以write方式指向该文件
2、父进程fork创建一个子进程,因为子进程创建以父进程为模版,则子进程的file_struct的fd_array与父进程一样
3、让父进程read,子进程write,则关闭父进行的write,保持父进程的read打开;关闭子进程的read,保持子进程的write打开
测试1
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main() {
5 int pipe_fd[2] = {0};
6 if (pipe(pipe_fd) < 0) {
7 perror("pipe");
8 return 1;
9 }
10 printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
11 return 0;
12 }
~
测试2
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <stdlib.h>
7
8
9 int main() {
10 int pipe_fd[2] = {0};
11 if (pipe(pipe_fd) < 0) {
12 perror("pipe");
13 return 1;
14 }
15 printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
16 pid_t id = fork();
17 if (id < 0) {
18 perror("fork");
19 }
20 if (id == 0) { //write
21 //child
22 close(pipe_fd[0]);
W> 23 char *msg = "hello world, i am child process";
24 int count = 5;
25 while (count) {
26 write(pipe_fd[1], msg, strlen(msg));
27 sleep(1);
28 count--;
29 }
30 close(pipe_fd[1]);
31 exit(0);
32 }else { // read
33 //parent
34 close(pipe_fd[1]);
35 ssize_t size = 0;
36 char buf[64] = {0};
37 while(1) {
38 size = read(pipe_fd[0], buf, sizeof(buf)-1);
39 if (size > 0) {
40 buf[size] = 0;
41 printf("parent get message from child#: %s\n", buf);
42 } else if (size == 0) {
43 printf("pipe file close, child quit!\n");
44 break;
45 }
46 }
47 close(pipe_fd[0]);
48 int status = 0;
49 pid_t ret = waitpid(id, &status, 0);
50 if (ret == -1) {
51 perror("waitpid");
52 return 1;
53 }
54 printf("child is waited, the exit code is %d\n", WEXITSTATUS(status));
55 }
56 return 0;
57 }
测试3:读端不读且关闭了read,写端会怎么样?
系统直接kill了写端,因为没人读,写就是做的无用功,系统不允许任何无用功
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
int main() {
int pipe_fd[2] = {0};
if (pipe(pipe_fd) < 0) {
perror("pipe");
return 1;
}
printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
pid_t id = fork();
if (id < 0) {
perror("fork");
}
if (id == 0) { //write
//child
close(pipe_fd[0]);
char *msg = "hello world, i am child process";
int count = 5;
while (count) {
write(pipe_fd[1], msg, strlen(msg));
sleep(1);
count--;
}
close(pipe_fd[1]);
exit(0);
}else { // read
//parent
close(pipe_fd[1]);
ssize_t size = 0;
char buf[64] = {0};
while(1) {
size = read(pipe_fd[0], buf, sizeof(buf)-1);
if (size > 0) {
buf[size] = 0;
printf("parent get message from child#: %s\n", buf);
} else if (size == 0) {
printf("pipe file close, child quit!\n");
break;
}else{
break;
}
close(pipe_fd[0]);
}
//close(pipe_fd[0]);
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if (ret == -1) {
perror("waitpid");
return 1;
}
printf("child is waited, the sig code is %d, the exit code is %d\n", (status & 0x7F), WEXITSTATUS(status));
}
return 0;
}
管道的生命周期是随着进程的生命周期的,因为一个进程关闭了,则该进程打开的文件也会关闭
2. 命名管道
用于没有血缘关系的两个进程之间的通信
mkfifo
:用于创建一个命名管道的命令,mkfifo filename
可以只在命令行创建一个名为filename的命名管道文件。
但是,向filename写数据时,我们的数据并不会写到磁盘上,只会写到struct file结构体的缓冲区里。(普通文件需要将数据刷新到磁盘来持久化存储)
测试1:在创建了myfifo的命名管道文件后
在bash1里:
while :; do echo "hello world"; sleep 1; done > myfifo
在bash2里:
cat < myfifo
可以看到“hello world”在bash2中被打印出来
测试2:代码级命名管道通信
int mkfifo(const char *filename, mode_t mode)
成功返回0,失败返回-1
创建一个servre,一个client,client发消息,server接收到消息
//server.c
1 #include "stdio.h"
2 #include <unistd.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8
9 #define FIFO "./fifo"
10
11 int main() {
12 int ret = mkfifo(FIFO, 0644);
13 if (ret < 0) {
14 perror("mkfifo");
15 }
16 int fd = open(FIFO, O_RDONLY);
17 if (fd < 0) {
18 perror("open");
19 return 1;
20 }
21
22 char buf[64] = {0};
23
24 while (1) {
25 ssize_t s = read(fd, buf, sizeof(buf)-1);
26 if (s > 0) {
27 buf[s] = 0;
28 printf("parent get message from client#: %s", buf);
29 }else if(s == 0) {
30 printf("client quit\n");
31 break;
32 }else {
33 break;
34 }
35 }
36 close(fd);
37 return 0;
38 }
//client.c
1 #include "stdio.h"
2 #include <unistd.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8
9 #define FIFO "./fifo"
10
11 int main() {
12 int fd = open(FIFO, O_WRONLY);
13 if (fd < 0) {
14 perror("open");
15 return 1;
16 }
17
18 char buf[128];
19
20 while (1) {
21 printf("please enter# ");
22 fflush(stdout);
23 buf[0] = 0;
24 ssize_t s = read(0, buf, sizeof(buf)-1);
25 if (s > 0) {
26 buf[s] = 0;
27 write(fd, buf, strlen(buf));
28 }else if(s == 0) {
29 break;
30 }else {
31 break;
32 }
33 }
34 close(fd);
35 return 0;
36 }
2、共享内存
不需要read/write等接口读和写,因为已经映射到内存了
共享内存:在物理内存上申请的一段空间,由OS将其分别通过两个进程的页表映射到各自的虚拟内存中(进程双方就看到了同一份资源),然后将地址返回给用户,就可以通过这段空间进行通信。
系统会对所有的共享内存进行管理:先描述再组织
1. 创建共享内存(man shmget
)
shmflg后面也可以跟权限:0644,一样的用|
分隔
grep -ER ‘IPC_CREAT’ /usr/include/:查看IPC_CREAT
可以看到其定义在vim /usr/include/bits/ipc.h中
2. 获取唯一性key(man get
)
只要两个进程ftok参数填一样的,那个获得的返回值key也是一样的,也就能指示同一个共享内存(管理共享内存的struct中存在标识它自己的key字段)
3. 删除共享内存(man shmctl
)
在*ctl中可以查看描述其对应资源的结构体(共享内存/消息队列/信号量)
第一个参数为共享内存标识码,第二个参数设置为IPC_RMID
表示删除共享内存,最后一个暂时设置为NULL
4. 将进程关联到共享内存/去关联共享内存(man shmat
)
第二个参数和第三个参数暂时设为NULL
和0
// 用于不断查看共享内存信息
while :; do ipcs -m | head -3 && ipcs -m | grep "xupeng";echo "###########"; sleep 1; done
5. 获取共享内存中的内容
(因为已经通过shmat获得到了虚拟内存的地址,直接像打印buffer也就是数组中的内容一样打印)
6. 把内容写入共享内存
共享内存生命周期随OS
共享内存不提供任何同步互斥,彼此独立
共享内存通信是最快的;没有拷贝到文件中的操作,没有用户态内核态的转换
shim是用户层操作的概念
key是系统表示唯一性
grep -ER ‘struct ipc_perm’ /usr/include/ :查找ipc_perm
3、消息队列
1. 获取消息队列(man msgget
)
参数与shmget
一样
2. 删除消息队列(man msgctl
)
信号的前缀是sem,semget/semctl…
发送消息时的结构体中的类型,可以指定两个进程进行通信,只有发送类型和接收类型一样的才能进行通信(type=0表示任何进程都可以接收这个消息)。
//client.c
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/ipc.h"
#include "sys/msg.h"
#define SIZE 100
struct Buf{
long type;
char text[SIZE];
};
int main() {
key_t key = ftok("/home/xupeng", 10);
if (key < 0) {
perror("ftok");
return 2;
}
int msgid = msgget(key, IPC_CREAT | 0644);
if (msgid < 0) {
perror("msgid");
return 2;
}
struct Buf bf;
bzero(&bf, sizeof(bf));
bf.type = 1;
bf.text[0] = 0;
strcpy(bf.text, "hello world");
msgsnd(msgid, &bf, strlen(bf.text), 1);
return 0;
}
//server.c
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/ipc.h"
#include "sys/msg.h"
#define SIZE 100
struct Buf{
long type;
char text[SIZE];
};
int main() {
key_t key = ftok("/home/xupeng", 10);
if (key < 0) {
perror("ftok");
return 2;
}
int msgid = msgget(key, IPC_CREAT);
if (msgid < 0) {
perror("msgid");
return 2;
}
struct Buf bf;
size_t size = msgrcv(msgid, &bf, SIZE, 1, 0);
printf("msgrcv return : %d", size);
bf.text[size] = 0;
printf("i get meg from client : %s\n", bf.text);
return 0;
}