本文大致介绍linux几种基本单机通信方式,并附测试实现代码,有问题可随时找我或及时留言
通信概述
linux进程间通信是通过在内核空间定义对象通信 (基于文件IO的思想)
进程存在于用户空间中,两个在用户空间的进程不可直接进行通信,必须通过内核空间,进程A通过系统调用函数在内核空间创建一个对象,来达到与进程B的通信
一般分为单机模式(一个Linux),socket(两个linux内核),本篇博客主要介绍在单机模式下的6种进程间的通信方式
单机模式下进程通信的大致分类:
定义对象方式 | 通信方式 |
---|---|
管道通信 | 无名管道 |
有名管道 | |
信号通信 | 信号的发送、接收、处理 |
IPC | 共享内存 |
消息队列 | |
信号灯 |
管道通信
无名管道
无名管道在文件系统中不存在文件节点(在内核下)
由单向顺序队列实现
特点
- 读完删除
- 读阻塞、写阻塞
- 无名管道只能实现父子进程
具体实现
int pipe(int pipefd[2]) //函数名称
包含头文件 #include <unistd.h>
参数: pipefd[0]代表读 pipefd[1]代表写
返回值: 管道创建成功则返回0,出错返回-1
测试代码
int main() //子写父读
6 {
7 int i = 0;
8 int fd[2] = {0};
9 int ret;
10 pid_t pid;
11 char process_inter[128] = {0};
12 ret = pipe(fd);
13 if(ret < 0)
14 {
15 printf("creat failure");
16 exit(1);
17 }
18
19 pid = fork();
20 if( pid > 0) //父进程
21 {
22 read(fd[0], &process_inter, sizeof(process_inter)); //if pipe NULL sleep
23 printf("%s\n", process_inter);
24 for(i = 0; i < 5; i++)
25 {
26 printf("this is father process i = %d\n", i);
27 usleep(100);
28 }
29 }
30 if(pid == 0) //子进程
31 {
32 for(i = 0; i < 5; i++)
33 {
34 printf("this is child process i=%d\n", i);
35 usleep(100);
36 }
37 scanf("%s", process_inter);
38 sleep(2);
39 write(fd[1], &process_inter, sizeof(process_inter));
40 }
41 close(fd[0]);
42 close(fd[1]);
43 return 0;
有名管道
有名管道在文件系统中有文件节点
特点
- 读完删除
- 读阻塞、写阻塞
- 有名管道可实现无血缘关系的进程间通信
具体实现
int mkfifo (const char* filename, mode_t mode) //函数名称
包含头文件
#include <sys/types.h>
#include <sys/stat.h>
参数: const char*filename : 创建的管道文件(p)名称
mode_t mode 文件权限,与遮罩码umask有关
返回值: 管道创建成功则返回0,出错返回-1
***注意:只有再用open打开管道文件时,才在内核空间生成管道缓存
实现函数
int main () //文件1 先运行,创建管道文件且写入
11 {
12 int i = 0;
13 int ret;
14 int fd;
15 char buf[128] = {0};
16 if(mkfifo("./myfifo", 0777) < 0)
17 {
18 perror("mkfifo");
19 exit(1);
20 }
21 printf("mkfifo create success!!\n");
22 fd = open("./myfifo", O_WRONLY);
23 if(fd == -1)
24 {
25 perror("open");
26 exit(1);
27 }
28
29 for (i = 0; i < 5; i++)
30 {
31 printf("this is first process i=%d\n", i);
32 }
33 printf("请输入:\n");
34 while(1)
35 {
36 scanf("%s",buf);
37 write(fd, buf, sizeof(buf));
38 if(strcmp(buf,"bye") == 0)
39 {
40 break;
41 }
42 memset(buf, 0, sizeof(buf));//清空buf内容
43 }
44 close(fd);
45 unlink("./myfifo"); //删除管道文件
46 return 0;
47 }
11 int main () //文件2 读管道文件
12 {
13 int i = 0;
14 int fd = open("./myfifo", O_RDONLY);
15 char buf[128] = {0};
16 if(fd == -1)
17 {
18 perror("open2");
19 exit(2);
20 }
21 for (i = 0; i < 5; i++)
22 {
23 printf("this is second process i=%d\n", i);
24 }
25 while(1)
26 {
27 sleep(1);
28 read(fd, buf, sizeof(buf));
29 if(strcmp(buf,"bye") == 0)
30 {
31 break;
32 }
33 printf("%s\n",buf);
34 }
35 close(fd);
36 unlink("./myfifo");
37 return 0;
38 }
测试结果
信号通信
进程A在内核空间创建信号对象,通过外部输入或者调用系统函数,发送信号来给另一个不相干的进程B执行相应操作
整个通信过程可分为: 信号的发送、信号的接收、信号的处理
常见的信号一般有64种,可通过kill -l
来查找
20 SIGTSTP //ctrl+z 进程切换到后台可通过fg命令来显示后台隐藏程序
2 SIGINT //ctrl+c 关闭进程
信号通信的函数
信号的发送: int kill(pid_t pid, int sig); //进程pid号, 具体信号
int raise(int sig); //给自己进程发消息
unsigned int alarm(unsigned int seconds); //alarm函数在经过seconds秒后給当前进程发送SIGALRM信号(默认终止)
信号的接收: int pause(void); //挂起进程,知道当前进程收到信号
unsigned int sleep(unsigned int seconds); //休眠seconds秒
int usleep(useconds_t usec); //休眠usec微秒
信号的处理: sighandler_t signal(int signum, sighandler_t handler); //信号,忽略或||处理函数
exit类似于 信号函数signal(17, ----);
*****signal(14,SIG_IGN); //可忽略输入的14信号
具体实现
void myfun(int signum)
{
int i;
i= 0;
while(i < 5)
{
printf("process signal = %d\n", signum);
sleep(1);
i++;
}
// return 0;
}
int main()
{
int i;
i = 0;
signal(2,myfun);
printf("alarm before\n");
alarm(9);
signal(14,SIG_IGN);
printf("alarm after\n");
while(i < 20)
{
i++;
printf("process %d\n", i);
sleep(1);
}
return 0;
}
IPC通信
IPC对象:共享内存、消息队列、信号灯
共享内存
创建共享内存、映射、操作、销毁
共享内存的函数
1 //无血缘关系的A进程共享内存通信 A进程
12 struct mybuf //定义缓存内部的结构体
13 {
14 int pid;
15 char buf[124];
16 };
17 void myfun(int signum)
18 {
19 return;
20 }
21
22 int main()
23 {
24 int shmid;
25 int key;
26 pid_t pid;
27 struct mybuf *p;
28 key = ftok("./a.c", 'b');
29 if(key == -1)
30 {
31 perror("key");
32 exit(1);
33 }
34 printf("key create success = %x\n", key);
35 shmid = shmget(key, 128, IPC_CREAT | 0777);
36 if(shmid == -1)
37 {
38 perror("shmid");
39 exit(1);
40 }
41 printf("creative shmid success shmid = %d\n", shmid);
42
43 signal(SIGUSR2, myfun);
44 // system("ipcs -m"); //查看ipc对象shmid号
45 p = (struct mybuf*)shmat (shmid,NULL,0);
46 if(p == NULL)
47 {
48 perror("fathere shmat failure");
49 exit(1);
50 }
51
52 //get client pid
53 p->pid = getpid(); //把服务端的pid写入共享内存
54 pause();
55 pid = p->pid;
56 while(1)
57 {
58 //write share menory
59 printf("A:\n");
60 fgets(p->buf, 128, stdin); //键盘输入
61 //printf("p.buf-----------==%s",p->buf);
62 //if(strcmp(p->buf, "bye") == 0)
63 // break;
64 kill(pid, SIGUSR1);
65 pause(); //等待子进程读
66
67 }
68
69 shmdt(p); //用户空间的映射缓存销毁
70 shmctl(shmid, IPC_RMID, NULL); //内核空间的缓存清除
71 // system("ipcs -m");
72 // system("ipcrm -m shmid"); //删除ipc对象shmid号
73 return 0;
74 }
//无血缘关系的B进程共享内存通信 B进程
11 struct mybuf
12 {
13 int pid;
14 char buf[124];
15 };
16 void myfun(int signum)
17 {
18 return;
19 }
20
21 int main()
22 {
23 int shmid;
24 int key;
25 struct mybuf *p;
26 pid_t pid;
27 key = ftok("./a.c", 'b');
28 if(key == -1)
29 {
30 perror("key");
31 exit(1);
32 }
33 printf("key create success = %x\n", key);
34 shmid = shmget(key, 128, IPC_CREAT | 0777);
35 if(shmid == -1)
36 {
37 perror("shmid");
38 exit(1);
39 }
40 printf("creative shmid success shmid = %d\n", shmid);
41
42 signal(SIGUSR1, myfun);
43 // system("ipcs -m"); //查看ipc对象shmid号
44 p = (struct mybuf*)shmat (shmid,NULL,0);
45 if(p == NULL)
46 {
47 perror("fathere shmat failure");
48 exit(1);
49 }
50 pid = p->pid; //read Apid
51 p->pid = getpid(); //write Bpid
52 kill(pid, SIGUSR2);
53
54 while(1)
55 {
56 pause(); //等待Awrite
57 printf("B:%s", p->buf);
58 // if(strcmp(p->buf, "bye") == 0)
59 // {
60 // printf("-------------");
61 // break;
62 // }
63 kill(pid,SIGUSR2); //读完发信号给A可以开始写
64 }
65
66 shmdt(p); //用户空间的映射缓存销毁
67 shmctl(shmid, IPC_RMID, NULL); //内核空间的缓存清除
68 // system("ipcs -m");
69 // system("ipcrm -m shmid"); //删除ipc对象shmid号
70 return 0;
71 }
运行结果
消息队列
相当于链式队列
测试代码 可实现双向传输
//A进程,创建消息队列,write 100 read 200
struct msgbuf
10 {
11 long type;
12 char voltage[124];
13 char ID[4];
14 };
15
16 int main ()
17 {
18 struct msgbuf sendbuf, recvbuf;
19 int readret;
20 int msgpid;
21 int key;
22 pid_t pid;
23 key = ftok("./a.c",'a');
24 if(key == -1)
25 {
26 perror("key");
27 exit(1);
28 }
29 msgpid = msgget(key, IPC_CREAT | 0777);
30 if(msgpid == -1)
31 {
32 perror("msgget:");
33 exit(1);
34 }
35 printf("msgpid=%d\n",msgpid);
36 pid = fork();
37 if(pid > 0) //父进程 write 100
38 {
39 sendbuf.type = 100;
40 //write
41 while(1)
42 {
43 memset(sendbuf.voltage, 0, 124);
44 printf("write:");
45 fgets(sendbuf.voltage,124,stdin);
46 msgsnd(msgpid,(void *)&sendbuf, strlen(sendbuf.voltage), 0);
47 // if(strcmp(sendbuf.voltage, "bye") == 0)
48 // {
49 // break;
50 // }
51 }
52
53 }
54 if (pid == 0) //read 200
55 {
56 while(1)
57 {
58 memset(recvbuf.voltage, 0, 124);
59 msgrcv(msgpid,(void *)&recvbuf, 124, 200, 0);
60 printf("read:%s", recvbuf.voltage);
61 }
62
63 }
64 msgctl(msgpid, IPC_RMID, NULL);
65 system("ipcs -q");
66 return 0;
67 }
//B进程,创建消息队列,write 200 read 100
9 struct msgbuf
10 {
11 long type;
12 char voltage[124];
13 char ID[4];
14 };
15
16 int main ()
17 {
18 struct msgbuf recvbuf, sendbuf;
19 int readret;
20 int msgpid;
21 int key;
22 pid_t pid;
23 key = ftok("./a.c",'a');
24 if(key == -1)
25 {
26 perror("key");
27 exit(1);
28 }
29 msgpid = msgget(key, IPC_CREAT | 0777);
30 if(msgpid == -1)
31 {
32 perror("msgget:");
33 exit(1);
34 }
35 printf("msgpid=%d\n",msgpid);
36 pid = fork();
37 if(pid > 0) //父进程 read 100
38 {
39 //read 100
40 while(1)
41 {
42 memset(recvbuf.voltage, 0, 124);
43 msgrcv(msgpid,(void *)&recvbuf, 124, 100, 0);
44 printf("read:%s", recvbuf.voltage);
45 // if(recvbuf.voltage == "bye")
46 // {
47 // break;
48 // }
49 }
50 }
51 else if(pid == 0) //子进程 write 200
52 {
53 sendbuf.type = 200;
54 //write
55 while(1)
56 {
57 memset(sendbuf.voltage, 0, 124);
58 printf("write:\n");
59 fgets(sendbuf.voltage,124,stdin);
60 msgsnd(msgpid,(void *)&sendbuf, strlen(sendbuf.voltage), 0);
61 // if(strcmp(sendbuf.voltage, "bye") == 0)
62 // {
63 // break;
64 // }
65 }
66
67 recvbuf.type = 100;
68 }
69 msgctl(msgpid, IPC_RMID, NULL);
70 system("ipcs -q");
71 return 0;
72 }
运行结果
信号灯
测试代码 信号量的集合(a进程创建对象,b先运行)
// 创建信号灯对象,等B进程先执行
#include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/ipc.h>
5 #include <sys/sem.h>
6
7 union semun
8 {
9 int val;
10 struct semid_ds *buf;
11 unsigned short *array;
12 struct seminfo *_buf;
13 };
14
15 int semid;
16 union semun mysemun;
17 struct sembuf mysembuf;
18 int main()
19 {
20 int i;
21 int key;
22 key = ftok("./1.c", 'a');
23 if(key == -1)
24 {
25 perror("create key failure\n");
26 exit(1);
27 }
28 semid = semget(key, 3, 0777 | IPC_CREAT);
29 if(semid == -1)
30 {
31 perror("semid");
32 exit(1);
33 }
34
35 system("ipcs -s");
36 mysemun.val = 0;
37 semctl(semid, 0, SETVAL, mysemun);
38
39 mysembuf.sem_num = 0;
40 mysembuf.sem_flg = 0;
41 //p
42 mysembuf.sem_op = -1;
43 semop(semid, &mysembuf, 1);
44
45 for(i = 0; i < 9; i++)
46 {
47 usleep(100);
48 printf("this is a fun i = %d\n", i);
49 }
50 while(1);
51 return 0;
52 }
1 //a初始化,先运行,运行
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/ipc.h>
6 #include <sys/sem.h>
7
8 union semun
9 {
10 int val;
11 struct semid_ds *buf;
12 unsigned short *array;
13 struct seminfo *_buf;
14 };
15
16 int semid;
17 union semun mysemun;
18 struct sembuf mysembuf;
19 int main()
20 {
21 int i;
22 int key;
23 key = ftok("./1.c", 'a');
24 if(key == -1)
25 {
26 perror("create key failure\n");
27 exit(1);
28 }
29 semid = semget(key, 3, 0777 | IPC_CREAT);
30 if(semid == -1)
31 {
32 perror("semid");
33 exit(1);
34 }
35
36 system("ipcs -s");
37 // mysemun.val = 0;
38 // semctl(semid, 0, SETVAL, mysemun);
39
40 mysembuf.sem_num = 0;
41 mysembuf.sem_flg = 0;
42 for(i = 0; i < 9; i++)
43 {
44 usleep(100);
45 printf("this is b fun i = %d\n", i);
46 }
47 //V
48 mysembuf.sem_op = 1;
49 semop(semid, &mysembuf, 1);
50 while(1);
51 return 0;
52 }
运行结果