linux 系统编程 -- 信号及信号处理(四)

本文详细介绍了Linux系统编程中的信号机制,包括信号的来源、种类、处理方式和发送方法。信号作为一种软件中断,提供了处理异步事件的方法,并且是进程间唯一的异步通信方式。内容涵盖SIGHUP到SIGALRM等常见信号的默认行为,以及如何通过函数如kill、raise、sigqueue来发送信号。
概念

信号是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。

信号的来源:
①硬件方式:按键会产生信号,比如ctrl+c组合键,产生一个SIGINT信号;硬件异常信号,比如除数为0,无效存储访问,由cpu通知内核,内核生成相应信号。

②软件方式: 用户在终端下调用kill命令向进程发送任意信号;进程调用kill或sigqueue函数发送信号;当某种条件已经具备时发送信号,如settimer定时器超时产生SIGALRM信号。

信号的种类:
使用kill -l 命令系统支持的全部信号。
在这里插入图片描述
①SIGHUP:用户退出shell,由该shell启动的所有进程将收到这个信号,默认动作为终止进程。

②SIGINT:用户按下ctrl+c组合键,用户终端向正在运行中的由该终端启动的进程发送此信号,默认动作为终止进程。

③SIGQUIT:用户按下ctrl+\组合键,用户终端向正在运行中的由该终端启动的进程发送此信号,默认动作为终止进程并产生core文件。

④SIGILL:cpu检测到某进程执行了非法指令,默认动作为终止进程并产生core文件。

⑤SIGTRAP:由断点指令或其他trap指令产生,默认动作为终止进程并产生core文件。

⑥SIGABRT:调用abort函数产生该信号,,默认动作为终止进程并产生core文件。

⑦SIGBUS:非法访问内存地址,包括内存地址对齐出错,默认动作为终止进程并产生core文件。

⑧SIGFPE:在发生致命的算术运算时产生,不仅包括浮点运算错误,还包括溢出及除数为0等错误,默认动作为终止进程并产生core文件。

⑨SIGKILL:无条件终止进程,本信号不能被忽略、处理和阻塞。默认动作为终止进程,它向系统管理员提供了一种可以杀死任何进程的方法。

⑩SIGUSR1:用户定义的信号,即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

⑪SIGSEGV: 指示进程进行了无效的内存访问。默认动作为终止进程并产生core文件。

⑫SIGUSR2:另一个用户定义的信号,即程序员可以在程序中定义并使用该信号。默认动作为终止进程。

⑬SIGPIPE:向一个没有读端的管道写数据,默认动作为终止进程。

⑭SIGALRM:定时器超时,超时的时间由系统调用alarm设置,默认动作为终止进程。

⑮SIGTERM:程序结束信号,与SIGKILL不同,这个信号可以被阻塞和处理。通常要求程序正常退出,执行shell命令kill时,缺省产生这个信号。默认动作为终止进程。

⑯SIGSTKFLT :该信号仅仅在Linux中有定义,出现在早期的Linux系统中,其原意是用于数字协处理器的堆栈错误,该信号在内核中并不会被产生,但是为了兼容性被保留下来了。

⑰SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略该信号。

⑱SIGCONT:让一个暂停的进程继续执行。

⑲SIGSTOP:停止进程的执行,注意它和SIGTERM以及SIGINT的区别。该进程还未结束,只是暂停执行,本信号不能被忽略、阻塞或处理。默认动作为暂停进程。

⑳SIGTSTP:停止进程的执行,按下ctrl+z组合键产生,本信号可以被忽略或处理。默认动作为暂停进程。

从34) SIGRTMIN到64 )SIGRTMAX为linux的实时信号,它们没有固定含义,或者说由用户自由使用,注意linux线程使用了前3个实时信号。
所有的实时信号默认动作为终止进程。

可靠与优先级

从1号SIGHUP信号到31号SIGSYS之间的信号都是继承自UNIX系统,是不可靠信号。
linux系统根据posix.4标准定义了SIGRTMIN(33号)至SIGRTMANX(64号)之间的信号,是可靠信号,也称实时信号。
在linux系统中,信号的可靠性是指信号是否会丢失,或者说信号是否支持排队。

当信号产生后,内核会在进程表中设置某种形式的标志,当内核设置了这个标志,我们说内核向一个进程递送了一个信号,在信号产生和递送之间的时间间隔,称为信号未决。

被递送多次的信号(即信号在未决队列里排队)称为可靠信号,只被递送一次的称为不可靠信号。

