(五)信号 1

信号:(signal)

中断分为软件中断和硬件中断,软件中断就是用软件的方式中断代码。信号是Unix/Linux系统下最常见的一种软件中断方式,提供了一种处理异步事件的方法。

信号都有一个宏名称,以SIG开头。本质就是一个非负整数,查看信号的命令:

kill -l

编程时,信号用宏名称,因为有些系统信号数字不同,但宏名称是一样的。

 

信号导致代码中断的条件很多,比如:

中断(Ctrl+C,SIGINT)、退出(Ctrl+\,SIGQUIT)、(挂起(Ctrl+Z,SIGSTP);段错误(SIGSEGV)、总线错误、整数除以0(SIGFPE)等

**浮点数可以 除以0,结果是 INF(无限大)。**

Unix系统信号 从 1到48,Linux系统从 1 -64 ,但不确保连续,而且 规范中没有规定信号的数量。在Linux系统中,1-31是 不可靠信号,是早期的信号,不支持排队,有可能丢失;34-64是可靠信号,支持排队,不可能丢失。信号分为可靠信号和不可靠信号。

 

信号处理方式有三种:

1.        默认处理,系统提供的,多半是终止进程。

2.        忽略信号。**SIGKILL和SIGSTOP不能被忽略**

3.        捕捉信号,自定义处理函数。

当前用户只能给自己的进程发信号,root可以给所有进程发信号。

 

父子进程之间的信号处理:

如果父进程用fork()创建的子进程,子进程与父进程的信号处理方式完全一致。如果父进程用vfork()+execl()方式创建的子进程,父进程自定义处理函数的子进程改为默认,其它信号的状态则不变。因为信号捕捉函数的地址很可能在新程序中已无意义。

 

信号的发送方式:

1.       键盘发送(部分信号)

Ctrl+C -> 信号2

Ctrl+\  -> 信号3

2.       程序错误(部分信号)

段错误、总线错误、整数被0除

3.       kill命令 (所有信号)

kill -信号 进程PID

4.       系统函数 (所有信号)

raise() kill()  alarm() sigqueue() ...

 

int kill(pid_t pid,int signo)  kill函数将信号发送给进程或进出组

返回:成功返回0,失败返回-1.

pid的值:最常用的是 正数,其次是 -1

正数:就是给指定的一个进程发信号(pid)

-1   :就是给所有进程发信号(有权限的)

0    :  本组所有的进程发信号

 <-1 : 指定进程组(组ID=-pid)的所有进程

int raise (int signo) 只允许进程向自身发送信号。

 

unsigned int alarm(unsigned int  seconds) 设置一个计时器,产生SIGALRM信号,返回0或以前设置闹钟时间的剩余秒数。

 

int pause(void) 使调用者挂起直至捕捉到一个信号,只有执行了一个信号处理程序并从其返回,pause才返回,并将errno设置为EINTR。

 

中断的系统调用

低速系统调用是可能会使进程永远阻塞的一类系统调用,包括

1.     读写、打开某些类型的文件(管道,终端设备,网络设备等)

2.     pause 函数和wait 函数

3.     某些ioctl 操作

4.     某些进程间通信函数

当阻塞与某个低速系统调用的一个进程捕获某个信号(信号在阻塞期间发生)且从相应处理函数返回时,该系统调用可能返回一个EINTR错误。根据具体情况处理是否需要重启被中断的系统调用。(即判断errno是否为EINTR,是则再次调用系统函数)

 

可重入函数

在进行信号处理(即信号处理程序中),应当调用可重入函数,否则其结果不可预见。不可重入函数的主要原因是:

1.     使用静态数据结构

2.     调用malloc 和free

3.     是标准I/O函数,标C函数很多都已不可重入方式使用全局数据结构。

当信号处理函数中调用可能修改errno值的函数时,应当先保存先前值,在其后恢复errno。

 

函数signal() / sigaction() 可以设置信号的处理方式。

typedef  voidSigfunc (int)

Sigfunc * signal (int signo , Sigfunc  * func)

即signal是一个指向Sigfunc类型函数的指针,其参数为signo和它自身一样的函数指针。**函数指针 signal(int 信号值 , 函数指针)**

func值:   SIG_IGN - 忽略该信号

                       SIG_DFL - 恢复默认处理

                       自定义处理函数

返回:成功返回之前的处理方式,失败返回SIG_ERR。

 

sleep()和usleep()

sleep() 函数会让程序休眠,直到休眠的时间到,或者有未忽略的信号到来。会返回剩余的秒数。

usleep()函数 更精确,休眠是以 微秒为单位。

 

信号集和信号屏蔽

系统定义了存放多个信号的数据结构,就是信号集 (sigset_t数据类型),本质是一个超大的整数,每个二进制位 代表一个信号。

信号集的运算结构(提供函数):

     sigaddset()- 增加一个信号 (某个二进制置1)

    sigfillset() - 所有信号全部增加(所有位全置1)

    sigdelset()  - 删除一个信号

    sigemptyset() - 删除所有信号(所有位置0)

    sigismember() - 查询一个信号是否在

 

函数 sigprocmask() 可以屏蔽/解除屏蔽。

int sigprocmask(int how,sigset_t*new,sigset_t* old)

参数how是信号屏蔽的方式。

new 是新的权限屏蔽字(屏蔽哪些权限)

        old是传出参数,返回以前的信号屏蔽字,便于后面恢复

how的三种方式:

SIG_BLOCK: 之前的屏蔽+new的屏蔽

SIG_UNBLOCK: 之前的屏蔽 - new的屏蔽

SIG_SETMASK:直接用新的屏蔽,与之前的无关

**SIG_SETMASK最常用。

 

sigsuspend

如果要在一个原子操作中先设置信号屏蔽字,然后使进程休眠,可以使用sigsuspend函数。(不可靠的实现sigpromask和pause之间的窗口期产生的信号会丢失)

       int  sigsuspend(const sigset_t  *sigmask)

它将进程的信号屏蔽字设置为sigmask指向的值,在捕捉到一个信号或发生了一个会终止该进程的信号前,该进程被挂起。捕捉到信号后返回之前的信号屏蔽字。

 

sigaction() 的绝大多数的功能都可由signal()实现,一些特殊的功能sigaction()可以实现。sigaction()可以让信号有更多的信息,甚至可以在发信号的时候附带额外的数据。signal不同的实现提供不同的信号语义以达成向后兼容,可以通过sigaction函数实现自己的signal函数。

       intsigaction (int signo,const struct sigaction * act , struct  sigaction *oact)

若act非空,则要修改其动作;若oact非空则经该指针返回该信号的上一个动作。sigaction结构如下

struct sigaction {

       void  (*sa_handler) (int)

       sigset_t  sa_mask ;

       int  sa_flags;

       void  (*sa_sigaction )(int ,siginfo_t  *, void *)

}

 

计时器(了解)

Linux计时器就是 N秒以后,每隔M秒产生一个信号。计时器有三种:真实、虚拟和实用,常用的是真实计时器,真实计时器产生SIGALRM信号。

函数setitimer()可以设置计时器,getitimer()可以获取计时器。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值