5.管道、命名管道
5.1 管道概述
管道(pipe)又称无名管道。无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。
任何一个进程在创建的时候,系统都会 给他分配4G的虚拟内存,分为3G的用户空间和1G的内核空间,内核空间是所有进程公有的,无名管道就是创建在内核空间的,多个进程知道同一个无名管道的空间,就可以利用他来进行通信。
无名管道虽然是在内核空间创建的,但是会给当前用户进程两个文件描述符,一个负责执行读操作,一个负责执行写操作。
管道是最古老的 UNIX IPC方式,其特点是:
- 半双工,数据在同一时刻只能在一个方向上流动;
- 数据只能从管道的一端写入,从另一端读出;
- 写入管道中的数据遵循先入先出的规则;
- 管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等;
- 管道不是普通的文件,不属于某个文件系统,其只存在于内存中;
- 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同;
- 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据;
- 管道没有名字,只能在具有公共祖先的进程之间使用;
5.2 无名管道创建- pipe函数
| #include <unistd.h> |
| int pipe(int pipefd[2]); |
| 功能:创建一个有名管道,返回两个文件描述符负责对管道进行读写操作; |
| 参数: |
| pipefd:int型数组的首地址,里面有两个元素; |
| pipefd[0]负责对管道执行读操作; |
| pipefd[1]负责对管道执行写操作 |
| 返回值: |
| 成功: 返回 0 |
| 失败: 返回 -1 |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| int fd_pipe[2]; |
| |
| if (pipe(fd_pipe) == -1) |
| { |
| perror("fail to pipe\n"); |
| exit(1); |
| } |
| |
| printf("fd_pipe[0] = %d\n", fd_pipe[0]); |
| printf("fd_pipe[1] = %d\n", fd_pipe[1]); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (write(fd_pipe[1], "hello world", 11) == -1) |
| { |
| perror("fail to write"); |
| exit(1); |
| } |
| write(fd_pipe[1], "I LOVE Beijing", strlen("I LOVE Beijing") + 1); |
| |
| |
| |
| |
| char buf[15] = ""; |
| ssize_t bytes; |
| if ((bytes = read(fd_pipe[0], buf, sizeof(buf))) == -1) |
| { |
| printf("fail to read"); |
| exit(1); |
| } |
| printf("[%s]\n", buf); |
| printf("bytes = %ld\n", bytes); |
| |
| bytes = read(fd_pipe[0], buf, sizeof(buf)); |
| printf("[%s]\n", buf); |
| printf("bytes = %ld\n", bytes); |
| |
| return 0; |
| } |
输出结果
| fd_pipe[0] = 3 |
| fd_pipe[1] = 4 |
| [hello worldI LO] |
| bytes = 15 |
| [VE Beijing] |
| bytes = 11 |
5.3 无名管道实现父子进程通信
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| |
| |
| |
| |
| int main(int argc, char const *argv[]) |
| { |
| int pipefd[2]; |
| if (pipe(pipefd) == -1) |
| { |
| perror("fail to pipe\n"); |
| exit(1); |
| } |
| |
| pid_t pid; |
| |
| if ((pid = fork()) < 0) |
| { |
| perror("fail to fork"); |
| exit(1); |
| } |
| else if (pid > 0) |
| { |
| char buf[128] = {}; |
| while (1) |
| { |
| fgets(buf, sizeof(buf), stdin); |
| { |
| buf[strlen(buf) - 1] = '\0'; |
| if (write(pipefd[1], buf, sizeof(buf)) == -1) |
| { |
| perror("fail to write"); |
| exit(1); |
| } |
| } |
| } |
| } |
| else |
| { |
| char buf[128] = ""; |
| while (1) |
| { |
| |
| if (read(pipefd[0], buf, sizeof(buf)) == -1) |
| { |
| perror("fail to read"); |
| exit(1); |
| } |
| printf("from parent: %s\n"); |
| } |
| } |
| |
| return 0; |
| } |
输出结果
| hello world |
| from parent: hello world |
注意: 利用无名管道实现进程问的通信,都是父进程创建无名管道,然后再创建子进程,子进程继承父进程的无名管道的文件描述符,然后父子进程通过读写无名管道实现通信。
5.4 无名管道读写规律
5.4.1 读写端都存在,只读不写
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| int pipefd[2]; |
| if (pipe(pipefd) == -1) |
| { |
| perror("fail to pipe\n"); |
| exit(1); |
| } |
| |
| |
| |
| write(pipefd[1], "hello world", 11); |
| |
| char buf[128] = ""; |
| if (read(pipefd[0], buf, sizeof(buf)) == -1) |
| { |
| perror("fail to read"); |
| exit(1); |
| } |
| printf("buf = %s\n", buf); |
| |
| if (read(pipefd[0], buf, sizeof(buf)) == -1) |
| { |
| perror("fail to read"); |
| exit(1); |
| } |
| printf("buf = %s\n", buf); |
| |
| return 0; |
| } |
输出结果
5.4.2 读写端都存在,只写不读
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| int pipefd[2]; |
| if (pipe(pipefd) == -1) |
| { |
| perror("fail to pipe\n"); |
| exit(1); |
| } |
| |
| |
| |
| int num = 0; |
| while (1) |
| { |
| if (write(pipefd[1], "hello world", 1024) == -1) |
| { |
| perror("fail to write"); |
| exit(1); |
| } |
| num++; |
| printf("num = %d\n", num); |
| } |
| |
| return 0; |
| } |
| num = 1 |
| num = 2 |
| num = 3 |
| num = 4 |
| num = 5 |
| num = 6 |
| num = 7 |
| num = 8 |
| num = 9 |
| num = 10 |
| num = 11 |
| num = 12 |
| num = 13 |
| num = 14 |
| num = 15 |
| num = 16 |
| num = 17 |
| num = 18 |
| num = 19 |
| num = 20 |
| num = 21 |
| num = 22 |
| num = 23 |
| num = 24 |
| num = 25 |
| num = 26 |
| num = 27 |
| num = 28 |
| num = 29 |
| num = 30 |
| num = 31 |
| num = 32 |
| num = 33 |
| num = 34 |
| num = 35 |
| num = 36 |
| num = 37 |
| num = 38 |
| num = 39 |
| num = 40 |
| num = 41 |
| num = 42 |
| num = 43 |
| num = 44 |
| num = 45 |
| num = 46 |
| num = 47 |
| num = 48 |
| num = 49 |
| num = 50 |
| num = 51 |
| num = 52 |
| num = 53 |
| num = 54 |
| num = 55 |
| num = 56 |
| num = 57 |
| num = 58 |
| num = 59 |
| num = 60 |
| num = 61 |
| num = 62 |
| num = 63 |
| num = 64 |
5.4.3 只有读端
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| int pipefd[2]; |
| if (pipe(pipefd) == -1) |
| { |
| perror("fail to pipe\n"); |
| exit(1); |
| } |
| write(pipefd[1], "hello world", 11); |
| close(pipefd[1]); |
| |
| char buf[128] = ""; |
| ssize_t bytes; |
| if ((bytes = read(pipefd[0], buf, sizeof(buf))) == -1) |
| { |
| perror("fail to read"); |
| exit(1); |
| } |
| |
| printf("bytes = %ld\n", bytes); |
| printf("buf = %s\n", buf); |
| |
| |
| memset(buf, 0, sizeof(buf)); |
| if ((bytes = read(pipefd[0], buf, sizeof(buf))) == -1) |
| { |
| perror("fail to read"); |
| exit(1); |
| } |
| |
| printf("bytes = %ld\n", bytes); |
| printf("buf = %s\n", buf); |
| |
| return 0; |
| } |
输出结果
| bytes = 11 |
| buf = hello world |
| bytes = 0 |
| buf = |
5.4.4 只有写端
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| void handler(int sig) |
| { |
| printf("SIGPIPE信号产生了,管道破裂了\n"); |
| } |
| |
| int main(int argc, char const *argv[]) |
| { |
| signal(SIGPIPE, handler); |
| int pipefd[2]; |
| if (pipe(pipefd) == -1) |
| { |
| perror("fail to pipe\n"); |
| exit(1); |
| } |
| |
| close(pipefd[0]); |
| int num = 0; |
| while (1) |
| { |
| if (write(pipefd[1], "hello world", 1024) == -1) |
| { |
| perror("fail to write"); |
| exit(1); |
| } |
| num++; |
| printf("num = %d\n", num); |
| } |
| |
| return 0; |
| } |
输出结果
| SIGPIPE信号产生了,管道破裂了 |
| fail to write: Broken pipe |
5.5 fcntl设置文件的阻塞特性
从管道中读数据的特点
- 默认用 read 函数从管道中读数据是阻塞的。
- 调用 write 函数向管道里写数据,当缓冲区已满时 write 也会阻塞。
- 通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到 SIGPIPE 信号)退出
编程时可通过fcntl
函数设置文件的阻塞特性:
| |
| fcntl(fd, F_SETFL, 0); |
| |
| fentl(fd, F_SETFL, O_NONBLOCK): |
非阻塞:
- 如果是阻塞,管道中没有数据,read会一直等待,直到有数据才会继续运行,否则一直等待;
- 如果是非阻塞,read函数运行时,会先看一下管道中是否有数据,如果有数据,则正常运行读取数据;如果管道中没有数据,则read函数会立即返回,继续下面的代码运行。
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/wait.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| int fd_pipe[2]; |
| char buf[] = "hello world\n"; |
| pid_t pid; |
| |
| if (pipe(fd_pipe) < 0) |
| { |
| perror("fail to pipe"); |
| exit(1); |
| } |
| |
| pid = fork(); |
| if (pid < 0) |
| { |
| perror("fail to fork"); |
| exit(1); |
| } |
| if (pid == 0) |
| { |
| while (1) |
| { |
| sleep(5); |
| write(fd_pipe[1], buf, strlen(buf)); |
| } |
| } |
| else |
| { |
| while (1) |
| { |
| |
| fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK); |
| |
| |
| memset(buf, 0, sizeof(buf)); |
| read(fd_pipe[0], buf, sizeof(buf)); |
| printf("buf = [%s] \n", buf); |
| sleep(1); |
| } |
| } |
| |
| return 0; |
| } |
输出结果
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [hello world |
| ] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [hello world |
| ] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [hello world |
| ] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [] |
| buf = [hello world |
| ] |
5.6 文件描述符概述
文件描述符是非负整数,是文件的标识。用户使用文件描述符(file descriptor)来访问文件。
利用open
打开一个文件时,内核会返回一个文件描述符。每个进程都有一张文件描述符的表,进程刚被创建时,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符0、1、2
记录在表中。
在进程中打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。
注意:
Linux中一个进程最多只能打开NR_OPEN_DEFAULT(即1024)
个文件,故当文件不再使用时应及时调用close
函数关闭文件。
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| #if 0 |
| close(0); |
| int fd1, fd2, fd3; |
| fd1 = open("file.txt", O_RDONLY | O_CREAT, 0664); |
| fd2 = open("file.txt", O_RDONLY | O_CREAT, 0664); |
| fd3 = open("file.txt", O_RDONLY | O_CREAT, 0664); |
| |
| printf("fd1 = %d\n", fd1); |
| printf("fd2 = %d\n", fd2); |
| printf("fd3 = %d\n", fd3); |
| return 0; |
| #endif |
| |
| #if 1 |
| int fd; |
| |
| |
| while (1) |
| { |
| if ((fd = open("file.txt", O_RDONLY | O_CREAT, 0664)) < 0) |
| { |
| perror("fail to open"); |
| exit(1); |
| } |
| |
| printf("fd = %d\n", fd); |
| } |
| |
| return 0; |
| #endif |
| } |
5.7 文件描述符的复制
dup,和 dup2 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件。
| int dup(int gldfd); |
| int dup2(int oldfd, int newfd); |
dup
和 dup2
经常用来重定向进程的stdin、stdout 和 stderr
5.7.1 dup函数
| #include <unistd.h> |
| int dup(int oldfd); |
| 功能:复制 oldfd文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符; |
| 参数: |
| 要复制的文件描述符 oldfd; |
| 返回值: |
| 成功:新文件描述符 |
| 失败:返回-1,错误代码存于 errno 中 |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| int main(void) |
| { |
| |
| int fd; |
| fd = dup(1); |
| printf("fd = %d\n", fd); |
| |
| write(fd, "nihao beijing\n", strlen("nihao beijing\n")); |
| |
| return 0; |
| } |
输出结果
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| int main(void) |
| { |
| |
| |
| |
| int fd_file; |
| fd_file = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664); |
| printf("fd = %d\n", fd_file); |
| |
| if (fd_file == -1) |
| { |
| |
| perror("fail to open"); |
| exit(1); |
| } |
| |
| close(1); |
| |
| |
| |
| |
| int fd = dup(fd_file); |
| printf("hello world\n"); |
| printf("fd = %d\n", fd); |
| |
| return 0; |
| } |
终端输出
test.txt
输出
5.7.2 dup2函数
| #include <unistd.h> |
| int dup2(int oldfd, int newfd) |
| 功能: 复制一份打开的文件描述符 oldfd,并分配新的文件描述符newfd,newfd,也标识 oldfd,所标识的文件。 |
| 参数: |
| 要复制的文件描述符 oldkd |
| 分配的新的文件描述符 newfd |
| 返回值: |
| 成功: 返回 newfd |
| 失败: 返回-1,错误代码存于error中 |
| 注意: newfd是小于文件描述符最大允许值的非负整数,如果 newfd,是一个已经打开的文件描述符,则首先关闭该文件,然后再复制。 |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| int fd1; |
| int fd2 = 3; |
| |
| |
| dup2(1, fd2); |
| printf("fd2 = %d\n", fd2); |
| fd1 = open("test01.txt", O_CREAT | O_RDWR, 0664); |
| |
| |
| dup2(fd1, 1); |
| printf("hello world\n"); |
| |
| |
| dup2(fd2, 1); |
| printf("nihao beijing\n"); |
| return 0; |
| } |
终端输出
test01.txt
输出
5.8 有名管道
5.8.1 有名管道概述
命名管道(FIF0)和管道(pipe)基本相同,但也有一些显著的不同,其特点是:
- 半双工,数据在同一时刻只能在一个方向上流动。
- 写入 FIFO 中的数据遵循先入先出的规则。
- FIFO 所传送的数据是无格式的,这要求 FIFO 的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。
- FIFO 在文件系统中作为一个特殊的文件而存在并且在文件系统中可见,但FIFO 中的内容却存放在内存中。
- 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
- 从 FIFO 读数据是一次性操作,数据一旦被读,它就从 FIFO 中被抛弃,释放空间以便写更多的数据。
- 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
- FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。
5.8.2 有名管道创建
| |
| |
| mkfifo 文件名 |
| |
| |
| $ mkfifo myfifo |
| $ ll |
| -rw-rw-r-- 1 spider spider 0 2月 26 11:24 file.txt |
| prw-rw-r-- 1 spider spider 0 2月 26 14:58 myfifo| |
| -rw-rw-r-- 1 spider spider 12 2月 26 14:29 test01.txt |
| -rw-rw-r-- 1 spider spider 19 2月 26 14:28 test.txt |
| |
| #include <sys/types.h> |
| #include<sys/stat.h> |
| int mkfifo(const char *pathname, mode_t mode); |
| 功能:创建一个有名管道,产生一个本地文件系统可见的文件pathname; |
| |
| 参数: |
| pathname:有名管道创建后生成的文件,可以带路径; |
| mode:管道文件的权限,一般通过八进制数设置即可,例如8664; |
| 返回值: |
| 成功: 0 |
| 失败: -1 |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <errno.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| if (mkfifo("fifo file", 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| return 0; |
| } |
终端
| $ ll |
| prw-rw-r-- 1 spider spider 0 2月 26 15:12 fifo_file| |
5.8.3 有名管道的基本读写操作
由于有名管道在本地创建了一个管道文件,所以系统调用的IO函数基本都可以对有名管道进行操作,但是不能使用Iseek修改管道文件的偏移量
注意:有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的操作实质就是对内核空间的操作
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME "fifo_file" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| if (mkfifo("fifo_file", 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| |
| int fd; |
| fd = open(FIFONAME, O_RDWR); |
| if (fd == -1) |
| { |
| perror("fail to open\n"); |
| exit(1); |
| } |
| |
| |
| if ((write(fd, "hello world", strlen("hello world")) == -1)) |
| { |
| perror("fail to write\n"); |
| exit(1); |
| } |
| write(fd, "nihao beijing", strlen("nihao beijing")); |
| |
| char buf[32] = ""; |
| if (read(fd, buf, sizeof(buf)) == -1) |
| { |
| perror("fail to read\n"); |
| exit(1); |
| } |
| |
| printf("buf = [%s]\n", buf); |
| |
| close(fd); |
| |
| return 0; |
| } |
输出结果
| buf = [hello worldnihao beijing] |
5.8.4 有名管道的进程通信
14_fifo_send.c
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME1 "fifo_file1" |
| #define FIFONAME2 "fifo_file2" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| if (mkfifo(FIFONAME1, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| if (mkfifo(FIFONAME2, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| int fd_w, fd_r; |
| if ((fd_w = open(FIFONAME1, O_WRONLY)) == -1) |
| { |
| perror("fail to write\n"); |
| } |
| |
| if ((fd_r = open(FIFONAME2, O_RDONLY)) == -1) |
| { |
| perror("fail to read\n"); |
| } |
| |
| char buf[128] = ""; |
| ssize_t bytes; |
| while (1) |
| { |
| fgets(buf, sizeof(buf), stdin); |
| buf[strlen(buf) - 1] = '\0'; |
| if (bytes = write(fd_w, buf, sizeof(buf)) == -1) |
| { |
| perror("fail to write\n"); |
| } |
| |
| if (bytes = read(fd_r, buf, sizeof(buf)) == -1) |
| { |
| perror("fail to read\n"); |
| } |
| |
| printf("from recv: %s\n", buf); |
| } |
| |
| return 0; |
| } |
15_fifo_recv.c
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME1 "fifo_file1" |
| #define FIFONAME2 "fifo_file2" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| if (mkfifo(FIFONAME1, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| if (mkfifo(FIFONAME2, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| int fd_w, fd_r; |
| if ((fd_r = open(FIFONAME1, O_RDONLY)) == -1) |
| { |
| perror("fail to read\n"); |
| } |
| |
| if ((fd_w = open(FIFONAME2, O_WRONLY)) == -1) |
| { |
| perror("fail to write\n"); |
| } |
| |
| char buf[128] = ""; |
| ssize_t bytes; |
| while (1) |
| { |
| if (bytes = read(fd_r, buf, sizeof(buf)) == -1) |
| { |
| perror("fail to read\n"); |
| } |
| printf("from recv: %s\n", buf); |
| |
| fgets(buf, sizeof(buf), stdin); |
| buf[strlen(buf) - 1] = '\0'; |
| if (bytes = write(fd_w, buf, sizeof(buf)) == -1) |
| { |
| perror("fail to write\n"); |
| } |
| } |
| |
| return 0; |
| } |
| |

