您的位置:首页 > 运维架构 > Linux

Linux进程间的通信方式之管道

2017-05-29 13:35 435 查看

简介

管道是进程间通信(IPC)的一种重要方式,在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的,如图。它由进程之间的关系可以分为匿名管道和命名管道。匿名管道即两进程之间存在血缘关系时使用的管道,命名管道即两进程之间不存在血缘关系时使用的管道。



匿名管道

我们所说的管道(pipe)它用于具有血缘关系的进程通信。创建管道使用语句

#include<unistd.h>
int pipe(int filedes[2])


调⽤pipe函数时在内核中开辟⼀块缓冲区(称为管道)⽤于通信,它有⼀个读端⼀个写端,然后通过filedes参数传出给⽤户程序两个⽂件描符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输⼊1是标准输出⼀样)。所以管道在⽤户程序看起来就像⼀个打开的⽂件,通过read(filedes[0]);或者write(filedes[1]);向这个⽂件读写数据其实是在读写内核缓冲区。pipe函数调⽤成功返回0,调⽤失败返回-1。



测试代码

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if(ret == -1)
{
printf("create pipe error!");
return 1;
}
pid_t id = fork();
if( id < 0 )
{
printf("fork error!");
return 2;
}
else if( id == 0 )
{
close(_pipe[0]);
int i =0;
char *_mesg_c=NULL;
while(i<100)
{
_mesg_c="i am child!";
write(_pipe[1], _mesg_c, strlen(_mesg_c)+1);
sleep(1);
i++;
}
}
else
{
close(_pipe[1]);
char _mesg[100];
int j = 0;
while(j<100)
{
memset(_mesg, '\0', sizeof(_mesg));
read(_pipe[0], _mesg, sizeof(_mesg));
printf("%s\n",_mesg);
j++;
}
}
return 0;
}


注意

1.两个进程通过⼀个管道只能实现单向通信。例如父进程写,子进程就只能读。

2.管道的生命周期是随进程的。

3.如果所有指向管道写端的⽂件描述符都关闭了,⽽仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后再次read会返回0,就像读到⽂件末尾⼀样。

4.如果有指向管道写端的⽂件描述符没关闭,⽽持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

5.如果所有指向管道读端的⽂件描述符都关闭了,这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终⽌。

6.如果有指向管道读端的⽂件描述符没关闭,⽽持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写⼊数据并返回。

命名管道

上面所说的只能用于有血缘关系的进程间通信的管道称为匿名管道。另一种可以用于没有血缘关系的进程通信的管道我们称之为命名管道(FIFO),FIFO提供了一个路径名与之关联,以FIFO的⽂件形式存储于⽂件系统中。命名管道是⼀个设备⽂件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(first input first output)总是按照先进先出的原则工作,第⼀个被写⼊的数据将⾸先从管道中读出。

命名管道的创建通常使用shell命令mknod和mkfifo:

mkfifo name


另一种fifo创建方式在程序中创建,使用函数mknod或者mkfifo:

#include <sys/types.h>
#include <sys/stat.h> //它们被包含在这个头文件中
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);


参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于⽂件创建的种类,它只在创建设备⽂件时才会⽤到。这两个函数调⽤成功都返回0,失败都返回-1。

测试代码

fifo read端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define _PATH_ "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
int fd = open(_PATH_, O_RDONLY);
if(fd < 0)
{
printf("open file error!\n");
return 1;
}
char buf[_SIZE_];
memset(buf, '\0', sizeof(buf));
while(1)
{
int ret = read(fd, buf, sizeof(buf));
if (ret <= 0)
{
printf("read end or error!\n");
break;
}
printf("%s\n", buf);
if( strncmp(buf, "quit", 4) == 0 )
{
break;
}

}
close(fd);
return 0;
}


fifo write端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#define _PATH_ "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
int ret = mkfifo(_PATH_,0666|S_IFIFO);
if(ret == -1)
{
printf("mkfifo error\n");
return 1;
}
int fd = open(_PATH_, O_WRONLY);
if(fd < 0)
{
printf("open error\n");
}
char buf[_SIZE_];
memset(buf, '\0', sizeof(buf));
while(1)
{
scanf("%s", buf);
int ret = write(fd, buf, strlen(buf)+1);
if(ret < 0)
{
printf("write error\n");
break;
}
if( strncmp(buf, "quit", 4) == 0 )
{
break;
}
}
close(fd);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ipc linux