Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2385304
  • 博文数量: 473
  • 博客积分: 12252
  • 博客等级: 上将
  • 技术积分: 4307
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-12 10:02
文章分类

全部博文(473)

文章存档

2012年(8)

2011年(63)

2010年(73)

2009年(231)

2008年(98)

分类: LINUX

2009-04-28 10:16:06


1. 问题引入

  我们想实现这样的一个功能:通过使用~SIGUSR1~信号实现对守护进程的重启。

  我们编写了如下代码:

/* restart1.c */
#include
#include

static void sig_usr(int signo)
{
    if (SIGUSR1 == signo)
     {
         printf("received SIGUSR1\n");
         system("restart.sh");
     }
    else
     {
         printf("received signal %d\n", signo);
     }
}

int main()
{
    if (SIG_ERR == signal(SIGUSR1, sig_usr))
     {
         printf("can't catch SIGUSR1\n");
     }

    for (;;)
     {
         pause();
     }
}

#!/bin/bash
# restart.sh
pkill -9 deamon
sleep 1
./deamon &

  经测试,发现的问题是当~deamon~程序第一次启动时,使用~kill -USR1 ~命令可以令其重启。但是对于此后重启的~deamon~进程再也接收不到~kill~发送的~USR1~信号。

2. 原因分析

  先是怀疑是不是使用~signal~存在不可靠信号丢失问题,于是改为~sigaction~进行测试。

  修改后的代码为:

/* restart2.c */
#include
#include

static void sig_usr(int signo)
{
    if (SIGUSR1 == signo)
     {
         printf("received SIGUSR1\n");
         system("restart.sh");
     }
    else
     {
         printf("received signal %d\n", signo);
     }
}

int main()
{
    struct sigaction action;

     sigaction(SIGUSR1, NULL, &action);
     action.sa_handler = sig_usr;
     sigaction(SIGUSR1, &action, NULL);

    for (;;)
     {
         pause();
     }
}

测试结果表明,问题依旧。

  开始怀疑会不会是前后创建的进程不属于相同用户(或者是用户组、TTY、父进程)所致,但是通过使用~ps -ajx~命令查看,发现这些属性是相同的。

  在测试过程还发现一个现象就是~deamon~在上一次接收过的信号,在其重启后都不能接收;但还没接收过的信号,仍然可以正常接收。

  在阅读~LINUX kernel~源码的时候,发现~LINUX~在派生进程时,存在继承父进程信号屏蔽标识的现象。于是怀疑是不是由于子~deamon~进程不断继承父进程的屏蔽标识,致使用不能接收~USR1~信号?但是父进程又为何要屏蔽~USR1~信号呢?

  查看 ~restart1.c~得知,我们在~sig\_usr~中执行~system~操作,从而派生了~deamon~进程。根据~LINUX~的信号 处理机制,我们知道为妨止在处理信号的过程中又来重复信号造成信号丢失,会采取屏蔽正在处理的信号标志位,以让重复信号排队;在处理完上一个信号后,再打 开标志位,接着处理重复信号。

  于是在执行~sig\_usr~时,USR1~标志位是屏蔽的,而此时派生的进程该标志位估计也是屏蔽的(后面我们采用例子验证)。因此当~deamon~进程起来后,就不能接收~USR1~信号了。

  一般~LINUX~编程建议,不要在中断中处理过于复杂的事情,因此首先简化~sig\_usr~进行测试。修改后的代码如下:

/* restart3.c */
#include
#include

static int resetflag = 0;

static void sig_usr(int signo)
{
    if (SIGUSR1 == signo)
     {
         printf("received SIGUSR1\n");
         resetflag = 1;
     }
    else
     {
         printf("received signal %d\n", signo);
     }
}

int main()
{
    if (SIG_ERR == signal(SIGUSR1, sig_usr))
     {
         printf("can't catch SIGUSR1\n");
     }

    for (;;)
     {
         pause();

        if (1 == resetflag)
         {
             system("restart.sh");
             exit(0);
         }
     }
}

测试通过。

  再来验证一下重启的~deamon~子进程是否继承了父进程的屏蔽标志位,我们尝试强行清除子进程的~USR1~信号屏蔽标志位即可。

  修改后的代码如下:

/* restart4.c */
#include
#include

static void sig_usr(int signo)
{
    if (SIGUSR1 == signo)
     {
         printf("received SIGUSR1\n");
         system("restart.sh");
     }
    else
     {
         printf("received signal %d\n", signo);
     }
}

int main()
{
    struct sigaction action;
     sigset_t set;

     sigaction(SIGUSR1, NULL, &action);
     action.sa_handler = sig_usr;
     sigaction(SIGUSR1, &action, NULL);

     sigaddset(&set, SIGUSR1);
    if (0 > sigprocmask(SIG_UNBLOCK, &set, NULL))
     {
         printf("sigprocmask error\n");
        return 1;
     }

    for (;;)
     {
         pause();
     }
}

测试仍然正常通过。

  另外,还可以通过查看~/proc//status~文件更直观地查看到指定进程的信息屏蔽情况。
+--------+-------------------------------+------------------+
| 段名 |        含义       |    例子    |
+--------+-------------------------------+------------------+
| SigPnd | The bitmap of pending signals | 0
000000000000000 |
|    | (usually 0)           |          |
+--------+-------------------------------+------------------+
| SigBlk | The bitmap of blocked signals | 0000000000000000 |
|    | (usually 0, 2 for shells)   |          |
+--------+-------------------------------+------------------+
| SigIgn | The bitmap of ignored signals | 0000000000000027 |
+--------+-------------------------------+------------------+
| SigCgt | The bitmap of catched signals | 0000000180000a00 |
+--------+-------------------------------+------------------+

3. 结论
  1) 不要在中断函数中执行过于复杂的处理流程;
  2) 在信号处理过程返回前,进程会对相同信号的标志进行屏蔽;
  3) 父进程会把当前的信号屏蔽标志位信息传递给它派生的子进程。

阅读(2273) | 评论(0) | 转发(1) |
0

上一篇:Linux信号介绍

下一篇:linux下top命令解析

给主人留下些什么吧!~~