ulimit命令是查看或者设置当前用户或者进程使用资源的阀值
如上图所示我们的core fiel的大小是0,因此需要 ulimit -c +大小,对其进行大小的设置,才能生成对应的Core Dump文件
当一个程序崩溃之后,操作系统会将程序奔溃前的内存之中的信息转储于磁盘之中,因为一个进程的终止,需要操作系统发送信号,而一个进程收到信号,有三种处理方式,分别为:执行默认的动作、执行自定义动作、忽略它。
所以说接收到了一个信号,并不是立即执行的,因此程序在崩溃前有时间将内存之中的信息,转存至硬盘之中
生成的core文件是二进制文件,因为core文件是给个编译器看的,方便调试,是一种事后调试(逐步逐过程为事前调试)
类似于linux的弹窗,报出错误在哪里
1.保护服务器
由2.1的图中可知,云服务器默认是关闭Core Dump的,这是因为当发生段错误时,会在磁盘之中生成临时文件,而我们的服务器出现了错误,一般都是先让服务器先恢复使用再进行错误的调式
如果服务器重启就发生错误,这样会导致生成很多临时文件,那么生成的临时文件将磁盘堆满,甚至将系统盘堆满,那么我们的系统就会出错,导致无法第一时间恢复使用
2.并不是所有的信号都需要Core Dumo
我们进程退出的时候,会有一个输出型参数status,低8位中的低7位表示退出信号,第8位表示Core Dump,如果为0则表示不需要,为1表示需要。比如我们的 kill -9号信号,系统直接终止进程,是不需要调式的
执行信号的处理动作叫做信号递达
信号从产生到递达之间的状态,称为信号未决
被阻塞的信号,产生时,将保持未决状态,当进程解除对此信号的阻塞,才会执行递达动作
阻塞和忽略是不一样的,忽略是一种处理动作,而信号只要被阻塞就不会被递达
信号的保存前提是,是否收到信号
因此我们需要记录的信息是,这个信号是谁,这个信号是否产生
而我们的实时信号有31种,因此使用一张位图可以进行保存
比特位的位置表示是那种信号,比特位为1表示该种信号产生了,为0表示该种信号没有产生
同理,我们对信号的阻塞信息,也会被保存在一张位图之中,对应的是当前信号是否被阻塞
这张位图保存在进程的PCB之中,与其说是操作系统发出信号,不如说是操作系统向进程PCB中的位图写入信号
信号的写入是由操作系统来执行的,如何处置这个信号则是由进程来决定的
我们的内核之中有两张结构一样的位图,来表示当前信号是否被阻塞,和是否处于未决状态,还有一个函数指针表示处理的动作
信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达,信号标志才会被清除
每个信号的阻塞或未决都是由一个比特位来表示的,不是0就是1,因此未决和阻塞标志可以使用同一样数据类型sigset_t来进行存储
sigset_t被称为信号集,表示每个信号是有效还是无效
在阻塞状态中,有效、无效表示是否被阻塞,阻塞信号集(block表)也被叫做当前进程的信号屏蔽字
在未决状态中,有效、无效表示信号是否处于未决状态
sigset_t是表示信号有效、无效状态的信号集,这个类型的实现是由系统来决定的,因此使用者只能调用下列函数的接口来操作sigset_t变量,而不是直接对其内部数据进行解释(比如,直接打印sigset_t类型变量,或者做位运算操作,这些都是没有意义的)
void handler(int sig)
{
printf("i am 2 signal\n");
}
void show(sigset_t *rset )
{
for(int i=1;i<=31;i++)//普通信号范围是0-31
{
if(sigismember(rset,i))//判断信号是否存在,存在输出1
printf("1");
else
printf("0");//不存在输出0
}
printf("\n");
}
int main()
{
signal(2,handler);//捕捉2号信号
sigset_t set;
sigset_t oset;
sigset_t rset;
sigemptyset(&set);//清空当前信号集
sigaddset(&set,2);//添加2号信号
sigprocmask(SIG_BLOCK,&set,&oset);//将2号信号屏蔽
int count=0;
while(1)
{
if(count==5)//10秒后,解除对2号信号的屏蔽
sigprocmask(SIG_SETMASK,&oset,NULL);//使用备份,恢复原来模样
sigpending(&rset);//获取当前信号集
show(&rset);//显示当前信号集
sleep(1);
count++;
}
return 0;
}
操作系统向进程发出信号,进程并不是立即执行信号的,而是在合适的时候,这个合适的时候是信号被递达的时候
一个信号递达,是在内核态切换回用户态时,进行信号的相关检测
我们的进程是不断在用户态和内核态进行切换的,因为内核的权限高,用户态的权限低,当我们需要执行操作系统代码的时候,用户态是无法执行的,因此需要切换成内核态
内核形态和用户形态是有物理地址支持的,操作系统有且仅有一个,因此只要有进程的存在,操作系统就会存在,每个进程的虚拟空间的内核空间对应的映射关系都是同样的,都是同一份内核代码
由上述可知,一个进程是可以产生多条执行流的,比如一条当前进程代码执行流,一条信号处理执行流
sigaction与signal是以样的,都是信号捕捉函数,只是sigaction的功能更加的丰富
两个执行流,进入同一个函数称为重入函数
常见不可重入函数:
1.调用malloc/free的函数,因为malloc也是用全局链表来管理堆的
2.调用了I/O函数库,标准I/O库中很多实现都以不可重入的方式使用全局数据结构
3.STL库,STL考虑的是效率问题,安全问题需要操作者自己考虑(比如迭代器失效问题,可以侧面反映)
volatile修饰的变量,是不可被"覆盖"的,读取变量必须读取变量真实的存储位置(不可读取缓存、寄存器等位置的值)
子进程退出,父进程可以通过阻塞或非阻塞的方式等待子进程结束,然后清理资源,但是不管是那种方式,程序的实现都是比较复杂的
有什么办法可以让父进程不用等待,又不会产生僵尸进程呢?
可以让子进程退出时,给父进程发送一个信号,父进程只需要在信号执行函数中调用wait函数清理资源,这样父进程就不必去检测子进程是否退出的问题了
1.进程终止时,会给父进程发送SIGCHLD信号
2.父进程如果通过signal或sigaction设置忽略这个信号,子进程终止时自动释放自己的资源,父进程不必再等待子进程(linux下可用,不保证其它系统上可用)
或者自定义SIGCHLD信号处理动作,调用wait等资源释放函数,也可以完成子进程资源的释放