进程间通信--管道

一、为什么要有进程间通信(目的)

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

二、怎样做到进程间通信?

无论是发送数据,还是共享数据或协同、控制

进程间通信的本质:是让不同的进程看到同一份资源

为了不破坏进程的独立性,这份资源不属于任何一个进程,而是由OS提供的一块特定形式的内存空间。

进程(用户)利用该空间进行通信,本质上就是访问OS。该空间的建立、释放,底层设计和接口都由OS来完成。

而管道是一种基于文件级别(不与磁盘交互)的通信方式。

三、匿名管道

1、匿名管道的原理/如何建立通信

如图所示,父子进程(有血缘关系的进程)通过继承体系,继承Task_struct的文件描述符表

父进程以WR方式打开该文件,引用计数为2,fork创建子进程,引用计数变为4.

思考:通信时产生的  访问冲突,临界资源竞争

OS在设计上为了简化整个过程,采用了单向通信的模式,即一个读,另一个写。

为实现双向通信,可以再创建一个管道,让通信方向相反即可。

2、打开管道的系统调用接口pipe

输出型参数  pipefd[0]  pipefd[1]分别为读写的fd。

例子:子进程sleep(1),父一直read

#define N 2
#define NUM 128
void Writer(int wfd)
{
    string str = "I am a child ";
    pid_t self = getpid();
    int number=0;

    //充当缓冲区
    char buffer[NUM];
    while(true)
    {
        //字符串清空,只是为了提醒阅读代码的人,把这个数组当成字符串了
        buffer[0]=0;

        snprintf(buffer,sizeof(buffer),"msg:%s--pid:%d--num:%d",str.c_str(),self,number++);
        write(wfd,buffer,strlen(buffer));//不用+1,系统层面不用加\0
        sleep(1);

    }
}
void Reader(int rfd)
{
    char buffer[NUM];
    while(true)
    {
        buffer[0]=0;
        ssize_t n = read(rfd,buffer,sizeof(buffer));
        if(n>0)
        {
            //读取之后按字符串处理
            buffer[n]='\0';
            printf("father get msg:%s father_pid:%d\n",buffer,getpid());
            // cout << "father get a message[" << getpid() << "]# " << buffer << endl;
        }
        else if(n==0)
        {
            cout<<"father read file done";
        }
        else break;
    }
}
// child--w   father--r
//IPC code
int main()
{
    int pipefd[N] = {0};
    int n = pipe(pipefd);
    if (n < 0)
        return 1;

    // cout<<"pipefd[0]:"<<pipefd[0]<<"  "<<"pipefd[1]:"<<pipefd[1]<<endl;

    pid_t id = fork();
    if (id < 0)
        return 2;
    //先形成单向信道
    if (id == 0)
    {
        // child  关掉3
        // 向4中写入
        close(pipefd[0]);
        Writer(pipefd[1]);

        close(pipefd[1]);
        exit(0);
    }

    // father关掉4
    // 从3中读取
    close(pipefd[1]);
    Reader(pipefd[0]);
    
    int rid = waitpid(id,NULL,0);
    if(rid<0)return 3;
    close(pipefd[0]);
    return 0;
}

3、管道的特点

进程退出,文件都关闭了,管道作为内存级文件也会被OS自动释放

4、管道4种情况

1、如果read完了管道内的所有数据,如果写端没有继续写入数据,那么读取端就只能继续等待。

w:1s写一次   r:读完后,也要等1s

2、如果写端把管道写满了就不能继续写入了。

w:短时间写多次直至写满,等待读取   r:5s读一次,一次读整个缓冲区大小

tips:管道大小

64kb 65536b

读写atomic原子性问题。一次读写的数据作为一个整体的单位 PIPE_BUF为4kb

例如hello world是一体的,不会只读一部分hello

3、如果我关闭了写入端,读取完了管道内的数据,继续读就会返回0,表示读取到了文件的结尾。

子进程退出后,父进程一直read(不阻塞),返回0表示读到文件结尾(多次调用read,每次都返回0,表明读到了文件结尾)

4、写端一直写入,但是把读端关闭,操作系统会直接杀死一直在写入的进程,并且关闭管道,操作系统会通过信号来终止进程13)SIGPIPE

读5s后关闭读端,立即发送13信号杀进程

四、管道应用--进程池

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值