程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 信號究竟發給誰

信號究竟發給誰

編輯:關於C

信號究竟發給誰

Linux中進程和線程本是一個東西,在內核中都是由task_struct結構標示。
兩者都是由do_fork內核函數來啟動,只是調用do_fork的參數不同。
如果進程只有一個線程,那麼發給該進程的信號顯然只能發給這個線程;
信號和信號處理函數是進程資源,那麼當進程有多個線程時,信號究竟發給誰呢?
我做了3個實驗來探討這個問題,實驗所采用的平台如下:

OS: fedora 11
kernel: 2.6.29.4-167.fc11.i686.PAE
gcc: (GCC) 4.4.0 20090506 (Red Hat 4.4.0-4)

編譯命令:gcc -g -W -Wall -Wextra -o mytest main.c -lpthread

實驗1
首先用下面的代碼作測試
main.c:
========================================
// 2011年 11月 29日 星期二 08:37:06 CST
// author: 李小丹(Li Shao Dan) 字 殊恆(shuheng)
// K.I.S.S
// S.P.O.T

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

#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>

void *work_thread(void *);

void work();

void handler(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGQUIT, &sa, 0);
    pthread_t tid;
    pthread_create(&tid, 0, work_thread, 0);
    work();
    return 0;
}

void handler(int s)
{
    char buf[32];
    snprintf(buf, sizeof(buf), "\n%ld recv signal", syscall(SYS_gettid));
    psignal(s, buf);
}

void *work_thread(void *p)
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, 0);

    work();
    return (void *)0;
}

void work()
{
    for(;;) {
        pause();
        fprintf(stderr, "I\'m Zuro %ld\n", syscall(SYS_gettid));
    }
}
===========================================================

我在主線程裡注冊SIGQUIT信號處理函數,在另一個線程裡注冊SIGINT信號處理函數,
但是當我執行該程序時無論我在終端鍵入Ctrl-C(發送SIGINT信號)
還是Ctrl-\(發送SIGQUIT信號),信號都是發給主線程!
程序輸出如下:
=========================================
^C
2536 recv signal: Interrupt
I'm Zuro 2536
^C
2536 recv signal: Interrupt
I'm Zuro 2536
^\
2536 recv signal: Quit
I'm Zuro 2536
^\
2536 recv signal: Quit
I'm Zuro 2536
==========================================
用kill命令殺死這個進程。

實驗2
我把上面的程序修改一下:
main.c:
=============================================
// 2011年 11月 29日 星期二 08:37:06 CST
// author: 李小丹(Li Shao Dan) 字 殊恆(shuheng)
// K.I.S.S
// S.P.O.T

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

#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>

void *work_thread(void *);

void work();

void handler(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    //sigaction(SIGQUIT, &sa, 0);
    sigaction(SIGSEGV, &sa, 0);
    pthread_t tid;
    pthread_create(&tid, 0, work_thread, 0);
    work();
    return 0;
}

void handler(int s)
{
    char buf[32];
    snprintf(buf, sizeof(buf), "\n%ld recv signal", syscall(SYS_gettid));
    psignal(s, buf);
}

void *work_thread(void *p)
{
    for(;;) {
        fprintf(stderr, "I\'m Zuro %ld\n", syscall(SYS_gettid));
        sleep(2);
        char *p = 0;
        *p = 'a';
    }

    return (void *)0;
}

void work()
{
    for(;;) {
        pause();
        fprintf(stderr, "I\'m Zuro %ld\n", syscall(SYS_gettid));
    }
}

=====================================================================
現在我在主線程裡注冊SIGSEGV信號處理函數,而在另一個線程裡產生一個SIGSEGV信號。
編譯執行,等一段時間後Ctrl-C終止該進程。發現這時信號不再發送給主線程,
而是發送給產生SIGSEGV信號的那個線程!

實驗3
我再把代碼作一下修改:
main.c:
=======================================================
// 2011年 11月 29日 星期二 08:37:06 CST
// author: 李小丹(Li Shao Dan) 字 殊恆(shuheng)
// K.I.S.S
// S.P.O.T

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

#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>

void *work_thread(void *);

void work();

void handler(int);

int main()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGQUIT, &sa, 0);
    pthread_t tid;
    pthread_create(&tid, 0, work_thread, 0);

    work();
    return 0;
}

void handler(int s)
{
    char buf[32];
    snprintf(buf, sizeof(buf), "\n%ld recv signal", syscall(SYS_gettid));
    psignal(s, buf);
}

void *work_thread(void *p)
{
    /*struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handler;
    sa.sa_flags = SA_RESTART;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, 0);*/

    for(;;) {
        fprintf(stderr, "I\'m Zuro %ld\n", syscall(SYS_gettid));
        sleep(2);
        raise(SIGQUIT);
    }

    return (void *)0;
}

void work()
{
    for(;;) {
        pause();
        fprintf(stderr, "I\'m Zuro %ld\n", syscall(SYS_gettid));
    }
}
===================================================
這次我在主線程注冊SIGQUIT信號處理函數,而在另一個線程
調用raise(SIGQUIT)函數產生這個信號。注意實驗1和實驗3的
區別:實驗1的SIGQUIT信號是由終端shell發送,而實驗3的信號
是由進程本身的一個線程產生。實驗表明:信號不是發送
給主線程,而是發送給產生SIGQUIT信號的線程。


綜上所訴,如果信號是由本進程產生,那麼信號發給產生這個信號
的線程;如果信號是由其他進程產生,則信號發送給主線程。


摘自 leeshuheng的專欄
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved