基本概念
操作系统为用户提供的几种进程间通信方式,让进程之间能够进行通信。
进程间通信----------多个进程之间数据的交互
Q:为什么不能直接进行数据交互?需要使用系统提供的接口来实现进程间通信?
A:进程间具有独立性,每个进程都有自己的虚拟地址空间,访问数据时是通过自己的虚拟地址访问的,一个进程将自己的某个变量的空间地址(虚拟地址)交给另一个进程,另一个进程是无法通过页表进行访问到的。
操作系统提供进程间通信方式其实就是给进程间提供一个空间的交叉点,让多个进程都可以访问的到,从而实现进程通信
操作系统提供进程间的通信方式(根据不同场景):
管道:适用于数据传输场景
共享内存:适用于数据共享场景
消息队列:适用于数据传输场景
信号量:适用于进程的协同控制(实现进程间的同步与互斥)
一、管道
基本概念
管道的特性:半双工通信(可以选择方向的单向通信—同一时间不能同时既发送又接收)
(1)单工通信:
单向通信,假设两端A,B,单工就是只能 A->B;
(2)双工通信:
双向通信,A->B & B->A 可以同时进行
(3)半双工通信:
可以选择方向的单向通信,要么 A->B,要么 B->A,俩个方向不能同时进行
管道的本质:
操作系统给进程之间提供的通信方式,本质是提供了一个空间的交叉点,都能访问
在程序中,管道其实就是内核中的一块缓冲区(一块内存)------- 由操作系统进行管理;多个进程通过访问同一块缓冲区来实现数据传输
但是在 Linux 下一切皆文件,故将管道当作文件来处理,不是传统意义上的磁盘文件,本质上是内存
操作句柄
对于空调电视机来说,句柄就是遥控器。
一个进程通过系统调用接口创建完管道后,这个系统调用接口就给进程返回一个管道的操作句柄。
匿名管道没有标识符,无法被其他进程找到,所以无法进行通信,因此只能通过创建子进程的方式来进行,子进程复制了父进程,也就复制了父进程所拥有的所有操作句柄,通过这个句柄它可以访问这个管道。
管道的分类
匿名管道
没有名字(标识符)的管道;将无法被其他进程找到
特性:只能用于具有亲缘关系的进程间通信
匿名管道类似于家族财产,只有同一个家族的人才有操作句柄能操作。
子进程复制父进程的资源,同时复制了父进程的管道信息(管道的操作句柄)
操作句柄:
文件描述符(因为管道被当作文件来进行操作)
通过文件描述符这个下标,就能够找到管道的描述信息,进而对文件进行操作
发送数据就是向管道中写入数据;接收数据就是从管道中读取数据。
程序替换会初始化虚拟地址空间,但是不会初始化文件的描述符表,因此重定向了子进程的标准输出后,进行程序替换依然有效;
程序替换之后,新程序初始化了虚拟地址空间,意味着以前保持了文件描述符的这个变量没了,因此创建管道后,将标准输入输出的描述符重定向到管道,这时候操作标准输入输出就是操作管道
在创建子进程之前先创建匿名管道,以便于子进程能够复制到父进程中的管道描述符信息
int pipe(int pipefd[2]);
功能:创建一个管道,并通过参数返回管道的俩个操作句柄
参数:pipefd - 两个整形元素的数组,内部创建管道会将描述符存储在数组中
(1) pipefd[0]: 用于从管道中读取数据
(2) pipefd[1] : 用于向管道中写入数据
但是正常情况下 读取 与 写入 不同时使用
返回值:创建失败返回 -1,成功返回 0
注意:创建匿名管道一定要在创建子进程之前,确保子进程能够复制父进程中的管道句柄信息
创建管道:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/wait.h>
4 #include<string.h>
5 #include<unistd.h>
6
7 int main()
8 {
9 int pipefd[2];
10 int ret=pipe(pipefd); //创建一个管道
11
12 if(ret<0){ //管道创建失败
13 perror("pipe error");
14 return -1;
15 }
16 //pipefd[0]读取 pipefd[1]写入
17
18 int pid1=fork();
19 if(pid1==0){
20 //创建一个子进程1
21 //pipefd[0]读取 pipefd[1]写入
22 //让子进程1写入数据,子进程2来读取
23
W> 24 char *data="好好学习Linux,否则没有工作!\n";
25 write(pipefd[1],data,strlen(data)); //向管道中写入数据
26 exit(0);
27 }
28
29
30 int pid2=fork();
31 if(pid2==0){
32 //子进程2
33 //读取数据
34 int buf[1024];
35 read(pipefd[0],buf,1023); //从管道中读取数据
W> 36 printf("收到了老大的警告: %s\n",buf);
37 exit(0);
38 }
39
40 wait(NULL); //等待任意子进程退出
41 wait(NULL); //等待任意子进程退出,只有当所有子进程都退出之后,父进程才能退出
42
43 return 0;
44 }