Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15357840
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2008-09-13 17:29:45

尝试搞清alarm信号、main主循环和pthread之间比较繁杂的交互关系

1.测试pthread线程pause,主循环也pause,那么当alarm信号产生时,将只激活主循环去执行,而不会对子线程有任何影响,子线程依然pause在那里.
#include <stdio.h>
#include <signal.h>
#include<pthread.h>
#include <stdlib.h>

//gcc -o luther -lpthread luther_pthread_test.c
int alarm_count;
int gloabl_count;

void sigalrm_fn(int sig)//信号回调,将打断while,执行该函数,直到退出,while才能继续执行[luther.gliethttp]
{
    printf("\nalarm=%d\n", alarm_count++);
    alarm(5);
}

void thread_procedure(void)
{
    for(;;)
    {
        pause();
        printf("pthread gloabl_count = %d\n", gloabl_count++);
    }
}

int main(void)
{
    int ret;
    pthread_t pt_id;

    signal(SIGALRM, sigalrm_fn);
    alarm(5);
    
    ret = pthread_create(&pt_id, NULL, (void *)thread_procedure, NULL);
    if(ret != 0)exit(1);

    while(1)
    {
       pause();
       printf("main gloabl_count = %d\n", gloabl_count++);
    }
}
[luther@gp tmp]$ gcc -o luther -lpthread luther_pthread_test.c
[luther@gp tmp]$ ./luther

alarm=0
main gloabl_count = 0

alarm=1
main gloabl_count = 1

alarm=2
main gloabl_count = 2

alarm=3
main gloabl_count = 3

2.测试pthread子线程,子线程创建函数pthread_create在glib库中,因为调用了内核clone,所以pthread子线程在kernel中有一个自己的
独立于主循环的另一个与它自己相对应current内核管理结构体,所以kernel也就能够独立的调度pthread_create创建了的thread子线程处理处理函数,所以main主循环和pthread_create创建出来的thread是真正意义上的,能够被内核分别感知和不做任何相关调度的并行执行线路,同时pthread子线程和main主循环共享所有全局变量,比如测试中的gloabl_count变量.
[luther@gp tmp]$ ./luther
pthread gloabl_count = 0
pthread gloabl_count = 1
pthread gloabl_count = 2
pthread gloabl_count = 3

alarm=0
main gloabl_count = 4
pthread gloabl_count = 5
pthread gloabl_count = 6
pthread gloabl_count = 7
pthread gloabl_count = 8
pthread gloabl_count = 9

alarm=1
main gloabl_count = 10
pthread gloabl_count = 11
pthread gloabl_count = 12
pthread gloabl_count = 13
pthread gloabl_count = 14
pthread gloabl_count = 15

alarm=2
main gloabl_count = 16
pthread gloabl_count = 17

3.和2测试基本一样,只是尝试加入同步机制,这里的同步并不完善,我们将在4中看到signal处理函数和main主循环或者子线程之间对全局数据进行保护的经典解决方案.
#include <stdio.h>
#include <signal.h>
#include<pthread.h>
#include <stdlib.h>

//gcc -o luther -lpthread luther_pthread_test.c
int alarm_count;
int gloabl_count;
int signal_count = 1;

void sigalrm_fn(int sig)//信号回调,将打断while,执行该函数,直到退出,while才能继续执行[luther.gliethttp]
{
    printf("\nalarm=%d\n", alarm_count++);
    for(;signal_count;)
    {
        fprintf(stderr, "waiting...\n");
        sleep(1);
    }
    fprintf(stderr, "be woken up\n");
    signal_count = 1;
    alarm(1);
}

void thread_procedure(void)
{
    int count = 0;
    for(;;)
    {
        sleep(1);
        printf("pthread gloabl_count = %d\n", gloabl_count++);
        if( (count++ % 5) == 4 )
        {
            signal_count = 0;
            fprintf(stderr, "wake up...\n");
        }
    }
}

int main(void)
{
    int ret;
    pthread_t pt_id;

    signal(SIGALRM, sigalrm_fn);
    alarm(1);
    
    ret = pthread_create(&pt_id, NULL, (void *)thread_procedure, NULL);
    if(ret != 0)exit(1);

    while(1)
    {
       pause();
       printf("main gloabl_count = %d\n", gloabl_count++);
    }
}
[luther@gp tmp]$ ./luther

alarm=0
waiting...
pthread gloabl_count = 0
waiting...
pthread gloabl_count = 1
waiting...
pthread gloabl_count = 2
waiting...
pthread gloabl_count = 3
waiting...
pthread gloabl_count = 4
wake up...
be woken up
main gloabl_count = 5
pthread gloabl_count = 6

alarm=1
waiting...
pthread gloabl_count = 7
waiting...
pthread gloabl_count = 8
waiting...
pthread gloabl_count = 9
waiting...
pthread gloabl_count = 10
wake up...
be woken up
main gloabl_count = 11
pthread gloabl_count = 12

alarm=2
waiting...
pthread gloabl_count = 13
waiting...
pthread gloabl_count = 14
waiting...
pthread gloabl_count = 15
waiting...
pthread gloabl_count = 16
wake up...
be woken up
main gloabl_count = 17
pthread gloabl_count = 18

