当前位置: 首页 > news >正文

Linux进程10-有名管道概述、创建、读写操作、两个管道进程间通信、读写规律(只读、只写、读写区别)、设置阻塞/非阻塞

目录

1.有名管道

1.1概述

1.2与无名管道的差异

2.有名管道的创建 

2.1 直接用shell命令创建有名管道

2.2使用mkfifo函数创建有名管道

 3.有名管道读写操作

3.1单次读写

3.2多次读写

4.有名管道进程间通信

4.1回合制通信

4.2父子进程通信

5.有名管道读写规律(默认阻塞)

5.1读写端都存在,只读不写

5.2读写端都存在,只写不读

5.3 在一个进程中,只有读端,没有写端

5.4 在一个进程中,只有写端,没有读端

5.5 一个进程只读,一个进程只写

5.5.1两个进程,一个只读一个只写

5.5.2两个进程,一个只读一个只写,关闭写端

5.5.3两个进程,一个只读一个只写,关闭读端

5.6读写端都存在,默认阻塞

6.有名管道的读写规律(设置:非阻塞)

6.1只读方式打开(设置非阻塞)

6.2只写方式打开(设置非阻塞)

6.3读写方式打开(设置非阻塞)


1.有名管道

1.1概述

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同,
其特点是:
1、半双工,数据在同一时刻只能在一个方向上流动。
2、写入FIFO中的数据遵循先入先出的规则。
3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格
式,如多少字节算一个消息等。
4、FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见,所以有名管道可
以实现不相关进程间通信,但FIFO中的内容却存放在内存中。
5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更
多的数据。
7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。

1.2与无名管道的差异

特性有名管道无名管道
进程关系允许无亲缘关系的进程通信仅限有亲缘关系的进程
存在形式文件系统中的 FIFO 文件内存中的临时缓冲区
持久性文件删除前一直存在进程终止后自动销毁
创建方式mkfifo()/mkfifoat()pipe()

2.有名管道的创建 

2.1 直接用shell命令创建有名管道

在终端直接用shell命令mkfifo创建有名管道

使用ls -l命令查看 myfifo1文件的类型为p,表示该文件是一个管道文件。

2.2使用mkfifo函数创建有名管道

mkfifo函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个有名管道,产生一个本地文件系统可见的文件pathname参数:
pathname:有名管道创建后生成的文件,可以带路径
mode:管道文件的权限,一般通过八进制数设置即可,例如0664返回值:成功:0失败:‐1

(1)管道文件不存在

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>int main(int argc, char const *argv[])
{//通过mkfifo函数创建有名管道if(mkfifo("fifo_file", 0664) == -1){perror("fail to mkfifo");exit(1);}return 0;
}

运行结果:管道文件不存在,创建了fifo_file管道文件

(2)管道文件存在

再次运行程序,报错:文件已存在

程序修改:如果错误码不是 EEXIST,继续运行

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>int main(int argc, char const *argv[])
{//通过mkfifo函数创建有名管道if(mkfifo("fifo_file", 0664) == -1){printf("errno = %d\n", errno);//如果管道文件已经存在,不需要报错退出,直接使用即可,//所以需要在错误输出之前把因为文件存在的错误排除if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}return 0;
}

运行结果:

终端输入: sudo find /usr/include -name "errno-base.h" ,查找错误码文件

 3.有名管道读写操作

由于有名管道在本地创建了一个管道文件,所以系统调用的IO函数基本都可以对有名管道

进行操作, 但是不能使用lseek修改管道文件的偏移量.

注意:有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在

内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的

操作实质就是对内核空间的操作。

3.1单次读写

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>#define FIFONAME "fifo_file" //管道文件int main(int argc, char const *argv[])
{//通过mkfifo函数创建有名管道if(mkfifo(FIFONAME, 0664) == -1){if(errno != EEXIST)//如果不存在报错{perror("fail to mkfifo");exit(1);}}//对有名管道进行读写操作//通过open函数打开管道文件并得到文件描述符int fd;fd = open(FIFONAME, O_RDWR);//权限:可读可写if(fd == -1){perror("fail to open");exit(1);}//通过write函数向管道中写入数据if(write(fd, "hello world", strlen("hello world")) == -1){perror("fail to write");exit(1);}//通过read函数读取管道中的数据char buf[32] = "";if(read(fd, buf, sizeof(buf)) == -1){perror("fail to read");exit(1);}printf("buf = [%s]\n", buf);//使用close函数关闭文件描述符close(fd);return 0;
}

 运行结果:

