Bootstrap

linux 进程通信

2011-3-27

现在linux下的进程间通信方式主要有以下几种

(1)                管道以及有名管道: 管道可以用于有亲缘关系的进程通信,而有名管道除了具有管道的功能外,还允许无亲缘关系进程间通信。

(2)                信号:信号是在软件层次上的中断机制的一种模拟

(3)                消息队列:消息的链表,具有写权限的进程可以向消息队列中按照一定的规则添加新消息

(4)                共享内存:对于共享内存,需要同步机制,如信号量和互斥锁等

(5)                Socket:这是更为一般的通信机制,可以用于不同机器之间进程通信。

一.     管道和有名管道(FIFO):

管道是linux中一种重要的机制,它是将一个程序的输出直接连接到另一个程序的输入,对于ls –l |grep XX这样的操作,使用的就是管道的原理。

管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read,write,但是它不是普通的文件,不属于任何文件系统,只是存在于内存中。

1)创建管道

#include<unistd.h>

管道的创建使用pipe函数,通过得到两个描述符,fd[0],fd[1],实现读写,fd[0]用于读,fd[1]用于写

Int pipefd[2];

Pipe(pipefd);

2)管道读写

事实上,在一个进程中对管道读写对于实现进程间通信没有任何意义,所以需要通过fork函数创建子进程,通过继承父进程创建的管道,然后再去除不必要的fd[0],fd[1],就能实现父进程写子进程读和子进程写父进程读的效果。父进程可以创建许多子进程,则这些子进程只要关闭相应的端口就可以实现子进程之间的通信

 

3)标准流管道

Popen,pclose,对于popen创建的管道,必须使用标准IO函数进行操作,而不能使用read,write这些不带缓冲的I/O函数

Popen完成的工作:

创建一个管道

Fork一个子进程

在父子进程中关闭不需要用到的文件描述符

执行exec函数族调用

执行函数中指定的命令

例如,

FILE *fp;

Char *cmd=”ps”;

Char buf[1024];

Fp=popen(cmd,”r”);//r为标准输出(将输出放到缓存中,w为标准输入,

While(fgets(buf,1024,fp)!=NULL)//通过调用带I/O缓存的函数,读取数据

           {…}  

Pclose(fp);

 

4FIFO有名管道

有名管道(称作FIFO)最大的好处是实现互不相关的进程之间的通信。读管道可以通过路径名来指出,并且在文件系统中是可见的。在建立管道之后,两个进程可以把它当做普通文件一样进行读写操作,不过FIFO是遵循先进先出原则的。对于管道以及FIFO的读总是从开始出返回数据,对其写则是将数据添加到尾部,不支持lseek()等文件定位操作。

还可以使用mknod管道名p来创建有名管道。

对于阻塞和非阻塞,记录如下,

读进程:O_NONBLOCK

若管道阻塞, FIFO无数据,则读进程一直阻塞直到有数据写入

如管道非阻塞,不论FIFO中是否有数据,都会立刻进行读操作。

写进程:

若管道阻塞,FIFO中数据满,则一直阻塞直到读进程读取数据。

若管道非阻塞,FIFO中没有读操作,则写进程立即执行读操作。

#include sys/types.h sys/state.h

Int mkfifo(const char *filename,mode_t mode)

二.     信号实现进程通信

类似于软中断

1. 信号发送与捕捉

Kill raise

子进程 raise(SIGSTOP) 子进程发出SIGSTOP信号

父进程 收集子进程发出的信号

           Waitpid(pid,NULL,WNOHANG)

           Kill(pid,SIGKILL) 调用kill进行相应的处理

alarm pause

    alarm alarm(5) 5秒之后,发送SIGALARM信号,SIGALARM默认的信号操作为终止进程

    pause用于将调用进程挂起直至捕捉到信号为止,这样alarm pause就形成了sleep功能。

       2. 信号的处理

              有简单的signal和信号集函数处理

              只说简单的signal处理

              Signal(SIGINT,func);

              Signal(SIGQUIT,func);

              即收到信号后,调用对应的处理函数。保证程序的安全退出常用到。

三.     共享内存

共享内存是一种最为高效的进程间通信方式。为了实现多个进程交换信息,内核专门留出一块内存区,这段内存区可以有需要访问的进程将其隐射到自己的私有数据空间,进程可以读写这块内存区而不需要进行数据的拷贝,从而提高效率。当然访问需要同步机制。

#include sys/types.h sys/ipc.h sys/shm.h

Shmget shmat shmdt shmctl

shmget(key_t key,int size,int shmflag)

Key:IPC_PRIVATE,返回shmid

Shmflag:同open的权限位,0666

shmat(int shmid,const void *shmaddr,int shmflag)

shmaddr:将共享内存attach到指定位置,如果0则将这段内存映射到调用进程的地址空间

shmdt(const void * shmaddr)

shmctl(int shmid,int cmd,struct shmid_ds *buf)

cmd控制命令IPC_RMID删除共享内存

shmctl(shmid,IPC_RMID,NULL);

Ipcs,用于查看报告进程间通信机制状态的命令,可以查看共享内存,消息队列等各种进程间通信机制的情况,这里使用system函数用于调用shell命令”ipcs”

这里查看共享内存,使用system” ipcs –m ”;

调试程序时,有时会用CTRL+C发送中断信号结束程序,这个时候申请的共享内存得不到释放,这个时候处理两种方法:

1.     在收到这个信号时,先释放共享内存在退出程序

2.     使用linux下的命令ipcrm shm shmid来释放

四.     消息队列

消息队列的实现包括创建和打开消息队列,添加消息,读取消息和控制消息队列。

创建和打开消息队列 msgget

添加消息队列 msgsnd

读取消息队列 msgrcv 可以指定读取某一条消息

控制消息队列 msgctl  IPC_RMID 从系统内核中移走消息队列

消息结构:

Struct msgbuf{

Long mtype;

Char mtext[SIZE];

}

例如,

Key_t key;

Key=ftok(“.”,’a’) 通过ftok产生key

Int qid=msgget(key,IPC_CREAT | 0666)

Fgets(&msg->mtext,SIZE,stdin)

Msgsnd(qid,&msg,len,0)

Msgrcv(qid,&msg,SIZE,0)

Msgctl(qid,IPC_RMID,NULL)

以上就完成消息队列的一些列操作。

五.Socket

       将结合电源管理程序来说明

;