由于进程的地址空间是独立的,所以进程间交互数据必须采用专门的通信机制。Linux下进程间通信主要方法有管道(pipe fifo)、信号量(semophore)、消息队列、共享内存(shared memory)、信号(signal)、套接字(socket)。
- 管道:半双工(单向,却可逆)的通信方式。
本质 伪文件 一旦被读取就不存在
(1)匿名管道(pipe):只能用于有血缘关系的进程间(父子进程共享打开的文件描述符)
原型Int pipe(int pipefd[2]); 注意管道两端的任务时间固定的fd[0]为管道读端,fd[1]为管道写端;
(2)命名管道(FIFO):一个设备文件,提供了一个路径名与之关联,已FIFO的文件形式存储于文件系统中。注意:FIFO总是按照先进先出(First In First Out)的原则工作,第一个被写入的数据将首先从管道中读出。
创建:Int mkfifo(const char *pathname,mode_t mode);
举例:父子进程间相互传输数据
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFFSIZE 1024
int main()
{
pid_t pid = -1;
char buff[BUFFSIZE] = {'\0'};
int pipefd_ps[2] = {-1};
int pipefd_pr[2] = {-1};
int rev = -1;
rev = pipe(pipefd_pr) | pipe(pipefd_ps);
if(-1 == rev)
{
perror("pipe error : ");
return -1;
}
pid = fork();
if(-1 == pid){
perror("fork error : ");
return -1;
}
else if(0 == pid){
close(pipefd_ps[0]);
close(pipefd_pr[1]);
while(1){
sleep(1);
memset(buff,'\0',BUFFSIZE);
strcpy(buff,"I'am parents ");
rev = write(pipefd_ps[1],buff,strlen(buff));
if(rev != strlen(buff)){
perror("write error : ");
return -1;
}
sleep(1);
memset(buff,'\0',BUFFSIZE);
strcpy(buff,"children said : ");
rev = read(pipefd_pr[0],buff + strlen(buff) - 1,BUFFSIZE - strlen(buff));
if(rev == 0){
perror("read error : ");
return -1;
}
strcat(buff,"\n");
write(STDOUT_FILENO,buff,strlen(buff));
}
}
else{
close(pipefd_ps[1]);
close(pipefd_pr[0]);
while(1){
sleep(1);
memset(buff,'\0',BUFFSIZE);
strcpy(buff,"parent said : ");
rev = read(pipefd_ps[0],buff + strlen(buff) - 1,BUFFSIZE- strlen(buff));
if(rev == 0){
perror("read error : ");
return -1;
}
strcat(buff,"\n");
write(STDOUT_FILENO,buff,strlen(buff));
sleep(1);
memset(buff,'\0',BUFFSIZE);
strcpy(buff,"I'am children ");
rev = write(pipefd_pr[1],buff,strlen(buff));
if(rev != strlen(buff)){
perror("write error : ");
return -1;
}
}
}
return 0;
}
信号:进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待 信号的到达,信号机制不仅有基本通知功能还可以传递附加信息。
可以从两个不同的分类角度对信号进行分类:
(1)可靠性方面:
不可靠信号:信号值小于SIGRTMIN的信号。主要问题是信号可能丢失。
可靠信号:这些信号支持排队,不会丢失。信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。(2)与时间的关系上:
非实时信号都不支持排队,都是不可靠信号;
实时信号都支持排队,都是可靠信号。信号的处理:
(1)signal(int signum,sighandler_t handler);捕捉
(2)int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));检查或设置进程在接收到信号时的动作
(3)int pause(void);函数使调用进程挂起直到捕捉到一个信号。
(4)int kill(pid_t pid,int sig);用来发送信号给直到的进程
(5)int raise(int sig);给调用它的进程发送信号
(6)sigqueue();发送信号
(7)alarm/getitimer/settimer 用来设置定时器
(8)abort()向进程发送SIGABORT信号信号的举例
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
//父进程每隔5S发送一个SIGQUIT给子进程 KILL 子进程 未收到信号 1S输出“”XXXDHR“
//收到信号输出 HELLO
void haha(int signum)
{
printf("hello");
}
void main()
{
pid_t pid=1;
//int signum=SIGQUIT;
pid=fork();
if(-1==pid)
{
return -1;
}
else if(0==pid)
{
signal(SIGQUIT,haha);
while(1)
{
printf("LCDHR");
sleep(1);
printf(" ");
}
}
else
while(1)
{
printf("yuio");
sleep(5);
kill(pid,SIGQUIT);
}
}
- 消息队列:一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识。与管道不同的是消息队列存放在内核中,只有内核重启(即操作系统重启)或者显示地删除一个消息队列,该消息队列才会被真正删除。
(1)创建:消息队列是随着内核 的存在而存在的,每个消息对列在系统范围内对应唯一的键值,要获得一个消息队列的描述符,只需提供该消息队列的键值即可。该键值通常由ftok返回。
key_t ftok(const char *pathname,int proj_id)
举例:ftok根据pathname,proj_id这两参数生成唯一的键值
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
int main()
{
int i;
for(i = 1; i <= 5;i++ )
{
printf("key[%d] = %ul \n",i,ftok(".",i));
}
exit(0);
}
运行结果如下:
key[1] = 16908424l
key[2] = 33685640l
key[3] = 50462856l
key[4] = 67240072l
key[5] = 84017288l
(2)写消息对列
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
(3)读消息队列
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
(4)获取和设置消息队列的属性
int msgctl(int msqid, int command, struct msgid_ds *buf);
msgctl系统调用对msqid标识的消息队列执行cmd操作,系统定义了3中如下cmd操作。
IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值
IPC_RMID:删除消息队列
- 共享内存:就是分配一块能被其他进程访问的内存
(1)共享内存的建立
void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t ffset);
Int shmget(key_t key,int size,int shmflg);
(参数key 由ftok获得,Size 文件大小 ,Shmflg 读写权限)
(2)共享内存的操作:在使用共享内存前,必须通过shmat将其附加到进程的地址空间,进程与共享内存就建立了链接。shmat调用成功返回一个指向内存空间的指针,使用该指针就能访问共享内存了。
Void * shmat(int shmid,const void *addr,int shmflg);
(3)当进程结束使用共享内存区时,要通过shmdt断开与共享内存去的连接
Int shmdt(void* shmaddr);