概念

关键

进程通信:在用户空间实现进程通信是不可能的,通过Linux内核通信
线程间通信:可以在用户空间就可以实现,可以通过全局变量通信。

方式

管道通信:无名管道、有名管道(文件系统中有名,文件系统)
信号通信:信号(通知)通信包括:信号的发送、信号的接收和信号的处理。
IPC(Inter-Process Communication)通信:共享内存、消息队列和信号灯。
以上是单机模式下的进程通信(只有一个L加ux内核)
Socket通信:存在于一个网络中两个进程之间的通信(两个Linux内核)。

通信实现的思想

基于文件IO理念

open:功能:创建或打开进程通信对象。函数形式不一样,有的是有多个函数完成。
write:功能:向进程通信对象中写入内容。函数形式可能不一样。
read:功能:从进程通信对象中读取内容。函数形式可能不一样。
close:功能:关闭或删除进程通信对象。形式可能不一样。

管道通信

原理

管道原理

管道文件是一个特殊的文件,是由队列来实现的。

无名管道

1
2
#include <unistd.h>
int pipe(int pipefd[2]);

水从高处流
The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.

  1. 管道是创建在内存中的,进程结束,空间释放,管道就不存在了;
  2. 管道中的东西,读完了就删除了;队列
  3. 如果管道中没有东西可读,则会阻塞

无名管道的创建

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
/*************************************************************************
> File Name: wmgd.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 18时59分17秒
************************************************************************/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
int fd[2];
int ret;
ret = pipe(fd);
if(ret < 0)
{
printf("create pipe failure");
return -1;
}
printf("create pip sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
return 0;
}

Linux内核在创建进程的时候会默认创建三个文件描述符,即0,1,2。所以无名管道返回的文件描述符为3,1、4

res

无名管道的读写

关键

  1. 管道是创建在内存中的,进程结束,空间释放,管道就不存在了;
  2. 管道中的东西,读完了就别除了:队列
  3. 如果管道中没有东西可读,则会阻塞。
  4. 验证写阻塞:可以计算出内核开辟的管道有多大,队列超过这么大会发生写堵塞。5456 5457.
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
/*************************************************************************
> File Name: wmgd.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 18时59分17秒
************************************************************************/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
int fd[2];
int ret;
char writebuff[]="hello Linux";
char readbuff[128]={0};
ret = pipe(fd);
if(ret < 0)
{
printf("create pipe failure");
return -1;
}
printf("create pip sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
// write
write(fd[1],writebuff,sizeof(writebuff));

//first read
read(fd[0],readbuff,128);
printf("first readbuff = %s\n",readbuff);

//second read

read(fd[0],readbuff,128);
printf("second readbuff = %s\n",readbuff);
close(fd[0]);
close(fd[1]);
return 0;
}

只能读一次,发生读堵塞

阻塞状态ps -axj

无名管道的缺点:不能实现不是父子进程(亲缘关系)之间的通信。

有名管道

由于无名管道的缺点,对无名管道进行改进:有名管道
所谓的有名,即文件系统中存在这个一样文件节点,每一个文件节点都有一个node号
而且这是一个特殊的文件类型:p管道类型

mkfifo函数

Create named pipes (FIFOs) with the given NAMEs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*************************************************************************
> File Name: ymgd.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 19时38分46秒
************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
int main()
{
int ret;
ret = mkfifo("./myfifo",0777);
if(ret<0)
{
printf("create pip failure by file. ret = %d/n", ret);
return -1;
}
printf("create sucess\n");
return 0;
}

基于文件系统的有名管道的创建

  1. 创建这个文件节点,不可以通过open函数,open函数只能创建普通文件,不能创建特殊文件(管道-mkdifo,套接字-socket,字符设备文件-mknod,块设备文件-mknod, 符号链接文件-ln-s,目录文件mkdir)
  2. 管道文件只有node号,不占磁盘块空间和套接字、字符设备文件、块设备文件一
    样。普通文件和符号链接文件及目录文件,不仅有node号,还占磁盘块空间。
  3. mkfifo用来创建管道文件的节点没有在内核中创建管道。只有通过open函数打开这个文件时才会在内核空间创建管道。

利用有名管道进行进程通信

