四十八、进程间通信——标准库中的管道操作及命名管道和匿名管道

48.1 标准库中的管道操作

48.1.1 标准库中的管道操作

1 #include <stdio.h>
2 FILE *popen(const char *cmdstring, congst char *type);
3 int pclose(FILE *fp)
  • 函数说明:
    • 使用 popen() 创建的管道必须使用 pclose() 关闭。其实,popen/pclose 和标准文件输入/输出流中的 fopen()/fclose 十分相似。
    • 封装管道的常用操作
  • 返回值:
    • popen:成功,返回文件指针;出错,返回 NULL
    • pclose:cmdstring 的终止状态,出错返回 -1

  

  

48.1.2 例子

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 
 6 int main(void)
 7 {
 8     FILE *fp;
 9     /** 命令执行的结果放置在 fp 指向的结构体缓存中 */
10     fp = popen("cat /etc/passwd", "r");
11     char buf[512];
12     memset(buf, 0, sizeof(buf));
13     while(fgets(buf, sizeof(buf), fp) != NULL){
14         printf("%s", buf);
15     }
16 
17     pclose(fp);
18 
19     printf("-----------------------------------------------------------------\n");
20     /** 为 wc 命令提供统计的数据 */
21     fp = popen("wc -l", "w");
22     /** 向 fp 指向的结构体缓存中写入数据 */
23     fprintf(fp, "1\n2\n3\n");
24     pclose(fp);
25 
26     return 0;
27 }

  编译运行:

  

48.2 命名管道和匿名管道

48.2.1 命名管道的创建

1 #include <sys/types.h>
2 #include <sys/stat.h>
3 int mkfifo(const char *pathname, mode_t mode);
  • 只要对 FIFO 有适当访问权限,FIFO 可用在任何两个没有任何关系的进程间通信。
  • 本质是内核中的一块缓存,另在文件系统中以一个特殊的设备文件(管道文件)存在
  • 在文件系统中只有一个索引块存放文件的路径,没有数据块,所有数据块存放在内核中
  • 命名管道必须读和写同时打开,否则单独读或者单独写会引发阻塞
  • 命令 mkfifo 创建命名管道(命令内部调用 mkfifo 函数)
  • 对 FIFO 的操作与普通文件一样
  • 一旦已经用 mkfifo 创建了一个 FIFO,就可用 open 打开它,一般的文件 I/O (close、read、write、unlink 等)都可用于 FIFO
  • FIFO 相关出错信息
    • EACCES:无存取权限
    • EEXIST:指定文件不存在
    • ENAMETOOLONG:路径不存在
    • ENOENT:包含的目录不存在
    • ENOSPC:文件系统剩余空间不足
    • ENOTDIR:文件路径无效
    • EROFS:指定的文件存在于只读文件系统中

48.2.2 命名管道例子

  创建两个没有关系的程序,即两个进程,通过命名管道进行读写

    fifo_read.c

  

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <fcntl.h>
 5 #include <memory.h>
 6 
 7 /** 从命名管道中读取数据 */
 8 int main(int argc, char *argv[])
 9 {
10     if(argc < 2){
11         printf("usage: %s fifo\n", argv[0]);
12         exit(1);
13     }
14     printf("open fifo read ...\n");
15     /** 打开命名管道 */
16     int fd = open(argv[1], O_RDONLY);
17     if(fd < 0){
18         perror("open error");
19     }
20     else {
21         printf("open file success: %d\n", fd);
22     }
23 
24     /** 从命名管道中读取数据 */
25     char buf[512];
26     memset(buf, 0, sizeof(buf));
27     while(read(fd, buf, sizeof(buf)) < 0){
28         perror("read error");
29     }
30     printf("%s\n", buf);
31 
32     close(fd);
33     return 0;
34 }

  fifo_write.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <memory.h>
 5 #include <fcntl.h>
 6 
 7 
 8 int main(int argc, char *argv[])
 9 {
10     if(argc < 2){
11         printf("usage: %s fifo \n", argv[0]);
12         exit(1);
13     }
14     printf("open fifo write...\n");
15 
16     /** 打开命名管道 */
17     int fd = open(argv[1], O_WRONLY);
18     if(fd < 0){
19         perror("open error");
20         exit(1);
21     }
22     else {
23         printf("open fifo success: %d\n", fd);
24     }
25 
26     char *s = "hello world";
27     size_t size = strlen(s);
28     if(write(fd, s, size) != size){
29         perror("write error");
30     }
31     close(fd);
32 
33     return 0;
34 }

  单独编译这两个文件为可执行程序,先在一个终端中运行read,再在另一个终端运行 write。

  在没有打开 write 之前,read 会阻塞,在打开之后,read 即可正常读取

48.3 匿名管道和命名管道读写的差异

  • 相同点
    • 默认都是阻塞性读写
    • 都适用于 socket 的网络通信
    • 阻塞不完整管道(有一端关闭)
      • 单独读时,在所有数据被读取后,read 返回 0,以表示到达了文件尾部
      • 单纯写时,则产生信号 SIGPIPE,如果忽略该信号或捕捉该信号并从处理程序返回,则 write 返回 -1,同时 errno 设置为 EPIPE
    • 阻塞完成管道(两端都开启)
      • 单纯读时,要么阻塞,要么读取到数据
      • 单纯写时,写到管道满时会出错
    • 非阻塞不完整管道(有一端关闭)
      • 单纯读时直接报错
      • 单纯写时,则产生信号 SIGPIPE,如果忽略该信号或捕捉该信号并从处理程序返回,则 write 返回 -1,同时 errno 设置为 EPIPE
    • 非阻塞完整管道(两端都开启)
      • 单纯读时直接报错
      • 单纯写时,写到管道满时会出错
  • 不同点:
    • 打开方式不一样
    • pipe(匿名管道) 通过 fcntl 系统调用来设置 O_NONBLOCK 来设置非阻塞性读写
    • FIFO 通过 fcntl 系统调用或者 open 函数来设置非阻塞性读写

猜你喜欢

转载自www.cnblogs.com/kele-dad/p/10293483.html