精华内容
下载资源
问答
  • 引言: 我们都知道,进程运行时是具有...1.匿名管道pipe(有“亲情”关系(多用于父子进程)的进程进行通信) 2.命名管道(实现不相关进程之间的通信) System V IPC System V 消息队列 System V 共享内存 System V...

    引言:

    我们都知道,进程运行时是具有独立性的,要让两个进程进行通信是一件很困难的事情。因此两个进程通信的前提条件是,需要让两个进程看到同一份资源(物理内存)。

    进程通信分类

    管道:

    • 1.匿名管道pipe(有“亲情”关系(多用于父子进程)的进程进行通信)
    • 2.命名管道(实现不相关进程之间的通信)

    System V IPC

    • System V 消息队列
    • System V 共享内存
    • System V 信号量

    POSIX IPC

    • 消息队列
    • 共享内存
    • 信号量
    • 互斥量
    • 条件变量
    • 读写锁

    管道

    • pipe函数:
    头文件: #include<unistd.h>
    原型:int pipe(int fd[2])
    参数:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
    成功,返回0。错误返回错误码
    
    • 工作原理
      • 父进程读写端一起打开文件(管道)
      • 用fork创建子进程(子进程拷贝父进程代码,一样由读写段打开文件)
      • 父子进程依照需求,分别关闭读端/写端。
      • 让我们看看代码是如何实现匿名管道的:
    	int main()
    {
        int fd[2] = {0};
        pipe(fd);
    
        pid_t id = fork();
        if(id == 0)//子进程写
        {
            close(fd[0]);//关闭读操作符
            char buf[] = "I'm child";
            while(1)
            {
                write(fd[1], buf, strlen(buf));
                sleep(1);
            }
        }
        else//父进程读
        {
            close(fd[1]);//关闭写操作符
            char ret[1024];
            while(1)
            {
                ssize_t s = read(fd[0], ret, sizeof(ret) - 1);
                if(s < 0)
                {
                    printf("printf error");
                    break;
                }
                else if(s > 0)
                {
                    ret[s] = 0;
                    printf("parent get child : %s\n", ret);
                }
                else
                {
                    printf("parent get child : %s\n", ret);
                    break;
                }
            }
        }
        return 0;
    }
    
    

    结果:我们可以看到父进程读到了子进程中的信息。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    管道读写的规则

    关于临界关系的一些概念:

    • 1.临界资源:多进程共享的内存资源(管道)
    • 2.临界区:访问临界资源的代码
    • 3.互斥:任何一个时刻只能由一个人访问临界区
    • 4.饥饿问题:一个排在R队列中的进程很久都无法获得资源
    • 5.同步:在保证临界资源安全的前提条件下(通常为互斥),让多进程访问临界资源具有一定的顺序性。功能:协同进程步调,避免饥饿问题
    • 6.原子性:对于临界资源,要么访问完毕,要么不访问。

    管道读写会发生的四种情况:

    • 1.读端不读,读端关闭(写端到/0处,出异常)
    • 2.写端不写,写端关闭(写端到/0处,出异常)
    • 3.写端一直写,读端一直不读(阻塞)
    • 4.读端一直读,写端一直不写(阻塞)

    让我们看看这四种情况:

    • 1.子进程一直在写,父进程不读
    #include<unistd.h>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    
    int main()
    {
        int fd[2] = {0};
        pipe(fd);
    
        pid_t id = fork();
        if(id == 0)//子进程写
        {
            close(fd[0]);
            int count = 0;
            char buf[] = "I'm child";
            while(1)
            {
                write(fd[1], buf, strlen(buf));
                printf("%d\n", count += strlen(buf));
            }
        }
        else//父进程不读
        {
            close(fd[1]);
            waitpid(id, NULL, 0);
    
        }
        return 0;
    }
    
    

    结果:进程运行到一定程度,停止(被阻塞)
    在这里插入图片描述
    原因:这个进程被阻塞,不能被调度,不能被运行,由R状态-》非R状态,进程被挂起,将PCB挂到等待队列中。

    • 2.写端不仅不写,写端写完毕后将描述符关闭。
    #include<unistd.h>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    
    int main()
    {
        int fd[2] = {0};
        pipe(fd);
    
        pid_t id = fork();
        if(id == 0)//子进程写完后关闭
        {
            close(fd[0]);
            int count = 0;
            char buf[] = "I'm child";
            while(1)
            {
                write(fd[1], buf, strlen(buf));
                printf("%d\n", count += strlen(buf));
                close(fd[1]);
                sleep(1);//子进程一秒之后关闭
                break;
            }
        }
        else//父进程读
        {
            close(fd[1]);
            char ret[1024];
            while(1)
            {
                sleep(1);
                ssize_t s = read(fd[0], ret, sizeof(ret) - 1);
                if(s < 0)
                {
                    printf("printf error");
                    break;
                }
                else if(s > 0)
                {
                    ret[s] = 0;
                    printf("parent get child : %s\n", ret);
                }
                else
                {
                    printf("read file end!\n");
                    break;
                }
            }
            waitpid(id, NULL, 0);
        }
        return 0;
    }
    
    

    结果:子进程一秒打完,父进程读取完之后,退出。
    在这里插入图片描述

    • 3.子进程一直写,父进程直接关闭
    #include<unistd.h>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    
    int main()
    {
        int fd[2] = {0};
        pipe(fd);
    
        pid_t id = fork();
        if(id == 0)//子进程写
        {
            close(fd[0]);
            int count = 0;
            char buf[] = "I'm child";
            while(1)
            {
                write(fd[1], buf, strlen(buf));
                printf("%d\n", count += strlen(buf));
                sleep(1);
    
            }
        }
        else//父进程读
        {
            close(fd[1]);
            char ret[1024];
            while(1)
            {
                sleep(1);
                ssize_t s = read(fd[0], ret, sizeof(ret) - 1);
                if(s < 0)
                {
                    printf("printf error");
                    break;
                }
                else if(s > 0)
                {
                    ret[s] = 0;
                    printf("parent get child : %s\n", ret);
                }
                else
                {
                    printf("read file end!\n");
                    break;
                }
                break;
            }
            close(fd[0]);
            int status = 0;
            waitpid(id, &status, 0);//获取信号
            printf("sign : %d\n", status & 0x7F);//信号再第7位
        }
        return 0;
    }
    

    结果:子进程退出,父进程读取子进程的退出码
    在这里插入图片描述
    原因:子进程被终止,父进程获得子进程的状态

    匿名管道的特点:

    • 1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信
    • 2.管道提供的是半双工的服务,是单向的
    • 3.管道是面向字节流服务的(连续的,无边界)
    • 4.文件生命周期随进程,进程结束了,管道也就释放了。

    命名管道

    如果我们想在不相关的进程之间交换数据,就需要用到FIFO文件,被称为命名管道。

    如何创建匿名管道

    • 1.通过mkfifo指令
      ```c
    • 2.通过mkfifo函数实现
      函数原型: int mkfifo(const char* filename, mode_t mode)
      参数:管道命名,设置权限。
      
      • 实现:
    #include<stdio.h>
    #include<unistd.h>
    
    int main()
    {
        mkfifo("ALA", 0644);
        return 0;
    }
    

    结果:创建了一个管道文件
    在这里插入图片描述

    命名管道与匿名管道的区别:

    • 创造方式不同:命名管道由mkfifo函数创建,匿名管道由pipe函数创建
    • 打开方式:命名管道通过open打开,匿名管道通过read打开
    • 适用范围不同:命名管道可以实现不想管进程间的通信,匿名管道只能实现有亲缘关系进程间的关系。

    命名管道打开的规则:

    • 1.如果当前操作是为读打开命名管道的:
      • O_NONBLOCK disable:阻塞直到有相应进程为写打开该命名管道(FIFO)
      • O_NONBLOCK enaable: 立即返回成功
    • 2.如果当前操作是为写打开FIFO时
      • O_NONBLOCK disable:阻塞直到有相应进程为读打开该命名管道(FIFO)
      • O_NONBLOCK enaable: 立即返回失败,错误码为ENXIO

    实现服务端和客户端的通信(单向)

    server(服务器端)

    #include<stdio.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<sys/stat.h>
    int main()
    {
        umask(0);
        mkfifo("ALA", 0644);//创建命名管道
        int op = open("ALA", O_RDONLY);//打开匿名管道
        char buf[1024];
        while(1)
        {
            printf("wait ....");
            ssize_t s = read(op, buf, sizeof(buf) - 1);//从管道中读数据
            if(s > 0)
            {
                buf[s] = 0;
                printf("server read : %s", buf);
            }
            else if(s == 0)
            {
                printf("client quit!\n");
                break;
            }
            else
            {
                printf("read error!\n");
                break;
            }
        }
        close(op);
        return 0;
    }
    

    client(客户端)

    #include<stdio.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<stdlib.h>
    #include<fcntl.h>
    
    int main()
    {
        int op = open("ALA", O_WRONLY);//打开命名管道
        char buf[1024];
        while(1)
        {
            printf("Please Enter : ");
            fflush(stdout);
            ssize_t s = read(0, buf, sizeof(buf) - 1);//从标准输入(键盘中读取数据)
            if(s > 0)
            {
                buf[s] = 0;
                write(op, buf, strlen(buf));//读完后,再写入管道中
            }
            else if(s == 0)
            {
                printf("client quit!\n");
                break;
            }
            else
            {
                printf("read error!\n");
                break;
            }
        }
        close(op);
        return 0;
    }
    

    结果:客户端从标准输入(键盘中)读取数据,放入命名管道中。客户端再从命名管道中读取数据,通过Printf打印到电脑屏幕上。
    在这里插入图片描述

    展开全文
  • windows进程通信可以通过很多方式实现,具体可见...本文章主要介绍如何通过命名管道实现跨进程通信。 1. 管道的初认识 管道分为两种,一种是匿名管道,一种是命名管道。两者都可以进行进程间...

    windows进程通信可以通过很多方式实现,具体可见https://blog.csdn.net/Quellaaa/article/details/83781316。其实进程通信的本质是让两个或多个进程能够看到同一块共同的资源(这块资源一般都是由内存提供)。

    本文章主要介绍如何通过命名管道实现跨进程的通信。

    1. 管道的初认识

    管道分为两种,一种是匿名管道,一种是命名管道。两者都可以进行进程间的通信,但匿名管道有局限性,它只能在本机上使用,而不能跨网络使用。只能用于具有血缘关系的进程间通信,通常用于父子进程建通信 。但是命名管道就不一样了,它弥补了匿名管道的局限性。

    简单介绍管道的几个函数:创建、连接和释放函数。

    *服务端

    ·创建管道

    要进行两个进行间的通信,必须要有一个进程先创建一个命名的管道,调用CreateNamedPipe即可创建一个命名的管道,其声明如下:

    HANDLE CreateNamedPipe(
    LPCTSTR lpName, // 管道名称,形式必须为\\.\pipe\pipeName
    DWORD dwOpenMode, // 打开管道的模式
    DWORD dwPipeMode, // 管道的模式,传输数据的形式
    DWORD nMaxInstances, // 最大连接客户端的个数
    DWORD nOutBufferSize, // 输出缓冲区的大小
    DWORD nInBufferSize, // 输入缓冲区的大小
    DWORD nDefaultTimeOut, // 默认的超时时间
    LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全属性,一般为NULL
    )

    参数 lpName  为一个字符串,其格式必须为 \\.\pipe\pipeName ,其中圆点 ”.” 表示的是本地机器

    如果想要与远程的服务器建立连接,那么这个圆点位置处应指定这个远程服务器的名称

    而其中的 “pipe” 这个是个固定的字符串,也就是不能改变的

    最后的 “pipename” 则代表的是我将要创建的命名管道的名称了

    ·连接管道

    服务端(创建管道的进程称为服务端)等待客户端连接请求。我们可以通过一个OVERLAPPED这个结构,该结构里有一个event事件,当有客户端进行连接时,事件对象就变成有信号。有了事件之后,我们就可以调用ConnectNamedPipe来等待一个客户端的连接,其声明如下:

    BOOL  WINAPI  ConnectNamedPipe(
    HANDLE hNamedPipe, // 命名管道对象
    LPOVERLAPPED lpOverlapped // OVERLAPPED结构
    );
    

    *客户端

    ·判断是否有可以利用的命名管道

    BOOL WaitNamedPipe(
    LPCTSTR lpNamedPipeName, // 管道名称,形式必须为\\.\pipe\pipeName
    DWORD nTimeOut // 超时时间,给NULL为默认的超时时间
    );
    

    参数 nTimeOut 用来指定超时间隔

    NMPWAIT_USE_DEFAULT_WAIT

    超时间隔即为服务器端创建该命名管道时指定的超时间隔。

    NMPWAIT_USE_DEFAULT_WAIT 一直等待,直到出现一个可用的命名管道的实例。

    通过调用该函数可以用来判断是否有可以利用的命名管道,该函数会一直等到,直到等待的时间间隔已过,或者指定的命名管道的实例可以用来连接了,也就是说该管道的服务器进程有正在等待被连接的的 ConnectNamedPipe 操作。 

    ·连接管道

    HANDLE CreateFile(
        PCTSTR  pszName,            //文件设备名,此处是命名管道名字
        DWORD   dwDesiredAccess,    //读取方式
        DWORD   dwShareMode,        //共享模式
        PSECURITY_ATTRIBUTES    psa,    //安全属性
        DWORD   dwCreationgDisposition, //
        DWORD   dwFlagsAndAttributes,
        HANDLE  hFileTemplate);
    // 该函数具体解释看https://blog.csdn.net/bxsec/article/details/76566011

    客户端进程调用 WaitNamedPipe 函数来测试指定名称的管道实例可用后,调用CreateFile 函数连接到一个正在等待连接的命名管道上,在这里客户端需要指定将要连接的命名管道的名称,当 CreateFile 成功返回后,客户进程就得到了一个指向已经建立连接的命名管道实例的句柄,到这里,服务器进程的 ConnectNamedPipe 也就完成了其建立连接的任务。

    客户端连接之后,两者之间就可以 进行通信了,通信的操作跟我们的文件操作是一样的,通过ReadFile和WriteFile来进行读和写。

    BOOL ReadFile(
        HANDLE hFile,                   //文件的句柄
        LPVOID lpBuffer,                //用于保存读入数据的一个缓冲区
        DWORD nNumberOfBytesToRead,     //要读入的字节数
        LPDWORD lpNumberOfBytesRead,    //指向实际读取字节数的指针
        LPOVERLAPPED lpOverlapped
        //如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。
        //该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
    );
    BOOL WriteFile(
    HANDLE  hFile,//文件句柄
    LPCVOID lpBuffer,//数据缓存区指针
    DWORD   nNumberOfBytesToWrite,//你要写的字节数
    LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针
    LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针
    );
    

     ·释放管道

    通信完后,我们可以调用DisconnectNamedPipe来进行断开连接,其声明如下:

    BOOL DisconnectNamedPipe(
    HANDLE hNamedPipe // 命名管道对象
    )

    2. 管道的代码实现

    管道的创建、连接、打开、读写封装成一个类,方便以后使用。 当工程代码中要用到管道通信时,直接调用该类即可,但由于管道的读写通过ReadFile和WriteFile实现,并且这两个函数共有的一个参数lpBuffer类型是void*,因此该类的读写函数用模板类实现。封装后的管道通信cpp文件如下所示:

    #include "stdafx.h"
    #include "Pipe.h"
    #include <iostream>
    using std::cout;
    #include <windows.h>
    
    
    CPipe::CPipe( CString strPipeName)
    	:m_strPipeName(strPipeName)
    	,m_hNamedPipe(NULL)
    {
    }
    
    
    CPipe::~CPipe()
    {
    	if (m_hNamedPipe)
    	{
    		Close();
    	}
    }
    
    
    BOOL CPipe::Create()
    {
    	m_hNamedPipe = CreateNamedPipe(m_strPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
    		PIPE_TYPE_BYTE, 1, 1024, 1024,0 , NULL);
    
    	//检查是否创建成功
    	if (m_hNamedPipe == INVALID_HANDLE_VALUE)
    	{
    		return FALSE;
    	}
    	else
    	{
    		return TRUE;
    	}
    }
    
    
    BOOL CPipe::Connect()
    {
    	//异步IO结构
    	BOOL fConnected;
    	OVERLAPPED op;
    	ZeroMemory(&op, sizeof(OVERLAPPED));
    
    	//创建一个事件内核对象
    	op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    	//等待一个客户端进行连接
    	fConnected = ConnectNamedPipe(m_hNamedPipe, &op) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
    
    	//当有客户端进行连接时,事件变成有信号的状态
    	if (WaitForSingleObject(op.hEvent, INFINITE) == 0)
    	{
    		return TRUE;
    	}
    	else
    	{
    		return FALSE;
    	}
    }
    
    
    void CPipe::Close()
    {
    	if (m_hNamedPipe)
    	{
    		DisconnectNamedPipe(m_hNamedPipe);
    		CloseHandle(m_hNamedPipe);
    		m_hNamedPipe = NULL;
    	}
    }
    
    // 客户端的连接
    int CPipe::Open()
    {
    	if(!WaitNamedPipe(m_strPipeName, 0))
    		return -1;
    
    	m_hNamedPipe = CreateFile(m_strPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    	if ( m_hNamedPipe == INVALID_HANDLE_VALUE)
    	{
    		cout << "连接失败\n";
    		return -2;
    	}
    
    	cout<<"连接成功\n";
    	return 0;
    }
    

    参考文章链接有:https://blog.csdn.net/shufac/article/details/29941239

    展开全文
  • 如何建立主进程与子进程之间的通信管道?2. 为什么一定要将Pipe中的某些端close()? 本文参考自:python 学习笔记 - Queue & Pipes,进程间通讯 1. 如何建立主进程与子进程之间的通信管道? 在Python中很多...

    本文参考自:python 学习笔记 - Queue & Pipes,进程间通讯

    1. 如何建立主进程与子进程之间的通信管道?

    在Python中很多时候需要用到多进程并行编程,由于每个进程都拥有自己的独立内存空间,无法像线程一样通过访问全局变量来共享数据。因此,进程之间的通信比线程通信要更加的复杂。在 Multiprocessing 包中存在 Pipe 类,Pipe(管道)能够实现进程之间更高效的通信,倘若我们现在由一个主进程,主进程创建了一个子进程,那么如何通过建立主进程与子进程之间的通信呢?

    • 当主进程创建Pipe的时候,Pipe的两个Connections连接的都是主进程;
    • 当主进程创建子进程后,Connections被拷贝了一份,此时一共有2(主进程)+ 2(子进程)= 4 个Connections;
    • 随后,我们关闭主进程中的 out_connection 和子进程中的 in_connection 端口,即可建立一条主进程通往子进程的管道了;

    NOTE: in_connectionout_connection 中的 in/out 是针对 Pipe 管道来说的,in 是指数据流入Pipe的那条管道。此外,由于 Pipe 对象默认是双向的,因此下图中的箭头管道上的数据流实际上可以是双向的,既能从上往下也能从下往上。

    实现代码如下所示:

    from multiprocessing import Pipe, Process
    
    
    def son_process(x, pipe):
    
    	""" 注意,out在前,in在后 """
        _out_pipe, _in_pipe = pipe
    
        """ 关闭拷贝过来的输入端 """
        _in_pipe.close()
        while True:
            try:
                msg = _out_pipe.recv()
                print msg
            except EOFError:
                """ 当out_pipe接受不到输出的时候且输入被关闭的时候,会抛出EORFError,可以捕获并且退出子进程 """
                break
    
    
    if __name__ == '__main__':
        out_pipe, in_pipe = Pipe(True)
        son_p = Process(target=son_process, args=(100, (out_pipe, in_pipe)))
        son_p.start()
    
        """ 等pipe被拷贝后,关闭主进程的输出端,这样,创建的Pipe一端连接着主进程的输入,一端连接着子进程的输出口 """
        out_pipe.close()
        for x in range(1000):
            in_pipe.send(x)
        in_pipe.close()
        son_p.join()
        print("主进程结束")
    

    2. 为什么一定要将Pipe中的某些端close()?

    由于Pipe之间的通信时通过,in_conn.send()、out_conn.recv() 这种方式进行通信的,因此如果当某一方调用了 .recv() 函数但一直没有另外的端口使用 .send() 方法的话,recv() 函数就会阻塞住。为了避免程序阻塞,我们在明确另一个端口不会再调用 .send() 函数后可以直接将发送端口给 close(),这样以来如果接收端还在继续调用 .recv() 方法的话程序就会抛出 EOFError 的异常,示例代码如下:

    from multiprocessing import Process
    from multiprocessing import Pipe
    import os
    import time
     
    def send(out_conn):
        out_conn.send("hello")
         """ 发完一次内容后,就将子进程中的 out_conn 给 close(),注意外部(主进程中)的 out_conn 也需要被 close """
        out_conn.close() 
     
    def recv(in_conn):
        try:
            print(in_conn.recv())
            print(in_conn.recv())		# 第二次调用 .recv() 会抛出 EOFError 异常,因为 out_conn 已经被关闭了
        except EOFError:
            print("end")
     
     
    if __name__ == "__main__":
     
        out_conn, in_conn = Pipe(True)
     
        p_send = Process(target=send, args=(out_conn,))
        p_send.start()
     
        p_recv = Process(target=recv, args=(in_conn,))
        p_recv.start()
        out_conn.close()  				# 在子进程拷贝完 Pipe Connections 之后,外部的 out_conn 也需要被关闭
        p_send.join()
        p_recv.join()
    
    展开全文
  • 最近在学习Qt,不知道Qt中是如何通过有名管道进行通信的,当然,也不知道QT是否已经封装好了相关的类,在网上查了很久都没有找到相关的资料(貌似有一个QCopChannel的类,但是也说得不是很清楚),还望走过路过的...
  • Linux中,每个进程都有各自的地址空间及自己的...也就是进程通信的本质,就是不同的进程通过看到公共资源来实现进程通信,而这里的公共资源一般由操作系统提供,不同的提供者、提供方式也决定了通信方式的不同。
      Linux中,每个进程都有各自的地址空间及自己的用户级页表,映射到物理内存的不同地方,因此进程间互不影响,即进程间相互独立。
    

    那么不同的进程要如何实现进程间通信呢?也就是进程间通信的本质,就是不同的进程通过看到公共资源来实现进程间通信,而这里的公共资源一般由操作系统提供,不同的提供者、提供方式也决定了通信方式的不同。
    进程间有五中通信方式,分别为:匿名管道、命名管道、信号量、消息队列、共享内存。这里先介绍一下匿名管道通信。
    匿名管道(pipe)
    函数:int pipe(int pipefd[2]);
    注:调用pipe函数时,首先在内核中开辟一块缓冲区用于通信,它有一个读端和一个写端,然后通过pipefd参数传出给用户进程两个文件描述符,pipefd[0]指向管道的读端,pipefd[1]指向管道的写段。在用户层面看来,打开管道就是打开了一个文件,通过read()或者write()向文件内读写数据,读写数据的实质也就是往内核缓冲区读写数据。

    返回值:成功返回0,失败返回-1。

    管道特征:
    1、管道只支持单向通信
    2、管道通信只能用于有血缘关系的通信,常用于父子通信,只用于父子进程的管道(匿名管道)
    3、管道的生命周期随进程的结束而终止
    4、管道是基于字节流通信的通信方式
    5、管道内部已经实现同步机制,能够保证数据一致性(保证数据的安全性,在写数据的时候不会被别人读,不会发生二义性)

    实现进程通信步骤:
    1、父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端
    2、父进程调用fork创建子进程,子进程也有两个文件描述符指向管道的两端
    3、父进程关闭fd[0](读端),子进程关闭fd[1](写端)

    例:

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<string.h>
    int main()
    {
        int fds[2];
        if(pipe(fds)<0)
        {
            perror("pipe");
            return 1;
        }
        pid_t id=fork();//fork创建子进程
        if(id==0)
        {
            //child
            close(fds[0]);//子进程关闭读端
            const char* child="hello father,I'm child";
            int i=0;
            while(i<10)
            {
                sleep(1);
                write(fds[1],child,strlen(child));
                i++;
            }
            exit(0);
        }
        else
        {
            //father
            close(fds[1]); //父进程关闭写端
            char buf[1024];
            while(1)
            {
                ssize_t s=read(fds[0],buf,sizeof(buf)-1);
                if(s>0)
                {
                    buf[s]=0;
                    printf("father received:%s\n",buf);
                }
            }
        }
        pid_t ret=waitpid(id,NULL,0);
        if(ret>0)
        {
            printf("wait success!\n");
        }
    }

    运行结果:
    这里写图片描述
    父进程成功收到子进程写入的字符串。

    使用管道时有4种特殊情况:
    1、读端没关闭,读端没有读数据,写端在写数据,管道被写满时写会阻塞,直到有空了才写入数据返回(读端没有在读,写端一直在写)

    代码验证:
    这里写图片描述

    运行结果:
    这里写图片描述
    父进程执行3次读操作后不再读取数据,而子进程一直在在写,管道写满时发生阻塞。

    2、指向管道的进程描述符的写端没关闭,持有管道写端的进程没有向管道写数据,此时有进程从管道读端读数据,管道剩余数据读取后,再次read会阻塞,知道有数据可读才读取数据返回(读方一直在读,写方没有在写)

    代码验证:
    这里写图片描述
    运行结果:
    这里写图片描述
    3、管道读端的文件描述符都关闭了,此时向管道的写端写,进程会收到信号SIGPIPE,通常导致进程异常终止
    代码验证:
    这里写图片描述
    运行结果:
    这里写图片描述
    父进程的读端read3次后关闭读端,此时子进程依然在write,进程异常终止。
    4、写方前期一直在写,某一时刻停止,并关闭写端,读方一直读,读到读完管道内所有数据管道结束,read返回0值,表示读到文件结尾
    代码验证:
    这里写图片描述
    运行结果:
    这里写图片描述

    展开全文
  • 进程通信--进程管道

    千次阅读 2014-09-19 23:06:30
    进程通信的几种常用方式:管道、命名管道、信号、信号量、共享内存、消息队列、套接字。...信号( sinal ):一个进程通过信号通知其他进程某事件已经发生,其他进程的反应如何及何时反应他都不管。信号量( message q
  • 进程通信(1)管道

    2018-10-31 14:35:42
    进程之间通信的方式有很多种,主要包括 管道 命名管道 信号 消息队列 共享内存 信号量 套接字 其中,管道是最早的一种进程通信...下面通过几个例子来看一下管道如何使用。 1. 函数原型 #include &amp;lt;un...
  • 进程通信值pipe管道

    2017-08-07 15:24:11
    1. 基本概念 管道是一种把两个进程之间的标准输入和标准输出连接起来的机制。...下图显示了管道如何将两个进程通过内核连接起来的状态。 管道是半双工的,只能由一端发一端接收,因此如果要实现全双工通信,必须要
  • 进程通信管道

    2015-04-12 09:05:56
    管道有什么样的特点呢? 1,管道传递数据是单向性的,又称...先简单说一下,两个进程进行读写数据是通过创建管道时系统设置的文件描述符进行的,文件描述符就是告诉这个进程要操作哪个文件。 每次能传输多少数据呢?
  •  其功能包括通过网络进行全双工通信和多个服务器实例;基于消息的通信;以及客户端模拟,这使得连接进程可在远程服务器上使用其自己的权限集。 下面的示例演示如何使用 NamedPipeServerStream 类创建命名管道...
  • 如何:使用命名管道通过网络在进程之间进行通信 命名管道提供的功能比匿名管道多。 其功能包括通过网络进行全双工通信和多个服务器实例;基于消息的通信;以及客户端模拟,这使得连接进程可在远程服务器上使用其...
  • 由于进程之间在执行时,是完全独立执行的,所有,进程间如果需要进行通信,就需要一块共享的区域,我们可以通过创建一个多个进程共享的区域,将所需要共享的数据放在这个共享区当中,当进程间需要通信的时候,写数据...
  • 匿名管道提供的功能比命名...不能使用匿名管道通过网络进行通信。 示例 下面的示例演示使用匿名管道将字符串从父进程发送到子进程的方式。 此示例使用 Out 的 PipeDirection 值在父进程中创建一个 ...
  • 文章目录概述进程通信方式管道管道的读写特性 概述 进程通信主要讲的是操作系统为进程间能够进行通信所提供的几种方式。 实际上,两个进程之间是无法直接进行通信的,操作系统为每个进程分配了虚拟地址空间,...
  •  对于没有亲缘关系的进程如何通信?本文来聊一聊命名管道FIFO. 一、概念  命名管道FIFO,提供一个路径名与之关联,以文件形式存储于文件系统中.  一个进程以r方式打开,另一个程序以w方式打开,即可在两个进程之间...
  • 无名管道通信原理如何操作无名管道无名管道的API无名管道特点为什么无名管道只能用于亲缘进程之间通信?父子进程单向通信SIGPIPE信号父子进程双向通信什么时候合适使用无名管道 无名管道通信原理 内核会开辟一个...
  • 其功能包括通过网络进行全双工通信和多个服务器实例;基于消息的通信;以及客户端模拟,这使得连接进程可在远程服务器上使用其自己的权限集。 下面的示例演示如何使用 NamedPipeClientStream 类创建命名管道。...
  • 首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络...
  • linux中使用匿名管道实现进程通信

    千次阅读 2016-09-02 19:18:25
    管道是最基本的IPC机制,由pipe函数创建。在调用pipe函数时,会在内存中创建一个缓冲区,称为管道。这个管道有两个端,一个读端和一个写端。通过filedes参数传给用户程序的两个文件描述...进程通信如何通过匿名管道
  • 第十五章 进程通信 这里先添加之前学习对管道的认识... 如何创建管道 + 如何将标准输入和输出通过管道连接起来  系统调用pipe也使用最低可用文件描述符   pipedemo1.c展示了在一个进程如何使用pipe(玩
  • 学习在 Linux 中进程如何与其他进程进行同步的。 -- Marty Kalin(作者) 本篇是 Linux 下 进程通信 (IPC)系列的第二篇文章。 第一篇文章 聚焦于通过共享文件和共享内存段这样的共享存储来进行 IPC。这篇...

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 315
精华内容 126
关键字:

进程如何通过管道通信