3.2多次读写

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>#define FIFONAME "fifo_file"int main(int argc, char const *argv[])
{//通过mkfifo函数创建有名管道if(mkfifo(FIFONAME, 0664) == -1){if(errno != EEXIST)//如果不存在报错{perror("fail to mkfifo");exit(1);}}//对有名管道进行操作//管道后写入的数据会保存在之前写入数据的后面,不会替换//如果管道中没有数据了,读操作会阻塞//通过open函数打开管道文件并得到文件描述符int fd;fd = open(FIFONAME, O_RDWR);if(fd == -1){perror("fail to open");exit(1);}//第一次 通过write函数向管道中写入数据if(write(fd, "hello world", strlen("hello world")) == -1){perror("fail to write");exit(1);}//第2次 通过write函数向管道中写入数据if(write(fd, "nihao", strlen("nihao")) == -1){perror("fail to write");exit(1);}//第1次 通过read函数读取管道中的数据,已将管道中的所有数据读取char buf[32] = "";if(read(fd, buf, sizeof(buf)) == -1){perror("fail to read");exit(1);}printf("第1次读取 buf = [%s]\n", buf);//第2次 通过read函数读取管道中的数据,管道中已无数据if(read(fd, buf, sizeof(buf)) == -1){perror("fail to read");exit(1);}printf("第2次读取 buf = [%s]\n", buf);//使用close函数关闭文件描述符close(fd);return 0;
}

运行结果:第1次 通过read函数读取管道中的数据,已将管道中的所有数据读取;第2次 通过read函数读取管道中的数据,管道中已无数据,read函数执行阻塞;

4.有名管道进程间通信

有名管道在本地创建了一个管道文件,所以不相关的进程间也可以实现通信。

4.1回合制通信

发送端程序:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char const *argv[])
{//如果没有创建有名管道,则创建有名管道//为了实现两个进程都可以收发数据,所以需要创建两个有名管道if(mkfifo("myfifo1", 0664) == -1){if(errno != EEXIST)//如果不存在报错{perror("fail to mkfifo");exit(1);//退出}}if(mkfifo("myfifo2", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//打开两个有名管道并得到文件描述符//fd_w:向管道myfifo1写入数据,fd_r:从管道myfifo2读取数据,//另一个接收方正好相反int fd_w, fd_r;if((fd_w = open("myfifo1", O_WRONLY)) == -1){perror("fail to open");exit(1);}if((fd_r = open("myfifo2", O_RDONLY)) == -1){perror("fail to open");exit(1);}char buf[128] = "";char buf1[128] = "";ssize_t bytes;while(1){printf("发送端运行中:请输入向接收端要发送的内容\n");fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';//将fgets最后输入的\n替换为\0,终端就没有空行//send进程负责将数据写入myfifo1,接着从myfifo2中读取数据//发送端将终端输入的数据写入myfifo1,接收端:myfifo2会读取写入的数据if((bytes = write(fd_w, buf, sizeof(buf))) == -1){perror("fail to write");exit(1);}//发送端从myfifo2读取数据if((bytes = read(fd_r, buf1, sizeof(buf1))) == -1){perror("fail to read");exit(1);}printf("发送端:读取到接收端发送的数据: %s\n", buf1);memset(buf, 0, sizeof(buf));//buf清0memset(buf1, 0, sizeof(buf1));}return 0;
}

