嵌入式 Linux 进程间通信几种方式小结

2019-07-12 14:43发布

一、进程间通信概述 进程通信有如下一些目的: A、数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 B、共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。 C、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。 D、资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。 E、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。 Linux 进程间通信(IPC: InterProcess Communication)以下以几部分发展而来: 早期UNIX进程间通信、基于System V进程间通信、基于Socket进程间通信和POSIX进程间通信。 1> UNIX进程间通信方式包括:管道、FIFO、信号。 2> System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存、 3> POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存。 现在linux使用的进程间通信方式: (1) 管道(pipe)和有名管道(FIFO) (2) 信号(signal) (3) 消息队列 (4) 共享内存 (5) 信号量 (6) 套接字(socket) 二、管道通信 管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起 写进程在管道的尾端写入数据,读进程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。 管道提供了简单的流控制机制。 进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。 管道主要用于不同进程间通信。 (1) 管道创建与关闭 创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。 创建一个管道之后,一般情况下进程将产生一个新的进程。 系统调用:pipe(); 所在头文件:unistd.h 原型:int pipe(int fd[2]); 返回值:如果系统调用成功,返回0。如果系统调用失败返回-1: errno = EMFILE( 没有空亲的文件描述符 ) EMFILE(系统文件表已满) EFAULT(fd数组无效) 注意:fd[0]用于读取管道,fd[1]用于写入管道。 管道的创建 #include
#include
#include
#include
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd)<0){
printf("pipe create error ");
return -1;
}
else
printf("pipe create success ");
close(pipe_fd[0]);
close(pipe_fd[1]);
}
(2) 管道的读写
管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork()函数创建一个子进程。 子进程写入和父进程读的命名管道: 管道读写注意事项 可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述符。 必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。 当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。 因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。 管道实例见:pipe_rw.c #include #include
#include
#include
#include
int main()
{
int pipe_fd[2];
pid_t pid;
char buf_r[100];
char* p_wbuf;
int r_num;
memset(buf_r,0,sizeof(buf_r));数组中的数据清0;
if(pipe(pipe_fd)<0){
printf("pipe create error ");
return -1;
}
if((pid=fork())==0){
printf(" ");
close(pipe_fd[1]);
sleep(2);
if((r_num=read(pipe_fd[0],buf_r,100))>0){
printf("%d numbers read from be pipe is %s ",r_num,buf_r);
}
close(pipe_fd[0]);
exit(0);
}else if(pid>0){
close(pipe_fd[0]);
if(write(pipe_fd[1],"Hello",5)!=-1)
printf("parent write success! ");
if(write(pipe_fd[1]," Pipe",5)!=-1)
printf("parent wirte2 succes! ");
close(pipe_fd[1]);
sleep(3);
waitpid(pid,NULL,0);
exit(0);
}
}
标准流管道
与linux中文件操作有文件流的标准I/O一样,管道的操作也支持基于文件流的模式。接口函数如下:
库函数:popen();
原型:FILE *open (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]='