Linux进程间通信
进程是程序运行资源分配的最小单位。每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,Inter-Process Communication)。
匿名管道
管道的创建
管道是一种最基本的进程间通信机制。管道由pipe函数来创建:
来看看通过管道进行通信的步骤:
》父进程创建管道,得到两个文件描述符指向管道的两端
》利用fork函数创建出子进程,则子进程也得到两个文件描述符指向同一管道
》父进程关闭读端(pipe[0]),子进程关闭写端pipe[1],则此时父进程可以往管道中进行写操作,子进程可以从管道中读,从而实现了通过管道的进程间通信。
示例代码
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
if(ret<0)
{
perror("pipe\n");
}
pid_t id=fork();
if(id<0)
{
perror("fork\n");
}
else if(id==0) // child
{
close(_pipe[0]);
int i=0;
char *mesg=NULL;
while(i<100)
{
mesg="I am child";
write(_pipe[1],mesg,strlen(mesg)+1);
sleep(1);
++i;
}
}
else //father
{
close(_pipe[1]);
int j=0;
char _mesg[100];
while(j<100)
{
memset(_mesg,'\0',sizeof(_mesg ));
read(_pipe[0],_mesg,sizeof(_mesg));
printf("%s\n",_mesg);
j++;
}
}
return 0;
}
pipe的特点:
-
只能单向通信
-
只能血缘关系的进程进行通信
-
依赖于文件系统
-
生命周期随进程
-
面向字节流的服务
-
管道内部提供了同步机制
说明:因为管道通信是单向的,在上面的例子中我们是通过子进程写父进程来读,如果想要同时父进程写而子进程来读,就需要再打开另外的管道;
管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承管道的件描述符。 上面的例子是父进程把文件描述符传给子进程之后父子进程之 间通信,也可以父进程fork两次,把文件描述符传给两个子进程,然后两个子进程之间通信, 总之 需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。
命名管道FIFO
在管道中,只有具有血缘关系的进程才能进行通信,对于后来的命名管道,就解决了这个问题。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是, FIFO(first input first output)总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。
命名管道的创建
创建命名管道的系统函数有两个:mknod
和mkfifo
。两个函数均定义在头文件sys/stat.h,
函数原型如下:
#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);
函数mknod
参数中path
为创建的命名管道的全路径名: mod
为创建的命名管道的模指明其存取权限; dev
为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。这两个函数调用成功都返回0,失败都返回-1。下面使用mknod
函数创建了一个命名管道:
umask(0);
if (mknod("/tmp/fifo",S_IFIFO | 0666) == -1)
{
perror("mkfifo error");
exit(1);
}
函数mkfifo
前两个参数的含义和mknod
相同。下面是使用mkfifo
的示例代码:
umask(0);
if (mkfifo("/tmp/fifo",S_IFIFO|0666) == -1)
{
perror("mkfifo error!");
exit(1);
}
-
"S_IFIFO|0666"指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写( 这里要注意umask对生成的管道文件权限的影响)。
-
命名管道创建后就可以使用了,命名管道和管道的使用方法法基本是相同的。只是使用命名管道时,必须先调用open()将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
-
需要注意的是,调用open()打开命名管道的进程可能会被阻塞。但如果同时用读写方式( O_RDWR)打开,则一定不会导致阻塞;如果以只读方式( O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写方打开管道;同样以写方式( O_WRONLY)打开也会阻塞直到有读方式打开管道。
运行示例
Server.c:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define _PATH_NAME_ "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
int ret=mkfifo(_PATH_NAME_,S_IFIFO|0666);
if(ret==-1){
printf("make fifo error\n");
return 1;
}
char buf[_SIZE_];
memset(buf,'\0',sizeof(buf));
int fd=open(_PATH_NAME_,O_WRONLY);
while(1)
{
//scanf("%s",buf);
fgets(buf,sizeof(buf)-1,stdin);
int ret=write(fd,buf,strlen(buf)+1);
if(ret<0){
printf("write error");
break;
}
}
close(fd);
return 0;
}
Client.c:
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#define _PATH_NAME "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
int fd=open(_PATH_NAME,O_RDONLY);
if(fd<0){
printf("open file error");
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",buf);
}
close(fd);
return 0;
}
标签:pipe,int,fifo,间通信,命名,管道,进程,include
From: https://www.cnblogs.com/zhou-huilin/p/16873807.html