linux进程间通讯----简介(1)linux
一、进程间通讯概述shell
进程间通讯有以下一些目的:数组
数据传输:一个进程须要将它的数莘⑺透 硪桓鼋 蹋 ⑺偷氖 萘吭谝桓鲎纸诘郊刚鬃纸谥 洹?br> 共享数据:多个进程想要操做共享数据,一个进程对共享数据的修改,别的进程应该马上看到。socket
通知事件:一个进程须要向另外一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。函数
资源共享:多个进程之间共享一样的资源。为了做到这一点,须要内核提供锁和同步机制。线程
进程控制:有些进程但愿彻底控制另外一个进程的执行(如Debug进程),此时控制进程但愿可以拦截另外一个进程的全部陷入和异常,并可以及时知道它的状态改变。设计
linux进程间通讯(IPC)由如下几部分发展而来:3d
早期UNIX进程间通讯、基于System V进程间通讯、基于Socket进程间通讯和POSIX进程间通讯。blog
UNIX进程间通讯方式包括:管道、FIFO、信号。继承
System V进程间通讯方式包括:System V消息队列、System V信号灯、System V共享内存。
POSIX进程间通讯包括:posix消息队列、posix信号灯、posix共享内存。
如今linux使用的进程间通讯方式:
(1)管道(pipe)和有名管道(named pipe):管道可用于具备亲缘关系进程间的通讯,有名管道克服了管道没有名字的限制,所以,除具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯;
(2)信号(signal):信号是比较复杂的通讯方式,用于通知接受进程有某种事件发生,除了用于进程间通讯外,进
程还能够发送信号给进程自己;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数
sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又可以统一对外接口,用sigaction函数从新实现了signal
函数);
(3┫ ⒍恿校∕essage):消息队列是消息的连接表,包括Posix消息队列system V消息队列。有足够权限的进程能够向队列中添加消息,被赋予读权限的进程则能够读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(4)共享内存:使得多个进程能够访问同一块内存空间,是最快的可用IPC形式。是针对其余通讯机制运行效率较低而设计的。每每与其它通讯机制,如信号量结合使用,来达到进程间的同步及互斥。
(5)信号量(semaphore):主要做为进程间以及同一进程不一样线程之间的同步手段。
(6)套接字(socket):更为通常的进程间通讯机制,可用于不一样机器之间的进程间通讯。起初是由Unix系统的BSD分支开发出来的,但如今通常能够移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
二、管道通讯
普通的Linux shell都容许重定向,而重定向使用的就是管道。例如:
ps | grep vsftpd
管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另外一个进程的标准输入链接在一块儿。写进程在管道的尾端写入数据,读进程
在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道
前,进程将一直阻塞。一样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据以前,写进程将一直阻塞。
管道主要用于不一样进程间通讯。
建立一个简单的管道,可使用系统调用pipe( )。它接受一个参数,也就是一个包括两个整数的数组。若是系统调用成功,此数组将包括管道使用的两个文件描述符。建立桓龉艿乐 螅 话闱榭鱿陆 探 桓鲂碌慕 獭?br> 系统调用:pipe( );
原型:int pipe( int fd[2] );
返回值:若是系统调用成功,返回0。若是系统调用失败返回-1:
errno = EMFILE (没有空闲的文件描述符)
EMFILE (系统文件表已满)
EFAULT (fd数组无效)
#include
#include
#include
#include
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd)
2.2 管道读写
管道主要用于不一样进程间通讯。实际上,一般先建立一个管道,再经过fork函数建立一个子进程。
子进程读和父进程写的命名管道:
2.3 管道读写注意事项
能够经过打开两个管道来建立一个双向的管道。但须要在子进程中正确地设置文件描述符。
必须在系统调用fork( )中调用pipe( ),不然子进程将不会继承文件描述符。
当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。由于管道存在于系统内核之中,因此任何不在建立管道的进程的祖先进程之中的进程都将没法寻址它。而在命名管道中却不是这样。
管道实例见:pipe_rw.c
2.4 标准流管道
与linux中文件操做有文件流的标准I/O同样,管道的操做也支持基于文件流的模式。接口函数以下
库函数:popen();
原型: FILE *popen ( char *command, char *type);
返回值:若是成功,返回一个新的文件流。若是没法建立进程或者管道,返回NULL。
管道中数据流的方向是由第二个参数type控制的。此参数能够是r或者w,分别表明读或写。但不能同时为读和写。在Linux系统下,管道将会以参数type中第一个字符表明的方式打开。因此,若是你在参数type中写入rw,管道将会以读的方式打开。
使用popen()建立的管道必须使用pclose( )关闭。其实,popen/pclose和标准文件输入/输出流中的fopen() / fclose()十分类似。
库函数: pclose();
原型: int pclose( FILE *stream );
返回值: 返回系统调用wait4( )的状态。
若是stream无效,或者系统调用wait4( )失败,则返回 -1。
注意此库函数等待管道进程运行结束,而后关闭文件流。
库函数pclose( )在使用popen( )建立的进程上执行wait4( )函数。当它返回时,它将破坏管道和文件系统。
#include
#include
#include
#include
#define BUFSIZE 1024
int main()
{
FILE *fp;
char *cmd = "ps -ef";
char buf[BUFSIZE];
buf[BUFSIZE] = '\0';
if((fp=popen(cmd,"r"))==NULL)
perror("popen");
while((fgets(buf,BUFSIZE,fp))!=NULL)
printf("%s",buf);
pclose(fp);
exit(0);
}
2.5 命名管道(FIFO)
2.5.1 基本概念
命名管道和通常的管道基本相同,但也有一些显著的不一样:
命名管道是在文件系统中做为一个特殊的设备文件而存在的。
不一样祖先的进程之间能够经过管道共享数据。
当共享管道的进程执行完全部的I / O操做之后,命名管道将继续保存在文件系统中以便之后使用。
管道只能由相关进程使用,它们共同的祖先进程建立了管道。可是,经过FIFO,不相关的进程也能交换数据。
名管道建立
#include
#include
int mkfifo(const char * pathname,
mode_t mode) ;
返回:若成功则为0,若出错则为- 1
一旦已经用mkfifo建立了一个FIFO,就可用open打开它。确实,通常的文件I / O函数(close、read、write、unlink等)均可用于FIFO。
当打开一个FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:
(1) 在通常状况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其余进程为写打开此FIFO。相似,为写而打开一个FIFO要阻塞到某个其余进程为读而打开它。
(2) 若是指定了O_NONBLOCK,则只读打开当即返回。可是,若是没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。
相似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。
FIFO相关出错信息:
EACCES (无存取权限)
EEXIST (指定文件不存在)
ENAMETOOLONG (路径名太长)
ENOENT (包含的目录不存在)
ENOSPC (文件系统剩余空间不足)
ENOTDIR (文件路径无效)
EROFS (指定的文件存在于只读文件系统中)
实例见:fifo_write.c 、 fifo_read.c