进程间通信——信号

信号类型
信号类型在[url=http://tomotoboy.iteye.com/admin/blogs/438130]Trap命令详解[/url]中有详细的介绍

[b]信号捕捉[/b]
示列程序实现对SIGINT的捕捉
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
int catch(int sig);
int main(void)
{
signal(SIGINT,catch); /* 将SIGINT 信号与catch函数关联 */
printf("xixi\n");
sleep(10);
printf("end\n");
return;
}
int catch(int sig)
{
printf("Catch succeed!\n");
return 1;
}


执行过程:

pro_1.c: In function `main':
pro_1.c:7: warning: passing arg 2 of `signal' from incompatible pointer type
/home/l/g/tomotoboy/signal >pro_1
xixi
^CCatch succeed!
end


[b]信号与系统调用的关系[/b]
当一个进程正在执行一个系统调用时,如果向该进程发送一个信号,那么对于大多数系统调用来说,这个信号在系统调用完成之前将不起作用,因为这些系统调用不能被信号打断。但是有少数几个系统调用能被信号打断,例如:wait(),pause()以及对慢速设备(终端、打印机等)的 read()、write()、open()等。如果一个系统调用被打断,它就返回-1,并将errno设为 EINTR。可以用下列代码来处理这种情况:
if (wirte(tfd,buf,SIZE)<0) {
if (errno==EINTR) {
warn(“ Write interrupted.” );


}
}



[b]信号的复位[/b]
在 Linux 中,当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。下面的程序演示了这一点:
#include <signal.h>
int interrupt()
{
printf(" Interrupt called\n" );
sleep(3);
printf(" Interrupt Func Ended.\n" );
}
main()
{
signal(SIGINT,interrupt);
printf(" Interrupt set for SIGINT\n" );
sleep(10);
printf(" Program NORMAL ended.\n" );
return;
}

执行过程:
/home/l/g/tomotoboy/signal >pro_2
Interrupt set for SIGINT
^C Interrupt called
^C
/home/l/g/tomotoboy/signal >pro_2
Interrupt set for SIGINT
^C Interrupt called
Interrupt Func Ended.Program NORMAL ended.

[color=red]问题:当我第二次按下Ctrl+C时,程序直接终止,现在还在找原因[/color]
但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断:
#include <signal.h>
int interrupt()
{
printf(" Interrupt called\n" );
sleep(3);
printf(" Interrupt Func Ended.\n" );
}
int catchquit()
{
printf(" Quit called\n" );
sleep(3);
printf(" Quit ended.\n" );
}
main()
{
signal(SIGINT,interrupt);
signal(SIGQUIT,catchquit);
printf(" Interrupt set for SIGINT\n" );
sleep(10);
printf(" Program NORMAL ended.\n" );
return;
}
执行这个程序的结果如下:
/home/l/g/tomotoboy/signal >gcc pro_3.c -o pro_3
pro_3.c: In function `main':
pro_3.c:16: warning: passing arg 2 of `signal' from incompatible pointer type
pro_3.c:17: warning: passing arg 2 of `signal' from incompatible pointer type
/home/l/g/tomotoboy/signal >pro_3
Interrupt set for SIGINT
^C Interrupt called
^\ Quit called
Quit ended.
Interrupt Func Ended.
Program NORMAL ended.

还要注意的是,在 Linux 系统中同种信号是不能积累的。而且如果两个信号同时产生,系统并不保证进程接收它们的次序。以上的两个缺点影响了信号作为进程通信手段的可靠性,因为一个进程不能保证它发出的信号不被丢失。当某个信号未被处理的时候,如果对该信号执行 signal调用,那么该信号将被注销。

[b]在进程间发送信号[/b]
一个进程通过对 signal()的调用来处理其它进程发送来的信号。同时,一个进程也可以向其它的进程发送信号。这一操作是由系统调用 kill()来完成的。 kill()在 linux 系统库 signal.h中的函数声明如下:
int kill(pid_t pid, int sig);

参数 pid 指定了信号发送的对象进程:它可以是某个进程的进程标识符(pid),也可以是以下的值:
[list]
[*]如果 pid 为零,则信号被发送到当前进程所在的进程组的所有进程。
[*]如果 pid 为-1,则信号按进程标识符从高到低的顺序发送给全部的进程(这个过程受到当前进程本身权限的限制);
[*]如果 pid 小于-1,则信号被发送给标识符为 pid 绝对值的进程组里的所有进程。
[*]参数 sig 指定发送的信号类型。它可以是任何有效的信号。
[/list]
需要说明的是,一个进程并不是向任何进程均能发送信号的,这里有一个限制,就是普通用户的进程只能向具有与其相同的用户标识符的进程发送信号。也就是说,一个用户的进程不能向另一个用户的进程发送信号。只有 root 用户的进程能够给任何线程发送信号。

由于调用 kill()的进程需要直到信号发往的进程的标识符,所以这种信号的发送通常只在关系密切的进程之间进行,比如父子进程之间。
#include <signal.h>
int ntimes=0;
main(){

int pid,ppid;
int p_action(),c_action();
signal(SIGUSR1,p_action);
switch(pid=fork()){
case -1:
perror("sychro");
exit(1);
case 0:
signal(SIGUSR1,c_action);
ppid=getppid();
for(;;){
sleep(1);
kill(ppid,SIGUSR1);
pause();
}
break;

default:
for(;;){
pause();
sleep(1);
kill(pid,SIGUSR1);

}
}
}
p_action()
{
printf("Patent caught signal #%d\n",++ntimes);
}
c_action()
{
printf("Child caught signal #%d\n",++ntimes);
}
执行过程如下:
/home/l/g/tomotoboy/signal >gcc pro_4.c -o pro_4
pro_4.c: In function `main':
pro_4.c:6: warning: passing arg 2 of `signal' from incompatible pointer type
pro_4.c:12: warning: passing arg 2 of `signal' from incompatible pointer type
/home/l/g/tomotoboy/signal >pro_4
Patent caught signal #1
Child caught signal #1
User Signal 1

[color=red]问题:本来按照程序的意图父、子应该循环交替执行,然而各自却只执行了一次就结束了[/color]
kill命令用于向一个运行进程发送信号,它发送的信号默认为 SIGTERM,但是也可以指定为其它信号。我们可以直接用信号的号码来指定 kill 命令所发送信号之类型,也可以用符号名指定。比如可以用下面的命令来完成向进程标识符为 1234 的进程发送SIGINT 信号:
kill – s SIGINT 1234


[b]系统调用 alarm()和 pause()[/b]
1.系统调用 alarm()
alarm()是一个简单而有用的系统调用,它可以建立一个进程的报警时钟,在时钟定时器到时的时候,用信号向程序报告。alarm()系统调用在 Linux 系统函数库 unistd.h中的函数
声明如下:
unsigned int alarm(unsigned int seconds);

下面这个程序使用上述方法来强制用户作出回答。在其中包括一个 quickreply()函数,它有一个参数 prompt,它是一个指向提示字符串的指针。quickreply 的返回值也是一个指针。它指向含有输入行信息的字符串。这个例行程序在试作五次之后,如果仍未得到输入信息,就返回一个 null 指针。每当quickreply 要提醒用户时,它就向终端发送 ASCII 码 007,这会使终端响铃。
#include <stdio.h>
#include <signal.h>
#define TIMEOUT 5
#define MAXTRIES 5
#define LINESIZE 100
#define BELL '\007'
#define TRUE 1
#define FALSE 0
static int time_out;/*判断超时是否已经发生的标志 */
static char inputline[LINESIZE];
char* quickreply (char* prompt);
main()
{
printf("%s\n",quickreply("Input"));
}
char* quickreply (char* prompt)
{
int (*was)(),catch(),ntries;
char* answer;
/*设定捕捉SIGALRM 的的关联并保存原有关联*/
was=signal(SIGALRM,catch);
for (ntries=0;ntries<MAXTRIES;ntries++)
{
time_out=FALSE;
printf("\n%s>",prompt);
/* 设定定时器 */
alarm(TIMEOUT);
/* 获取输入 */
answer=gets(inputline);
/* 关闭定时器 */
alarm(0);
if (!time_out)
break;
}
/*恢复原有的SIGALRM 关联 */
signal(SIGALRM,was);
return (time_out?((char*) 0):answer);
}
/*SIGALRM 信号处理函数*/
catch()
{
/*设定超时标志*/
time_out=TRUE;
/* 响铃警告 */
putchar(BELL);
}

执行结果:
/home/l/g/tomotoboy/signal >gcc pro_5.c -o pro_5
pro_5.c: In function `quickreply':
pro_5.c:20: warning: passing arg 2 of `signal' from incompatible pointer type
pro_5.c:20: warning: assignment from incompatible pointer type
pro_5.c:31: warning: passing arg 2 of `signal' from incompatible pointer type
/home/l/g/tomotoboy/signal >pro_5

Input>
Input>Alarm Clock
/home/l/g/tomotoboy/signal >pro_5

Input>hello
hello
/home/l/g/tomotoboy/signal >pro_5

Input>
Input>Alarm Clock
/home/l/g/tomotoboy/signal >

[color=red]不知道怎么回事,今天这几个程序都没有循环起来,信号也只是发了一次,原因查找当中……[/color]

2.系统调用 pause()
系统调用 pause()能使调用进程暂停执行,直至接收到某种信号为止。pause()在 Linux系统函数库 unistd.h中的函数声明如下:
int pause(void);
该调用没有任何的参数。它的返回始终是 -1 ,此时 errno 被设置为ERESTARTNOHAND。


参考资料:
《linux网络编程》李卓恒等译
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值