栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Linux信号

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux信号

Linux信号
  • 1.信号的产生
  • 2.信号的存储与执行
  • 参考资料

  使用 kill -l 可查看所有的信号,其中1- 31是普通信号(对于两个以上的相同信号会丢失,因为只有一位位图保存);34 - 64是实时信号(用链表队列形式将实时信号进行保存,可以保存多份)。

NumberNameDefault actionCorresponding event
1SIGHUPterminate终端线挂起
2SIGINTterminate来自键盘的中断(Ctrl + c)
3SIGQUITterminate来自键盘的退出(Ctrl + )
4SIGILLterminate非法指令
5SIGTRAPterminate and dump core跟踪陷阱
6SIGABRTterminate and dump core来自abort函数的终止信号
7SIGBUSterminate总线错误
8SIGFPEterminate and dump core浮点异常( 计算1 / 0,Floating point exception)
9SIGKILLterminate*杀死进程(无法被signal自定义捕捉,系统级杀死进程)
10SIGUSR1terminate用户定义的信号1
11SIGSEGVterminate and dump core无效的存储器引用(野指针、segmentation fault)
12SIGUSR2terminate用户定义的信号2
13SIGPIPEterminate向一个没有读用户的管道写操作
14SIGALRMterminate来自alarm函数的定时信号
15SIGTERMterminate软件终止信号
16SIGSTKFLTterminate协处理器上的栈故障
17SIGCHLDignore一个子进程暂停或终止
18SIGCONTignore继续进程如果该进程停止
19SIGSTOPstop until next SIGCONT*不来自终端的暂停信号
20SIGTSTPstop until next SIGCONT来自终端的暂停信号(Ctrl + z)
21SIGTTINstop until next SIGCONT后台进程从终端读
22SIGTTOUstop until next SIGCONT后台进程从终端写
23SIGURGignore套接字上的紧急情况
24SIGXCPUterminateCPU时间限制超出
25SIGXFSZterminate文件大小限制超出
26SIGVTALRMterminate虚拟定时器期满
27SIGPROFterminate剖析定时器期满
28SIGWINCHignore窗口大小变化
29SIGIOterminate在某个描述符上可执行I/O操作
30SIGPWRterminate电源故障
1.信号的产生

所有的信号都是由操作系统发送的,因为操作系统会考虑独立和安全

  • 键盘按键输入,但是只能用来终止前台进程。

以下程序可以用来查看按键输入的信号对应的number

#include 
#include 
#include 
#include 
void handler(int signum)
{
	printf("receive signal: %dn", signum);
    exit(1);
}
int main()
{
	for (int i = 1; i <= 31; i++)
	{ 
    	signal(i, handler);
    } 
    while(1)
    { 
        printf("------------------------------n");
        sleep(1);
    } 
    return 0;
}
  • 程序中的异常问题

  本质是程序中存在异常问题导致软件或硬件的错误,被操作系统识别后,操作系统向对应的进程发送相应的信号。
Floating point exception(8) segmentation fault(11)

  异常退出会设置退出码和退出信号,有时也会设置core dump标志位为1(不是所有情况)。

ulimit -a  # 查看core的大小,若core file size = 0,则不允许core dump。
ulimit -c 1024 # 设置core的大小,使得运行core dump
gdb main


core.1331——1331是进程的PID。

(gdb) core-file core.1331  # 使用gdb事后调试,直接查看异常的位置

程序查看异常退出码以及core dump标志位:

#include 
#include 
#include 
#include 
#include 

int main()
{
	int status = 0;
	if(fork() == 0)
    { 
    	while(1)
        {   
            printf("child PID:%dn",getppid());
            int *p = NULL;
            *p = 10; 
        }   
    }   
    waitpid(-1, &status, 0); 
    printf("exit code: %d , exit signal: %d , core dump : %dn", 
    	(status>>8)&0xff, status & 0x7f, (status >> 7) & 0x01);
    return 0;                                                                      
}

  • 由系统函数产生
int kill(pid_t pid, int sig); 	//给进程发相应的信号
int raise(int sig); 			// 给自己发相应的信号
void abort(void);  				//SIGABRT
  • 由alarm函数产生
#include 
#include 
#include 

int main()
{
	int ret = alarm(1); // 返回值为剩余的几时数
	while(1)
	{
		printf("hello worldn");
        alarm(0); // 取消闹钟
	}
	return 0;
}
2.信号的存储与执行

  进程收到信号后不会立即处理,会将信号以位图的形式存储在进程的PCB中;当进程从内核态返回用户态的时候,进行检测并处理。
  用户的数据和代码会被加载到内存,对应的虚拟内存和物理内存的映射叫用户级页表;而操作系统的数据和代码也会被加载到内存,对应的映射关系叫系统级(内核)页表,而这个页面只有一份,所有进程共享一份操作系统的内存。进程可以通过CPU中相应的寄存器切换用户和内核的访问权限。

内核态:执行系统的代码和数据,使用内核级页表
用户态:执行用户的代码和数据,使用用户级页表

信号的处理过程分为:

  • 实际执行——递达:
    • 默认动作(SIG_DFL : 0)
    • 忽略 ( SIG_IGN : 1)
    • 自定义捕捉(handler)
  • 产生到递达间——未决:在信号位图中暂存
  • 信号阻塞

OS可以使用系统调用接口设置位图的数据结构:sigset_t

#include 
#include 
#include 
int main()
{
    sigset_t isset, osset;
    sigset_t pending;

    //清空结构体内容
    sigemptyset(&isset);
    sigemptyset(&osset);

    //设置3号信号的屏蔽字——block位图
    sigaddset(&isset, 3);
    //设置
    sigprocmask(SIG_SETMASK, &isset, &osset);

    while(1)
    {
        printf("hello worldn");
        sigemptyset(&pending);
        sigpending(&pending); // 读出pending的位图

        int count = 0;
        int i = 1;
        for(; i <= 31; i++)
        {
            if(sigismember(&pending, i))
                printf("1");
            else
                printf("0");
        }
        printf("n");
        
        count++;
        if(count == 10)
        {
            sigprocmask(SIG_SETMASK, &osset, NULL);  //将旧的信号屏蔽字写回
        }
        sleep(1);                                                                          
    }
    return 0;
}

  使用ctrl + 发现无法终止该程序,原因是3号信号被屏蔽了。另外使用kill -3 + PID也无法杀死该进程,但是会使得对应的pending位图置1。过10秒后,信号屏蔽字被重新置0,进程会默认立即终止,不会再执行后面的语句。

信号执行的过程可以总结为:

  用户态进入内核态调用系统函数,从内核态返回用户态进行相应的信号检测。若无信号产生直接返回用户态,若产生信号,则执行信号的相应处理过程(默认、忽略、自定义捕捉),信号捕捉在用户态执行,执行完重新返回内核。

信号捕捉的接口函数:

sighandler_t signal(int signum, sighandler_t handler);

int sigaction(int signum, const struct sigaction *act, struct sigaction *oact);

struct sigaction
{
	void (*sa_handler) (int); // 捕捉函数
	...
	..
	sigset_t sa_mask; //设置进程捕捉过程中的屏蔽字
}
#include 
#include 
#include 
#include 

void handler(int signum)
{
    while(1)
    {   
        printf("signal number:%dn",signum);
        sleep(1);                                                                          
    }   
}

int main()
{
    struct sigaction act;
    memset(&act, 0, sizeof(act));
           
    //act.sa_handler = SIG_IGN; //将某一个信号设置成忽略
    //act.sa_handler = SIG_DFL; // 将某一个信号设置成默认
    act.sa_handler = handler; 
    //在执行信号自定义捕捉时,系统会设置信号屏蔽字,防止自定义捕捉被调用多次

    sigemptyset(&act.sa_mask); 
    //手动设置信号的屏蔽字,当执行3号信号捕捉时,该2号的信号被屏蔽
    sigaddset(&act.sa_mask, 2); 

    sigaction(3, &act, NULL); // 注册3号信号    
    return 0;
}
参考资料

[1]: 《Computer Systems:A Programmer’s Perspective》

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/862495.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号