进程间的通信 [1] —— 无名管道PIPE、有名管道FIFO

文章转载请注明出处,加上原文链接,谢谢!https://blog.csdn.net/weixin_46959681/article/details/113748000



无名管道 PIPE

|特点

管道,常指无名管道,是 UNIX 系统中 IPC 最古老的形式。

无名管道为半双工类型可以被视为一种特殊类型的文件,不存在于任何库文件系统中,只存在内核中。创建无名管道时没有实际的磁盘节点仅作为一个内存对象生成内存节点(iNode),故数据在一个方向上流动且读取后立即销毁。

因内存对象和普通文件一致,故与读写操作使用相同的函数API接口 writeread 等,同时创建了两个文件描述符代表读端fd[0] 、写端 fd[1]

因为这两个文件描述符是专用的(意为不能显式打开且没有任何标记),只能用于具有亲缘关系的进程之间的通信 —— 父子进程、兄弟进程,或者其他继承了祖先进程的管道文件对象的两个进程间使用。

函数原型:

#include <unistd.h>
// 返回值:若成功返回0,失败返回-1。
int pipe(int fd[2]); 

|代码演示: pipe.c

/* pipe.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>

int mian()
{
    
    
		int fd[2];
        pid_t pid;
        char buf[128];

		// 创建无名管道。
        if(pipe(fd) == -1){
    
      
                printf("Create pipe Error!\n");
                perror("why:");
        }

        pid = fork();
        
        if (pid < 0){
    
    
                printf("Fork failed.\n");
                perror("why:");
        }
		else if(pid > 0){
    
    
				//睡眠2秒时系统资源被子进程争夺到。
                sleep(2);
                printf("Father process.\n");
                close(fd[0]);
                write(fd[1],"(father)hello",strlen(" 'hello' from father."));
                //函数wait作用是等待子进程的退出,不致于运行时父进程提前退出。
                wait(NULL);
        }else{
    
    
                printf("Child process.\n");
                close(fd[1]);
                //buf是读取数据后放置数据的容器。
                read(fd[0],buf,128);
                printf("read form father: %s\n",buf);
                //使用函数 exit(0) 退出子进程。
                exit(0);
        }
        return 0;
}	

运行结果:

在这里插入图片描述

|代码逻辑

使用函数 fork() 创建父子进程时,不管内核首先调用父进程亦或子进程与否,调用父进程时休眠两秒,系统资源被子进程争夺到。因子进程是数据读取端,父进程是数据写入端。当子进程先运行时读取不到父进程写入的数据时陷入阻塞,系统资源又被父进程争夺到并写数据流入管道PIPE,后再回转到子进程。


有名管道 FIFO

|特点

有名管道 FIFO 与无名管道不同,有相关联的路径名,以一种特殊设备文件形式存在于文件系统中,可以在任意两个或多个进程之间进行通讯(交换数据)。因为它在文件目录树中有一个文件标示(FIFO) 实际不占据磁盘空间,数据缓存在内存上。它与普通文件类似,都遵循打开,读,写,关闭的过程,但读写的内部实现和普通文件不同,和无名管道一样。

函数原型:

#include <sys/stat.h>
// 返回值:成功返回0,出错返回-1
int mkfifo(const char *pathname, mode_t mode);

|代码演示:read_fifo1.cwrite_fifo1.c

第一步:在读取端创建管道并运行 read_fifo1.c

/* read_fifo1.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main()
{
    
    
		int fd;
        int cnt = 0;
        char buf[40] = {
    
    0};
        int nread = 0;

		// 创建有名管道FIFO。
		if(mkfifo("./fifo", 0666) < 0 && errno!=EEXIST)
		{
    
    
				printf("Create fifo Failed.\n");
				perror("why:");
		}        
		//以只读的方式打开FIFO。
        fd = open("./fifo",O_RDONLY);
        if(fd < 0){
    
    
				printf("Open fifo failed");
				perror("why");
				exit(-1);
		}else{
    
    
				printf("Open fifo success.\n");
		}
		
        while(1){
    
    
                cnt++;
                nread = read(fd, buf,40);
                printf("read %d bytes from fifo, context: %s\n",nread,buf);
                if(cnt == 3){
    
    
                        exit(0);
                }
        }
        //在读取端关闭FIFO。
        close(fd);
        return 0;
}

第二步:运行写入端 write_fifo1.c

/* write_fifo1.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main
{
    
    
       	int fd;
        int cnt = 0;
        char *str = "Message through fifo.\n";

		//以只写的方式打开FIFO。
        fd = open("./fifo",O_WRONLY);
        if(fd < 0){
    
    
				printf("Open fifo with read failed.\n");
				perror("why");
				exit(-1);
		}else{
    
    
				printf("Write open fifo success.\n");
		}
        
        while(1){
    
    
                cnt++;
                sleep(1);
                write(fd,str,strlen(str));
                if(cnt == 3){
    
    
                        exit(0);
                }
        }
        //在写入端关闭FIFO。
        close(fd);
        return 0;
}

运行结果: (先运行读取端,后运行写入端。)

读取端 写入端
在这里插入图片描述 在这里插入图片描述

管道的数据操作逻辑

  1. 把位于用户空间内存放数据的容器 Buf 内里所有的数据全部拷贝到内核中;
  2. 内核将数据拷贝到内存中;
  3. 把内存中的数据拷贝到内核中;
  4. 把内核的数据传到用户空间的的数据容器 Buf

在这里插入图片描述

逻辑示意图

参考资料


文章更新记录

  • 无名管道PIPE 一节完成。 「2021.2.25 22:15」
  • 替换了无名管道一节中的“结果”图片。 「2021.2.26 19:18」
  • 有名管道FIFO 一节完成。 「2021.2.26 19:55」
  • 增加“管道的数据操作逻辑”一节。 「2021.3.9 16:59」

P.S. 写博客真苦呀……那些不积累博客的人最后只会剩下印象吗?

猜你喜欢

转载自blog.csdn.net/weixin_46959681/article/details/113748000