【Linux操作系统】--进程间通信--匿名管道和命名管道

进程间通信介绍

有时候进程之间可能会存在特定的协同工作的场景!那么进程之间的协同工作,就是进程之间的通信。进程的通信就是一个进程要把自己的数据交付给另一个进程,让其进行处理。因为进程是具有独立性的,如果要进行通信,那么通信双方一定是通过某种介质来进行通信。比如我跟你通信是通过某信进行交流。所以进程间通信的介质时操作系统,操作系统要设计通信方式

因为进程是具有独立性的!并且交互数据,成本很高。一个进程是看不到另一个进程的资源,所以必须得先看到一份公共的资源,这里的资源就是一段内存,这个公共资源是属于操作系统的。

所以进程间通信的前提本质:其实是由OS参与,提供一份所有通信进程都能看到的公共资源。这段内存提供公共资源的方式可能以文件方式提供,也可能以队列方式提供,也可能提供的就是原始的内存块。这也是通信方式很多种的原因。

进程间通信目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

进程间通信发展

  • 管道
  • System V进程间通信
  • POSIX进程间通信

匿名管道

什么是管道

当创建了父子进程,因为父子进程是独立的两个进程,进程的PCB块是描述进程的控制块,其中也包括了文件操作符数组。当我们要向文件写入信息,要传入一个文件操作符,通过这个文件操作符fd,来查找对应的文件。就比如下图的文件属性结构体它的文件操作符是3,如果要向3这个文案金写入内容,那么就通过PCB里面的文件指针找到文件操作符数组,再通过文件操作符数组最硬的3找到对应的文件属性。

因为进程具有独立性,所以父子进程是两个独立的进程。因为是独立的进程,所以子进程要将父进程的所有内容拷贝一份,其中也包括文件操作符数组。但是文件属性结构体并不属于进程,而属于操作系统的,因为操作系统要向磁盘读取文件,就要把文件的信息都读入操作系统,这些属性放在一个结构体中,struct file和进程只是有关系,但并不属于进程。所以文件属性结构体不用复制两份。

当对文件进行写操作时,比如说write(3,"hello world");找到文件结构体后,在文件属性中找到对应的写操作,将进程缓冲区的“hello world”写到OS缓冲区中,然后找到磁盘驱动对应的写操作。进而将OS缓冲区的“hello world”写进磁盘文件中。

当父子进程都是指向同一块文件结构体,这就是操作系统参与,让不同的进程看到统一个内容,操作系统就起到了媒介作用。当父进程将“hello world”写进OS的内核缓冲区中,不刷新磁盘,不调用底层磁盘驱动的读写方法,将“hello world保留在缓冲区中,那么另一个进程子进程就可以通过它的文件描述符找到对应的同一个struct file,找到同一个缓冲区的数据。此时就做到了将一个进程的数据交给下一个进程,这就叫做让不同进程看到同一份资源,这种基于文件的通信方式叫做管道。

【Linux操作系统】--进程间通信--匿名管道和命名管道_第1张图片

站在文件描述符角度-深度理解管道

【Linux操作系统】--进程间通信--匿名管道和命名管道_第2张图片

 第一步:父进程创建管道。创建管道用到的系统结构是pipe,它的参数是一个输出型参数:我们想通过这个参数读取到打开的两个fd。通过传递一个数组,数组会发生降维,传递一个数组就是传递一个指针,最后pipe将数据写入数组中,我们就能拿到对应的文件描述符fd。

pipe的返回值,文档告诉我们,如果返回0创建fd成功,如果失败返回-1.

【Linux操作系统】--进程间通信--匿名管道和命名管道_第3张图片

 第二步:父进程fork出子进程。分别以读方式和写方式打开一个管道,实际上这里的管道可以看作内核的缓冲区。在同一个进程中,文件可以被打开两次。就像管道,即可以读文件,又可以写文件,但我们通常只读或只写,不会两个都做。

管道是一个只能单向通信的通信信道,想要双向通信就建立两个管道。

第三步:如果子进程写,父进程读。子进程将读文件fd[0]关闭,父进程将写文件fd[1]关闭。

匿名管道

建立信道

第一步:创建管道

因为管道是对两个进程进行通信,所以数组有两个描述符。我们将它初始化为0。当管道创建完毕,我们可以知道这两个描述符是3和4,因为0,1,2是stdin,stdout,stderr,所以第一个最小空文件描述符是3,从3开始。

那么创建好的数组后,pipefd[0]=3,pipefd[1]=4。规定0下标代表读,1下标代表写。

#include 
#include 

int main()
{
  int pipefd[2]={0};
  if(pipe(pipefd)!=0)//如果返回的不是0,创建失败,打印错误信息,然后返回错误码
  {
    perror("pipe error!");
    return 1;
  }

  printf("pipe[0]:%d\n",pipefd[0]);
  printf("pipe[1]:%d\n",pipefd[1]);
  return 0;
}

#写一个makefile文件
[wjy@VM-24-9-centos pipe]$ cat makefile
pipe_process:pipe_process.c
	gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
	rm -f pipe_process

#运行结果
[wjy@VM-24-9-centos pipe]$ make
gcc -o pipe_process pipe_process.c -std=c99
[wjy@VM-24-9-centos pipe]$ ./pipe_process 
pipe[0]:3
pipe[1]:4

第二步:创建父子进程,并让父进程读,子进程写。

创建了父子进程,那么就创建好了双向通信的信道。当我子进程写,关闭读pipefd[0]文件;当父进程读,关闭写pipefd[1]文件,这样才建立好管道,可以进行通信了。

[wjy@VM-24-9-centos pipe]$ cat pipe_process.c
#include 
#include //exit的头文件
#include 

int main()
{
  int pipefd[2]={0};
  if(pipe(pipefd)!=0)//如果返回的不是0,创建失败,打印错误信息,然后返回错误码
  {

你可能感兴趣的:(Linux,linux,运维,服务器)