接收端程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char const *argv[])
{if(mkfifo("myfifo1", 0664) == -1){if(errno != EEXIST)//如果不存在报错{perror("fail to mkfifo");exit(1);}}if(mkfifo("myfifo2", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//fd_w:向管道myfifo2写入数据,fd_r:从管道myfifo1读取数据,int fd_w, fd_r;if((fd_r = open("myfifo1", O_RDONLY)) == -1){perror("fail to open");exit(1);}if((fd_w = open("myfifo2", O_WRONLY)) == -1){perror("fail to open");exit(1);}char buf[128] = "";char buf1[128] = "";ssize_t bytes;while(1){//recv进程负责从myfifo1中读取数据,接着将终端输入数据写入myfifo2//接收端:读取myfifo1写入数据if((bytes = read(fd_r, buf, sizeof(buf))) == -1){perror("fail to read");exit(1);}printf("接收端:读取到发送端发送的数据: %s\n", buf);printf("接收端运行中,清输入要发送的内容: \n");fgets(buf1, sizeof(buf1), stdin);buf1[strlen(buf1) - 1] = '\0';//接收端:向myfifo2写入数据,发送端:myfifo1会读取写入的数据if((bytes = write(fd_w, buf1, sizeof(buf1))) == -1){perror("fail to write");exit(1);}memset(buf, 0, sizeof(buf));//buf清0memset(buf1, 0, sizeof(buf1));}return 0;
}

运行结果:打开两个终端,分别运行发送端和接收端程序。发送和接收端程序运行时,发送端:先给接收端发送数据,保存在管道1;接着等待从管道2读取接收端发送的数据。接收端:从管道1读取发送端发送的数据;接着向发送端发送数据,保存在管道2。

程序缺点:只能一端输出后,等待另一端回复;回合制发送和接收

优化:选择父子进程。一个用于发送,一个用于接收。

(1)发送端

(2)接收端

4.2父子进程通信

(1)发送端程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char const *argv[])
{//如果没有创建有名管道,则创建有名管道//为了实现两个进程都可以收发数据,所以需要创建两个有名管道if(mkfifo("myfifo1", 0664) == -1){if(errno != EEXIST)//如果不存在报错{perror("fail to mkfifo");exit(1);//退出}}if(mkfifo("myfifo2", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//打开两个有名管道并得到文件描述符//fd_w:向管道myfifo1写入数据,fd_r:从管道myfifo2读取数据,//另一个接收方正好相反int fd_w, fd_r;if((fd_w = open("myfifo1", O_WRONLY)) == -1){perror("fail to open");exit(1);}if((fd_r = open("myfifo2", O_RDONLY)) == -1){perror("fail to open");exit(1);}char buf[128] = "";char buf1[128] = "";ssize_t bytes;//使用fork函数创建子进程pid_t pid;if((pid = fork()) < 0){perror("fail to fork");exit(1);}else if(pid > 0) // 父进程{while(1){printf("发送端运行中:请输入向接收端要发送的内容\n");fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';//将fgets最后输入的\n替换为\0,终端就没有空行//send进程负责将数据写入myfifo1,接着从myfifo2中读取数据//发送端将终端输入的数据写入myfifo1,接收端:myfifo2会读取写入的数据if((bytes = write(fd_w, buf, sizeof(buf))) == -1){perror("fail to write");exit(1);}memset(buf, 0, sizeof(buf));//buf清0sleep(1);}}else // 子进程{while(1){//发送端从myfifo2读取数据if((bytes = read(fd_r, buf1, sizeof(buf1))) == -1){perror("fail to read");exit(1);}printf("发送端:读取到接收端发送的数据: %s\n", buf1);	if(strcmp("end_end",buf1) == 0){printf("发送端:读取到接收端发送的: 终止信号,退出\n");exit(1);}memset(buf1, 0, sizeof(buf1));sleep(1);}}return 0;
}

