尝试搞清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]!
|