5.9 有名管道的读写规律
5.9.1 读写端都存在,只读不写
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME1 "my_fifo" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| if (mkfifo(FIFONAME1, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| |
| int fd; |
| |
| if ((fd = open(FIFONAME1, O_RDWR)) == -1) |
| { |
| perror("fail to open\n"); |
| } |
| |
| 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; |
| } |
输出结果
5.9.2 读写端都存在,只写不读
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME1 "my_fifo" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| if (mkfifo(FIFONAME1, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| |
| int fd; |
| |
| |
| |
| if ((fd = open(FIFONAME1, O_RDWR)) == -1) |
| { |
| perror("fail to open\n"); |
| } |
| |
| int num=0; |
| while (1) |
| { |
| write(fd, "", 1024); |
| num++; |
| printf("num = %d\n", num); |
| } |
| |
| return 0; |
| } |
| |
输出结果
| num = 1 |
| num = 2 |
| num = 3 |
| num = 4 |
| num = 5 |
| num = 6 |
| num = 7 |
| num = 8 |
| num = 9 |
| num = 10 |
| num = 11 |
| num = 12 |
| num = 13 |
| num = 14 |
| num = 15 |
| num = 16 |
| num = 17 |
| num = 18 |
| num = 19 |
| num = 20 |
| num = 21 |
| num = 22 |
| num = 23 |
| num = 24 |
| num = 25 |
| num = 26 |
| num = 27 |
| num = 28 |
| num = 29 |
| num = 30 |
| num = 31 |
| num = 32 |
| num = 33 |
| num = 34 |
| num = 35 |
| num = 36 |
| num = 37 |
| num = 38 |
| num = 39 |
| num = 40 |
| num = 41 |
| num = 42 |
| num = 43 |
| num = 44 |
| num = 45 |
| num = 46 |
| num = 47 |
| num = 48 |
| num = 49 |
| num = 50 |
| num = 51 |
| num = 52 |
| num = 53 |
| num = 54 |
| num = 55 |
| num = 56 |
| num = 57 |
| num = 58 |
| num = 59 |
| num = 60 |
| num = 61 |
| num = 62 |
| num = 63 |
| num = 64 |
5.9.3 在一个进程中,只有读端,没有写端
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME1 "my_fifo" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| if (mkfifo(FIFONAME1, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| printf("************************\n"); |
| |
| int fd; |
| |
| |
| |
| if ((fd = open(FIFONAME1, O_RDONLY)) == -1) |
| { |
| perror("fail to open\n"); |
| } |
| |
| printf("-------------------------------\n"); |
| |
| char buf[128] = ""; |
| ssize_t bytes; |
| if ((bytes = read(fd, buf, sizeof(buf)) == -1)) |
| { |
| perror("fail to read\n"); |
| exit(1); |
| } |
| |
| printf("bytes = %ld\n", bytes); |
| printf("buf = %s\n", buf); |
| |
| return 0; |
| } |
输出结果
5.9.4 在一个进程中,只有写端,没有读端
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #define FIFONAME1 "my_fifo" |
| |
| int main(int argc, char const *argv[]) |
| { |
| |
| |
| if (mkfifo(FIFONAME1, 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fall to mkfifo"); |
| exit(1); |
| } |
| } |
| printf("************************\n"); |
| |
| int fd; |
| |
| |
| |
| if ((fd = open(FIFONAME1, O_WRONLY)) == -1) |
| { |
| perror("fail to open\n"); |
| } |
| |
| printf("-------------------------------\n"); |
| |
| write(fd, "hello world", 11); |
| printf("666\n"); |
| |
| return 0; |
| } |
| |
输出结果
5.9.5 一个进程负责只写端,另一个进程负责只读端
将5.9.3 和5.9.4 中的代码同时运行,保证有名管道的读写端都存在。
规律:
- 只要保证有名管道的读写端都存在,就不会发生阻塞。
- 如果一个进程只读,一个进程只写,都运行后,关闭写端,读端read会返回0
- 如果一个进程只读,一个进程只写,都运行后,关闭读端,写端立即产生SIGPIPE信号(断管),默认处理方式是退出。
5.9.6 有名管道非阻塞规律
指定 O_NONBLOCK
(即 open 位或 O_NONBLOCK
):
- 先以只读方式打开: 如果没有进程已经为写而打开一个 FIFO, 只读 open 成功,并且 open 不阻塞;
- 先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写 open 将出错返回-1;
- read、write 读写命名管道中读数据时不阻塞;
- 通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会收到SIGPIPE 信号退出。
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| int main(int argc, char const *argv[]) |
| { |
| int fd; |
| if (mkfifo("myfifo", 0664) == -1) |
| { |
| if (errno != EEXIST) |
| { |
| perror("fail to mkfifo"); |
| exit(1); |
| } |
| } |
| |
| #if 0 |
| |
| fd = open("myfifo", O_RDONLY | O_NONBLOCK); |
| if (fd < 0) |
| { |
| perror("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); |
| } |
| |
| #endif |
| |
| #if 0 |
| char send[100] = "hello I love you"; |
| |
| fd = open("myfifo", O_WRONLY | O_NONBLOCK); |
| if (fd < 0) |
| { |
| perror("open fifo"); |
| exit(1); |
| } |
| write(fd, send, strlen(send)); |
| printf("write to my_fifo buf = %s\n",send); |
| char recv[100]; |
| read(fd, recv, sizeof(recv)); |
| printf("read from my_fifo buf = [%s]\n", recv); |
| |
| #endif |
| |
| #if 1 |
| char send[100] = "hello I love you"; |
| |
| fd = open("myfifo", O_RDWR | O_NONBLOCK); |
| if (fd < 0) |
| { |
| perror("open fifo"); |
| exit(1); |
| } |
| write(fd, send, strlen(send)); |
| printf("write to my_fifo buf = %s\n",send); |
| char recv[200]; |
| read(fd, recv, sizeof(recv)); |
| printf("read from my_fifo buf = [%s]\n", recv); |
| |
| #endif |
| |
| return 0; |
| } |
| |
输出结果
| write to my_fifo buf = hello I love you |
| read from my_fifo buf = [hello I love you] |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步