目录
管道
实现原理
特质
局限性
读写行为
读管道
写管道
缓冲区大小
返回值
优缺点
优点
缺点
pipe
参数pipefd[2]
返回值
测试代码1
测试结果
测试代码2
测试结果
测试代码3
测试结果
fifo
创建方式
参数pathname
参数mode
返回值
测试代码4
测试结果
测试代码5
测试结果
进程间文件通信
测试代码6
测试结果
内核戒指环形队列机制,使用内核缓冲区实现,较为简单。
伪文件。
管道中的数据只能一次读取。
数据在管道中,只能单向流动。
只能自己写,不能自己读。
数据不可以反复读。
半双工通信。
血缘关系进程间可用。
管道有数据:read返回实际读到的字节数。
管道无数据:
写端全部关闭,read函数返回0。
写端不关闭,read函数阻塞等待。
读端全部关闭,异常终止,SIGPIPE信号导致。
读端不关闭:
管道数据已满,阻塞等待。
管道数据未满,返回写出的字节个数。
ulimit -a
man 3 fpathconf
成功:管道的大小。
失败:-1。
管道相比信号,套接字实现进程间通信,简单很多。
只能单向通信,双向通信需建立两个管道。
只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用fifo有名管道解决。
创建并打开管道。
man 2 pipe
pipefd[0]:读端。
pipefd[1]:写端。
成功:0
失败:-1
父进程用管道写内容,子进程用管道读内容,将读取到内容输出到终端上。
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int GuanDao_flag; //管道标志位
int pipefd[2];
char data[1024]; //接收的数据
int leng; //接收数据的长度
pid_t JinCheng_ID; //进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建管道!\n");
GuanDao_flag = pipe(pipefd); //创建管道
if (GuanDao_flag == -1)
{
perror("创建管道错误");
}
printf("开始创建进程!\n");
JinCheng_ID = fork();
if (JinCheng_ID > 0) //父进程
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), JinCheng_ID);
close(pipefd[0]); //关闭读端
write(pipefd[1], "你好,世界!\n", strlen("你好,世界!\n")); //通过管道向子进程发送数据
sleep(1);
close(pipefd[1]);
printf("这是父进程,父进程结束。\n");
}
else if (JinCheng_ID == 0) //子进程
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[1]); //子进程关闭写端
leng = read(pipefd[0], data, sizeof(data)); //接收父进程发送的数据
write(STDOUT_FILENO, data, leng); //将数据写到终端上
close(pipefd[0]);
printf("这是子进程,子进程结束。\n");
}
return 0;
}
用管道实现ls | wc -l命令。
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int GuanDao_flag; //管道标志位
int pipefd[2];
char data[1024]; //接收的数据
int leng; //接收数据的长度
pid_t JinCheng_ID; //进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建管道!\n");
GuanDao_flag = pipe(pipefd); //创建管道
if (GuanDao_flag == -1)
{
perror("创建管道错误");
exit(1);
}
printf("创建管道完成!\n");
printf("开始创建进程!\n");
JinCheng_ID = fork();
if (JinCheng_ID == 0) //子进程
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[1]); //子进程关闭写端
dup2(pipefd[0], STDIN_FILENO); //终端输入重定向到管道的读端
execlp("wc", "wc", "-l", NULL);
perror("子进程错误");
}
else if (JinCheng_ID > 0) //父进程
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), JinCheng_ID);
close(pipefd[0]); //关闭读端
dup2(pipefd[1], STDOUT_FILENO); //终端输出重定向到管道的写端
execlp("ls", "ls", NULL);
perror("父进程错误");
}
else if (JinCheng_ID == -1)
{
perror("创建进程错误");
exit(1);
}
return 0;
}
使用兄弟进程间实现ls | wc -l命令。
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int GuanDao_flag; //管道标志位
int pipefd[2];
pid_t JinCheng_ID; //进程ID
int i;
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建管道!\n");
GuanDao_flag = pipe(pipefd); //创建管道
if (GuanDao_flag == -1)
{
perror("创建管道错误");
exit(1);
}
printf("创建管道完成!\n");
printf("开始创建进程!\n");
for (i = 0; i < 2; i++)
{
JinCheng_ID = fork();
if (JinCheng_ID == -1)
{
perror("创建进程错误");
exit(1);
}
else if (JinCheng_ID == 0) //子进程
{
break;
}
}
if(i==0){
printf("这是子1进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[0]); //子1进程关闭读端
dup2(pipefd[1], STDOUT_FILENO); //终端输出重定向到管道的写端
execlp("ls", "ls", NULL);
perror("子1进程错误");
}
else if(i==1){
printf("这是子2进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[1]); //子2进程关闭写端
dup2(pipefd[0], STDIN_FILENO); //终端输入重定向到管道的读端
execlp("wc", "wc", "-l", NULL);
perror("子进程错误");
}
else if(i==2){
printf("这是父进程,当前进程的ID是%d。\n", getpid());
close(pipefd[0]); //父进程关闭读端、写端,保证兄弟进程之间形成单向通信
close(pipefd[1]);
wait(NULL);
wait(NULL);
printf("父进程结束。\n");
}
return 0;
}
FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO,不相关的进程也能交换数据。 FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各进程可以打开这个文件进read/write,实际上是在读写内核通道,实现了进程间通信。
mkfifo 管道文件名
man 3 mkfifo
管道文件名。
管道文件权限。
成功:0
失败:-1
使用mkfifo创建一个管道文件。
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int flag;
flag=mkfifo("GuanDao",0664);
if(flag==-1){
perror("创建管道文件错误");
}
return 0;
}
利用管道,将两个毫无相关的进程进行连接通信。
/*
CeShi5_1.c
接收CeShi5_2进程的数据
*/
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fd, leng;
char data[4096];
printf("程序开始运行。\n");
printf("开始打开管道文件的读端。\n");
fd = open(argv[1], O_RDONLY);
if (fd == -1)
{
perror("打开管道文件错误");
exit(1);
}
printf("打开管道文件的读端完成。\n");
printf("开始读取管道文件读端的数据。\n");
while (1)
{
leng = read(fd, data, sizeof(data));
if (leng > 0)
{
//printf("读取到数据为:");
write(STDOUT_FILENO, "读取到数据为:", strlen("读取到数据为:"));
write(STDOUT_FILENO, data, leng);
}
}
close(fd);
return 0;
}
/*
CeShi5_2.c
向CeShi5_1进程发送数据
*/
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fd, i;
char data[4096];
printf("程序开始运行。\n");
printf("开始打开管道文件的写端。\n");
fd = open(argv[1], O_WRONLY);
if (fd == -1)
{
perror("打开管道文件错误");
exit(1);
}
printf("打开管道文件的写端完成。\n");
printf("开始向管道文件写端写数据。\n");
i = 1;
while (1)
{
sprintf(data, "你好,世界!这是写进程第%d次向管道写端写数据。\n", i);
write(fd, data, strlen(data));
printf("第%d次写成功。\n", i);
i++;
sleep(1);
}
close(fd);
return 0;
}
使用文件完成毫无关系进程间的通信。
/*
CeShi6_1.c
优先执行数据的写入文件
*/
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fd, flag;
char *data = "你好,世界!这是CeShi6_1进程写的。\n";
char data1[1024];
int leng;
printf("程序开始运行。\n");
printf("开始打开文件。\n");
fd = open("temp.txt", O_RDWR | O_TRUNC | O_CREAT, 0664);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("开始文件写数据。\n");
write(fd, data, strlen(data));
printf("写的数据是:%s", data);
printf("文件写数据完成。\n");
printf("开始睡大觉。\n");
sleep(5);
printf("睡醒了,康康文件的数据。\n");
lseek(fd, 0, SEEK_SET); //设置偏移量从头开始
leng = read(fd, data1, sizeof(data1));
flag=write(STDOUT_FILENO,data1,leng);
if(flag==-1){
perror("输出错误");
exit(1);
}
printf("我真是服了,把我数据给改了,CeShi6_2你个老6。\n");
close(fd);
return 0;
}
/*
CeShi6_2.c
读取文件数据和修改文件数据
*/
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fd, flag;
char *data = "你好,世界!这是CeShi6_2进程写的。\n";
char data1[1024];
int leng;
printf("程序开始运行。\n");
printf("开始睡大觉。\n");
sleep(2);
printf("睡醒了,我是老6,把你CeShi6_1进程写的数据给改了。\n");
printf("开始打开文件。\n");
fd = open("temp.txt", O_RDWR);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("让我康康你写了啥。\n");
leng = read(fd, data1, sizeof(data1));
write(STDOUT_FILENO, data1, leng);
printf("哦豁,写了这玩意。\n");
printf("开始文件写数据。\n");
lseek(fd, 0, SEEK_SET); //设置偏移量从头开始
write(fd, data, strlen(data));
printf("写的数据是:%s", data);
printf("文件写数据完成。\n");
close(fd);
return 0;
}