(2)接收端程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char const *argv[])
{//如果没有创建有名管道,则创建有名管道//为了实现两个进程都可以收发数据,所以需要创建两个有名管道if(mkfifo("myfifo1", 0664) == -1){if(errno != EEXIST)//如果不存在报错{perror("fail to mkfifo");exit(1);//退出}}if(mkfifo("myfifo2", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//打开两个有名管道并得到文件描述符//fd_w:向管道myfifo1写入数据,fd_r:从管道myfifo2读取数据,//另一个接收方正好相反int fd_w, fd_r;if((fd_w = open("myfifo1", O_WRONLY)) == -1){perror("fail to open");exit(1);}if((fd_r = open("myfifo2", O_RDONLY)) == -1){perror("fail to open");exit(1);}char buf[128] = "";char buf1[128] = "";ssize_t bytes;//使用fork函数创建子进程pid_t pid;if((pid = fork()) < 0){perror("fail to fork");exit(1);}else if(pid > 0) // 父进程{while(1){printf("发送端运行中:请输入向接收端要发送的内容\n");fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = '\0';//将fgets最后输入的\n替换为\0,终端就没有空行//send进程负责将数据写入myfifo1,接着从myfifo2中读取数据//发送端将终端输入的数据写入myfifo1,接收端:myfifo2会读取写入的数据if((bytes = write(fd_w, buf, sizeof(buf))) == -1){perror("fail to write");exit(1);}memset(buf, 0, sizeof(buf));//buf清0sleep(1);}}else // 子进程{while(1){//发送端从myfifo2读取数据if((bytes = read(fd_r, buf1, sizeof(buf1))) == -1){perror("fail to read");exit(1);}printf("发送端:读取到接收端发送的数据: %s\n", buf1);	if(strcmp("end_end",buf1) == 0){printf("发送端:读取到接收端发送的: 终止信号,退出\n");exit(1);}memset(buf1, 0, sizeof(buf1));sleep(1);}}return 0;
}

运行结果:打开两个终端,分别运行发送端和接收端程序。发送和接收端程序运行时,发送端既可以接收多次数据,也可以发送多次数据;接收端也支持同时发送和接收多次数据。发送特殊字符end_end退出运行。

(1)发送端

(2)接收端

5.有名管道读写规律(默认阻塞)

5.1读写端都存在,只读不写

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{if(mkfifo("myfifo", 0664) == -1){//如果管道文件已经存在,不需要报错退出,直接使用即可,//所以需要在错误输出之前把因为文件存在的错误排除if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//读写端都存在,只读不写//如果原本管道中有数据,则正常读取//如果管道中没有数据,则read函数会阻塞等待int fd;if((fd = open("myfifo", O_RDWR)) == -1){perror("fail to open");exit(1);}//先写入数据验证,管道中有数据,则正常读取write(fd, "hello world", 11);char buf[128] = "";read(fd, buf, sizeof(buf));printf("buf = %s\n", buf);//管道中已无数据,读取会阻塞。read(fd, buf, sizeof(buf));printf("buf = %s\n", buf);return 0;
}

运行结果:读写端都存在,只读不写。

第一次读取,管道中有数据,正常读取;第二次读取,管道中已经无数据,在read处阻塞。

5.2读写端都存在,只写不读

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{//如果管道文件已经存在,不需要报错退出,直接使用即可,//所以需要在错误输出之前把因为文件存在的错误排除if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//读写端都存在,只写不读//当有名管道的缓冲区写满后,write函数会发生阻塞//默认有名管道的缓冲区为64K字节int fd;if((fd = open("myfifo", O_RDWR)) == -1){perror("fail to open");exit(1);}int num = 0;while(1){write(fd, "", 1024);num++;printf("num = %d\n", num);}return 0;
}

运行结果:读写端都存在,只写不读 ;当有名管道的缓冲区写满后,write函数会发生阻塞;默认有名管道的缓冲区为64K字节。

5.3 在一个进程中,只有读端,没有写端

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//在一个进程中,只有读端,没有写端//会在open函数的位置阻塞printf("***********************\n");int fd;if((fd = open("myfifo", O_RDONLY)) == -1){perror("fail to open");exit(1);}printf("------------------------\n");char buf[128] = "";ssize_t bytes;if((bytes = read(fd, buf, sizeof(buf))) == -1){perror("fail to read");exit(1);}printf("bytes = %ld\n", bytes);printf("buf = %s\n", buf);return 0;
}

运行结果:在一个进程中,只有读端,没有写端;会在open函数的位置阻塞(printf("------------------------\n"); 未执行)。

5.4 在一个进程中,只有写端,没有读端

程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{//如果管道文件已经存在,不需要报错退出,直接使用即可,//所以需要在错误输出之前把因为文件存在的错误排除if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//在一个进程中,只有写端,没有读端//会在open函数的位置阻塞printf("*****************************\n");int fd;if((fd = open("myfifo", O_WRONLY)) == -1){perror("fail to open");exit(1);}printf("-----------------------------\n");write(fd, "hello world", 11);printf("666\n");sleep(1);return 0;
}

