顾得泉:个人主页
个人专栏:《Linux操作系统》 《C++从入门到精通》 《LeedCode刷题》
键盘敲烂,年薪百万!
一、进程间通信
1.进程间通信的目的
1.数据传输:一个进程需要把他的数据传给另外一个进程。
2.资源共享:多个进程之间共享同样的资源。
3.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
4.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
2.进程间通信发展
最早的进程间通信方式是使用操作系统提供的共享内存和消息队列机制。共享内存允许多个进程将同一段内存映射到它们自己的地址空间,从而实现数据共享;消息队列则允许进程通过发送和接收消息来进行通信。
随着网络技术的发展,进程间通信也逐渐向网络通信发展。网络通信使得不同计算机上的进程能够进行跨机器的通信和协作。常见的网络通信方式包括套接字(socket)、远程过程调用(RPC)和消息传递接口(MPI)等。
近年来,随着多核处理器和分布式计算的普及,进程间通信的需求也越来越高。为了提高通信效率和方便编程,各种新的进程间通信技术应运而生。例如,管道(Pipe)和FIFO允许两个相关进程之间进行通信;信号量(Semaphore)和互斥锁(Mutex)等同步机制可以确保多个进程之间的正确协作;套接字和远程过程调用等技术允许分布在不同计算机上的进程进行通信。
3.进程间通信分类
管道
1.匿名管道pipe
2.命名管道
System V IPC
1.System V 消息队列
2.System V 共享内存
3.System V 信号量
POSIX IPC
1.消息列队
2.共享内存
3.信号量
4.互斥量
5.条件变量
6.读写锁
二、管道
1.管道简介
在Linux中,管道(pipeline)是一种机制,用于将一个命令的输出作为另一个命令的输入。通过使用管道,可以将多个命令组合在一起,以便实现更复杂的操作。
在命令行中,使用竖线符号( | )来表示管道。
2.匿名管道
#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码
实例代码:
从键盘读取数据,写入管道,读取管道,写到屏幕
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( void )
{
int fds[2];
char buf[100];
int len;
if ( pipe(fds) == -1 )
perror("make pipe"),exit(1);
// read from stdin
while ( fgets(buf, 100, stdin) )
{
len = strlen(buf);
// write into pipe
if ( write(fds[1], buf, len) != len )
{
perror("write to pipe");
break;
}
memset(buf, 0x00, sizeof(buf));
// read from pipe
if ( (len=read(fds[0], buf, 100)) == -1 )
{
perror("read from pipe");
break;
}
// write to stdout
if ( write(1, buf, len) != len )
{
perror("write to stdout");
break;
}
}
}
3.用fork来共享管道原理
fork系统调用会在父进程中创建一个新的子进程,这个子进程与父进程几乎完全相同,包括代码段、数据段和堆栈段。这意味着子进程可以继承父进程的打开文件描述符等资源。
在使用fork创建子进程后,父进程和子进程可以通过管道来进行通信。管道是一种特殊的文件,它能够连接一个进程的输出和另一个进程的输入。在Linux中,管道可以通过pipe系统调用来创建。
当父进程创建管道后,它可以使用fork来创建子进程。接下来,父进程可以关闭管道的读端,子进程可以关闭管道的写端。这样,父进程就可以将数据写入管道的写端,子进程可以从管道的读端读取数据。
具体操作如下:
1.父进程使用pipe系统调用创建一个管道。
2.父进程使用fork系统调用创建子进程。
3.子进程关闭管道的写端。
4.父进程关闭管道的读端。
5.父进程将要传输的数据写入管道的写端。
6.子进程从管道的读端读取数据。
父进程可以通过管道将数据传输给子进程,子进程可以通过管道接收父进程传输的数据。这样,父进程和子进程就可以进行进程间通信了。
需要注意的是,管道的容量是有限的,如果管道被写满后继续写入数据,写入操作会被阻塞,直到有数据被读取出来为止。同样,如果管道为空时读取数据,读取操作也会被阻塞,直到有数据被写入为止。因此,在使用共享管道进行进程间通信时,需要合理控制数据的读写操作,以避免阻塞问题的发生。
三、深度理解管道
1.文件描述符角度
1.父进程创建管道
2.父进程fork出子进程
3.父进程关闭fd[0],子进程关闭fd[1]
2.内核角度-内核本质
管道的实现机制:内核中的管道是通过一对文件描述符来实现的。一个文件描述符用于读取管道数据,另一个文件描述符用于写入管道数据。内核维护着一个缓冲区,用于存放从写入端写入的数据,并且从读取端读取的数据会从缓冲区中删除。
管道的数据传输:管道是一种半双工的通信方式,数据只能单向流动。当写入端写入数据时,数据会被放入缓冲区中,直到被读取端读取。如果缓冲区已满,则写入操作会被阻塞,直到缓冲区有空闲空间。同样,如果缓冲区为空,则读取操作会被阻塞,直到缓冲区有数据。
管道的进程同步:当多个进程使用管道通信时,可能会出现进程同步的问题。例如,如果写入端连续写入数据,而读取端没有及时读取,那么缓冲区可能会溢出。为了解决这个问题,内核提供了一些同步机制,例如管道的阻塞和非阻塞模式、管道的读取和写入端都关闭时的特殊情况等。
管道的文件描述符操作:在内核中,文件描述符是一种操作系统提供给用户空间的接口。通过文件描述符的操作,可以实现对管道的读取和写入。对于读取操作,可以使用read系统调用从管道中读取数据;对于写入操作,可以使用write系统调用向管道中写入数据。
结语:关于理解Linux系统下的进程间通信与管道基础的分享到这里就结束了,没有进行展示的操作大家可以自行练习,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~