进程间通信

进程间通信方式

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

1586244102037

1 管道模型

1
2
3
4
5
6
7
8
> #include <unistd.h>
> /** fd[1]的输出是fd[0]的输入。
> * @return 0:成功 1:出错
> * @param fd[0] 管道的读端
> * @param fd[1] 管道的写端
> */
> int pipe (int fd[2]);
>

1.1 匿名管道( Anonymous Pipes)

只能在管道创建进程及其后代之间通信

1
$ ps -ef | grep 关键字 | awk '{print $2}' | xargs kill -9

img img

img

管道父子通信的步骤:

  1. 父进程创建管道,得到两个文件描述符指向管道两端
  2. 父进程fork出子进程,子进程也有两个文件描述符指向同一管道
  3. 父进程关闭fd[0],子进程关闭fd[1](只能单向通信)

管道兄弟通信步骤:

  1. 在上面的最后一步改为父进程关闭fd[1],子进程关闭fd[0],再fork另外一个子进程B

  2. shell保留的读取端的fd[0]也被复制到了子进程B中,只要父进程关闭fd[0],管道双方就换成了A 和B

    img
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
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc, char *argv[])
{
int fds[2];
if (pipe(fds) == -1)
perror("pipe error");

pid_t pid;
pid = fork();
if (pid == -1)
perror("fork error");

if (pid == 0){
dup2(fds[1], STDOUT_FILENO); // 将 STDOUT_FILENO 指向创建的管道文件
close(fds[1]);
close(fds[0]);
execlp("ps", "ps", "-ef", NULL);
} else {
dup2(fds[0], STDIN_FILENO); // 将 STDIN_FILENO 指向创建的管道文件
close(fds[0]);
close(fds[1]);
execlp("grep", "grep", "systemd", NULL);
}

return 0;
}

1.2 命名管道

通过管道文件名, 可以在任意进程之间通信

1
2
3
4
5
6
$ mkfifo hello
$ echo "hello world" > hello

another shell
$ cat < hello
hello world

实际上是内核里面的一串缓存

2 消息队列

信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

3 共享内存

无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;

4 信号量

常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段

5 信号

不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

6 套接字

略,使用参考ICMP重定向实验