基于上一步创建的管道节点进行通信,进程一等待五秒后向通道发送信息。

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
/*************************************************************************
> File Name: ymgd_1.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 19时57分15秒
************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
int main()
{
int fd;
char process_state=0;
int i=0;
fd = open("./myfifo",O_WRONLY);
if(fd<0)
{
printf("open pipe node failure\n");
return -1;
}
printf("open pipe node sucess\n");


for(i=0; i<5;i++)
{
printf(" this is first process i=%d\n",i);
usleep(100);
}
process_state = 1;
sleep(5);
write(fd,&process_state,1);
while(1);
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
/*************************************************************************
> File Name: ymgd_1.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 19时57分15秒
************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
int main()
{
int fd;
char process_state=0;
int i=0;
fd = open("./myfifo",O_RDONLY);
if(fd<0)
{
printf("open pipe node failure\n");
return -1;
}
printf("open pipe node sucess\n");
read(fd,&process_state,1);
while(process_state==0);
for(i=0; i<5;i++)
{
printf(" this is second process i=%d\n",i);
usleep(100);
}
while(1);
return 0;
}

ret

信号通信

信号发送

Linux中的64种信号

kill命令

raise函数

alarm函数
信号通信的框架

  1. 信号的发送(发送信号进程):kill raise alarm
  2. 信号的接收(接收信号进程):pause() sleep while(1)
  3. 信号的处理(接收信号进程):signal

kill函数的使用

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
/*************************************************************************
> File Name: kill_0.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 22时04分08秒
************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
int sig;
int pid;
if(argc<3)
{
printf("please input param\n");
return -1;
}
sig = atoi(argv[1]);
pid = atoi(argv[2]);
printf("sig=%d, pid = %d\n",sig,pid);

kill(pid,sig);
return 0;
}

ret

简单使用kill,waitpid

案例: 开始时主进程处于睡眠状态S,子进程处于暂停T. 八秒后主进程杀死子进程并利用while进入S. 但是子进程被杀死后没有回收资源所以进入僵尸状态Z.

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
/*************************************************************************
> File Name: jiangshijinchengyanshi.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 22时21分49秒
************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>

int main()
{
pid_t pid;
pid = fork();
if(pid > 0)
{
sleep(8);
if(waitpid(pid,NULL,WNOHANG) ==0) //等待进程状态,返回值为0时表示并未退出。pid为进程id,NULL为不获取进程返回值,WNOHANG为非阻塞
{
kill(pid,9);
}
//wait(NULL);//回收进程资源
while(1);
}
if(pid == 0)
{
printf("raise function before\n");
raise(SIGTSTP);//SIGSTP 暂停交互,== Ctrl+Z
printf("raise function after\n");
exit(0);
}
return 0;

}

process

使用wait函数回收资源

信号处理

signal函数

signal函数:返回一个函数指针. 含有两个参数,第一个为信号值, 第二个为一个函数指针,其中返回值为void.
默认处理信号方式为终止信号.

alarm处理信号

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
/*************************************************************************
> File Name: signal_0.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 23时56分47秒
************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>

void myfun(int signum)
{
int i;
i=0;
while(i<10)
{
printf("process sleep %d s!\n",i);
sleep(1);
i++;
}
return;
}

int main()
{
int i =0;
signal(14,myfun); // 14为SIGALRM
printf("alarm start\n");
alarm(9);//九秒后触发myfun
printf("next step\n");
while(i<20)
{
i++;
sleep(1);
printf("main do %d s\n",i);
}
return 0;
};


ret

signal信号处理

父子进程进行通信

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
/*************************************************************************
> File Name: jiangshijinchengyanshi.c
> Author: Merlynr
> Mail: lcq1013962426@gmail.com
> Created Time: 2023年01月31日 星期二 22时21分49秒
************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
void myfun(int signum)
{
int i=0;
while(i<5)
{
printf("receive signum = %d, i = %d\n",signum,i);
sleep(1);
i++;
}
}
void myfun1
int main()
{
pid_t pid;
pid = fork();
printf("PID is %d\n",pid);
if(pid > 0)
{
int i = 0;
signal(10,myfun);
while(1)
{
printf("A say %d\n",i);
sleep(1);
i++;
}
}
if(pid == 0)
{
sleep(10);
kill(getppid(),10);
sleep(3);
exit(0); //kill SIGCHILD
}
return 0;

}

ret