赞
踩
本节关键字:C语言 进程间通信 管道 FIFO
相关库函数:pipe、mkfifo、mknod、write、read
管道通常指“无名管道”,是Unix系统中最古老的IPC通信方式。
#include <unistd.h>
int pipe(int fd[2]);
/**
@brief 创建管道,需要手动关闭管道,即手动关闭两个文件描述符
@param fd 承接管道创建时的两个文件描述符,fd[0]为读端描述符,fd[1]为写端描述符
@return 成功返回0,失败返回-1并设置错误码error
错误码error类型:
EFAULT pipefd无效
EINVAL (pipe2())标志中的值无效
EMFILE 进程正在使用的文件描述符过多
ENFILE 打开文件总数已达到系统限制
*/
// 管道使用例程:创建管道和进程,子进程读管道,父进程写管道 #include <sys/wait.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(int argc, char *argv[]) { int pipefd[2]; pid_t cpid; char buf; assert(argc == 2); // 断言:有一个命令行参数,argv[1]将被写入管道 if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } cpid = fork(); if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (cpid == 0) // 子进程:关闭写文件描述符,读管道 { close(pipefd[1]); while (read(pipefd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1); write(STDOUT_FILENO, "\n", 1); close(pipefd[0]); _exit(EXIT_SUCCESS); } else // 父进程:关闭读文件描述符,写管道,将命令行参数argv[1]写入管道 { close(pipefd[0]); write(pipefd[1], argv[1], strlen(argv[1])); close(pipefd[1]); // 读端将收到EOF wait(NULL); // 等待子进程退出 exit(EXIT_SUCCESS); } return 0; }
#include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); /** @brief 创建FIFO管道,需要手动关闭管道,即手动关闭文件描述符。FIFO特殊文件不是匿名通信通道,而是通过调用mkfifo()输入到文件系统中 @param pathname FIFO的路径名,文件真实存在 @param mode 创建FIFO文件的权限,创建的文件的权限为(mode&~umask) @return 成功返回0,失败返回-1并设置错误码error 错误码error分类: EACCES 路径名中的一个目录没有搜索(执行)权限 EEXIST 路径名已存在。这包括路径名是符号链接的情况,无论是否悬空 ENAMETOOLONG 路径名的总长度大于PATH_MAX,或者单个文件名组件的长度大于NAME_MAX。在GNU系统中,对文件名的总长度没有强制限制,但一些文件系统可能会对组件的长度进行限制 ENOENT 路径名中的目录组件不存在,或者是悬挂的符号链接 ENOSPC 目录或文件系统没有空间容纳新文件 ENOTDIR 在路径名中用作目录的组件实际上不是目录 EROFS 路径名是指只读文件系统 Trip:一旦你以这种方式创建了一个FIFO特殊文件,任何进程都可以像普通文件一样打开它进行读取或写入。然而,在您可以继续对其进行任何输入或输出操作之前,它必须同时在两端打开。打开FIFO以正常读取块,直到其他进程打开相同的FIFO进行写入,反之亦然。有关fifo特殊文件的非阻塞处理,请参见fifo(7)。 */
FIFO使用例程:创建FIFO,多个个服务端读,多个个客户端写,验证管道的互斥功能
// FIFO使用例程 服务端,循环读取管道中的信息,直到管道的所有写端关闭 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> int main(void) { char pathname[] = "./myfifo"; int ret, fd; char buffer[1024]; umask(0); ret = mkfifo(pathname, 0666); // mode附加读写权限 fd = open(pathname, O_RDONLY); // 服务端只读打开FIFO文件 if (fd < 0) return -1; for ( ; ; ) { bzero(buffer, 0); ret = read(fd, buffer, sizeof(buffer)-1); if (ret > 0) { printf("server recv: %s\n", buffer); fflush(stdout); } else if (ret == 0) { printf("server: client quit\n"); break; } else { perror("server failed to read myfifo\n"); break; } } close(fd); return 0; }
// FIFO使用例程:客户端,将用户输入的信息写入管道,循环执行6次退出 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> int main(void) { char pathname[] = "./myfifo"; int ret, fd, cnt; char buffer[1024]; fd = open(pathname, O_WRONLY); // 客户端只写开打FIFO文件 if (fd < 0) return -1; do { printf("please input message: "); fflush(stdout); bzero(buffer, 0); ret = read(STDIN_FILENO, buffer, sizeof(buffer)-1); if (ret <= 0) { perror("read"); } else { write(fd, buffer, ret); } } while (++cnt <= 5); close(fd); return 0; }
/// 从两个服务端的运行结果可知,管道有互斥功能,同一时刻只有一个进程可以从管道读取数据 // 服务端1: $ ./fifoserver server recv: client1: 1 server recv: client2: 1 server recv: client1: 2 server recv: client1: 3 server recv: client1: 5 server recv: client2: 4 server recv: client2: 6 server: client quit // 服务端2: $ ./fifoserver server recv: client1: 4 server recv: client1: 6 server recv: client2: 2 server recv: client2: 3 server recv: client2: 5 server: client quit // 客户端1: $ ./fifoclient please input message: client1: 1 please input message: client1: 2 please input message: client1: 3 please input message: client1: 4 please input message: client1: 5 please input message: client1: 6 // 客户端2: $ ./fifoclient please input message: client2: 1 please input message: client2: 2 please input message: client2: 3 please input message: client2: 4 please input message: client2: 5 please input message: client2: 6
启动server后使用ll命令查看目录文件信息,可以看到已经创建了myfifo文件
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。