1)管道概念
管道是单向的,先进先出的(FIFO),它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道头部读出数据。
2)管道创建
管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中任意两个进程间的通信。
无名管道由pipe()函数创建:
int pipe(int fd[2]);
当一个管道建立时,它会创建两个文件描述符,fd[0]用于读管道,fd[1]用于写管道,即一个管道的头部和尾部的描述符。
3)管道关闭
关闭管道只需要将这两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。
4)管道读写
管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。管道创建于内核中,fd[1]创建于父进程中,用于向管道中写数据;fd[0]创建于子进程中,用于从管道中读数据。
注意:必须在系统调用fork()之前调用pipe(),否则子进程将不会继承文件描述符。如果先调用fork()再调用pipe(),则会创建两个管道,即父子进程各用各的管道,达不到通信的目的。
5)例 pipe_test.c
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
int main() {
int pipe_fd[2];
pid_t pid;
char buf[100];
char *p_buf;
int r_num;
memset(buf, 0, sizeof(buf));
//创建管道
if (pipe(pipe_fd) < 0) {
printf("pipe create error\n");
return -1;
}
//创建子进程
if ((pid = fork()) == 0) {
printf("\n");
close(pipe_fd[1]);
sleep(2); //睡眠2s,等待父进程将数据写入管道
if ((r_num = read(pipe_fd[0], buf, 100)) > 0) {
printf("%d numbers read from the pipe is %s\n", r_num, buf);
}
close(pipe_fd[0]);
exit(0);
} else if (pid > 0) {
close(pipe_fd[0]);
if (write(pipe_fd[1], "Hello", 5) != -1) {
printf("parent process write Hello!\n");
}
if (write(pipe_fd[1], " Pipe", 5) != -1) {
printf("parent process write Pipe!\n");
}
close(pipe_fd[1]);
sleep(3);
waitpid(pid, NULL, 0); //等待子进程结束
exit(0);
}
}
6)命令管道(FIFO)
命名管道和无名管道基本相同,不同点:无名管道只能用于父子进程;但通过命名管道,不相关的进程也能交换数据。
7)FIFO创建
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
参数: pathname FIFO文件名
mode 属性(与文件操作的mode属性一样)
一旦创建一个FIFO,就可使用一般的文件访问函数(open、close、
read、write等)操作FIFO。命名管道实际上就是一个文件。
8)FIFO操作
当打开FIFO是,不阻塞标志(O_NONBLOCK)将对以后的读写产生如下影响:
1,没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如想要读取空的FIFO就会导致进程阻塞。
2,使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO。
9)例:fifo_rd.c和fifo_wr.c(两个进程,所以需要两个文件)
fifo_rd.c
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FIFO "/tmp/myfifo"
main(int argc, char **argc) {
char buf_r[100];
int fd;
int read_len;
//创建管道
if ((mkfifo(FIFO, O_CREAT | O_EXCL)<0) && (errno!=EEXIST)) {
printf("can't create fifoserver\n");
}
memset(buf_r, 0, sizeof(buf_r));
//打开管道
if ((fd = open(FIFO, O_RDONLY | O_NINBLOCK, 0)) == -1) {
perror("open");
exit(1);
}
while(1) {
memset(buf_r, 0, sizeof(buf_r));
if ((read_len=read(fd, buf_r, 100)) == -1) {
if (errno == EAGAIN)
printf("has not data\n");
}
printf("read %s from FIFO\n", buf_r);
sleep(1);
}
pause(); //暂停,等待信号
unlink(FIFO);
}
fifo_wr.c
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FIFO_SERVER "/tmp/myfifo"
main(int argc, char **argv) {
int fd;
char w_buf[100];
int write_len;
//打开管道
fd = open(FIFO_SERVER, O_WRONLY | O_NONBLOCK, 0);
if (argc == 1) {
printf("Please send something\n");
exit(-1);
}
strcpy(w_buf, argv[1]);
//向管道写入数据
if ((write_len = write(fd, w_buf, 100)) == -1) {
if (errno == EAGAIN) {
printf("The FIFO has not been read\n");
}
} else {
printf("write %s to the FIFO\n", w_buf);
}
}
注意:管道里面的数据被读过之后就会自动清除,即存入和取出。上面两个程序要先运行fifo_rd.c,因为管道在该文件中创建。