精华内容
下载资源
问答
  • 为了客户端进程收到SIGPIPE不退出,我打算忽略信号,下面是我用过的方法: (1)间接忽略 static void SignalHandler(int nSigno) { signal(nSigno, SignalHandler); switch(nSigno) { case SIGPIPE: printf...

    为了客户端进程收到SIGPIPE不退出,我打算忽略该信号,下面是我用过的方法:
    (1)间接忽略

    static void SignalHandler(int nSigno)
    {
        signal(nSigno, SignalHandler);
        switch(nSigno)
        {
        case SIGPIPE:
            printf("Process will not exit\n");
            break;
        default:
            printf("%d signal unregister\n", nSigno);
            break;
        }
    }
     
    atic void InitSignalHandler()
    {
        signal(SIGPIPE , &SignalHandler);
    }
     
    int main()
    {
            InitSignalHandler();
               ........
             return 0;
    }
    

    (2)直接忽略

    signal(SIGPIPE,SIG_IGN);
    

    (3)重载signaction

    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigaction( SIGPIPE, &sa, 0 );
    

    (1)(2)(3)都不能够忽略,客户端收到SIGPIPE之后依然进程退出。
    于是乎,查SIGPIPE这个信号的特性:

    如果在写到管道时读进程已终止,则产生此信号。当类型为SOCK_STREAM的套接字已不再连接时,进程写到该套接字也产生此信号。
    ——《UNIX环境高级编程》中10.2节

    由于我的客户端是用send()进行发送数据的,通过man手册查看send()函数,看到有一条这样说:
    MSG_NOSIGNAL
    Requests not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned.

    于是将send()最后一个参数flags改为MSG_NOSIGNAL,再次启动客户端测试。SIGPIPE被忽略,客户端没有因为该信号退出。

    转自《Linux下忽略信号SIGPIPE的方法》

    展开全文
  • Linux信号忽略,屏蔽和捕捉 正文 Linux信号的三种处理方式: 1.执行默认动作 2.忽略 3.捕捉 信号忽略 #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, ...

    Linux 之 信号的忽略,屏蔽和捕捉

    正文

    Linux 下信号的三种处理方式:
    1.执行默认动作
    2.忽略
    3.捕捉

    信号的忽略

    #include <signal.h>

    typedef void (*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);
    参数:
    signum: 信号
    handler:处理动作,传SIG_IGN表示忽略

    #include<stdio.h>
    #include<string.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<signal.h>
    
    int main()
    {
       signal(SIGINT,SIG_IGN);
       while(1);
    }
    

    信号的捕捉

    signal
    #include<stdio.h>
    #include<string.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<signal.h>
    void fun(int sig)
    {
      printf("%d\n",sig);
    }
    
    int main()
    {
       signal(SIGINT,fun);
       while(1);
    }
    
    sigaction

    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    参数:
    act:传入参数,新的处理方式。
    oldact:传出参数,旧的处理方式。
    成功:0;失败:-1,设置errno

    #include<stdio.h>
    #include<string.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<signal.h>
    void fun(int sig)
    {
      printf("%d\n",sig);
    }
    
    int main()
    {
       struct  sigaction action;
       action.sa_handler = fun;
      sigemptyset(&action.sa_mask);
      action.sa_flags = 0;
      sigaction(SIGINT,&action,NULL);
        while(1);
    }
    

    struct sigaction结构体

       struct sigaction {
            void     (*sa_handler)(int);
            void     (*sa_sigaction)(int, siginfo_t *, void *);
            sigset_t   sa_mask; 
            int       sa_flags; 
            void     (*sa_restorer)(void);
        };
    

    sa_restorer:该元素是过时的,不应该使用,POSIX.1标准将不指定该元素。(弃用)
    sa_sigaction:当sa_flags被指定为SA_SIGINFO标志时,使用该信号处理程序。(很少使用)
    重点掌握:
    ① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 或 SIG_DFL表执行默认动作
    ② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
    ③ sa_flags:通常设置为0,表使用默认属性。

    信号捕捉特性

    1.进程正常运行时,默认PCB中有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为☆。
    2.XXX信号捕捉函数执行期间,XXX信号自动被屏蔽。
    3.阻塞的常规信号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)

    信号的屏蔽

    sigset_t set; // typedef unsigned long sigset_t;
    int sigemptyset(sigset_t *set); 将某个信号集清0 成功:0;失败:-1
    int sigfillset(sigset_t *set); 将某个信号集置1 成功:0;失败:-1
    int sigaddset(sigset_t *set, int signum); 将某个信号加入信号集 成功:0;失败:-1
    int sigdelset(sigset_t *set, int signum); 将某个信号清出信号集 成功:0;失败:-1
    int sigismember(const sigset_t *set, int signum);判断某个信号是否在信号集中 返回值:在集合:1;不在:0;出错:-1

    sigprocmask

    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 成功:0;失败:-1,设置errno

    参数:
    set:传入参数,是一个位图,set中哪位置1,就表示当前进程屏蔽哪个信号。
    oldset:传出参数,保存旧的信号屏蔽集。
    how参数取值: 假设当前的信号屏蔽字为mask
    1.SIG_BLOCK: 当how设置为此值,set表示需要屏蔽的信号。相当于 mask = mask|set
    2.SIG_UNBLOCK: 当how设置为此,set表示需要解除屏蔽的信号。相当于 mask = mask & ~set
    3.SIG_SETMASK: 当how设置为此,set表示用于替代原始屏蔽及的新屏蔽集。相当于 mask = set若,调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

    sigpending

    读取当前进程的未决信号集
    int sigpending(sigset_t *set); set传出参数。 返回值:成功:0;失败:-1,设置errno

    设置信号屏蔽字并打印未决信号集

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<signal.h>
    void print(sigset_t set)
    {
       for(int i=1;i<=32;i++)
       {
         if(sigismember(&set,i) == 0)
         printf("%c",'0');
         else
                 printf("%c",'1');
       }
       printf("\n");
    }
    
    int main()
    {
       sigset_t myset,oldset;
       sigemptyset(&myset);
       sigaddset(&myset,SIGINT);
       sigprocmask(SIG_BLOCK,&myset,&oldset);
    
       while(1)
       {
         sigset_t set;
         sigpending(&set);
         print(set);
         sleep(1);
       }
    }
    
    展开全文
  • Linux 进程信号

    万次阅读 2018-06-20 15:06:46
    Linux 进程信号 信号的概念 程序在正常执行过程中出现的异常情况, Linux可以响应这些异常的情况,当这些异常情况发生时,我们的程序就要做出相应的动作 产生信号的来源 用户按键 非法的内存访问 硬件故障...

    Linux 进程信号

    信号的概念

    程序在正常执行过程中出现的异常情况, Linux可以响应这些异常的情况,当这些异常情况发生时,我们的程序就要做出相应的动作

    产生信号的来源

    用户按键
    非法的内存访问
    硬件故障
    浮点数溢出
    软件条件产生

    处理信号的方式

    缺省处理
    忽略(SIGKILL SIGSTOP 不能忽略)
    捕捉信号(SLGKILL SIGSTOP 不能被捕获处理)

    常见的信号


    13号信号 SIGPIPE 管道破裂信号
    18号信号 SIGCONT 继续执行信号
    29号信号 SIGIO 异步IO信号

    注册信号

    函数原型:

    void (*signal(int signum, void (*handler)(int)))(int);
    
    void (*signal(int signum, //要进行处理的信号
         void (*handler)(int)) //信号处理函数
                               //SIG_IGN 忽略
                               //SIG_DEF 缺省处理动作
                               //SIG_ERR 
         )(int);

    例子:先让Ctrl+C不执行中断功能,当按Ctrl+\后让Ctrl+C恢复原功能

    #include<stdio.h>
    #include<unistd.h>
    #include<signal.h>
    
    void handler1(int s)
    {
        printf("收到%d号信号,但是我不死\n", s);
    }
    
    void (*handler_t)(int) = NULL;
    
    
    void handler_quit(int s)
    {
        printf("收到%d号信号\n", s);
        signal(SIGINT, handler_t);
    }
    
    
    int main()
    {
        handler_t = signal(SIGINT, handler1);
        signal(SIGQUIT, handler_quit);
        for(; ;)
        {
            printf(".");
            sleep(1);
            fflush(stdout);
        }
        return 0;
    }

    信号的分类

    不可靠信号(1~31):
    1. 会出现丢失
    2. 执行完信号处理函数之后,会恢复到缺省动作(Linux解决)

    可靠信号(34~64):
    1. 不会出现信号丢失
    2. 不会出现恢复到缺省动作

    非实时信号:不可靠信号都是非实时信号,在信号递达之前产生多次只计一次

    实时信号:可靠信号都是实时信号,在递达之前产生多次可以依次放在一个队列里

    操作系统发送信号的原理

    对于不可靠信号,在进程的PCB中有一个32bit的位图,位图的每一位代表了一个相应编号的信号。操作系统在向某个发送信号时,首先找到该进程的PCB,然后将PCB中位图的相应位置值1。信号对于进程的控制流来说是异步的。

    信号在内核中的示意图

    产生信号的方法

    1. 键盘组合按键
    2. bash中的kill命令
    3. 系统调用 kill函数 、raise函数、abort函数
    4. 由软件条件产生信号 alarm函数

    alarm函数:

    #include<unistd.h>
    unsigned int alarm(unsigend int seconds);

    当seconds秒后向当前进程发送闹钟信号SIGALARM。

    阻塞信号

    信号递达 delvery:实际执行信号的处理动作
    信号未决 pending:信号从产生到递达的中间状态
    信号阻塞 block:被阻塞的信号将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达动作。

    三张表:
    block(位图) 为1表示信号被屏蔽,不会被抵达
    pending(位图) 为1表示产生该信号
    handler(函数指针数组) 对应位置表示对信号的处理方法
    三张表
    如上图所示,在task_struct结构体(PCB)中有两张位图表和一个函数指针数组,每个位图的低32位中的每一位都代表一种信号;相应的,函数指针数组中的每一个函数指针都对应着一种信号。在图示中,1号信号SIGHUP没有产生也没有被阻塞,该信号响应默认的处理动作;2号信号SIGINT产生了但是被阻塞了,该信号响应的处理动作为忽略;3号信号SIGOUT产生了,没有被阻塞,该信号响应的处理动作为用户自定义的处理动作。

    进程什么时候处理信号?

    当进程从内核态切换为用户态时检查是否有信号(查看pending表)

    信号捕捉过程

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

    信号捕捉过程
    如上图所示,用户态的进程在运行过程中遇到异常(收到信号),进入内核态保存上下文,由于定义了信号捕捉函数,此时再转去用户态执行自定义的函数,自定义函数执行完后,返回到内核态去,再调用内核中的函数来返回到原来的位置。

    函数重入

    函数被不同的控制流程调用,发生在一个调用还没返回时就再次进入该函数的现象。

    可重入的条件:
    函数只访问自己的局部变量或参数

    符合下列条件之一就是不可重入:
    1. 调用了malloc或free,因为它们是用全局链表来管理的,有可能因为重入造成错乱。
    2. 调用了标准I/O库。因为标准I/O库的很多实现都是以不可重入的方式在使用全局数据结构。

    竞态条件

    程序在运行过程中由于时序问题而导致的错误,称为竞态条件

    展开全文
  • LinuxLinux信号

    千次阅读 2018-08-15 22:37:27
    Linux信号是一种系统或进程发出的通知,它的主要作用是用来激活信号接收者的一段程序,除此之外,也可以携带少量信息。从实现方式上来看,它是一种用软件构建的中断系统,只不过接收及处理中断请求的不是处理器...

    Linux的信号是一种系统或进程发出的通知,它的主要作用是用来激活信号接收者的一段程序,除此之外,也可以携带少量信息。从实现方式上来看,它是一种用软件构建的中断系统,只不过接收及处理中断请求的不是处理器而是进程。与外设向处理器的中断请求一样,它是一种异步通信方式。

     

    基本概念

    计算机系统必须创建某种机制,要使发生事件的实体能在事件发生时将这个事件发送出去,同时还要使希望感知这个事件的实体能够接收到这个事件,并做出下一步的行为。也就是说,信号就是一个携带少量信息的通知。

    系统中的很多事件都可以产生一个信号。Linux为系统中可能产生的信号都进行了命名和编号,并为接收信号的进程提供了默认服务,即每个信号都附带一个信号默认服务程序。也就是说,进程在接收到某个信号时会执行另一个服务程序,如果进程没有提供该服务程序,将会执行一个系统提供的默认服务程序。

    系统常用的信号和默认服务有:

    • 当用户按某些终端键时会产生信号。如:DELETE键;
    • 硬件异常产生的信号。例如:除数为0、无效的存储访问等等;
    • 进程使用函数kill(2)可将信号发送给另一个进程或进程组。自然,有些限制:接收信号的进程和发送信号的进程的所有者必须相同,或发送信号进程所有者必须为超级用户;
    • 用户可用命令kill(1)将信号发送给其它进程。常用于中止一个失控的后台进程;
    • 当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。这里指的不是硬件条件(如被0除),而是软件条件,例如在网络连接上传来非规定波特率的数据。

    Linux部分信号的名称、编号、用途及默认服务见下表:

    Linux部分信号名称、编号、默认服务
    信号名编号默认服务用途
    SIGHUP1进程中止控制TTY断开连接
    SIGINT2进程中止用户在键盘上按下Ctrl+C
    SIGQUIT3进程中止,内存转储TTY键盘上按下了Ctrl+\
    SIGILL4进程中止,内存转储非法指令
    SIGABRT6进程中止,内存转储异常终止abort()
    SIGFPE8进程中止,内存转储浮点异常
    SIGKILL9进程中止中止信号
    SIGSEGV11进程中止,内存转储非法内存访问
    ............

    从接收信号进程的角度来看,信号的产生是随机的,它实质上是向进程发出的中断请求,即信号的到来意味着进程要中断现行工作去执行信号服务程序。所以人们也将信号机制叫做“软中断”,只不过这不是向处理器而是向进程请求的中断,如下图所示:

    具体来说,进程接收到某个信号之后,可以有如下三种反应:

    • 忽略信号。大多数信号都可使用这种方式进行处理,但有两种信号却不能被忽略:SIGKILL和SIGSTOP,因为它们为超级用户提供了一种使进程中止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如非法存储访问或除以0),则进程的行为是未定义的;
    • 捕捉信号。为了做到这一点,进程必须为该信号提供一个用户信号处理程序;
    • 执行默认操作。对于大多数信号而言,系统默认的操作是中止该进程。

     

    信号的发送

    系统中,中断、异常服务程序及进程都可以调用发送信号函数来发送信号。用来发送信号的主要函数有kill()、raise()、sigqueue()、alarm()、setitimer()及abort()等。

    下面以函数kill()和alarm()为例简单介绍发送信号函数的作用。

    函数kill()的原型如下:

    int kill(pid_t pid, int signo);

    其中,参数pid为进程标识符ID。根据pid的值,函数kill()有如下四种不同的操作:

    • pid>0,将信号发送给进程ID为pid的进程;
    • pid==0,将信号发送给ID与发送进程所在进程组ID相等的进程组;
    • pid<0,将信号发送给一个进程组,该进程组的ID等于pid绝对值;
    • pid==-1,未定义此等情况。

    函数alarm()的原型如下:

    unsigned int alarm(unsigned seconds);

    其中,参数seconds的值是秒数,经过了指定的seconds秒后会产生信号SIGALRM。

    函数alarm()的功能是为一个“闹钟”设定一个定时时间,当时间值到达或超过时间设定值时,会产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作时中止该进程。

    函数alarm()的返回值为0或以前设置的闹钟时间的剩余时间数(秒)。

     

    信号的安装

    如果进程要处理某一信号,那么就要在进程中安装该信号。所谓信号的安装,就是把信号编号及其对应的处理函数加入到进程控制块中。

    Linux用来安装信号的函数有两个:signal()和sigaction()。其中,signal()是在系统调用sys_signal()基础上实现的库函数,不支持信号传递信息;sigaction()是在系统调用sys_sigaction()实现的库函数,它与sigqueue()系统调用配合,允许信号传递附加信息。

    函数sigaction()的原型如下:

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

    其中,参数signo是信号的编号;参数指针act为一个包含要安装的信号处理程序的sigaction结构;参数指针oact,则返回该信号的原有包含了信号处理程序的sigaction结构。

    当函数调用成功后,返回0,否则返回-1。

    函数中所使用的sigaction结构如下:

    struct sigaction {
    	__sighandler_t	sa_handler;        //信号处理函数指针
    	unsigned long	sa_flags;            //信号处理函数行为方式标志
    	sigset_t	sa_mask;	/* 屏蔽位图 */
    };

    这个结构可以看作是带有管理信息的信号处理函数指针。

    域sa_handler是一个函数指针,它指向的函数就是对应信号的服务程序,如果用户设计了进程的信号处理函数,那么就应该把这个处理函数与这个指针关联起来,这样当进程响应这个信号时就可以引用而执行信号服务程序了。

    至于结构中的域sa_mask则相当于中断屏蔽控制寄存器。它是一个叫做信号集sigset_t类型的变量。该类型定义如下:

    typedef struct {
    	unsigned long sig[_NSIG_WORDS];
    } sigset_t;

    它其实是一个位图,每一位都对应一个信号。如果位图中的某一位为1,就表示在执行当前信号处理程序期间,位图为1的位所对应的信号都被“屏蔽”,以防止这些信号中断当前信号服务程序的执行。

    系统定义了下列五个对信号集进行操作的函数:

    int sigemptyset(sigset_t * set);    //初始化函数,排除信号集中的所有信号
    int sigfillset(sigset_t * set);    //初始化函数,使信号集包括所有信号
    int sigaddset(sigset_t * set, int signo);    //添加信号
    int sigdelset(sigset_t * set, int signo);    //删除信号
    int sigismember(const sigset_t * set, int signo);    

    结构sigaction中的sa_flags是用来指定信号处理函数操作方式的参数,其可选值如下表:

    信号处理的选择项标志(sa_flags)
    sa_flags说明
    SA_NOCLDSTOP若signo是SIGCHLD,则当一子进程停止时(作业控制),不产生此信号。当一子进程中止时,仍旧产生此信号。
    SA_RESTART由此信号中断的系统调用自动再启动
    SA_ONSTACK若用sigalstack(2)已说明了一替换栈,则此信号传送给替换栈上的进程
    SA_NOCLDWAIT若signo是SIGCHLD,则当调用进程的子进程中止时,不创建僵死进程。若调用进程在后面调用wait,则阻塞到它所有子进程都中止,此时返回-1,errno设置为ECHILD。
    SA_NODEFER若捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号。注意,此种类型的操作对应于早期的不可靠信号。
    SA_RESETHAND对此信号的处理方式在此信号捕捉函数的入口处复置为SIG_DFL。注意,此种类型的信号对应于早期的不可靠信号。
    SA_SIGINFO此选项对信号处理程序提供了附加信息

    sigaction action结构如下图所示:

    在文件linux/kernel/signal.c中定义的系统调用sys_signal()的原型如下:

    asmlinkage long sys_signal(int sig, __sighandler_t handler);

    在文件linux/kernel/signal.c中定义的系统调用sys_rt_sigaction()的原型如下:

    asmlinkage long sys_rt_sigaction(
                                     int sig,            //信号编号
    				 const struct sigaction __user * act,        //sigaction结构的实例
    				 struct sigaction __user * oldact,
    				 size_t);

    该函数的第一个参数为信号的编号,可以去除SIGKILL及SIGSTOP外的任何一个特定有效的信号;第二个参数是指向结构sigaction的一个实例的指针;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。

     

    进程的信号向量表

    与处理器使用中断向量表把中断源和中断服务程序关联起来,并对其进行管理的方法类似,进程在自己的控制块中也设置了一个信号向量表,前面所述的信号的安装,其实就是要把sigaction结构安装到这个向量表中。

    进程控制块中有很多与信号有关的域,其中最重要的就是指向信号向量表的指针sighand:

    struct task_struct {
            ...
    	struct sighand_struct *sighand;        //信号向量表指针
    	struct sigpending pending;            //未决信号对列指针
            ...
    };

    在文件include/linux/sched.h中,信号向量表结构sighand_struct的定义如下:

    struct sighand_struct {
    	atomic_t		count;
    	struct k_sigaction	action[_NSIG];            //信号数组
    	spinlock_t		siglock;
    	wait_queue_head_t	signalfd_wqh;
    };

    其中,信号数组action[_NSIG]的每一个元素都对应一个信号,其下标就是信号的编号。数组元素是一个k_sigaction结构:

    struct k_sigaction {
    	struct sigaction sa;
    	__sigrestore_t ka_restorer;
    };

    也就是说,进程与信号向量表之间的关系如下图所示:

     

    进程响应信号的时机

    既然对进程来说信号是一种中断请求,那么进程什么时候可以响应并处理这种中断呢?

    因为信号的处理程序都运行在进程空间,所以Linux规定,进程对信号的检测与响应总是发生在系统调用或中断服务的末尾处,也就是说当进程由系统空间返回到用户进程空间之前。即内核在时钟中断timer_interrupt处理程序最后会跳转去的ret_from_sys_call及其他系统调用的后面有对函数do_singal()的调用,如下图所示:

     

    信号的生命期及可靠性

    从信号发送到相应的处理函数执行完毕,这是一个信号完整的生命期。因此,一个信号是否能完整地度过其生命期,取决于信号的可靠性。Linux的信号分为不可靠信号和可靠信号。

    信号的生命期

    一个信号的生命期可分为三个阶段,这三个阶段可由四个重要事件来刻画:诞生、在进程中注册、在进程中的注销以及信号处理程序执行完毕。

    当有事件发生时,如检测到硬件异常、定时器超时以及调用信号发送函数kill()或sigqueue()等,即会诞生相应的信号。

    在接收信号的进程端维护着一个信号队列,该队列是一个sigqueue结构的链表。结构sigqueue的定义如下:

    struct sigqueue {
    	struct sigqueue * next;            //指向下一个
    	siginfo_t info;
    };

    其队列头是一个digpending结构:

    struct sigpending {
    	struct sigqueue * head, ** tail;
    	sigset_t signal;
    };

    在这个结构中,除了用队列头尾两个指针之外还有一个sigset_t类型的位图signal。前面讲过,系统中的每个信号都在位图中占有一个固定的位置,可以在某个信号的固定位置以1或0表示这个信号的状态。所以结构sigpending中的这个位图就用其中各个位的值来表示对应信号的到来情况:用1表示信号已经到来,但还未被进程处理;用0表示该位对应的信号未到,或已处理完毕。因此,这个位图也叫做进程的未决信号位图。

    进程对信号管理的结构如下图所示:

    当信号诞生后,该信号及其相关信息会被加入进程的信号向量表(信号的安装),同时还会将其加入到目标进程未决信号队列,并记录在该队列中的未决信号集中,等待进程处理。这个行为叫做信号向进程注册。

    每次在目标进程从系统空间返回到用户空间的过程中,会检测未决信号位图,以发现等待处理的信号。如果存在未决信号并且该信号没有被进程阻塞,则在未决信号链中卸掉该信号的结点并立即执行相应的信号处理函数,执行完毕后,信号生命期结束。

    不可靠信号

    Linux信号机制基本上是从Unix系统中继承过来的。早期的Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,那些建立在早期机制上的信号就叫做“不可靠信号”。

    不可靠信号的主要问题是:进程在处理一个信号后,信号会自动将该信号的响应设置为默认服务。因此,进程在下次接收到这个信号后,信号执行的是默认服务,而在某些情况下,这个默认服务并不是进程所希望的服务。所以,常常需要在信号处理程序中,重新安装该信号,但这种做法很不靠谱,极易造成信号的丢失。

    因为这种信号采用的是位图来进行注册,而没有采用队列,所以当进程中已有先到的同样信号处在未决状态时,后到的这个信号将会被丢弃,从而造成信号丢失。因此,这种信号叫做“不可靠信号”。

    可靠信号

    通过信号排队的方法实现了“可靠信号”,即在多个信号集中到达时,内核用信号队列记住这些信号,这样就从根本上解决了信号丢失的问题。

    即当一个信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,并加入到未决信号队列,这意味着同一个信号可以在同一个进程的未决信号队列中占有多个sigqueue结构。因此,信号不会丢失,是“可靠信号”。

    也就是说,可靠信号与不可靠信号的区别在于:

    可靠性是指信号是否会丢失,即该信号是否支持排队; 如果支持排队就是可靠的,不支持排队就是不可靠的。

    但由于已经很多的实际应用已经采用了原有的不可靠信号,所以为了兼容,Linux仍然保留了原本的那些不可靠信号,同时新增了一些可靠信号。通常:

    • SIGHUP(1号) 至 SIGSYS(31号)之间的信号都是继承自UNIX系统,是不可靠信号,也称为非实时信号;
    • SIGRTMIN(33号) 与 SIGRTMAX(64号)之间的信号,它们都是可靠信号,也称为实时信号。

     

    总结

    匿名管道、命名管道和共享内存都是以文件形式出现的通信方式,三者的共同特点都是以文件作为通信双方的中介,也都是属于特殊文件。但匿名管道是虚文件,它并不存在于外部存储器。又由于它是通过继承方式来实现共享的,所以它只能用来在具有亲缘关系的进程之间进行通信。另外,它是依靠单向通信来维护进程间的同步的。

    消息队列和管道基本上都是4次拷贝,而共享内存(mmap, shmget)只有两次。

    • 4次:1,由用户空间的buf中将数据拷贝到内核中。2,内核将数据拷贝到内存中。3,内存到内核。4,内核到用户空间的buf;
    • 2次: 1,用户空间到内存。 2,内存到用户空间。

    消息队列和管道都是内核对象,所执行的操作也都是系统调用,而这些数据最终是要存储在内存中执行的。因此不可避免的要经过4次数据的拷贝。但是共享内存不同,当执行mmap或者shmget时,会在内存中开辟空间,然后再将这块空间映射到用户进程的虚拟地址空间中,即返回值为一个指向一个内存地址的指针。当用户使用这个指针时,例如赋值操作,会引起一个从虚拟地址到物理地址的转化,会将数据直接写入对应的物理内存中,省去了拷贝到内核中的过程。当读取数据时,也是类似的过程,因此总共有两次数据拷贝。

    命名管道就是一个文件,它与普通文件的最大区别有两点:第一,它是严格按照FIFO方式工作的;第二,它只能单向操作。而共享内存则是一种只存在于内存的虚文件,由于它既可读又可写,因此它需要使用另外的同步措施。

    消息队列与匿名管道以及命名管道相比,具有更大的灵活性。因为,它提供有格式的字节流,有利于减少开发人员的工作量。同样,消息队列可以在几个进程间复用,而不管这几个进程是否具有亲缘关系,这一点与命名管道很类似。

    消息类似于一种中断,因此它有类似于中断号的信号编号,也有类似于中断服务程序的信号服务程序。它是一种异步通信方式,是事件与进程通信的手段。

     

    展开全文
  • system函数返回值问题(system的实现)—signal(SIGCHLD,SIG_DFL);ret=system("command");...2011-03-19 17:33:53| 分类: Linux系统编程|字号 订阅 1、关于在system中获取子进程的返回值与SIGCHLD
  • 文章目录Linux 命令 nohup - 以忽略挂机信号的方式运行 - 后台运行1、命令简介2、命令提供的参数和选项3、指定外部输出源 Linux 命令 nohup - 以忽略挂机信号的方式运行 - 后台运行 1、命令简介 nohup命令可以将...
  • Linux信号信号处理

    千次阅读 2017-01-02 13:50:26
    Linux信号信号处理 信号(signal)是一种软件中断,它提供了一种处理异步事件的方法,也是进程间惟一的异步通信方式。在Linux系统中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某种程序发生了什么事件...
  • 深入探索 Linux 进程信号的奥秘

    千次阅读 多人点赞 2021-04-04 20:33:48
    Linux 进程信号0 学习进程信号的过程1 Linux 进程信号的基本概念1.1 对信号的基本认知2 Linux 进程信号的产生方式3 Linux 进程信号的捕捉的一般方式二级目录三级目录 0 学习进程信号的过程 1 Linux 进程信号的基本...
  • Linux进程信号

    千次阅读 2018-06-04 18:57:34
    信号的基本概念   首先,我们可以用kill -l命令查看系统中定义的信号列表:   每个信号都有⼀个编号和⼀个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义#define SIGINT 2。编号34以上的是实时...
  • Linux进程信号——信号的产生

    千次阅读 2021-04-09 07:40:12
    信号的产生方式4.1通过终端按键产生4.2调用系统函数向进程发送信号4.3.1调用kill4.3.2调用rasize4.3.2.1使用演示4.3.2.2linux中9号信号不能被捕捉4.3.3调用abort4.3软件条件产生信号4.3.1使用演示4.3.2 利用alarm...
  • Linux系统信号

    千次阅读 2014-09-20 11:13:25
    1、Linux系统信号  1  SIGHUP  2 SIGINT  3 SIGQUIT  4 SIGILL  5 SIGTRAP  6 SIGABRT  7 SIGBUS  8 SIGFPE  9 SIGKILL  10 
  • Linux系统编程】Linux信号列表

    千次阅读 2019-11-08 18:36:40
    Linux 下,每个信号的名字都以字符 SIG 开头,每个信号和一个数字编码相对应,在头文件 signum.h 中,这些信号都被定义为正整数。信号名定义路径:/usr/include/i386-linux-gnu/bits/signum.h 要想查看这些信号和...
  • 信号处理(一)在Linux下当我们想强制结束一个程序的时候,我们通常会给它发送一个信号然后该进程捕捉到信号,再然后该进程执行一定操作最终被终止.信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号...
  • Linux 信号列表

    万次阅读 2015-05-26 17:40:43
    Linux 下,每个信号的名字都以字符 SIG 开头,每个信号和一个数字编码相对应,在头文件 signum.h 中,这些信号都被定义为正整数。信号名定义路径:/usr/include/i386-linux-gnu/bits/signum.h 列表中,编号为 1 ~...
  • LinuxC信号信号处理(1)
  • SIGCHLD 忽略信号 当子进程停止或退出时通知父进程 SIGTTOU 停止进程 后台进程写终端 SIGTTIN 停止进程 后台进程读终端 SIGXGPU 终止进程 CPU时限超时 SIGXFSZ 终止进程 文件长度过长 SIGWINCH 忽略...
  • Linux信号机制

    千次阅读 2018-08-23 13:53:42
    信号机制是一种使用信号来进行进程之间传递消息的方法,信号的全称为软中断信号,简称软中断。信号的本质是软件层次上对中断的一种模拟(软中断)。它是一种异步通信的处理机制,事实上,进程并不知道信号何时到来。...
  • linux 信号

    千次阅读 2011-11-02 09:39:58
    一、概念 信号是运载消息的工具,是消息的载体。...进程收到信号后,也可以采用系统的默认动作,忽略信号、终止或停止进程。但是有两个信号是不能被忽略和捕捉:SIGKILL和SIGSTOP。 早期的信号机制只是一
  • linux信号详解

    千次阅读 2018-05-09 18:16:05
    本文讲述了信号的基本概念,信号列表的详细介绍,如何用系统调用操作信号,以及常用信号示例。
  • linux0.11信号处理

    千次阅读 2014-12-19 13:09:55
    linux中的信号处理函数提供了对程序异步处理的功能,比如键盘按下ctrl+c键,该操作将产生一个SIGINT信号,并被发送到当前前台的进程中。内核采用一个无符号长整形变量中(32bit)的每一个比特位来表示不同的信号,...
  • Linux进程信号之阻塞信号

    千次阅读 2018-04-18 21:34:53
    先来了解一下信号的三种状态:* 信号递达(Delivery):实际执行信号的处理动作 * 信号未决(Pending):信号从产生到递达之间的状态 * 信号阻塞(Block):进程可以选择阻塞某个信号,被阻塞的信号产生时将保
  • Linux 信号

    千次阅读 2016-08-11 16:22:17
    第十篇 信号 本片索引: 1、引言 2、信号 3.程序启动 4、signal函数 5、系统调用的中断和系统调用的重启(了解) 6、可再入与不可再入函数(了解) 7、kill函数和raise函数 8、alarm函数和pause函数 9、...
  • linux常用信号列表

    千次阅读 2016-04-26 12:54:16
    比如,登录linux时,系统会自动分配给登录用户一个控制终端,在这个终端运行的所有程序,包括前台和后台进程组,一般都属于同一个会话。当用户退出时,所有进程组都将收到该信号,这个信号的默认操作是终止进程。...
  • Linux信号类型

    千次阅读 2020-11-07 19:59:00
    Linux信号类型 1、信号种类 root@yjc:~/apue/signal# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) ...
  • linux kill信号列表

    千次阅读 2010-12-29 16:07:00
    linux kill信号列表

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,166
精华内容 24,466
关键字:

linux忽略信号

linux 订阅