信号量

一、信号的基本概念
1、用户输入命令,在Shell下启动一个前台进程;
通过组合键ctrl+c、ctrl+l、ctrl+z可以向前台进程发送信号;
进程所收到的信号都是由操作系统写的。
2、用户按下ctrl+c,键盘输入产生一个硬件中断;
3、如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理硬件中断;
4、一个命令后加&可以放到后台运行,这样shell不必等待进程结束就可以接受新的命令,启动新的进程;
5、shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到控制键产生的信号;
6、前台进程在运行过程中用户随时可能按下ctrl+c而产生一个信号,即该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,故信号相对于进程的控制流程来说是异步的。
二、产生信号的方式
1、通过终端按键产生信号;
2、通过命令KILL产生信号;
3、调用系统函数向进程发信号;(kill函数可以发送前、后台命令)
4、由软件条件产生信号;
5、通过硬件异常产生信号。
三、信号处理常见方式
1、忽略
2、执行该信号的默认处理动作(终止该进程)
3、提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数(自定义捕捉)
四 、阻塞信号
1、信号及其他相关常见概念
·实际执行信号的处理动作称为信号递达;
`信号从产生到递达之间的状态称为信号未决
·进程可以选择阻塞某个信号;
·被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才可执行递达的动作;
·阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
2、在内核中的表示
信号在内核中的表示示意图
这里写图片描述
若信号连续被发送多次,最终只能记录一次
3、阻塞信号集—–sigset_t
sigset_t称为信号集,未决和阻塞标志可以用相同的数据类型sigset_t来存储。
阻塞信号集也叫做当前进程的信号屏蔽字,这里的“屏蔽”应理解为阻塞而不是忽略。
4、信号集操作函数
使用者只能调用以下函数来操作sigset_t变量,而不应该对它的内部数据做任何解释。

    int sigemptyset(sigset_t *set); //初始化set所指向的信号集,使所有信号的对应bit清零
    int sigfillset(sigset_t *set); //初始化set所指向的信号集,使所有信号的对应bit置位
    int sigaddset(sigset_t *set,int signo);  //添加有效信号
    int sigdelset(sigset_t *set,int signo);  //删除有效信号
    int sigismember(const sigset_t *set,int signo); //判定一个信号是否在信号集中
    //包含:返回1;不包含:返回0;出错:返回-1.
    返回值:成功返回0,错误返回-1.

sigprocmask——读取或更改进程的信号屏蔽字(新增/修改block表)

#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oset)
//how:如何更改;set:非空指针,更改进程的信号屏蔽字;oset:非空指针,将原set值保存到oset中
返回值:成功返为0;失败返回-1.

sigpending——读取当前进程的未决信号集
返回值:成功返回0;出错返回-1.

五、捕捉信号
1、信号的捕捉
以下是信号的捕捉过程,为了方便我们理解,我们可以将其看作无穷大的符合。
进程从内核态切换回用户态时要对信号进行检查处理。
这里写图片描述
2、sigaction——自定义用户函数捕捉

//可以读取和修改与指定信号相关联的处理动作
#include <signal.h>
int sigaction(int signo,const struct sigaction *act,struct sigaction *oact);
//signo:指定信号的编号;act:根据act修改该信号的处理动作;oact:保存原act的值

3、pause——将当前运行的进程挂起
挂起:指将PCB放入等待队列,且将R状态转换为其他状态。

#include <unistd.h>
int pause(void)
//只有当收到一个自定义捕捉信号时,进程才会出错退出。
//若信号的处理动作是终止进程,则此进程终止,pause函数无返回;
// 若信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;
// 若信号的处理动作是捕捉,则调用pause函数后会返回-1.即pause只有出错的返回值。

六、可重入函数
一个函数被多个执行流进入时,称为可重入函数。
若一个函数只访问自己的局部变量或参数,也称为可重入函数。
可重入函数不安全,可能会导致错误。
七、不可重入函数
访问全局链表的函数是一种不可重入函数。
满足以下条件之一的函数称为不可重入函数:
·调用了malloc或free,因为malloc也是用全局链表来管理堆的;
·调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

若一个函数调用了不可重入函数,那么该函数也是不可重入的。
八、竞态条件与sigsuspend函数
竞态条件:指由于时序问题而导致的错误
解决方法:sigsuspend函数

//sigsuspend包含了pause的挂起等待功能,同时解决了竞态条件的问题,在对时序要求严格的情况下,应调用此函数
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
返回值:和pause一样,成功无返回值,只有执行了信号处理函数sigsuspend才返回-1.

九、SIGCHLD信号
父进程:
(1) 可以阻塞等待子进程结束;(父进程阻塞后就不能处理自己的工作了)
(2) 可以非阻塞的查询是否有子进程结束等待清理(即轮询);(父进程在处理自己的工作的同时还要时不时的轮询一下,程序实现复杂)。
子进程:子进程终止时会给父进程发送SIGCHLD信号,该信号的默认处理动作是忽略,如此,父进程只需专心处理自己的工作,当子进程终止时会通知父进程,此时父进程调用wait清理子进程即可。
父进程调用sigaction函数将SIGCHLD的处理动作设为SIG_IGN,这样fork出来的子进程在终止时会被系统自动回收,不会产生僵尸进程,也不会通知父进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值