运行结果:在一个进程中,只有写端,没有读端;会在open函数的位置阻塞

5.5 一个进程只读,一个进程只写

5.5.1两个进程,一个只读一个只写

只要保证有名管道的 读写端都存在 ,不管是几个进程,都 不会再open这阻塞 了。
(1)只读程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//在一个进程中,只有读端,没有写端//会在open函数的位置阻塞printf("***********************\n");int fd;if((fd = open("myfifo", O_RDONLY)) == -1){perror("fail to open");exit(1);}printf("------------------------\n");char buf[128] = "";ssize_t bytes;if((bytes = read(fd, buf, sizeof(buf))) == -1){perror("fail to read");exit(1);}printf("bytes = %ld\n", bytes);printf("buf = %s\n", buf);return 0;
}

(2)只写程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{//如果管道文件已经存在,不需要报错退出,直接使用即可,//所以需要在错误输出之前把因为文件存在的错误排除if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//在一个进程中,只有写端,没有读端//会在open函数的位置阻塞printf("*****************************\n");int fd;if((fd = open("myfifo", O_WRONLY)) == -1){perror("fail to open");exit(1);}printf("-----------------------------\n");write(fd, "hello world", 11);printf("666\n");sleep(1);return 0;
}

运行结果:打开2个终端,对同一个管道进行读写操作,读写端同时存在,程序没有阻塞在open。

(1)只写端,程序没有阻塞在open,(printf("------------------------\n"); 已执行)

(2)只读端,程序没有阻塞在open,(printf("------------------------\n"); 已执行)

5.5.2两个进程,一个只读一个只写,关闭写端

如果一个进程只读,一个进程只写,都运行后,如果关闭写端,读端read会返回0。
(1)只写端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//在一个进程中,只有写端,没有读端//会在open函数的位置阻塞printf("*****************************\n");int fd;if((fd = open("myfifo", O_WRONLY)) == -1){perror("fail to open");exit(1);}printf("-----------------------------\n");while(1){write(fd, "hello world", 11);printf("666\n");sleep(1);}return 0;
}
(2)只读端程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char const *argv[])
{if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//在一个进程中,只有读端,没有写端//会在open函数的位置阻塞printf("***********************\n");int fd;if((fd = open("myfifo", O_RDONLY)) == -1){perror("fail to open");exit(1);}printf("------------------------\n");char buf[128] = "";ssize_t bytes;while(1){if((bytes = read(fd, buf, sizeof(buf))) == -1){perror("fail to read");exit(1);}printf("bytes = %ld\n", bytes);printf("buf = %s\n", buf);sleep(1);}return 0;
}

运行结果:打开两个终端运行。一个进程只读,一个进程只写,都运行后,如果关闭写端,读端read会返回0。

(1)写端,运行几次后,关闭写端,终止运行。

(2)读端,关闭写端,读端read会返回0。

5.5.3两个进程,一个只读一个只写,关闭读端

如果一个进程只读,一个进程只写,都运行后, 如果关闭读端,写端会立即产生 SIGPIPE信号(管道破裂),默认的处理方式是退出进程
(1)只写端程序
与5.5.2两个进程,一个只读一个只写,关闭写端;程序相同。
(2)只读端程序
与5.5.2两个进程,一个只读一个只写,关闭写端;程序相同。
运行结果:打开两个终端运行
(1)读端,关闭后,写端产 生SIGPIPE信号(管道破裂),默认的处理方式是退出进程。
(2)写端

5.6读写端都存在,默认阻塞

有名管道默认读写状态为阻塞,读写端同时存在,不会再open函数处阻塞

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int fd;if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//如果open设置为可读可写,默认:阻塞效果char send[100] = "nihao world";fd = open("myfifo", O_RDWR);//读写打开if(fd < 0){perror("fail open fifo");exit(1);}write(fd, send, strlen(send));char recv[100] = "";read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);return 0;
}
运行结果:有名管道默认读写状态为阻塞,读写端同时存在,不会再open函数处阻塞。

6.有名管道的读写规律(设置:非阻塞)

