信号通信

signal

#include<signal.h>

#include<sys/types.h>

int kill(pid_t pid,int sig)

pid的取值:

正数:要发送信号的进程号

0:信号被发送到所有和 pid 进程在同一个进程组的进程

1:信号发给所有的进程表中的进程(除了进程号最大的进程外)

int raise(int sig) 进程向自身发送信号

 

#include <unistd.h>

unsigned int alarm(unsigned int seconds)

alarm当定时器指定的时间到时, 它就向进程发送 SIGALARM 信号。

int pause(void)

挂起进程直到有信号到来,且当信号处理函数返回后pause()才返回-1。

unsigned int sleep(unsigned int seconds);

休息seconds秒后返回;或者被信号中断且信号处理函数返回后sleep()返回0。

所以如果不计较返回值的话,pause()的功能相当于无限期的sleep()。

最简单的函数如:

int main(){

  int ret=alarm(5);

  pause();

  printf(“I have been waken up.\n”);

}

可靠信号(实时信号)和不可靠信号(非实时信号):

一个不可靠信号的处理过程是这样的:如果发现该信号已经在进程中注册,那么就忽略该信号。因此,若前一个信号还未注销又产生了相同的信号就会产生信号丢失。而当可靠信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此信号就不会丢失。所有可靠信号都支持排队,而不可靠信号则都不支持排队。

signal()主要用于非实时信号的处理,不支持信号传递信息。

sigaction()可用于实时信号处理,支持信号传递消息。

信号处理函数必须是返回void,有且只有一个int参数。

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
 /*自定义信号处理函数 */
void foo(int sig_no)        //必须是返回void,有且只有一个int参数
{
    if (sig_no == SIGINT)    //按下Ctrl+C产生SIGINT信号
        printf("I have got SIGINT\n");
    if (sig_no == SIGQUIT)    //按下Ctrl+\产生SIGQUIT信号
        printf("I have got SIGQUIT\n");
}

int main()
{
    puts("Waiting for SIGINT or SIGQUIT...");
    signal(SIGINT, foo);
    signal(SIGQUIT, foo);
    pause();        //等待信号到达
    return0;
}

sigaction

#include <signal.h>

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

signum:信号的值,可以为除 SIGKILL 及 SIGSTOP 外的任何一个特定有效的信号

act:指向结构 sigaction 的一个实例的指针,指定对特定信号的处理

oldact:保存原来对相应信号的处理

struct sigaction {
  void (*sa_handler)(int);
  void (*sa_sigaction)(int, siginfo_t *, void *);
  sigset_t sa_mask;
  int sa_flags;
  void (*sa_restorer)(void);
};

通过sa_mask设置信号掩码集。

信号处理函数可以采用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。到底采用哪个要看sa_flags中是否设置了SA_SIGINFO位,如果设置了就采用void (*sa_sigaction)(int, siginfo_t *, void *),此时可以向处理函数发送附加信息;默认情况下采用void (*sa_handler)(int),此时只能向处理函数发送信号的数值。

sa_falgs还可以设置其他标志:

SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL

SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用

SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#define INPUTLEN 100
void inthandler(int);
int main(){
struct sigaction newhandler;
sigset_t blocked; //被阻塞的信号集
char x[INPUTLEN];
newhandler.sa_flags=SA_RESETHAND;
newhandler.sa_handler=inthandler;
sigemptyset(&blocked); //清空信号处理掩码
sigaddset(&blocked,SIGQUIT);
newhandler.sa_mask=blocked;
if(sigaction(SIGINT,&newhandler,NULL)==-1)
perror("sigaction");
else
while(1){
fgets(x,INPUTLEN,stdin); //fgets()会在数据的最后附加"\0"
printf("input:%s",x);
}
}
void inthandler(int signum){
printf("Called with signal %d\n",signum);
sleep(signum);
printf("done handling signal %d\n",signum);
}

Ctrl-C向进程发送SIGINT信号,Ctrl-\向进程发送SIGQUIT信号。

$ ./sigactdemo
^CCalled with signal 2
^\done handling signal 2
Quit (core dumped)

由于把SIGQUIT加入了信号掩码集,所以处理信号SIGINT时把SIGQUIT屏蔽了。当处理完SIGINT后,内核才向进程发送SIGQUIT信号。

$ ./sigactdemo
^CCalled with signal 2
^Cdone handling signal 2

 由于设置了SA_RESETHAND,第一次执行SIGINT的处理函数时相当于执行了signal(SIGINT,SIG_DFL),所以进程第二次收到SIGINT信号后就执行默认操作,即挂起进程。

修改代码,同时设置SA_RESETHAND和SA_NODEFER。

newhandler.sa_flags=SA_RESETHAND|SA_NODEFER;

$ ./sigactdemo
^CCalled with signal 2
^C

在没有设置SA_NODEFER时,在处理SIGINT信号时,自动屏幕SIGINT信号。现在设置了SA_NODEFER,则当SIGINT第二次到来时立即响应,即挂起了进程。 

posted @ 2011-12-22 11:48  张朝阳  阅读(3111)  评论(0编辑  收藏  举报