Linux下的线程是所谓的轻量级进程(LWP: light weight process),其与普通进程一样拥有一个庞大的task_struct结构体,一个进程中的多个线程共享内存空间,毕竟它们属于同一个进程,所以需要向外呈现一个统一的pid,因此各线程的pid存放的是进程号,又由于线程同样也是进程,因此其有自己的进程id号,Linux为了支持多线程,于是添加了一个tid字段用于存放本线程的进程号,线程组的主线程pid同tid一致。getpid系统调用用于获取本进程的进程号,那怎么获取本线程(所谓的轻量级进程)的进程号呢?我刚从网上学到了一个技巧,据说是由于glibc没有包装该系统调用,所以只好自己手动使用syscall去调用了。gettid的系统调用号保存在”/usr/include/asm/unistd_32.h”头文件中,打开该文件可以看到有如下内容:
/* 223 is unused */
#define __NR_gettid 224
#define __NR_readahead 225
因此可以使用如下方式来实现gettid函数:
pid_t gettid()
{
//直接使用224代替SYS_gettid也可以
return syscall(SYS_gettid);
}
下面这个程序较好地演示了多线程环境下使用信号的特点。线程的标识为pthread_t,它是一个结构体,直接打印会是一个地址,因此可以使用上面的gettid函数来获取本线程的进程号。
* sleep函数: #include <unistd.h> unsigned int sleep(unsigned int seconds); 此函数使调用进程被挂起,直到满足以下条件之一:
1)已经过了seconds所指定的墙上时钟时间
2)调用进程捕捉到一个信号并从信号处理程序返回
注:由于其他系统活动,实际返回时间比所要求的会迟一些,像alarm一样。 sleep的返回值:
1)在上述第一种情形中,返回值是0
2)当由于捕捉到某个信号sleep提前返回时,返回值是未睡够的时间(所要求的时间减去实际休眠时间) */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <linux/unistd.h>
#define NUMTHREADS 3
//获取线程的pid
pid_t gettid()
{
returnsyscall(SYS_gettid);
}
void sighand(int signo);
//普通线程组的执行函数
void*threadfunc(void *parm)
{
pthread_t tid = pthread_self();
int rc;
printf("Thread %d entered/n", gettid());
rc = sleep(10);
//需要仔细观察sleep函数的返回值
printf("Thread %d did not get expected results! rc=%d/n", gettid(), rc);
return NULL;
}
//屏蔽所有信号的线程组的执行函数
void *threadmasked(void*parm)
{
pthread_t tid = pthread_self();
sigset_t mask;
int rc;
printf("Masked thread %d entered/n", gettid());
sigfillset(&mask);
//这组线程阻塞所有的信号
rc = pthread_sigmask(SIG_BLOCK, &mask, NULL);
if (rc != 0)
{printf("%d, %s/n", rc, strerror(rc));
return NULL;
}
rc = sleep(5);
//所以它们可以安心地睡到自然醒
//如果rc不能0就不正常了~
if (rc != 0)
{
printf("Masked thread %d did not get expected results! ""rc=%d /n", gettid(), rc);
returnNULL;
}
printf("Masked thread %d completed masked work/n", gettid());
returnNULL;
}
int main(int argc, char **argv)
{
int rc;
int i;
struct sigaction actions;
//两组线程
pthread_t threads[NUMTHREADS];
pthread_t maskedthreads[NUMTHREADS];
printf("Enter Testcase - %s/n", argv[0]);
printf("Set up the alarm handler for the process/n");
memset(&actions, 0,sizeof(actions));
sigemptyset(&actions.sa_mask);
actions.sa_flags = 0;
//信号处理函数
actions.sa_handler = sighand;
//给定时器事件注册处理器
//如果不捕捉该信号,该信号会对整个进程起作用
//Linux下收到SIGALRM信号不处理,默认会打印"提醒时钟"
//然后进程就退出了
rc = sigaction(SIGALRM, &actions, NULL);
printf("Create masked and unmasked threads/n");
//开启两组线程
for(i=0; i < NUMTHREADS; ++i)
{
rc = pthread_create(&threads[i], NULL, threadfunc, NULL);
if (rc != 0)
{
printf("%d, %s/n", rc, strerror(rc)); return -1;
}
rc =pthread_create(&maskedthreads[i], NULL, threadmasked, NULL);
if (rc != 0)
{
printf("%d, %s/n", rc, strerror(rc));
return -1;
}
}
//睡眠3秒再发信号
sleep(3);
printf("Send a signal to masked and unmasked threads/n");
//向两组线程发送信号
for(i=0; i < NUMTHREADS; ++i)
{
rc = pthread_kill(threads[i], SIGALRM);
rc = pthread_kill(maskedthreads[i], SIGALRM);
}
printf("Wait for masked and unmasked threads to complete/n");
//等待子进程退出
sleep(15);
printf("Main completed/n");
return 0;
}
//SIGALRM信号处理函数
voidsighand(int signo)
{
printf("Thread %d in signal handler/n", gettid());
return;
}
程序运行的结果如下:
ecy@ecy-geek:~/pthreads$ ./pthread_mask
Enter Testcase – ./pthread_mask
Set up the alarm handler for the process
Create masked and unmasked threads
Thread 11145 entered
Masked thread 11146 entered
Thread 11147 entered
Masked thread 11148 entered
Thread 11149 entered
Masked thread 11150 entered
Send a signal to masked and unmasked threads
Wait for masked and unmasked threads to complete
Thread 11149 in signal handler
Thread 11149 did not get expected results! rc=7
Thread 11145 in signal handler
Thread 11145 did not get expected results! rc=7
Thread 11147 in signal handler
Thread 11147 did not get expected results! rc=7
Masked thread 11146 completed masked work
Masked thread 11148 completed masked work
Masked thread 11150 completed masked work
Main completed
分析上述结果可知,在主线程设置好对某个信号的处理方法,同样会适用于子线程。上面的程序中,主线程设置好了SIGALRM信号处理函数,子线程收到SIGALRM信号会调用信号处理函数sighand来处理该信号(当然是在没有修改的情况下);使用pthread_kill可以向指定的线程发送信号;每个线程都可以有自己的信号屏蔽表,如果不屏蔽SIGALRM信号,SIGALRM会中断sleep的执行,sleep函数返回剩余的秒数。