指定 O_NONBLOCK( open 位或 O_NONBLOCK)
1 、先以只读方式打开:如果没有进程已经为写而打开一个 FIFO, 只读 open 成功,并且 open 不阻塞。
2 、先以只写方式打开:如果没有进程已经为读而打开一个 FIFO ,只写 open 将出错返回 -1
3 read write 读写命名管道中读数据时不阻塞。
4 、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出。

6.1只读方式打开(设置非阻塞)

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int fd;if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//如果open标志位设置为非阻塞,并且以只读的方式打开管道文件//open函数和read函数都不会阻塞fd = open("myfifo", O_RDONLY | O_NONBLOCK);if(fd < 0){perror("fail open fifo");exit(1);}while(1){char recv[100] = "";bzero(recv, sizeof(recv));read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0;
}

运行结果:管道只读 非阻塞打开,在 open函数 不阻塞,程序继续运行。

6.2只写方式打开(设置非阻塞)

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int fd;if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//如果open标志位设置为非阻塞,并且以只写的方式打开管道文件//open函数会直接报错//如果open设置为可读可写,那么跟阻塞是一样的效果char send[100] = "Hello world";fd = open("myfifo", O_WRONLY | O_NONBLOCK);//fd = open("myfifo", O_RDWR | O_NONBLOCK);if(fd < 0){perror("fail open fifo");exit(1);}write(fd, send, strlen(send));char recv[100] = "";read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);return 0;
}

运行结果:管道只写 非阻塞打开管道文件,open函数会直接报错。(不加非阻塞设置,在open函数处,阻塞。见5.4章节)

6.3读写方式打开(设置非阻塞)

程序:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int fd;if(mkfifo("myfifo", 0664) == -1){if(errno != EEXIST){perror("fail to mkfifo");exit(1);}}//如果open设置为可读可写,那么跟阻塞是一样的效果char send[100] = "Hello world";//fd = open("myfifo", O_WRONLY | O_NONBLOCK);fd = open("myfifo", O_RDWR | O_NONBLOCK);//读写打开if(fd < 0){perror("fail open fifo");exit(1);}write(fd, send, strlen(send));char recv[100] = "";read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);return 0;
}

运行结果:读写端同时存在,设置为非阻塞,不会再open函数处阻塞。效果与读写端同时存在,设置为阻塞,效果相同。

相关文章:

  • Spark处理过程-转换算子和行动算子
  • Lodash isEqual 方法源码实现分析
  • Spring Cloud Sleuth 链路追踪
  • Java面试高阶篇:Spring Boot+Quarkus+Redis高并发架构设计与性能优化实战
  • ZYNQ笔记(二十):Clocking Wizard 动态配置
  • 【开源工具】深度解析:基于PyQt6的Windows时间校时同步工具开发全攻略
  • bazel迁移cmake要点及具体迁移工程示例(apollo radar)
  • 技术视界 | 青龙机器人训练地形详解(四):复杂地形精讲之斜坡
  • 智表 ZCELL 插件快速入门指南(原创)
  • 详解 IRC协议 及客户端工具 WeeChat 的使用
  • 华为ensp实现跨vlan通信
  • 全视通智慧病房无感巡视解决方案:科技赋能,重塑护理巡视新篇
  • 【数据结构】——队列
  • web:InfiniteScroll 无限滚动
  • iOS safari和android chrome开启网页调试与检查器的方法
  • 基于Vue3.0的高德地图api教程005:实现绘制线并编辑功能
  • iOS即时通信的技术要点
  • fiddler 配置ios手机代理调试
  • AI赋能:构建个性化智能学习规划系统
  • 专题二:二叉树的深度搜索(求根节点到叶节点数字之和)
  • 刘国中:持续加强护士队伍建设,更好保障人民身体健康
  • 山东枣庄同一站点两名饿了么骑手先后猝死,当地热线:职能部门正调查
  • 黄土是他们的气质:打破宁夏当代油画创作的沉寂
  • 佩斯科夫:俄会考虑30天停火提议,但试图对俄施压无用
  • 烈士沈绍藩遗孤、革命家帅孟奇养女舒炜逝世,享年96岁
  • 晋级中部非省会第一城,宜昌凭什么