int kill(pid-t pid,int sig); //给进程号为pid的进程发送sig信号;
int raise(int sig); //进程给自己发送信号;
(4)软件触发产生信号:
void abort(void);//认为进程是异常的,给进程发送6号信号,使得进程提前退出;
unsigned int alarm(unsigned int second);//间隔second秒后发送一个信号;
信号的处理动作:
(1)默认处理(大部分信号的默认处理为终止进程);
(2)忽略处理;
(3)自定义动作(信号的捕捉);
信号捕捉函数:
sighandler_t signal(int signum, sighandler_t handler);
参数:signum:表示要捕捉的信号;
handler:捕捉到该信号后所执行的自定义动作;
信号的表示:
信号递达:实际执行信号的处理动作称为信号的递达;
信号未决:信号从产生到递达之间的状态称为信号未决;
阻塞信号:当进程把某个信号阻塞以后,信号产生以后就保持未决状态,直到进程解除对于此信号的阻塞;
block表和pending表的实现都是用位图来实现的;
block表:保存进程对于信号的阻塞(屏蔽)情况,下标表示信号,对应下标保存的值表示该信号是否被阻塞;如上图:对于1号信号来说,没有被进程阻塞;则:2号信号和3号信号,都被进程阻塞,即使受到1号和2号信号,也只能处于未决状态,直到进程取消对于这个信号的阻塞;
pending表:保存进程中的信号是否处于未决状态,下标表示信号,对应下表保存的值表示该信号是否处于未决状态。如上图:对于1号和3号信号来说,就是没有产生;对于2号信号来说,已经产生并且处于未决状态;
handler表:保存进程中信号处理信号的动作。如上图:对于1号信号来说:执行的是默认动作(对于大多数信号来说,默认动作就是终止进程);对于2号信号来说:执行的是忽略动作;对于3号信号来说是执行自定义动作(也就是对于信号的捕捉);
解析上表:1号信号没有产生,也没有被进程阻塞,它的处理动作为默认处理;2号信号产生了,但是该信号被该进程阻塞,所以处于未决状态,此信号一直处于未决状态,直到该信号被该进程取消阻塞。当该信号被该进程取消阻塞后:执行该信号的处理动作:忽略处理;3号信号被该进程设置为阻塞,此时并没有产生,若产生,则处于未决状态;
handler表:或许是一个指针数组吧,保存的是都是指针,这些指针分别指向该信号所对应的处理操作。当一个信号被捕捉后,进行自定义的处理时:其实就是修改这个指针的指向。
注:(1)普通信号的发送只能被记录一次,若连续发送,只能记录一次有效的,其余被丢弃(由于位图只有一个bit为来保存数据,所以只 能记录该信号是否产生这个状态,不能记录这个信号发送的次数);
(2)实时信号可以通过队列来对于连续发送的信号进行记录,不会被丢弃;
信号集操作函数:
int sigemptyset(sigset_t *set); //初始化该信号集为空,不包含任何一个信号;
int sigfillset(sigset_t *set); //初始化该信号集,包含所有信号;
int sigaddset(sigset_t *set, int signum); //在该信号集中添加有效的信号;
int sigdelset(sigset_t *set, int signum); //在该信号集中删除有效的信号;
int sigismember(const sigset_t *set, int signum); //判断该信号集中是否包含signum这个有效信号;
读取或者更改进程的信号屏蔽字(阻塞信号集)
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数:how (1)how = SIG_BLOCK; //对于信号设置屏蔽;
(2)how = SIG_UNBLOCK; //对于被屏蔽的信号取消屏蔽;
(3)how = SIG_SETMASK; //设置当前信号屏蔽字为set的值;;
set:信号集
oldset:表示未操作之前的信号集,便于恢复;
读取当前进程的未决信号集
int sigpending(sigset_t *set);
参数:set:作为输出型参数,保存的是当前进程的未决信号集;
信号捕捉:
信号捕捉是发生在进程从内核态切换到用户态的时候;
(1)当代码在主函数中执行遇到中断,异常或者系统调用时,需要进去内核态进行处理;
(2)在内核态处理完中断要返回用户态之时,系统会自动检查是否有可以递达的信号;
(3)若有可以递达的信号:
a:当信号的处理动作为默认的时候:(4)系统修改进程的信号的pending字段,并且返回用户态的主函数上次执行的地方;
b:当信号的处理动作为默认时:(4)大多数信号的默认动作为终止进程)系统会直接将该进程终止;
c:当信号的处理动作为自定义时:(4)函数进入用户态,返回到信号的处理动作函数,执行次函数;
(5)信号的自定义处理动作函数执行结束,返回到内核态;
(6)从内核态返回到
用户态主函数被中断的地方,继续执行。
读取和修改与指定信号相关联的动作:
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
参数:
signo:要操作的信号量;
act:本次操作之后信号量的状态;
oact:信号量被操作之前的状态;
返回值:
返回0表示操作成功;返回-1表示操作失败;
挂起进程,直到有信号递达
int pause(void)
(1)当信号递达的处理动作为默认,那么系统会终止这个进程,pause函数没有机会返回;
(2)当信号递达的处理动作为忽略,那么,此进程继续挂起,不会返回;
(3)当进程的处理动作为自定义时:调用完信号处理函数之后,pause返回-1,pause只有出错的返回值;
使用alarm和pause实现了一个sleep函数:
#include
#include
#include
void myhandler()
{}
void mysleep(int timeout)
{
struct sigaction act ,oact;
act.sa_handler = myhandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM,&act,&oact);
alarm(timeout); //set a alarm;
pause(); // hung the process;
int ret = alarm(0);
sigaction(SIGALRM,&oact,NULL);
}
int main()
{
while(1)
{
mysleep(1);
}
return 0;
}
可重入函数:
可重入函数也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境,这样的函数就是可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如果确实需要访问和静态变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量或静态变量付出一些性能代价。
线程安全:
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的;否则:线程是不安全的。
注:线程安全都是由全局变量和静态变量引起的;
可重入函数和线程安全的联系:
(1)线程安全的函数不一定是 可重入的,但是可冲入的一定是线程安全的;
(2)如果使用互斥锁控制对于临界资源的访问,对于线程安全来讲是安全的,但是对于可重入函数来讲不是可重入的;
(3)对于使用静态变量或者全局变量的,并且没有对其实行保护措施的,既不是线程安全,也不是可重入的,它会造成数据的二义性。
作者水平有限,若有问题,请留言,谢谢!!!