Linux进程间通信之管道
在Linux中进程间通信的方式一共有八种,接下来给大家介绍的是无名管道与有名管道。
无名管道(PIPE)
首先介绍创建管道所需要的函数:
所需头文件:
#include <unistd.h>
函数原型:
int pipe(int pipefd[2]);
参数说明:
pipefd:创建无名管道时所产生的文件描述符数组。pipefd[0]是管道的读端,pipefd[1]是管道的写端。
返回值:
成功时返回0,失败时返回-1并将失败原因写入errno。
堵塞
管道顾名思义,就像一根管子有出入口。入口就是将数据放入管道的写端,出口自然就是读端。当进程使用无名管道进行通信时,如果管道中没有数据时,当读端先执行它将会等待写端写入数据。当管道中的数据满了以后,写端将等待读端进行读取数据。这是管道的堵塞特性。
作用范围
使用无名管道进行通信时,它的文件描述符只能被其子进程或孙子进程以下的进程所继承且只能打开一次。也就是说只能与跟他有血缘关系的进程之间通信,只能用于家族中通信。
双半工通信
当无名管道的在读时,则不能进行写,在写时不能进行读。
不具备原子性
当一个进程在对管道进行读写时,另一个进程再进行读写时会打断上一个进程。从而造成数据错乱,所以不具备原子性。
读写异常
当读端的全部文件描述符被关闭时,再去写入内容是会被信号SIGPIPE杀死进程,默认退出程序。
当写端的全部文件描述符被关闭时,再去读取内容则有内容读出来无内容返回0并退出程序。
小练习
a:用pipe产生一个无名管道之后,再连续产生两个子进程A、B
b:子进程A在往管道写了一句hello 后就退出了
c:子进程B读到A写入的数据,再往管道中写入hello world
d:主进程读取最后管道中的数据
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
int pipe_fd[2],retvel;
pid_t pid,pid2;
char buffer[20];
retvel = pipe(pipe_fd);/*创建无名管道*/
if(retvel == -1)/*错误判断*/
{
perror("ceart pipe err");
return -1;
}
pid = fork();/*创建子进程一*/
if(pid == 0)
{
write(pipe_fd[1],"A:hello",8);
close(pipe_fd[0]);
close(pipe_fd[1]);
exit(0);
}
if(pid > 0)
{
pid2 = fork();/*创建子进程二*/
if(pid2 == 0)
{
read(pipe_fd[0],buffer,20);
printf("%s\n",buffer);
write(pipe_fd[1],"B:hello word",13);
close(pipe_fd[0]);
close(pipe_fd[1]);
exit(0);
}
int status;
if(pid2 > 0)waitpid(pid2,&status,0);/*等待子进程二结束*/
read(pipe_fd[0],buffer,20);
printf("%s\n",buffer);
}
close(pipe_fd[0]);
close(pipe_fd[1]);/*关闭无名管道*/
return 0;
}
有名管道(FIFO)
首先介绍创建管道所需要的函数:
所需头文件:
#include <sys/types.h>
#include <sys/stat.h>
函数原型:
int mkfifo(const char *pathname, mode_t mode);
参数说明:
pathname:管道文件的路径及名字
mode:文件权限(文件权限会被~mode&子网掩码)
返回值:
成功时返回0,失败时返回-1并将失败原因写入errno。
堵塞
读写时与无名管道相似,管道无内容时读也会进行等待动作。
管道文件
在使用有名管道进行通信时,需要先创建一个特殊文件(传输介质)就是管道文件。管道文件不属于硬盘,它存在于内存,读写速度都要高于普通文件但是操作方式与普通文件相同。
作用范围
不需要血缘关系也能使用,适用于所有进程。
全双工通信
可以一边读一边写。
具有原子性
读写操作时不会被其他进程打断,安全可靠。
读写异常
当有名管道被打开的时候,里面所写入的内容会先保存起来的,当所有的有名管道文件被关闭,原本写入的东西(还没有被读出来)便会丢失。
小练习
利用管道文件实现数据传输,
进程1:循环从键盘输入数据,并写入到管道文件中,直到输入exit,退出循环
进程2:循环从管道文件中读取数据,如果读到的数据是exit,则退出循环
/*写端进程.c*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_NAME "m_fifo"/*管道文件名字*/
int main(void)
{
int retvel,fifo_fd;
retvel = mkfifo(FIFO_NAME,0666);/*创建管道文件*/
/*当文件存在时会报错,这里由于时间关系没有进行出错处理*/
if(retvel == -1)
{
perror("creat fifo err");
return -1;
}
fifo_fd = open(FIFO_NAME, O_RDWR);/*用系统IO以读写的权限打开管道文件*/
while(1)
{
char buffer[100];
bzero(buffer,100);/*清除缓冲区*/
scanf("%s",buffer);
write(fifo_fd,buffer,strlen(buffer));
if(!strcmp(buffer,"exit"))break;/*判断是否符合退出条件*/
}
close(fifo_fd);/*关闭管道文件描述符*/
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO_NAME "m_fifo"/*管道文件名字*/
int main(void)
{
int retvel,fifo_fd;
fifo_fd = open(FIFO_NAME, O_RDWR);/*用系统IO以读写的权限打开管道文件*/
while(1)
{
char buffer[100];
bzero(buffer,100);/*清除缓冲区*/
read(fifo_fd,buffer,sizeof(buffer));
printf("%s\n",buffer);
if(!strcmp(buffer,"exit"))break;/*判断是否符合退出条件*/
}
close(fifo_fd);/*关闭管道文件描述符*/
return 0;
}
结尾
补充一点无论是无名管道还是有名管道都不能使用定位函数对其进行操作。这里是一只正在努力的菜鸟,如果有哪里写的不对的地方,希望各位大佬多多指正,我会加以改进!