如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核按照发送的顺序来递送;如果存在多个实时未决信号,内核按照信号的编号越小越先递送;如果即存在不可靠信号,又存在可靠信号,内核优先递送不可靠信号。

进程对信号的响应

当信号发生时,用户可以要求进程以下列3种方式之一来做出响应:
① 捕捉信号:对于要捕捉的信号,可以为其制定信号处理函数,信号发生时改函数自动被调用

② 忽略信号:大多数信号都可使用这种方式处理,但是SIGKILL和SIGSTOP这两个信号不能被忽略与捕捉。

③按照系统默认方式处理:即终止进程。

在这里插入图片描述
signal函数捕捉并处理信号。
typedef void (*sighandler_t)(int); sighandler_t是一个函数指针型,它指向的函数有一个int参数,返回值是void。

 #include <signal.h>
 #include <stdio.h>


void handler_sigint(int signo)
{
  printf("recv SIGINT %d\n",signo);
}

int main()
{
  signal(SIGINT, handler_sigint);
  while(1)
  ;
  
  return 0;
}

在这里插入图片描述
按下ctrl+c组合键,产生信号SIGINT,被程序捕捉并处理。最后按下ctrl+\组合键发送SIGQUIT信号,由于程序没有处理该信号,按照默认方式处理–即结束进程。

信号的发送

信号的发送主要由函数kill、raise、sigqueue、alarm、setitimer以及abort来完成。
在这里插入图片描述

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

① kill第一个参数是进程号,第二个参数是信号编号。
函数执行成功返回0,错误返回-1.
只有具有root权限的进程才能向其他任意进程发送信号,非root权限的进程只能向属于同一个组或同一个用户的进程发送信号。

② raise函数是ANSI C而非POSIX标准定义的。
在这里插入图片描述
在单线程中,它等价于 kill(getpid(), sig);

③sigqueue 函数是一个比较新的发送信号函数,它支持信号带有参数。与函数sigaction配合使用,因此这个额外的信号带有参数就能完成数据的传递。

④alarm 函数用来设置定时器,定时器超时会产生SIGALRM信号给调用进程。
在这里插入图片描述
参数代表设定的秒数,经过seconds秒后,内核将给调用该函数的进程发送SIGALRM信号。如果seconds为0,则不再发送SIGALRM信号。
最新一次调用alarm函数将取消之前一次的设定。
注意alarm只设定为发送一次信号,如果要多次发送,就要对alarm进行多次调用。

 #include <signal.h>
 #include <stdio.h>
 #include <unistd.h>

void send_ip()
{
  printf("send a icmp echo request panckt\n");
}

void recv_ip()
{
  printf("recv a ip\n");
  while(1)
    ;
}
void handler_sigalarm(int signo)
{
//信号处理程序再次设定定时器
  send_ip();
  alarm(2);
}

int main()
{
  //安装信号处理
  signal(SIGALRM, handler_sigalarm);
  //触发信号给本进程
  raise(SIGALRM);
  recv_ip();
  return 0;
}

如果没有raise的话,第一次就无法触发,在信号处理程序中反复设定定时器,以获得多次发送效果。
在这里插入图片描述
执行结果来看,当程序运行到 raise(SIGALRM)时,已经触发了handler_sigalarm,所以输出send a icmp echo request panckt,之后再执行
recv_ip。

⑤setitimer函数 也是用来设置定时器,它的功能更强大。
在这里插入图片描述
第一个参数which制定使用哪一个定时器,种类有ITIMER_REAL,ITIMER_VIRTUAL,ITIMER_PROF。

 #include <signal.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/time.h>

void handler_sigtime(int signo)
{
  switch (signo)
  {
    case SIGALRM:
      printf("recv SIGALRM\n");
      break;
    case SIGPROF:
      printf("recv STGPROF\n");
      break;
    default:
      break;
  }
}

int main()
{
  struct itimerval value;
//相关信号共用一个处理函数
  signal(SIGALRM, handler_sigtime);
  signal(SIGPROF, handler_sigtime);
  
  //第一次1秒触发信号
  value.it_value.tv_sec=1;
  value.it_value.tv_usec=0;
 //第二次开始每5秒触发信号
  value.it_interval.tv_sec=5;
  value.it_interval.tv_usec=0;
  
//设置定时器
  setitimer(ITIMER_REAL,&value,NULL);
  setitimer(ITIMER_PROF,&value,NULL);
  while(1)
    ;
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值