Linux进程间通信 -- 匿名管道和FIFO
匿名管道
管道包括三种:
- 匿名管道pipe. 特点:
- 一是单工(单项传输)
- 二是只能在父子或者兄弟进程使用
- 流管道s_pipe. 特点:
- 半双工(可以双向传输,但不能同时读写)
- 只能在父子或兄弟进程间使用
- 命名管道FIFO. 特点:
- 半双工
- 不相关进程之间也可进行通讯
管道特点是由Linux内核开辟一段缓冲区, 管道的两端分别连接两个进程, 管道有如下特点:
- 管道一端连接的进程向管道中进行输入,管道另一端的进程从管道中读取数据. 一般一个管道被设计成环形数据结构,可以被循环利用.
- 如果管道中没有信息的话,从管道中读的进程会进等待,直到另一端的进程写入数据到管道中.
- 当管道被写满时,尝试写入的进程会等待,直到另外一端读出信息.
- 当两个进程都终结的时, 管道也会自动消失.
具体api如下:
#include <unistd.h>
int pipe(int filedes[2]);
如果创建成功,会打开两个文件描述符:
-
filedes[0]
用于读出数据,读取时必须关闭写入端,即close(filedes[1])
; -
filedes[1]
用于写入数据,写入时必须关闭读取端,即close(filedes[0])
。
实现ps aux | grep bash
:
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2];
pipe(fd);
pid_t pid = fork();
if (pid == 0) {
// son -- ps
// 1. redirect
close(fd[0]); // close read
dup2(fd[1], STDOUT_FILENO); // stdout -> fd[1]
execlp("ps", "ps", "aux", NULL);
} else if (pid > 0) {
close(fd[1]);
// parent
// redirect: stdin -> fd[0]
dup2(fd[0], STDIN_FILENO);
execlp("grep", "grep", "bash", NULL);
}
return 0;
}
对读管道:
- 写端全部关闭 -- read读到0,相当于读到文件末尾
- 写端没有全部关闭
- 有数据 -- read读到数据
- 没有数据 -- read堵塞, 使用
fcntl
函数可以改成非阻塞
对写管道:
- 读端完全关闭 -- ? 产生一个信号
SIGPIPE
,程序异常终止 - 读端未全部关闭
- 管道已满 -- write阻塞
- 管道未满 -- write正常写入
可以使用ulimit -a
, 可以打印出当前进程的各种限制, 其中pipe size
就是系统创建的匿名管道的大小:
查看限制情况 ulimit -a
可以看到如下信息
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
pending signals (-i) 1024
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
有名管道FIFO
FIFO是有名管道, 可以实现无血缘关系的进程间通信.
- 我们可以用Linux命令
mkfifo
创建一个.实际创建的是一个伪文件. - 也可以用函数
int mkfifo(...)
创建
Linux内核会针对FIFO文件开辟一个缓冲区,操作FIFO文件,实现进程间通信, 其实可以认为FIFO只是借用了文件系统(file system
,命名管道是一种特殊类型的文件).
linux系统API创建FIFO, 和创建文件差不多:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int res = mkfifo("/tmp/my_fifo", 0777);
if (res == 0)
{
printf("FIFO created/n");
}
exit(EXIT_SUCCESS);
}
从FIFO文件中读:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("./a.out fifoname\n");
return -1;
}
// open fifo file
int fd = open(argv[1], O_RDONLY);
char buf[256];
int ret = 0;
while (1) {
ret = read(fd, buf, sizeof(buf));
if (ret > 0) {
printf("read:%s\n", buf);
}
sleep(1);
}
close(fd);
return 0;
}
向FIFO中写:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("./a.out fifoname\n");
return -1;
}
// open fifo file
int fd = open(argv[1], O_WRONLY);
char buf[256];
int num = 1;
while (1) {
memset(buf, 0x00, sizeof(buf));
sprintf(buf, "xiaoming%04d", num++);
write(fd, buf, strlen(buf));
sleep(1);
}
close(fd);
return 0;
}
注意: FIFO的open时,read端会阻塞等待write端open, wirte端同理,也会阻塞等待另一端打开.
网友评论