一、Unix/Linux操作系统体系结构
1. 两种状态
类Unix操作系统体系结构分为两个状态 :1. 用户态,2. 内核态
操作系统/内核:用来控制计算机硬件资源,提供应用程序运行的环境;我们写的程序,他要么运行在用户态,要么运行在内核态。一般来讲运行在用户态;当程序要执行 一些特殊代码的时候,程序就可能切换到内核态,这种切换由操作系统控制,不需要人为介入;换种角度理解:用户态:最外圈应用程序的活动空间;
系统调用:就是一些函数(系统函数),你只需要调用这些函数;
shell: bash(borne again shell[重新装配的shell]),它是shell的一种,linux上默认采用的是bash这种shell;通俗一点理解,bash是一个可执行程序;主要作用是:把用户输入的命令翻译给操作系统(命令解释器);分隔系统调用 和应用程序; 有胶水的感觉;
用户态:内核态之间的切换
运行于用户态的进程可以执行的操作和访问的资源会受到极大限制(用户态权限小);而运行在内核态的进程可以执行任何操作并且在资源的使用上没有限制(内核态权限大);一个进程执行的时候,大部分时间是处于用户态下的,只有需要内核所提供的服务时 才会切换到内核态,内核态做的事情完成后,又转回到用户态;
malloc();printf(); 这种状态在转换是操作系统干的,不需要我们介入;
2. 为什么要区分用户态,内核态
- 一般情况下,程序都运行在用户态状态,权限小,不至于危害到系统其它部分;当你干一些危险的事情的时候,系统给你提供接口,让你去干;
- 既然这些接口是系统提供给你的,那么这些接口也是操作系统统一管理的;资源是有限的, 如果大家都来访问这些资源,如果不加以管理,一个是访问冲突,一个是被访问的资源如果耗尽,那系统还可能崩溃;
系统提供这些接口,就是为了减少有限的资源的访问以及使用上冲突;
3. 什么时候从用户态切换到内核态去呢?
- 系统调用,比如调用malloc();
- 异常事件,比如来了个信号;
- 外围设备中断:
总结起来,大家只需要知道 用户态,内核态,两者根据需要自动切换
二、signal函数范例
信号来了之后,我们可以忽略,可以捕捉,咱们可以用signal函数来搞定这个事;
ps -eo pid,ppid,sid,tty,pgrp,comm | grep -E 'bash|PID|nginx'
你的进程收到了信号,这个事 就会被内核注意到;
1. 可重入函数
严格意义:muNEfunc()函数不应该是一个可重入函数
所谓的可重入函数:就是我们在信号处理函数中 调用它 是安全的;
可重入函数:在信号处理程序中保证调用安全的函数,这些函数是可重入的并被称为异步信号安全的;
有一些大家周知的函数都是不可重入的,比如malloc(),printf();
int errno = 20;
在写信号处理函数的时候,要注意的事项:
- 在信号处理函数中,尽量使用简单的语句做简单的事情,尽量不要调用系统函数以免引起麻烦;
- 如果必须要在信号处理函数中调用一些系统函数,那么要保证在信号处理函数中调用的 系统函数一定要是可重入的;
- 如果必须要在信号处理函数中调用那些可能修改errno值的可重入的系统函数,那么 就得事先备份errno值,
从信号处理函数返回之前,将errno值恢复;
2. 不可重入函数的错用演示
一旦在信号处理函数中用了不可重入函数,可能导致程序错乱,不正常。。。。。
signal因为兼容性,可靠性等等一些历史问题;不建议使用(我们的策略,坚决不用),建议用sigaction()函数代替;
高手:我们摸不清楚(有可能有坑)的东西(地方)我们主动回避,不去踩;
三、信号集
一个进程,必须能够记住 这个进程 当前阻塞了哪些信号
000000000000000000000
我们需要 “信号集 ”的这么一种数据类型(结构),能够把这60多个信号都表示下(都装下)。
0000000000,0000000000,0000000000,00,0000000000,0000000000,0000000000,00 (64个二进制位)
linux 是用sigset_t结构类型来表示信号集的;
typedef struct{
unsigned long sig[2];
}sigset_t
信号集的定义:信号集表示一组信号的来(1)或者没来(0)
信号集相关的数据类型: sigset_t;
四、信号相关函数
- sigemtpyset():把信号集中的所有信号都清0,表示这60多个信号都没有来;00000000000000000000000000…
- sigfillset();把信号集中的所有信号都设置为1,跟sigemptyset()正好相反;
11111111111111111111111111… - 用sigaddset(),sigdelset() 就可以往信号集中增加信号,或者从信号集中删除特定信号;
- sigprocmask,sigmember
一个进程,里边会有一个信号集,用来记录当前屏蔽(阻塞)了哪些信号;
如果我们把这个信号集中的某个信号位设置为1,就表示屏蔽了同类信号,此时再来个同类信号,那么同类信号会被屏蔽,不能传递给进程;
如果这个信号集中有很多个信号位都被设置为1,那么所有这些被设置为1的信号都是属于当前被阻塞的而不能传递到该进程的信号;
sigprocmask()函数,就能够设置该进程所对应的信号集中的内容;
五、sigprocmask等信号函数范例演示
sleep()函数能够被打断:
(1)时间到达了;
(2)来了某个信号,使sleep()提前结束,此时sleep会返回一个值,这个值就是未睡够的时间;
sigaction()函数;
#include <stdio.h>
#include <stdlib.h> //malloc
#include <unistd.h>
#include <signal.h>
//信号处理函数
void sig_quit(int signo)
{
printf("收到了SIGQUIT信号!\n");
if(signal(SIGQUIT,SIG_DFL) == SIG_ERR)
{
printf("无法为SIGQUIT信号设置缺省处理(终止进程)!\n");
exit(1);
}
}
int main(int argc, char *const *argv)
{
sigset_t newmask,oldmask; //信号集,新的信号集,原有的信号集,挂起的信号集
if(signal(SIGQUIT,sig_quit) == SIG_ERR) //注册信号对应的信号处理函数,"ctrl+\"
{
printf("无法捕捉SIGQUIT信号!\n");
exit(1); //退出程序,参数是错误代码,0表示正常退出,非0表示错误,但具体什么错误,没有特别规定,这个错误代码一般也用不到,先不管他;
}
sigemptyset(&newmask); //newmask信号集中所有信号都清0(表示这些信号都没有来);
sigaddset(&newmask,SIGQUIT); //设置newmask信号集中的SIGQUIT信号位为1,说白了,再来SIGQUIT信号时,进程就收不到,设置为1就是该信号被阻塞掉呗
//sigprocmask():设置该进程所对应的信号集
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0) //第一个参数用了SIG_BLOCK表明设置 进程 新的信号屏蔽字 为 “当前信号屏蔽字 和 第二个参数指向的信号集的并集
{ //一个 ”进程“ 的当前信号屏蔽字,刚开始全部都是0的;所以相当于把当前 "进程"的信号屏蔽字设置成 newmask(屏蔽了SIGQUIT);
//第三个参数不为空,则进程老的(调用本sigprocmask()之前的)信号集会保存到第三个参数里,用于后续,这样后续可以恢复老的信号集给线程
printf("sigprocmask(SIG_BLOCK)失败!\n");
exit(1);
}
printf("我要开始休息10秒了--------begin--,此时我无法接收SIGQUIT信号!\n");
sleep(10); //这个期间无法收到SIGQUIT信号的;
printf("我已经休息了10秒了--------end----!\n");
if(sigismember(&newmask,SIGQUIT)) //测试一个指定的信号位是否被置位(为1),测试的是newmask
{
printf("SIGQUIT信号被屏蔽了!\n");
}
else
{
printf("SIGQUIT信号没有被屏蔽!!!!!!\n");
}
if(sigismember(&newmask,SIGHUP)) //测试另外一个指定的信号位是否被置位,测试的是newmask
{
printf("SIGHUP信号被屏蔽了!\n");
}
else
{
printf("SIGHUP信号没有被屏蔽!!!!!!\n");
}
//现在我要取消对SIGQUIT信号的屏蔽(阻塞)--把信号集还原回去
if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0) //第一个参数用了SIGSETMASK表明设置 进程 新的信号屏蔽字为 第二个参数 指向的信号集,第三个参数没用
{
printf("sigprocmask(SIG_SETMASK)失败!\n");
exit(1);
}
printf("sigprocmask(SIG_SETMASK)成功!\n");
if(sigismember(&oldmask,SIGQUIT)) //测试一个指定的信号位是否被置位,这里测试的当然是oldmask
{
printf("SIGQUIT信号被屏蔽了!\n");
}
else
{
printf("SIGQUIT信号没有被屏蔽,您可以发送SIGQUIT信号了,我要sleep(10)秒钟!!!!!!\n");
int mysl = sleep(10);
if(mysl > 0)
{
printf("sleep还没睡够,剩余%d秒\n",mysl);
}
}
printf("再见了!\n");
return 0;
}