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);
(4)FIFO有名管道
有名管道(称作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
将结合电源管理程序来说明