• 进程间通信:无名管道+有名管道


    进程间通信(Inter-Process Communication)

    为什么需要进程间通信

    当程序是多进程协同工作时,进程间基本都会涉及到数据共享

    如何实现进程间数据的共享?

    使用进程间通信来实现数据共享

    进程间有时需要传递消息
    --但是进程在系统有自己的地址空间,os不允许其他进程随意进入该地址空间
    --所以内核提供了一种机制:既可以保证通信,又可以保证进程安全
      --这个机制就叫:进程间通信
    
    • 1
    • 2
    • 3
    • 4

    本章内容和意义

    Linux OS所提供的进程间通信机制:

    • 信号
    • 管道(有名、无名)
    • 消息队列
    • 共享内存
    • 信号量

    C程序在windows和Linux下想要实现“进程控制”和“进程间通信”的话:

    必须调用系统API或者说该系统自己特有C库(不是标准c库)

    java提供“进程控制”和“进程间通信”的库接口

    +学习意义
    1.对开发者来说:了解进程间的互斥、进程的同步、资源保护等这些概念很必要
    2.有助于理解其它 OS 的进程间通信(Linux 类似)
    3.加深对进程的理解,帮助后续对比学习c线程
    
    +学习重点
    理解进程通信的实现原理,通过代码将进程间通信理解清楚
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    进程间通信其实就是:多进程相互通信,共享信息和交换信息的方法

    为什么进程空间完全独立

    ​ 进程空间完全独立,会使得进程间共享数据很困难,但是为什么OS还是要将进程空间,弄成是完全独立的呢?

    什么是进程空间

    其实就是程序运行的内存空间

    不过OS有提供虚拟内存时,我们所说的进程空间,指的都是虚拟内存空间

    虚拟内存是基于物理内存实现的,说虚拟内存时,本身就包含了虚拟内存所对应的底层物理内存空间

    虚拟地址与物理地址
    • 虚拟内存有自己的虚拟地址(就是一些编号)

      虚拟地址是因为这些地址并不对应真实物理内存

    • CPU取指运行时,PC是通过虚拟地址来取指的,但是程序指令肯定是放在了真实的物理内存上的,所以虚拟地址最终会被转换为物理地址,然后到真实的物理内存中取出指令,再供CPU执行

    每一个进程空间的虚拟地址都是一样的
    --原因:如果不一样的话,管理起来会非常的麻烦
    
    对于32位的 OS 来说,虚拟内存的虚拟地址的编码范围为:0~4G-1
    --虚拟内存的虚拟地址也不是全用上了,这个范围太大了,OS 做了限制,实际上虚拟地址也只使用了其中的一部分而已
    
    • 1
    • 2
    • 3
    • 4
    • 5

    虚拟内存这么大,底层对应的物理内存空间也这么大吗?

    当然不可能,如果是这样的话运行40个进程,所占物理内存空间就 == 4G*40(160G)

    所以每个进程在运行时,虚拟内存底层实际所需的物理内存空间并不大

    为什么要在物理内存上营造出虚拟内存

    有一个很重要的原因是:通过虚拟内存机制,可以让每个进程拥有完全独立的进程空间

    虚拟内存的一个重要作用就是:可以让每个进程拥有完全独立的进程空间

    也就是说:

    虽然每个进程空间拥有完全相同的虚拟地址,但是不会相互干扰,各自操作各自的代码和数据

    • 所有进程空间(虚拟内存空间)的虚拟地址(编号)是一样的
    • 但是各自在物理内存上,实际所对应的物理内存空间完全不同
    • 虚拟内存通过特殊的实现机制,它可以严格保证每个虚拟内存,各自对应的是完全独立的物理内存空间

    总结:

    为什么说每个进程的进程空间是完全独立的

    虚拟内存机制可以保证,每个程序完全运行在各自独立的物理内存空间,而且还能保证它们绝对不会误访问得到对方的物理内存空间

    进程间通信

    IPC原理

    所有进程通过同一OS来实现数据的转发

    进程间通信的原理:

    OS作为所有进程共享的第三方,会提供相关的机制,以实现进程间数据的转发,达到数据共享的目的

    广义上的进程间通信
    A进程——————文件———————B进程				
    A进程—————数据库——————B进程
    
    一般来说,这种广义的进程间通信,并不被算作真正的“进程间通信”
    
    • 1
    • 2
    • 3
    • 4

    只有OS所提供的专门的通信机制,才能算作是真正的“进程间通信”

    LINUX提供的进程间通信

    Linux的进程间通信,其实都是继承于Unix

    • 信号

    前面讲的信号其实也是进程间通信的一种,只不过信号是非精确通信

    本章讲的IPC是精确通信
    ---精确通信,就是能告诉你详细信息
    ---而信号这种非精确通信,只能通知某件事情发生了,但是无法告诉详细信息
    
    • 1
    • 2
    • 3

    进程间通信

    • 管道

      • 无名管道
      • 有名管道

    system V IPC(系统5)

    • 消息队列:通过消息队列机制来通信
    • 共享内存:通过共享内存机制来通信
    • 信号量:借助通信来实现资源的保护(一种加锁机制)

    无名管道

    无名管道的通信原理

    ​ 具体来说就是,内核会开辟一个“管道”,通信的进程通过共享这个管道,从而实现通信

    操作无名管道

    文件的方式来读写管道

    • 有读写用的文件描述符
    • 读写时会用write、read等文件Io函数
    为什么叫无名管道

    无名管道文件比较特殊,它没有文件名,但可以通过“文件描述符”来操作管道,它就是一个文件(管道文件)

    无名管道API
    #include 
     /* On all other architectures */
    int pipe(int pipefd[2]);
    
    • 1
    • 2
    • 3
    功能

    创建一个用于亲缘进程(比如:父子进程)之间通信的无名管道(本质上是:缓存),并将管道与两个读写文件描述符关联起来

    无名管道只能用于亲缘进程之间通信

    参数

    缓存地址:缓存用于存放读写管道的文件描述符

    这个缓存就是一个拥有两个元素的int型数组

    1)元素[0]:里面放的是读管道的读文件描述符
    2)元素[1]:里面放的是写管道的写文件描述符
    
    • 1
    • 2

    这里的读和写文件描述符,是两个不同的文件描述符

    并不是所有的文件描述符,都是通过open函数打开文件得到的

    这里无名管道的读、写文件描述符,就是直接在创建管道时得到的,与open没有任何关系

    这里也没办法使用open函数,因为open函数需要文件路径名,无名管道没有文件名

    无名管道特点

    只能用于亲缘进程之间通信

    由于没有文件名,因此进程没办法使用open打开管道文件,从而得到文件描述符
    ----方法----
    父进程先调用pipe创建出管道,并得到读写管道的文件描述符。
    然后再fork出子进程,让子进程通过继承父进程打开的文件描述符
    父子进程就能操作同一个管道,从而实现通信。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xlWUjn8l-1668593716547)(/home/guojiawei/.config/Typora/typora-user-images/image-20221116180900282.png)]

    内核在调用无名管道函数,会创建出一个缓存,也就是管道,一端用于读一端用于写

    子进程被父进程fork后,会继承父进程的文件描述符,实现亲缘通信关系

    只要是存在继承关系的进程就是亲缘进程:

    (1)直接继承关系
    			父进程————>子进程
    (2)间接继承关系
    			父进程————>子进程————>子进程————>...
    
    • 1
    • 2
    • 3
    • 4

    读管道时,如果没有数据的话,读操作会休眠(阻塞)

    无名管道代码

    父子进程单向通信
    (a)父进程在fork之前先调用pipe创建无名管道----获取读写文件描述符
    (b)fork创建出子进程,子进程继承无名管道读写文件描述符
    (c)父子进程使用各自管道的读写文件描述符进行读写操作,实现通信
    
    • 1
    • 2
    • 3
    #include 
    #include 
    #include 
    #include 
    /*单向通信---父进程写数据,子进程读数据并打印*/
    int main(void){
        /*fork前优先创建管道*/
        int pipefd[2]={0};//用于存放管道读写文件描述符 [0]:读 [1]:写
        int ret=0;
        ret=pipe(pipefd);//int pipe(int pipefd[2]);
        if(ret==-1){
            perror("pipe fail\n");
            exit(-1);
        }
        /*调用fork函数*/
        ret=fork();
        if(ret>0){
           while(1){
                 write(pipefd[1],"hello",5);//写
                 sleep(0.5);
           }
        }
        else if(ret==0){
                 while(1){
                    char buf[30]={0};
                    //清空缓存
                    bzero(buf,sizeof(buf));
                    read(pipefd[0],buf,sizeof(buf));
                    printf("child,data=%s",buf);
                 }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    关闭没有用到的文件描述符

    • 两个close
    #include 
    #include 
    #include 
    #include 
    /*单向通信---父进程写数据,子进程读数据并打印*/
    int main(void){
        /*fork前优先创建管道*/
        int pipefd[2]={0};//用于存放管道读写文件描述符 [0]:读 [1]:写
        int ret=0;
        ret=pipe(pipefd);//int pipe(int pipefd[2]);
        if(ret==-1){
            perror("pipe fail\n");
            exit(-1);
        }
        /*调用fork函数*/
        ret=fork();
        if(ret>0){
            close(pipefd[0]);
           while(1){
                 write(pipefd[1],"hello",5);//写
                 sleep(0.5);
           }
        }
        else if(ret==0){
            close(pipefd[1]);
                 while(1){
                    char buf[30]={0};
                    //清空缓存
                    bzero(buf,sizeof(buf));
                    read(pipefd[0],buf,sizeof(buf));
                    printf("child,data=%s",buf);
                 }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    SIGPIPE信号
    写管道时,如果管道的读端被close了话,向管道“写”数据的进程会被内核发送一个SIGPIPE信号
    ------发这个信号的目的就是想通知你,管道所有的“读”都被关闭了。
    
    • 1
    • 2

    水管的出口(读)给堵住了,结果你还一直往里面灌水(写),别人跟定会警告你,因为你这样可能会对水管造成损害

    这个信号的默认动作是终止,所以收到这个信号的进程会被终止

    注意只有两个读端被关闭后,才会产生这个信号(父子进程都没有出口)

    父子进程双向通信

    单个无名管道无法实现双向通信


    因为父子进程会争夺读数据

    自己发送给对方的数据,就被自己给抢读到

    解决

    • 创建两个管道,父子进程各有四个读写端

    • 关闭一个管道的子进程读写端,和另一个管道的读进程读写端即可达到目的

    #include 
    #include 
    #include 
    #include 
    /*双向通信---父子进程读写数据*/
    int main(void){
        /*fork前优先创建管道*/
        //创建两个管道
        int pipefd1[2]={0};
        int pipefd2[2]={0};
        pipe(pipefd1);
        pipe(pipefd2);
        /*调用fork函数*/
        int ret=0;
        ret=fork();
        /*父进程写hello子进程接收*/
        if(ret>0){
            //关闭父进程的管道1的读文件描述符
           close(pipefd1[0]);
            //关闭父进程管道2的写文件描述符
           close(pipefd2[1]);
           char buf1[30]={0};
           while(1){
                 write(pipefd1[1],"hello",5);
                 /*父进程通过管道二读数据*/
                 bzero(buf1,sizeof(buf1));
                 read(pipefd2[0],buf1,sizeof(buf1));
                 printf("parent=%s\n",buf1);
           }
        }
        else if(ret==0){
            //关闭子进程管道1的写文件描述符
            close(pipefd1[1]);
            //关闭子进程管道1的读文件描述符
            close(pipefd2[0]);
            char buf2[30]={0};
                 while(1){
                    /*子进程通过管道2写文件*/
                    write(pipefd2[1],"world",5);
    
                    bzero(buf2,sizeof(buf2));//清空缓存
                    read(pipefd1[0],buf2,sizeof(buf2));
                    printf("child=%s\n",buf2);
                 }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    有名管道

    有名管道之所以叫“有名管道”,是因为它有文件名

    不管是有名管道,还是无名管道,本质其实都是一样的:

    管道都是内核所开辟的一段缓存空间。
    ---进程间通过管道通信时,本质上就是通过共享操作这段缓存来实现
    ---只不过操作这段缓存的方式,是以读写文件的形式来操作的。
    
    • 1
    • 2
    • 3
    有名管道的特点
    1. 能够用于非亲缘进程之间的通信

      • 因为有文件名,所以进程可以直接调用open函数打开文件,从而得到文件描述符
      • 无名管道,必须在通过继承的方式才能获取到文件描述符
      有名管道通信:
      任何两个进程调用open函数打开同一个“有名管道”文件,然后对同一个“有名管道文件”进行读写操作,即可实现通信  A进程 —————————> 有名管道 ————————> B进程
      
      • 1
      • 2
    2. 读管道时,如果管道没有数据的话,读操作同样会阻塞(休眠)

      就是等待数据的写入

    3. 当进程写一个所有读端都被关闭了的管道时,进程会被内核返回SIGPIPE信号

      如果不想被该信号终止的话,我们需要忽略、捕获、屏蔽该信号

    有名管道的使用
    1. 进程调用mkfifo创建有名管道
    2. open打开有名管道
    3. read/write读写管道进行通信
    • 对于通信的两个进程来说,创建管道时,只需要一个人创建,另一个直接使用即可

    • 为了保证管道一定被创建,最好是两个进程都包含创建管道的代码,谁先运行就谁先创建

      后运行的发现管道已经创建好了,那就直接open打开使用

    有名管道API
    #include 
    #include 
    //创建有名管道文件,创建好后便可使用open打开
    int mkfifo(const char *pathname, mode_t mode);
    /*
       pathname:被创建管道文件的文件路径名
       mode:指定被创建时原始权限,一般为0664(110110100),三组读写权限
             创建新文件时,文件被创建时的真实权限=mode & (~umask)
    */
    //返回值:成功返回0,失败则返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 如果是创建普通文件的话,我们可以使用open的O_CREAT选项来创建,比如:
      open(“./file”, O_RDWR|O_CREAT, 0664);
    • 但是对于“有名管道”这种特殊文件,这里只能使用mkfifo函数来创建。
    单向通信
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define FIFONAME "./fifo"
    void print_err(char* str){
    	perror(str);
    	exit(-1);
    }
    int main(void){
    	/*使用mkfifo创建管道文件*/
    	int ret=0;
    	ret=mkfifo(FIFONAME,0664);
    	if(ret==-1) print_err("mkfifo fails\n");
    	/*使用open打开管道文件*/
    	int fd=-1;
    	fd=open(FIFONAME,O_WRONLY);//只写打开
    	if(fd==-1) print_err("open fails\n");
    	/*从键盘获取*/
    	char buf[100]={0};
    	while(1){
    		bzero(buf,sizeof(buf));
    		scanf("%s",buf);
    		write(fd,buf,sizeof(buf));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    用于写的管道

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define FIFONAME "./fifo"
    void print_err(char* str){
    	perror(str);
    	exit(-1);
    }
    //封装创建管道+打开管道
    int create_open_fifo(char *fifoname,int open_mode){
    	int ret=0;
    	int fd=-1;
    	ret=mkfifo(FIFONAME,0664);
    	if(ret==-1 && errno!=EEXIST) print_err("mkfifo fails\n");
    	/*使用open打开管道文件*/
    	fd=open(FIFONAME,open_mode);//以只写方式打开
    	if(fd==-1) print_err("open fails\n");
    	return fd;
    }
    /*信号捕获,通信结束,删除管道文件,退出进程*/
    void signal_fun(int signo){
    	//unlink()
    	remove(FIFONAME);
    	exit(-1);
    }
    int main(void){
    	signal(SIGINT,signal_fun);//捕获:必须在create_open_fife前
    	/*因为读端堵塞,以只写方式打开*/
    	int fd=create_open_fifo(FIFONAME,O_WRONLY);
    	
    	while(1){
    		char buf[30]={0};
    		bzero(buf,sizeof(buf));
    		scanf("%s",buf);
    		write(fd,buf,sizeof(buf));
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    用于读的管道

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define FIFONAME "./fifo"
    void print_err(char* str){
    	perror(str);
    	exit(-1);
    }
    //封装创建管道+打开管道
    int create_open_fifo(char *fifoname,int open_mode){
    	int ret=0;
    	int fd=-1;
    	ret=mkfifo(FIFONAME,0664);
    	if(ret==-1 && errno!=EEXIST) print_err("mkfifo fails\n");
    	/*使用open打开管道文件*/
    	fd=open(FIFONAME,open_mode);//以只写方式打开
    	if(fd==-1) print_err("open fails\n");
    	return fd;
    }
    /*信号捕获,通信结束,删除管道文件,退出进程*/
    void signal_fun(int signo){
    	//unlink()
    	remove(FIFONAME);
    	exit(-1);
    }
    int main(void){
    	signal(SIGINT,signal_fun);//捕获:必须在create_open_fife前
    	/*因为读端堵塞,以只读方式打开*/
    	int fd=create_open_fifo(FIFONAME,O_RDONLY);
    	char buf[30]={0};
    	while(1){
    		bzero(buf,sizeof(buf));
    		read(fd,buf,sizeof(buf));
    		printf("buf=%s\n",buf);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    双向通信
    • 同样的,使用一个“有名管道”是无法实现双向通信的,因为也涉及到抢数据的问题。
    • 所以双向通信时需要两个管道

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQQBlgMi-1668838782776)(/home/guojiawei/.config/Typora/typora-user-images/image-20221119141656507.png)]

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define FIFONAME1 "./fifo1"
    #define FIFONAME2 "./fifo2"
    void print_err(char* str){
    	perror(str);
    	exit(-1);
    }
    /*封装创建管道+打开管道*/
    int create_open_fifo(char *fifoname,int open_mode){
    	int ret=0;
    	int fd=-1;
    	ret=mkfifo(fifoname,0664);
    	if(ret==-1 && errno!=EEXIST) print_err("mkfifo fails\n");
    	//使用open打开管道文件
    	fd=open(fifoname,open_mode);
    	if(fd==-1) print_err("open fails\n");
    	return fd;
    }
    /*信号捕获,通信结束,删除管道文件,退出进程*/
    void signal_fun(int signo){
    	remove(FIFONAME1);
    	remove(FIFONAME2);
    	exit(-1);
    }
    int main(void){
    	int fd1=-1;
    	int fd2=-1;
    	//fd1管道去写,fd2管道去读
    	fd1=create_open_fifo(FIFONAME1,O_WRONLY);
    	fd2=create_open_fifo(FIFONAME2,O_RDONLY);
        char buf[30]={0};
    	int ret=fork();
    	if(ret>0){//父进程管道2只读
    		signal(SIGINT,signal_fun);
    	    while(1){
    		   bzero(buf,sizeof(buf));
    		   read(fd2,buf,sizeof(buf));
    		   printf("从管道2读出的数据:%s\n",buf);
    	 }
    	}
    	else if(ret==0){//子进程管道1只写
    		while (1){
    		   bzero(buf,sizeof(buf));
    		   scanf("%s",buf);
    		   write(fd1,buf,sizeof(buf));
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define FIFONAME1 "./fifo1"
    #define FIFONAME2 "./fifo2"
    void print_err(char* str){
    	perror(str);
    	exit(-1);
    }
    /*封装创建管道+打开管道*/
    int create_open_fifo(char *fifoname,int open_mode){
    	int ret=0;
    	int fd=-1;
    	ret=mkfifo(fifoname,0664);
    	if(ret==-1 && errno!=EEXIST) print_err("mkfifo fails\n");
    	//使用open打开管道文件
    	fd=open(fifoname,open_mode);
    	if(fd==-1) print_err("open fails\n");
    	return fd;
    }
    /*信号捕获,通信结束,删除管道文件,退出进程*/
    void signal_fun(int signo){
    	remove(FIFONAME1);
    	remove(FIFONAME2);
    	exit(-1);
    }
    int main(void){
    	int fd1=create_open_fifo(FIFONAME1,O_RDONLY);
    	int fd2=create_open_fifo(FIFONAME2,O_WRONLY);
    	char buf[30]={0};
    	int ret=fork();
    	if(ret>0){//父进程从管道2只写
    	    signal(SIGINT,signal_fun);
    	    while(1){
    		   bzero(buf,sizeof(buf));
    		   scanf("%s",buf);
    		   write(fd2,buf,sizeof(buf));
    	}}//子进程从管道1只读
    	else if(ret==0){
    		while(1){
    			bzero(buf,sizeof(buf));
                read(fd1,buf,sizeof(buf));
    			printf("从管道1读出数据:%s\n",buf);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    什么时候使用有名管道

    当两个进程需要通信时,不管是亲缘的还是非亲缘的,我们都可以使用有名管道来通信。

  • 相关阅读:
    Loongson Laptop应用/系统崩溃进入initramfs界面
    ASEMI快恢复二极管SF2006参数,SF2006规格,SF2006封装
    用欧拉路径判断图同构推出reverse合法性:1116T4
    【多线程】线程安全(重点)
    Spring启动源码分析以及生命周期
    glib-2.78.0交叉编译
    服务器怎么买,腾讯云服务器新手购买的流程方法步骤
    基础算法篇——前缀和与差分
    maven 项目报错--Using ‘UTF-8‘ encoding to copy filtered resources
    快鲸scrm系统:助力母婴门店实现私域长效增长
  • 原文地址:https://blog.csdn.net/weixin_47173597/article/details/127886881