Chinaunix首页 | 论坛 | 博客
  • 博客访问: 178945
  • 博文数量: 42
  • 博客积分: 2185
  • 博客等级: 大尉
  • 技术积分: 455
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-11 21:32
文章分类

全部博文(42)

文章存档

2012年(5)

2011年(13)

2010年(6)

2009年(18)

我的朋友

分类: LINUX

2009-11-06 14:41:51

《UNIX环境高级编程》第十章说:“如果对话期首进程(session leader)终止,则也产生SIGHUP。此信号被发送给该对话期前台进程组中的每一个进程。”

对这个不少人可能有疑问,而实际上,上文应该少说了一个条件:即session leader必须还有一个控制终端(CTTY)。

以下是测试代码(写的比较烂,不过仅仅为了演示...),注意,为了模仿有CTTY的情况,将stdin定向到了/dev/ttyS0;如果没有session leader没有任何CTTY设备,则不会发送SIGHUP。如果只是session中的非leader退出,则和通常情况一样(如父进程会受到SIGCHLD)。

(gcc sess01.c -o sess01 -Wall -D_XOPEN_SOURCE=500)

/**
 * SIGHUP test01: how if a session leader associated with a ctty is killed,
 * what happen to it's clild processes.
 *
 * ONLY IF the session leader have assoicated with a ctty, (and if the
 * session exited), its child processes will receive SIGHUP.
 *
 * compile with -D_XOPEN_SOURCE=500 (>=500)
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#ifndef _XOPEN_SOURCE
#error "please compile with -D_XOPEN_SOURCE=500 (>=500)"
#endif

#ifdef _XOPEN_SOURCE
#if _XOPEN_SOURCE < 500
#error "please compile with -D_XOPEN_SOURCE=500 (>=500)"
#endif
#endif

enum {
        SA_IGNORE,
        SA_DEFAULT,
        SA_HANDLER,
};

enum {
        SF_ONCE,
        SF_KILL,
};

struct my_sig_act {
        int signo;
        char* signame;
        int sigact;
        int flags;
};

static struct my_sig_act def_sig_act[] = {
        {SIGHUP, "SIGHUP", SA_HANDLER, SF_KILL,},
        {SIGINT, "SIGINT", SA_HANDLER, SF_KILL,},
        {SIGQUIT, "SIGQUIT", SA_HANDLER, SF_KILL,},
        {SIGILL, "SIGILL", SA_HANDLER, SF_KILL,},
        {SIGTRAP, "SIGTRAP", SA_HANDLER, SF_KILL,},
        {SIGABRT, "SIGABRT", SA_HANDLER, SF_KILL,},
        {SIGIOT, "SIGIOT", SA_HANDLER, 0,},
        {SIGBUS, "SIGBUS", SA_HANDLER, SF_KILL,},
        {SIGFPE, "SIGFPE", SA_HANDLER, SF_KILL,},
        {SIGKILL, "SIGKILL", SA_HANDLER, SF_KILL,},
        {SIGUSR1, "SIGUSR1", SA_HANDLER, 0,},
        {SIGSEGV, "SIGSEGV", SA_HANDLER, SF_KILL,},
        {SIGUSR2, "SIGUSR2", SA_HANDLER, 0,},
        {SIGPIPE, "SIGPIPE", SA_HANDLER, SF_KILL,},
        {SIGALRM, "SIGALRM", SA_HANDLER, 0,},
        {SIGTERM, "SIGTERM", SA_HANDLER, SF_KILL,},
        {SIGSTKFLT, "SIGSTKFLT", SA_HANDLER, 0,},
        {SIGCLD, "SIGCLD", SA_DEFAULT, SF_KILL,},
        {SIGCHLD, "SIGCHLD", SA_DEFAULT, SF_KILL,},
        {SIGCONT, "SIGCONT", SA_HANDLER, SF_KILL,},
        {SIGSTOP, "SIGSTOP", SA_HANDLER, SF_KILL,},
        {SIGTSTP, "SIGTSTP", SA_HANDLER, SF_KILL,},
        {SIGTTIN, "SIGTTIN", SA_HANDLER, SF_KILL,},
        {SIGTTOU, "SIGTTOU", SA_HANDLER, SF_KILL,},
        {SIGURG, "SIGURG", SA_HANDLER, SF_KILL,},
        {SIGXCPU, "SIGXCPU", SA_HANDLER, SF_KILL,},
        {SIGXFSZ, "SIGXFSZ", SA_HANDLER, SF_KILL,},
        {SIGVTALRM, "SIGVTALRM", SA_HANDLER, SF_KILL,},
        {SIGPROF, "SIGPROF", SA_HANDLER, SF_KILL,},
        {SIGWINCH, "SIGWINCH", SA_HANDLER, SF_KILL,},
        {SIGPOLL, "SIGPOLL", SA_HANDLER, SF_KILL,},
        {SIGIO, "SIGIO", SA_HANDLER, SF_KILL,},
        {SIGPWR, "SIGPWR", SA_HANDLER, SF_KILL,},
        {SIGSYS, "SIGSYS", SA_HANDLER, SF_KILL,},
        {SIGUNUSED, "SIGUNUSED", SA_HANDLER, 0,},
        {0, 0, 0, 0,},
};

static void my_sigchld_action(int signo, siginfo_t* info, void* p)
{
        int status;

        if(signo == SIGCHLD) {
                int pid;
                while(1) {
                        pid = waitpid(0, &status, WNOHANG);
                        if(pid < 0) {
                                perror("waitpid");
                                break;
                        }

                        if(WIFEXITED(status)) {
                                printf("child (%d) exited.\n", pid);
                                break;
                        } else {
                                printf("status = %d\n", status);
                        }
                }
        }
}

static void my_action(int signo, siginfo_t* info, void* p)
{
        pid_t self = getpid();

        fprintf(stderr, "%s: signo: %d, pid: %d, ppid: %d, pgid: %d, sid: %d\n", __func__,
                signo, self, getppid(), getpgid(self), getsid(0));
}

static void def_sighandler(void)
{
        struct my_sig_act* a = def_sig_act;
        struct sigaction sa;

        sa.sa_handler = NULL;
        sa.sa_sigaction = my_action;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO | SA_RESTART;

        while(a->signo) {
                if(a->sigact == SA_HANDLER) {
                        if(a->flags & SF_ONCE) {
                                sa.sa_flags |= SA_RESETHAND;
                        }
                        sigaction(a->signo, &sa, NULL);
                }
                ++a;
        }
}

static void def_sigchld(void)
{
        struct sigaction sa;

        sa.sa_handler = NULL;
        sa.sa_sigaction = my_sigchld_action;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO | SA_RESTART;

        sigaction(SIGCHLD, &sa, NULL);
}

static void do_child(void)
{
        pid_t pids[2];
        pid_t sid, self;
        int i, fd;
        int nb = 2;

        sid = setsid();
        self = getpid();

        def_sighandler();
        def_sigchld();

        for(i = 0; i <= 2; i++) {
                close(i);
        }

        /* open a real terminal device */
        fd = open("/dev/ttyS0", O_RDONLY);
        assert(fd == 0);
        fd = open("/tmp/stdout.log", O_WRONLY | O_CREAT, 0644);
        assert(fd == 1);
        fd = open("/tmp/stderr.log", O_WRONLY | O_CREAT, 0644);
        assert(fd == 2);

        printf("child:pid = %d, session id: %d, pgid: %d\n",
                self, sid, getpgid(self));

        for (i = 0; i < nb; i++) {
                pids[i] = fork();

                if(pids[i] < 0) {
                        perror("fork2: ");
                        exit(1);
                } else if (pids[i] == 0) {
                        while(1) {
                                printf("%s: child, pid = %d, ppid = %d\n",
                                        __func__, getpid(), getppid());
                                sleep(5);
                        }
                } else {
                        printf("%s: parent, pid = %d, ppid = %d\n",
                                __func__, pids[i], getppid());
                }
        }

        while(1) {
                printf("%s: \n", __func__);
                sleep(1);
        }
}

