linux进程间通信----简介(1)
1、进程间通信概述
进程间通信有如下一些目的:
数据传输:一个进程需要将它的数莘⑺透 硪桓鼋 蹋 ⑺偷氖 萘吭谝桓鲎纸诘郊刚鬃纸谥 洹?br> 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
linux进程间通信(IPC)由以下几部分发展而来:
早期UNIX进程间通信、基于System V进程间通信、基于Socket进程间通信和POSIX进程间通信。
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的变种都支持套接字。
2、管道通信
普通的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
转载于:https://my.oschina.net/u/263896/blog/59181