传统的进程间通信方式 1.无名管道(pipe) 2.有名管道(fifo) 3.信号(signal)

linux下进程间的通信方式:
传统的进程间通信方式 1.无名管道(pipe) 2.有名管道(fifo) 3.信号(signal)
System V IPC 对象 1.共享内存(share memory) 2.消息队列(message queue ) 3.信号灯 (semaphore)
BSD
套接字(socket)
下面进行逐一介绍:
无名管道:这里所说的主要只无名管道,它具有如下特点,只能用于具有亲缘关系的进程之间的通信,半双工的通信模式,具有
固定的读端和写端,管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO,如read、write函数。
管道是基于文件描述符的通信方式,当一个管道建立时,他会创建两个文件描述符 fd[0]和fd[1].其中fd[0]固定用于读管道,而
fd[1]固定用于写管道。这样就构成了一个半双工的通道。

管道创建函数:

所需头文件 : #include <unistd.h>
函数原型 : int pipe(int fd[2])
函数参数 : fd 包含两个元素的整型数组
函数返回 : 成功0,出错 -1;
例如 ;

int main()
{
int pfd[2];
if(pipe(pfd)<0)
{
perror("pipe");
return -1;
}
else
{
printf("pipe create success\n");
}
close(fd[0]);
close(fd[1]);
}
当管道中无数据时,读操作会阻塞
向管道写数据时候,linux将不能保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据,
如果读进程不读走管道缓冲区中的数据,那么写操作会一直阻塞。
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将受到内核传来的SIFPIPE信号(通常Broken pipe错误)
无名管道只能由于具有亲缘关系的进程之间,这就限制了无名管道的使用范围,有名管道可以使互不相关的两个进程互相通信,有名管道可以通过路径来指明,
并且在文件系统中可见。
进程通过文件IO来操作有名管道,fifo原则,不支持lseek操作。

创建管道函数:

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
函数原型 ;
int mkfifo (const char * filename ,mode_t mode );
函数参数: filename :要创建的管道
mode :指定创建的管道的访问权限,一般用8进制表示
函数返回值 :成功0,失败-1


信号通信:
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它赖通知用户空间进程发生了哪些系统事件。
如果该进程当前并未处于执行态,则该信号就由内核保护起来,知道该进程恢复执行再传递给它,如果一个信号被进程设置为阻塞,则该信号的
传递被延迟,直到其阻塞被取消时菜被传递给进程。
用户进程对信号的响应方式:

忽略信号: 对信号不做任何处理,但是有两个信号不能忽略SIGKILL、SIGSTOP
捕捉信号: 定义信号处理函数,当信号发生时,执行响应的处理函数。
执行缺省操作: Linux对每种信号都规定了默认操作。


信号的发送和捕捉

信号的发送:
kill()和raise()
kill函数同读者熟知的kill系统命令一样,可以发送信号给进程或进程组(kill系统命令只是kill函数的一个用户接口)

kill -l 命令查看系统支持的信号列表

raise函数允许进程向自己发送信号。

所需头文件:
#include <signal.h>
#include <sys/types.h>
函数原型:
int kill(pid_t pid,int sig)
函数参数:
pid:正数,要接受的进程的进程号
0,信号被发送到所有和pid进程在同一个进程组的进程
-1,信号发给所有的进程表中的进程(除了进程号最大的进程)
sig:信号
返回 成功 0,失败-1


int raise(int sig);

例子:

int main()
{
pid_t pid;
int ret;
if((pid = fork())<0)
{
perror("fork");
exit(-1);
}
if(pid == 0)
{
raise(SIGSTOP);
printf("");
exit(0);
}
else
{
printf("pid =%d\n",pid);
if((waitpid(pid,NULL,WNOHANG))==0)
{
kill(pid,SIGKILL);
printf("kill %d\n",pid);
}
}
}


另外两个比较重要的函数

alarm(),pause()
alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到的时候,内核就向进程发送SIGALARM信号。

pause()函数是用于将调用进程挂起,直到收到信号为止。

函数原型;
unsigned int alarm(unsigned int seconds)
函数参数:
seconds 指定秒数

函数返回值: 成功则返回 如果调用alarm()前,进程中已经设置了闹钟时间,则返回上个闹钟时间的剩余时间,否则返回0
失败 -1

int pause(void)

返回值 :-1,并且把error值设为EINTR。

alarm只是定时器,在时间未到的阶段程序会继续往下执行。


那么既然发出了信号,如何进行捕捉处理呢?因此引出了信号的捕捉及处理函数。
信号处理有2种方式:简单的signal()函数,使用信号集函数组
signal()
使用该函数的时候,需要指定要处理的信号和处理信号的函数。
函数原型:

void (*signal(int signum,void(*handler)(int)))(int);
函数参数:
signum :指定的信号
handler:SIG_IGN 忽略该信号
SIG_DFL 采用系统默认的方式处理信号
自定义的信号处理函数指针
函数返回值: 成功 设置之前的信号处理方式。
失败 -1;

例如:

void my_fun(int signo)
{
if(signo == SIGINT)
printf("get SIGINT\n");

}
int main()
{
signal(SIGINT,my_fun);
pause();
exit(0);

}
经典例子:

用信号的知识实现司机售票员问题:
1、售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印("let's gogogog")
2、售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印("stop the bus")
3、司机捕捉SIGTSTP(代表车到终点站)信号,向售票员发送SIGUSR1信号,售票员打印("please get off the bus")


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

pid_t pid;

void conductor(int signo)
{
switch ( signo )
{
case SIGINT:
kill(getppid(), SIGUSR1);
break;
case SIGQUIT:
kill(getppid(), SIGUSR2);
break;
case SIGUSR1:
printf("all get off the bus\n");
exit(0);
}

return;
}

void driver(int signo)
{
switch ( signo )
{
case SIGUSR1:
printf("gogogo\n");
break;
case SIGUSR2:
printf("stop the bus\n");
break;
case SIGTSTP:
kill(pid, SIGUSR1);
wait(NULL);
exit(0);
}

return;
}

int main()
{

if ((pid = fork()) < 0)
{
perror("fail to fork");
exit(-1);
}
else if (pid == 0) // conductor
{
signal(SIGINT, conductor);
signal(SIGQUIT, conductor);
signal(SIGUSR1, conductor);
signal(SIGTSTP, SIG_IGN);
while ( 1 )
{
pause();
}
}
else // driver
{
signal(SIGUSR1, driver);
signal(SIGUSR2, driver);
signal(SIGTSTP, driver);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
while ( 1 )
{
pause();
}
}

return 0;
}

 

 

 

pipe :具有亲缘关系的进程间,单工,数据在内存中
fifo:可用于任意进程间,双工,有文件名,数据在内存
signal:唯一的异步通信方式
msg:常用在cs模式中,按消息类型访问,可有优先级
shm:效率最高(直接访问内存),需要同步、互斥机制
sem:配合共享内存使用,用以实现同步和互斥

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2012-07-05 19:56  孟浩依然  阅读(3006)  评论(0编辑  收藏  举报