进程间通信 - 管道

it2022-05-06  8

进程间通信 - 管道

进程间通信的方式:

管道:匿名管道和有名管道信号 - 系统开销小共享映射区 - 有无血缘关系的进程间通信都可以本地套接字 - 稳定

IPC(InterProcess Communication):进程间通信

1. 管道

1.1管道的概念:管道是内核缓冲区,可以叫做伪文件(不占用磁盘文件) (1)管道的特点: ①管道有读端和写端。数据从写端流入,读端流出。读端和写端是两个文件描述符。 ②含有管道的进程结束后,管道被释放 ③管道默认是阻塞的。数据必须读端流入,写端流出

2.管道的原理:

(1)内部实现方式:队列(环形队列),先进先出。 (2)缓冲区大小:默认为4k,大小会根据实际情况做适当的调整

3.管道的局限性

(1)数据只能读取一次,不能重复使用,数据传输方向是单向的(半双工)

单工:遥控器 半双工:数据传输的方向是单向的,对讲机 双工:数据时双向流动的,电话

(2)匿名管道:只适用于由血缘关系的进程间通信

4.创建匿名管道

函数:int  pipe(int  pipefd[2]);

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <string.h> #include <sys/stat.h> int main() { int fd[2]; int ret = pipe(fd); if(ret == -1){ perror("pipe"); exit(1); } printf("pipe[0] = %d\n", fd[0]); printf("pipe[1] = %d\n", fd[1]); close(fd[0]); close(fd[1]); return 0; }

5.父子进程使用匿名管道通信

单个进程也能使用管道完成通信,平时没有这个必要

例:父进程将数据写入管道,子进程从管道中读取数据

/* 实现 ps aux | gerp "bash" */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <string.h> #include <sys/stat.h> int main() { int fd[2]; int ret = pipe(fd); if(ret == -1){ perror("pipe"); exit(1); } pid_t pid = fork(); if(pid == -1){ perror("fork"); exit(1); } //父进程 ps aux //子进程 grep "bush" if(pid > 0){ //写管道操作,关闭读端 close(fd[0]); //重定向,STDOUT -> 管道的写端 dup2(fd[1], STDOUT_FILENO); //执行ps aux execlp("ps", "ps", "aux", NULL); perror("execlp"); exit(1); } else if(pid == 0){ close(fd[1]); dup2(fd[0], STDIN_FILENO); execlp("grep", "grep", "bash", "--color=auto", NULL); perror("execlp"); exit(1); } close(fd[0]); close(fd[1]); return 0; }

注意事项: 父进程写数据 – 关闭写端 子进程读数据 – 关闭读端 因为管道是阻塞的

程序中的重定向: 父进程中 – dup2(fd[1], STDOUT_FILENO); //STDOUT_FILENO跟随 fd[1] 子进程中 – dup2(fd[0], STDIN_FILENO); //STDIN_FILENO跟随 fd[0]

5.1 管道的读写行为

1.读操作

(1)管道中有数据:

read(fd) - 正常读数据,返回读出的字符数

(2)管道中无数据:

写端全部关闭:read解除阻塞,返回0,相当于读文件时读到了尾部没有全部关闭:read阻塞

2.写操作

(1)读端全部关闭

管道破裂,进程被终止:内核给当前进程发信号SIGPIPE(13号信号)

(2)读端没有被全部关闭

缓冲区写满:write阻塞缓冲区没满:write继续写

(3)设置管道为非阻塞

获取原来的flags:int flag = fcntl(fd[0].F_GETFL);设置新的flags:flag |= O_NONBLOCK; //flags  =  flags  |  O_NONBLOCK;fcntl(fd[0],  F_SETFL,  flags);

5.2 查看管道缓冲区大小

1.命令查看:ulimit -a 2.函数:

#include <unistd.h> long fpathconf(int fd, int name); long pathconf(const char *path, int name);

6. 有名管道(fifo)

6.1 fifo的特点

在磁盘上有文件磁盘上的文件为伪文件,占用的大小为0内核中由对应的缓冲区

6.2 fifo的使用

1.使用场景:没有血缘关系的进程间通信 2.创建方式:

命令:mkfifo 管道名函数:mkfifo #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);

参数: ①pathname:fifo文件名 ②mode:fifo文件的打开方式 例:该进程以只读的方式打开 myfifo

int fd = open("myfifo", O_RDONLY);

6.3 fifo文件的IO函数操作

open() / close()read() / write()不能使用lseek()

6.4 进程间通信:使用 myfifo 进程通信

现有两个进程a,b属于不同的进程组。 a进程进行读操作,b进程进行写操作 a.c —> read

int fd = open("myfifo", O_RDONLY); read(fd, buf, sizeof(buf); close(fd);

b.c —>read

int fd1 = open("myfifo", O_WRONLY); write(fd1, "hello,world", 11); close(fd1);

最新回复(0)