您的位置:首页 > 其它

传统的进程间通信方式 1.无名管道(pipe) 2.有名管道(fifo) 3.信号(signal); 闹钟信号应用实例

2014-04-27 18:56 375 查看
传统的进程间通信方式 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(
a752
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:配合共享内存使用,用以实现同步和互斥

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: