linux哪些信号不能被捕获_c程序捕获信号 linux 信号类型 - CSDN
  • 信号的基本概念 每个信号都有一个编号和一个宏定义名称 ,这些宏定义可以在 signal.h 中找到。 使用kill -l命令查看系统中定义的信号列表: 1-31是普通信号 regular signal(非可靠信号); 34-64是实时信号 real...

    信号的基本概念

    每个信号都有一个编号和一个宏定义名称 ,这些宏定义可以在 signal.h 中找到。

    使用kill -l命令查看系统中定义的信号列表: 1-31是普通信号 regular signal(非可靠信号); 34-64是实时信号 real time signal(可靠信号)

    所有的信号都由操作系统来发!

    对信号的三种处理方式

    1、忽略此信号:大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILLSIGSTOP。这两种信号不能被忽略的,原因是:它们向超级用户提供一种使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如非法存储访问或除以0),则进程的行为是示定义的。

    2、直接执行进程对于该信号的默认动作 :对大多数信号的系统默认动作是终止该进程。

    3、捕捉信号:执行自定义动作(使用signal函数),为了做到这一点要通知内核在某种信号发生时,调用一个用户函数handler。在用户函数中,可执行用户希望对这种事件进行的处理。注意,不能捕捉SIGKILLSIGSTOP信号。

    ?

    1

    2

    3

    #include <signal.h>

    typedef void( *sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);

    signal函数的作用:给某一个进程的某一个特定信号(标号为signum)注册一个相应的处理函数,即对该信号的默认处理动作进行修改,修改为handler函数所指向的方式。

    1、第一个参数是信号的标号

    2、第二个参数,sighandler_t是一个typedef来的,原型是void (*)(int)函数指针,int的参数会被设置成signum

    举个代码例子:

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    #include<stdio.h>

    #include<signal.h>

    void handler(int sig)

    {

     printf("get a sig,num is %d\n",sig);

    }

      

    int main()

    {

     signal(2,handler);

     while(1)

     {

      sleep(1);

      printf("hello\n");

     }

     return 0;

    }

      修改了2号信号(Ctrl-c)的默认处理动作为handler函数的内容,则当该程序在前台运行时,键入Ctrl-c后不会执行它的默认处理动作(终止该进程)

    信号的处理过程:

    进程收到一个信号后不会被立即处理,而是在恰当 时机进行处理!什么是适当的时候呢?比如说中断返回的时候,或者内核态返回用户态的时候(这个情况出现的比较多)。

    信号不一定会被立即处理,操作系统不会为了处理一个信号而把当前正在运行的进程挂起(切换进程),挂起(进程切换)的话消耗太大了,如果不是紧急信号,是不会立即处理的。操作系统多选择在内核态切换回用户态的时候处理信号,这样就利用两者的切换来处理了(不用单独进行进程切换以免浪费时间)。

    总归是不能避免的,因为很有可能在睡眠的进程就接收到信号,操作系统肯定不愿意切换当前正在运行的进程,于是就得把信号储存在进程唯一的PCB(task_struct)当中。

    产生信号的条件

    1.用户在终端按下某些键时,终端驱动程序会发送信号给前台程序。

         例如:Ctrl-c产生SIGINT信号,Ctrl-\产生SIGQUIT信号,Ctrl-z产生SIGTSTP信号

    2.硬件异常产生信号。

         这类信号由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。

         例如:当前进程执行除以0的指令,CPU的运算单元会产生异常,内核将这个进程解释为SIGFPE信号发送给当前进程。
                   当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

    3.一个进程调用kill(2)函数可以发送信号给另一个进程。

         可以用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作是终止进程。

    信号的产生

    1.通过终端按键产生信号

    举个栗子:写一个死循环,前台运行这个程序,然后在终端键入Ctrl-c

      当CPU正在执行这个进程的代码 , 终端驱动程序发送了一 个 SIGINT 信号给该进程,记录在该进程的 PCB中,则该进程的用户空间代码暂停执行 ,CPU从用户态 切换到内核态处理硬件中断。

      从内核态回到用户态之前, 会先处理 PCB中记录的信号 ,发现有一个 SIGINT 信号待处理, 而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行。

     2.调用系统函数向进程发信号

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    /*************************************************************************

     > File Name: test.c

     > Author:Lynn-Zhang

     > Mail: iynu17@yeah.net

     > Created Time: Fri 15 Jul 2016 03:03:57 PM CST

     ************************************************************************/

      

    #include<stdio.h>

    int main()

    {

     printf("get pid :%d circle ...\n",getpid());

     while(1);

     return 0;

    }

    写一个上面的程序在后台执行死循环,并获取该进程的id,然后用kill命令给它发送SIGSEGV信号,可以使进程终止。也可以使用kill -11 5796,11是信号SIGSEGV的编号。

    打开终端1,运行程序:

    利用终端2,给进程发送信号

     终端1 显示进程被core了:

    kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定信号

    raise函数可 以给当前进程发送指定的信号 (自己给自己发信号 )

    ?

    1

    2

    3

    #include<signal.h>

    int kill(pid_t pid,int signo);

    int raise(int signo);

    这两个函数都是成功返回0,错误返回-1.

    除此之外,abort函数使当前进程接收到SIGABRT信号而异常终止。

    ?

    1

    2

    #include<stdlib.h>

    void abort(void);

    就像 exit函数一样 ,abort 函数总是会成功的 ,所以没有返回值。

    3.由软件条件产生信号

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    /*************************************************************************

     > File Name: alarm.c

     > Author:Lynn-Zhang

     > Mail: iynu17@yeah.net

     > Created Time: Fri 15 Jul 2016 08:52:02 PM CST

     ************************************************************************/

      

    #include<stdio.h>

      

    int main()

    {

     int count=0;

     alarm(1);

     while(1)

     {

     printf("%d\n",count);

     count++;

     }

     return 0;

    }

     通过实现以上代码,调用alarm函数可以设定一个闹钟,告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

       该程序会在1秒钟之内不停地数数,并打印计数器,1秒钟到了就被SIGALRM信号终止。由于电脑配置等的不同,每台电脑一秒钟之内计数值是不同的一般是不同的。

    ?

    1

    2

    #include <unistd.h>

    unsigned int alarm(unsigned int seconds);

      alarm函数的返回值是0或上次设置闹钟剩余的时间。

    阻塞信号

     1.信号在内核中的表示:

    信号递达delivery:实际执行信号处理信号的动作

    信号未决pending:信号从产生到抵达之间的状态,信号产生了但是未处理

    忽略:抵达之后的一种 动作

    阻塞block:收到信号不立即处理     被阻塞的信号将保持未决状态,直到进程解除对此信号的阻塞,才执行抵达动作

    信号产生和阻塞没有直接关系 抵达和解除阻塞没有直接关系!

    进程收到一个信号后,不会立即处理,它会在恰当的时机被处理。

    每个信号都由两个标志位分别表示阻塞和未决,以及一个函数指针表示信号的处理动作。

    在上图的例子中,

      1. SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

      2. SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没 有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

      3. SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。阻塞信号集也叫作信号屏蔽字。

    信号产生但是不立即处理,前提条件是要把它保存在pending表中,表明信号已经产生。

    2.信号集操作函数

    ?

    1

    2

    3

    4

    5

    6

    #include <signal.h>

    int sigemptyset(sigset_t *set); //初始化set所指向的信号集,使所有信号的对应位清0

    int sigfillset(sigset_t *set); //初始化set所指向的信号集,表示该信号集的有效信号包括系统支持的所有信号

    int sigaddset(sigset_t *set, int signo); //在该信号集中添加有效信号

    int sigdelset(sigset_t *set, int signo); //在该信号集中删除有效信号

    int sigismember(const sigset_t *set, int signo); //用于判断一个信号集的有效信号中是否包含某种信号

    参数解析:

    sigset_t结构体的参数表示信号集,信号操作的时候都是以信号集合的方式进行操作,需要事先创建一个该结构体的对象,然后把想要操作的信号添加到信号集合对象当中去

    signo就是信号的标号了

    3.调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

    ?

    1

    2

    #include <signal.h>

    int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

       一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。调用函数sigprocmask可以检测或更改(或两者)进程的信号屏蔽字。如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中 一个信号递达。

    参数解析:

     how,有三个宏

         SIG_BLOCK      添加到block表当中去

         SIG_UNBLOCK  从block表中删除

         SIG_SETMASK  设置block表 设置当前信号屏蔽字为set所指向的值

     set表示新设置的信号屏蔽字,oset表示当前信号屏蔽字

    处理方式:

          set 非空, oset 为NULL :按照how指示的方法更改set指向信号集的信号屏蔽字。

          set 为NULL,oset 非空:读取oset指向信号集的信号屏蔽字,通过oset参数传出。

          set 和 oset 都非空 :现将原来的信号屏蔽字备份到oset里,然后根据sethow参数更改信号屏蔽字。

    4. sigpending读取当前进程的未决信号集,通过set参数传出

    ?

    1

    2

    #include <signal.h>

    int sigpending(sigset_t *set);

    这是一个输出型参数,会把当前进程的pending表打印到传入的set集中。

    实例验证上面几个函数:

     

    一开始没有任何信号,所以pending表中全是0,我通过Ctrl+C传入2号信号,看到pending表中有2号被置位了,经过10秒取消阻塞,2号信号被处理(经过我自定义的函数)

    Linux下捕捉信号

    信号由三种处理方式:

         忽略

         执行该信号的默认处理动作

         捕捉信号

    如果信号的处理动作是用户自定义函数,在信号递达时就调用这个自定义函数,这称为捕捉信号

    进程收到一个信号后不会被立即处理,而是在恰当时机进行处理!即内核态返回用户态之前 !

    但是由于信号处理函数的代码在用户空间,所以这增加了内核处理信号捕捉的复杂度。

    内核实现信号捕捉的步骤:

          1、用户为某信号注册一个信号处理函数sighandler

          2、当前正在执行主程序,这时候因为中断、异常或系统调用进入内核态。

          3、在处理完异常要返回用户态的主程序之前,检查到有信号未处理,并发现该信号需要按照用户自定义的函数来处理。

          4、内核决定返回用户态执行sighandler函数,而不是恢复main函数的上下文继续执行!(sighandlermain函数使用的是不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程)

          5、sighandler函数返回后,执行特殊的系统调用sigreturn从用户态回到内核态

          6、检查是否还有其它信号需要递达,如果没有 则返回用户态并恢复主程序的上下文信息继续执行。

    signal

    给某一个进程的某一个信号(标号为signum)注册一个相应的处理函数,即对该信号的默认处理动作进行修改,修改为handler函数指向的方式;

    ?

    1

    2

    3

    #include <signal.h>

    typedef void (*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);<br>//即:<br>void (*signal(int, void(*)(int)))(int);

    signal函数接受两个参数:一个整型的信号编号,以及一个指向用户定义的信号处理函数的指针。  

    此外,signal函数的返回值是一个指向调用用户定义信号处理函数的指针。

    sigaction

    sigaction函数可以读取和修改与指定信号相关联的处理动作。

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    #include <signal.h>

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

    struct sigaction

    {

      void (*sa_handler)(int);  //信号处理方式

      void (*sa_sigaction)(int, siginfo_t *, void *); //实时信号的处理方式 暂不讨论

      sigset_t sa_mask; //额外屏蔽的信号

      int sa_flags;

      void (*sa_restorer)(void);

    };

    signum是指定信号的编号。

    处理方式:

         1、若act指针非空,则根据act结构体中的信号处理函数来修改该信号的处理动作。

         2、若oact指针非 空,则通过oact传出该信号原来的处理动作。

         3、现将原来的处理动作备份到oact里,然后根据act修改该信号的处理动作。

    (注:后两个参数都是输入输出型参数!)

    将sa_handler三种可选方式:

         1、赋值为常数SIG_IGN传给sigaction表示忽略信号;

         2、赋值为常数SIG_DFL表示执行系统默认动作;

         3、赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。

    (注:这是一个回调函数,不是被main函数调用,而是被系统所调用)

      当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。

     pause

    pause函数使调用进程挂起直到有信号递达!

    ?

    1

    2

    #include <unistd.h>

    int pause(void);

    处理方式: 

         如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;

         如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;

         如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR。

         所以pause只有出错的返回值(类似exec函数家族)。错误码EINTR表示“被信号中断”。

     举个栗子

         1、定义一个闹钟,约定times秒后,内核向该进程发送一个SIGALRM信号;

         2、调用pause函数将进程挂起,内核切换到别的进程运行;

         3、times秒后,内核向该进程发送SIGALRM信号,发现其处理动作是一个自定义函数,于是切回用户态执行该自定义处理函数;

         4、进入sig_alrm函数时SIGALRM信号被自动屏蔽,从sig_alrm函数返回时SIGALRM信号自动解除屏蔽。然后自动执行特殊的系统调用sigreturn再次进入内核,之后再返回用户态继续执行进程的主控制流程(main函数调用的mytest函数)。

         5、pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理 动作。

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    /*************************************************************************

     > File Name: Pause.c

     > Author:Lynn-Zhang

     > Mail: iynu17@yeah.net

     > Created Time: Sun 14 Aug 2016 12:27:03 PM CST

     ************************************************************************/

      

    #include<stdio.h>

    #include<signal.h>

    #include<unistd.h>

    void sig_alarm(int signum)

    {

     printf("I am a custom handler!\n");

    }

    void mysleep(unsigned int times)

    {

     //注册两个信号处理动作

     struct sigaction new,old;

     new.sa_handler=sig_alarm; //信号处理函数

     sigemptyset(&new.sa_mask);//不屏蔽任何信号屏蔽字

     new.sa_flags=0;

      

     //对SIGALRM 信号的默认处理动作修改为自定义处理动作

     sigaction(SIGALRM,&new,&old);

     alarm(times);

     pause(); //挂起等待

     alarm(1);

     sleep(2);

     alarm(0); //取消闹钟

     //恢复SIGALRM 信号到默认处理动作

     sigaction(SIGALRM,&old,NULL);

     alarm(1);

     sleep(2);

    }

    int main()

    {

     while(1)

     {

     mysleep(2);

     printf("many seconds passed\n");

     printf("###################\n");

     }

     return 0;

    }

       

    定义一个闹钟并挂起等待,收到信号后执行自定义处理动作,在没有恢复默认处理动作前,收到SIGALRM信号都会按照其自定义处理函数来处理。恢复自定义处理动作之后收到SIGALRM信号则执行其默认处理动作即终止进程!

    总结

    展开全文
  • Linux下,作为普通用户的我们会发现我们有时候会有一些文件不能进入、有一些文件不能创建、有一些信息不能看、我们创建的进程总数总是有上限的,我们访问了硬件层面的 东西,感觉自己限制了,其实...

    信号的捕捉过程

    我们现在知道了信号在产生之后不是被立即处理的,而是在合适的时候才进行处理,那么什么时候是合适的时候呢?信号又是怎么被捕捉的呢?

    合适的时候是指:从内核态切换为用户态时进行信号的捕捉。

    在Linux下,作为普通用户的我们会发现我们有时候会有一些文件不能进入、有一些文件不能创建、有一些信息不能看、我们创建的进程总数总是有上限的,我们访问不了硬件层面的 东西,感觉自己被限制了,其实这就是一种权限。

    内核态和用户态都是一种状态,这两种状态表示了两种身份,这两种身份表示了不同的权限等级,我们在访问0-3G的地址空间时,我们用的是用户权限,而我们通过系统调用接口以及我们想要访问地址空间里的3-4G空间时,我们就需要把自己的身份从内核态转换为用户态。因此,内核态和用户态最大的区别就是权限,内核态允许访问3-4G的空间

    从用户态到内核态的过程是权限升级的过程,而从内核态到用户态的过程是要从安全方面考虑的,因为操作系统不相信任何人,只相信它自己,所以要想要访问操作系统内部进行访问时必须通过系统调用接口。操作系统的代码只能由操作系统执行,用户的代码就只能由用户执行,因此就会有内核态和用户态两种状态之间的相互转变。

    如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码
    是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行
    main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号
    SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler
    和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返
    回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复
    main函数的上下文继续执行了。如下图:

    信号捕捉函数

    1.signal

    #include <signal.h>
    sighandler_t signal(int signum, sighandler_t handler);
    

    捕捉2号信号 

    2.sigaction

    #include <signal.h>
    int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
    

    捕捉2号信号 

     

     

     

     

     

     

     

     

    展开全文
  • trap命令可以对信号进行捕获,命令格式如下: ...删除信号捕获,命令格式 trap -- singal [root@localhost shell]# cat test.sh #!/bin/bash trap "echo 'Sorry,Ctrl-C is trapped.'" SIGIN...

    trap命令可以对信号进行捕获,命令格式如下:

    trap command singal
    

    第二个参数是信号名称,第一个参数是遇到信号singal执行的命令。

    删除信号捕获,命令格式

    trap -- singal
    
    [root@localhost shell]# cat test.sh 
    #!/bin/bash
    trap "echo 'Sorry,Ctrl-C is trapped.'" SIGINT
    count=1
    while [ $count -le 5 ]
    do
    	echo "Loop #$count"
    	sleep 1
    	count=$[ $count + 1 ]
    done
    
    trap -- SIGINT
    echo "I just removed the trap"
    
    count=1
    while [ $count -le 5 ]
    do
    	echo "Second Loop #$count"
    	sleep 1
    	count=$[ $count + 1 ]
    done
    
    

    运行情况如下:

    [root@localhost shell]# ./test.sh 
    Loop #1
    Loop #2
    Loop #3
    Loop #4
    ^CSorry,Ctrl-C is trapped.
    Loop #5
    I just removed the trap
    Second Loop #1
    Second Loop #2
    ^C
    
    

    在第一个循环中按ctrl+C会执行捕获的命令,由于信号是在捕获被移除前接收到的,那么脚本会按照原先trap命令中的设置进行处理。

    展开全文
  • 最近在搞一个linux项目,碰巧遇到了一个段错误的问题。经过一段时间的排查和学习,对段错误这个概念有了些许的理解,现总结如下。一、什么是段错误;二、段错误是如何产生的;三、段错误该如何捕获

    最近在搞一个linux项目,碰巧遇到了一个段错误的问题。经过一段时间的排查和学习,对段错误这个概念有了些许的理解,现总结如下:

    一、什么是段错误

    一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等等情况。这里贴一个对于“段错误”的准确定义:

    A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.

    Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, but much of the terminology of segmentation is still used, “segmentation fault” being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.

    On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.

    二、为什么会出现段错误

    很多非法的操作都会导致系统产生段错误,大致分为以下几类:

    1、访问不存在的内存地址

    #include<stdio.h>
    #include<stdlib.h>
    void main()
    {
            int *ptr = NULL;
            *ptr = 0;
    }

    2、访问系统保护的内存地址

    #include<stdio.h>
    #include<stdlib.h>
    void main()
    {
            int *ptr = (int *)0;
            *ptr = 100;
    }

    3、访问只读的内存地址

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    void main()
    {
            char *ptr = "test";
            strcpy(ptr, "TEST");
    }

    4、栈溢出

    #include<stdio.h>
    #include<stdlib.h>
    void main()
    {
            main();
    }

    5、其他原因

    1. 使用非法的指针,包括使用未经初始化及已经释放的指针(指针使用之前和释放之后置为NULL);
    2. 内存读/写越界。包括数组访问越界,或在使用一些写内存的函数时,长度指定不正确或者这些函数本身不能指定长度,典型的函数有strcpy(strncpy),sprintf(snprint)等等;
    3. 对于C++对象,请通过相应类的接口来去内存进行操作,禁止通过其返回的指针对内存进行写操作,典型的如string类的data()和c_str()两个接口;
    4. 函数不要返回其中局部对象的引用或地址,当函数返回时,函数栈弹出,局部对象的地址将失效,改写或读这些地址都会造成未知的后果;
    5. 避免在栈中定义过大的数组,否则可能导致进程的栈空间不足,此时也会出现段错误;
    6. 操作系统的相关限制,如:进程可以分配的最大内存,进程可以打开的最大文件描述符个数等,这些需要通过ulimit或setrlimit或sysctl来解除相关的限制;
    7. 多线程的程序,涉及到多个线程同时操作一块内存时必须进行互斥,否则内存中的内存将不可预料;
    8. 使用非线程安全的函数调用,例如strerror函数等;
    9. 在有信号的环境中,使用不可重入函数调用,而这些函数内部会读或写某片内存区,当信号中断时,内存写操作将被打断,而下次进入时将不避免的出错;
    10. 跨进程传递某个地址;
    11. 某些有特殊要求的系统调用,例如epool_wait,正常情况下使用close关闭一个套接字后,epool会不再返回这个socket上的事件,但是如果你使用dup或dup2操作,将导致epool无法进行移除操作。

    三、怎么捕获段错误信息

    可以通过向系统注册一个段错误的捕获函数,来实现段错误的捕获。如下:

    
    /**************************************************************************************************
    **  函数名称:  segv_error_handle
    **  功能描述:  段错误的实际处理函数
    **  输入参数:  无
    **  输出参数:  无
    **  返回参数:  无
    **************************************************************************************************/
    static void segv_error_handle(int v)
    {
        system("sync");
    
        printf("segv_error(value: %d), proc is going to exit now!!!\n", v);
    
        exit(1);
    }
    
    /**************************************************************************************************
    **  函数名称:  install_segv_handler
    **  功能描述:  注册段错误的处理函数
    **  输入参数:  无
    **  输出参数:  无
    **  返回参数:  无
    **************************************************************************************************/
    static void install_segv_handler()
    {
        struct sigaction siga;
    
        siga.sa_handler = segv_error_handle;
        siga.sa_flags = 0;
    
        memset(&siga.sa_mask, 0, sizeof(sigset_t));
    
        sigaction(SIGSEGV, &siga, NULL);                                           /* 捕获段非法错误的信号 */
        sigaction(SIGTERM, &siga, NULL);                                           /* 捕获软件终止的信号 */
        sigaction(SIGINT,  &siga, NULL);                                           /* 捕获进程中断的信号 */
    }
    
    /**************************************************************************************************
    **  函数名称:  main
    **  功能描述:  主函数
    **  输入参数:  无
    **  输出参数:  无
    **  返回参数:  无
    **************************************************************************************************/
    int main(int argc, char *argv[])
    {
        install_segv_handler();                                                    /* 段错误的处理函数 */
        ........                                                                   /* 其他代码 */
    }

    可以看到,上述代码中,向系统注册了3类的段错误,分别为SIGSEGV,SIGTERM,SIGINT。那么,这几个类型到底代表什么意思?除了他们之外,还有哪些其他的类型呢?参见下表:

    01)SIGHUP:本信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一session内的各个作业,这时它们与控制终端不再关联;
    02)SIGINT:程序终止(interrupt)信号,在用户键入INTR字符(通常是Ctrl-C)时发出;
    03)SIGQUIT:和SIGINT类似,但由QUIT字符(通常是Ctrl-)来控制。进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号;
    04)SIGILL:执行了非法指令。通常是因为可执行文件本身出现错误,或者试图执行数据段。堆栈溢出时也有可能产生这个信号;
    05)SIGTRAP:由断点指令或其它trap指令产生,由debugger使用;
    06)SIGABRT:程序自己发现错误并调用abort时产生;
    06)SIGIOT:在PDP-11上由iot指令产生,在其它机器上和SIGABRT一样;
    07)SIGBUS:非法地址,:包括内存地址对齐(alignment)出错。eg:访问一个四个字长的整数,但其地址不是4的倍数;
    08)SIGFPE:在发生致命的算术运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误;
    09)SIGKILL:用来立即结束程序的运行。本信号不能被阻塞,处理和忽略;
    10)SIGUSR1:留给用户使用;
    11)SIGSEGV:试图访问未分配给自己的内存,或试图往没有写权限的内存地址写数据;
    12)SIGUSR2:留给用户使用;
    13)SIGPIPE:Broken:pipe;
    14)SIGALRM:时钟定时信号,计算的是实际的时间或时钟时间。alarm函数使用该信号;
    15)SIGTERM:程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出。shell命令kill缺省产生这个信号;
    17)SIGCHLD:子进程结束时,父进程会收到这个信号;
    18)SIGCONT:让一个停止(stopped)的进程继续执行。本信号不能被阻塞。可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作。例如,重新显示提示符;
    19)SIGSTOP:停止(stopped)进程的执行。注意它和terminate以及interrupt的区别:该进程还未结束,只是暂停执行。本信号不能被阻塞,处理或忽略;
    20)SIGTSTP:停止进程的运行,但该信号可以被处理和忽略。用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号;
    21)SIGTTIN:当后台作业要从用户终端读数据时,该作业中的所有进程会收到SIGTTIN信号。缺省时这些进程会停止执行;
    22)SIGTTOU:类似于SIGTTIN,但在写终端(或修改终端模式)时收到;
    23)SIGURG:有”紧急”数据或out-of-band数据到达socket时产生;
    24)SIGXCPU:超过CPU时间资源限制。这个限制可以由getrlimit/setrlimit来读取/改变;
    25)SIGXFSZ:超过文件大小资源限制;
    26)SIGVTALRM:虚拟时钟信号。类似于SIGALRM,但是计算的是该进程占用的CPU时间;
    27)SIGPROF:类似于SIGALRM/SIGVTALRM,但包括该进程用的CPU时间以及系统调用的时间;
    28)SIGWINCH:窗口大小改变时发出;
    29)SIGIO:文件描述符准备就绪,可以开始进行输入/输出操作;
    30)SIGPWR:Power:failure;

    其中有两个信号可以停止进程:SIGTERM和SIGKILL。

    • SIGTERM比较友好,进程能捕捉这个信号,根据您的需要来关闭程序。在关闭程序之前,您可以结束打开的记录文件和完成正在做的任务。在某些情况下,假如进程正在进行作业而且不能中断,那么进程可以忽略这个SIGTERM信号。
    • 对于SIGKILL信号,进程是不能忽略的。这是一个“我不管您在做什么,立刻停止”的信号。假如发送SIGKILL信号给进程,linux就将进程停止在那里。

    四、其他注意事项

    1. 出现段错误时,首先应该想到段错误的定义,从它出发考虑引发错误的原因。
    2. 在使用指针时,定义了指针后记得初始化指针,在使用的时候记得判断是否为NULL。
    3. 在使用数组时,注意数组是否被初始化,数组下标是否越界,数组元素是否存在等。
    4. 在访问变量时,注意变量所占地址空间是否已经被程序释放掉。
    5. 在处理变量时,注意变量的格式控制是否合理等。
    展开全文
  • linux c/c++后端编程的过程中,我们经常对捕获捕获一些信号的处理。主要是在程序收到相关信号进行安全的退出,做一些善后处理。 如下场景: Linux下的线程实质上是轻量级进程(light weighted process),...
  • Linux信号捕获

    2019-08-05 09:16:39
    目录信号处理注册函数 信号处理注册函数 typedef void (*sighandler_t)(int); 功能:自定义的函数,以...sighandler_t signal(int signum, sighandler_t handler); ...signum:信号的编号,1~31,也可以是宏 handler...
  • linux c捕获信号

    2016-09-18 20:37:23
    捕获SIGINT、SIGTERM、SIGQUIT信号; 如何确定是哪个进程给自己发送信号
  • 前言 Linux利用信号与运行在系统中的进程进行通信。...trap命令允许你来指定shell脚本要监看并从shell中拦截的Linux信号。如果在脚本收到了trap命令中列出的信号,该信号不再由shell处理,而是交...
  • c语言捕获kill信号Programs may want to catch the kill signals sent by the kill command in C programs on Linux so that it can gracefully shutdown itself before it is killed. For example, I have a daemon...
  • 1、信号是什么?信号本质是什么? 日常生活中,绿灯是一个信号,停止标牌是一个信号,裁判手势也是一个信号。 信号本质(计算机):软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上...
  • #ifndef SIGNALDBGER_H #define SIGNALDBGER_H void action(int signum); #define Perror(s) { \ printf("Line %d: %s: Error %d: %s\n", \ __LINE__
  • Linux程序经常需要捕获一些中断信号,如按下CTRL+C就会触发SIGTERM信号等, if (atexit(&cleanup)) //atexit现在鼓励用了,常用来做程序退出的后处理工作 { fprintf(stderr, "cannot set exit ...
  • 信号处理(一)在Linux下当我们想强制结束一个程序的时候,我们通常会给它发送一个信号然后该进程捕捉到信号,再然后该进程执行一定操作最终终止.信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号...
  • typedef void( *sighandler_t)(int); 1.用typedef给类型起一个别名。 2.为函数指针类型定义别名, 3.函数指针(指向函数的指针) ...sighandler_t signal(int signum, sighandler...3.第一个参数是信号的标号,第二...
  • linux通过信号来在运行在系统的进程之间通信,也可以通过信号来控制shell脚本的运行 1.常用信号等级 1 ##进程重新加载配置 ... 9 ##强行结束单个进程(不能被阻塞) 15 ##正常关闭进程(可能会被阻塞) 18 ...
  • 一、Signal信号处理机制 可以用函数signal注册一个信号捕捉函数,其函数原型为: 123#include typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);signal函数的第一个...
  • 首先需要知道signals和interrupt是不同的。 signal属于进程通信机制的一种实现方式 (还有的实现为:Pipes、Sockets 其中Sockets又可分为: System V IPC Mechanisms、Message Queues、Semaphores、Shared Memory...
  • Linux 多个信号 捕获

    2018-06-17 14:18:46
    test.c #include &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;stdio.h&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; #include &amp;amp;amp;amp;amp;...a
  • Linux下的信号(一):信号的基本概念与产生 Linux下的信号(二):阻塞信号一,什么是捕捉信号?1,捕捉信号信号处理方式三种方式中的一种,意思是既忽略该信号,又执行信号默认的动作,而是让信号执行...
  • #include #include #include #include #include #include #include #include // 程序退出时的函数操作 void test(int n,struct siginfo *siginfo,void *myact) ... printf("signal number:%d\n.../** 打印出信号
1 2 3 4 5 ... 20
收藏数 21,741
精华内容 8,696
关键字:

linux哪些信号不能被捕获