int main(int argc, char* argv[])
{
        pid_t pid;

        def_sighandler();
        def_sigchld();

        pid = fork();

        if (pid < 0) {
                perror("fork:");
                exit(1);
        } else if (pid == 0) {
                do_child();
        } else {
                exit(0);
        }

        return 0;
}


以下是测试的结果(Linux kernel 2.6.30, gcc-4.3.4, glibc-2.9):

[code]
$ ps auxw | grep sess
baojunwa 14945  0.0  0.4  41432  4432 pts/3    T    09:37   0:07 vim sess01.c
baojunwa 16214  0.0  0.0   3660   264 ttyS0    Ss+  14:26   0:00 ./sess01
baojunwa 16215  0.0  0.0   3660   220 ttyS0    S+   14:26   0:00 ./sess01
baojunwa 16216  0.0  0.0   3660   212 ttyS0    S+   14:26   0:00 ./sess01
$ kill -9 16214
$ cat /tmp/stderr.log
my_action: signo: 1, pid: 16215, ppid: 1, pgid: 16214, sid: 16214
my_action: signo: 1, pid: 16216, ppid: 1, pgid: 16214, sid: 16214
$ cat /tmp/stdout.log
$ ps auxw | grep sess
baojunwa 14945  0.0  0.4  41432  4432 pts/3    T    09:37   0:07 vim sess01.c
baojunwa 16215  0.0  0.0   3660   256 ?        S    14:26   0:00 ./sess01
baojunwa 16216  0.0  0.0   3660   252 ?        S    14:26   0:00 ./sess01
$ killall -9 sess01
$ cat /tmp/stdout.log
$ cat /tmp/stderr.log
my_action: signo: 1, pid: 16215, ppid: 1, pgid: 16214, sid: 16214
my_action: signo: 1, pid: 16216, ppid: 1, pgid: 16214, sid: 16214
[/code]

内核在tty_hangup()中,如果发现调用进程为session leader,则会首先发送一个SIGHUP, 再接着发送一个SIGCONT:

(tty_io.c: do_tty_hangup()):

    if (tty->session) {
        do_each_pid_task(tty->session, PIDTYPE_SID, p) {
            spin_lock_irq(&p->sighand->siglock);
            if (p->signal->tty == tty) {
                p->signal->tty = NULL;
                /* We defer the dereferences outside fo
                 the tasklist lock */

                refs++;
            }
            if (!p->signal->leader) {
                spin_unlock_irq(&p->sighand->siglock);
                continue;
            }
            __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
            __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
            put_pid(p->signal->tty_old_pgrp); /* A noop */
            spin_lock_irqsave(&tty->ctrl_lock, flags);
            if (tty->pgrp)
                p->signal->tty_old_pgrp = get_pid(tty->pgrp);
            spin_unlock_irqrestore(&tty->ctrl_lock, flags);
            spin_unlock_irq(&p->sighand->siglock);
        } while_each_pid_task(tty->session, PIDTYPE_SID, p);
    }



- When the process that dies is the session leader of a session that is
attached to a terminal device, SIGHUP is sent to all processes in
the foreground process group of that terminal device.
- When the death of a process causes a process group to become orphaned,
and one or more processes in the orphaned group are stopped, then
SIGHUP and SIGCONT are sent to all members of the orphaned
group. (An orphaned process group is one where no process in the group
has a parent which is part of the same session, but not the same process
group.)

ref:
阅读(1851) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~