alarm=3
waiting...
pthread gloabl_count = 19
waiting...
pthread gloabl_count = 20
waiting...
pthread gloabl_count = 21
waiting...
pthread gloabl_count = 22
wake up...
be woken up
main gloabl_count = 23
pthread gloabl_count = 24

alarm=4
waiting...
pthread gloabl_count = 25
waiting...
pthread gloabl_count = 26
waiting...
pthread gloabl_count = 27
waiting...
pthread gloabl_count = 28
wake up...
pthread gloabl_count = 29
be woken up
main gloabl_count = 30

alarm=5
waiting...
pthread gloabl_count = 31
pthread gloabl_count = 32
4.经典的signal和main主线程之间全局数据保护方法.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

//gcc -o luther -lpthread luther_pthread_test.c
int alarm_count;
int gloabl_count;

void sigalrm_fn(int sig)//信号回调,将打断while,执行该函数,直到退出,while才能继续执行[luther.gliethttp]
{
    printf("\nalarm gloabl_count = %d\n", gloabl_count++);
    alarm(1);
}


int main(void)
{
    int i;
    struct sigaction sa;
    sigset_t global_sa_mask;
    
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = sigalrm_fn;
    sa.sa_flags = SA_INTERRUPT;
    sigaction(SIGALRM, &sa, 0);

    alarm(1);
        
    sigemptyset(&global_sa_mask);
    sigaddset(&global_sa_mask, SIGALRM);
    while(1)
    {
       sigprocmask(SIG_BLOCK, &global_sa_mask, NULL);
//如果在处理如下全局量期间发生alarm信号,那么暂时屏蔽alarm信号的处理,等到SIG_UNBLOCK调用时再作处理[luther.gliethttp].
       for(i = 0;i < 5;i++)
       {
            printf("main gloabl_count = %d\n", gloabl_count++);
            sleep(1);
       }
       sigprocmask(SIG_UNBLOCK, &global_sa_mask, NULL);
//如果在n秒之前发生了alarm,那么将释放SIG锁的同时,立即调用alarm处理函数,来完成alarm信号的推迟处理.
    }
}
[luther@gp tmp]$ ./luther
main gloabl_count = 0
main gloabl_count = 1
main gloabl_count = 2
main gloabl_count = 3
main gloabl_count = 4

alarm gloabl_count = 5
main gloabl_count = 6
main gloabl_count = 7
main gloabl_count = 8
main gloabl_count = 9
main gloabl_count = 10

alarm gloabl_count = 11
main gloabl_count = 12
main gloabl_count = 13
main gloabl_count = 14
main gloabl_count = 15
main gloabl_count = 16

alarm gloabl_count = 17
main gloabl_count = 18
main gloabl_count = 19
main gloabl_count = 20
main gloabl_count = 21
main gloabl_count = 22

5.那么在下面加入一个alarm,会得到和上面同样的现象,即从内核实现来看,信号不会有次数累计,alarm处理函数并不会连续执行5次.
    ...
       for(i = 0;i < 5;i++)
       {
            printf("main gloabl_count = %d\n", gloabl_count++);
            sleep(1);
            alarm(1);
       }
    ...
6.那么如果在信号函数中加入呢,因为alarm(1)表示执行完该语句之后,经过1秒,如果没有任何对该信号的处理阻塞,那么
因为时间到达而继续执行,sigalrm_fn信号处理函数,会一直执行,所遇main的主循环以后将永远不会得到执行.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void sigalrm_fn(int sig)//信号回调,将打断while,执行该函数,直到退出,while才能继续执行[luther.gliethttp]
{
    printf("in alarm\n");
    alarm(1);
    sleep(1);

    alarm(1);//加不加都一样了,因为1秒已经过去了,信号已经触发了

//但是alarm(1);alarm(2);alarm(3);alarm(4);如果这样写代码,因为覆盖前一个alarm设置之前,前一个alarm设置的时间还没有到达,所以最后

//一个alarm(4)将覆盖前面的设置,当然前提时四个;分号之间保持连续,不要被调度器调度出去1秒,

//这样就只会在4秒后产生一个alarm信号.

}


int main(void)
{
    signal(SIGALRM, sigalrm_fn);
    alarm(1);

    while(1)
    {
        printf("main");
    }
}
[luther@gp tmp]$ ./luther
......
mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm
in alarm

综述:从以上的多个实验可以看到,信号的实现细节我们必须要掌握,只有我们对信号的实现流程,实现细节每天在心中默念上那么几遍,那么以后遇到信号程序设计的项目时,也才能少犯些本可以规避掉的错误,不过在实际的程序编写中我们应该让信号处理程序尽量简单,以使main主循环能够尽早得到执行,比如main中的select或者poll可能都在等待非常大量的数据到来,所以缩短信号处理函数中的代码执行时间,也才能让我们的main主循环中的select或者poll多循环上那么几圈,或许就可以使得远程接入的用户感觉到无与伦比的爽[luther.gliethttp]!

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