pthread_kill在NDK中崩溃
Android NDK在v5版本后不再提供全部的POSIX线程库的API(比如pthread_cancel和pthread_setcancelstate)。原因之一是线程被标记结束后不一定会把自己拥有的资源释放掉,甚至不一定会结束,因此很可能造成内存泄露或死锁等问题,而这些问题在移动设备上更加突出。比较安全的方法是使用更安全pthread_kill函数代替。
int kill_rc = pthread_kill(thread_id,0);
if(kill_rc == ESRCH)
printf("the specified thread did not exists or already quit\n");
else if(kill_rc == EINVAL)
printf("signal is invalid\n");
else
printf("the specified thread is alive\n");
pthread_kill引发的争议:
pthread_kill会引发段错误,pthread_kill的man page描述着如果对于一个invalid pthread_t,将返回ESRCH,而不是crash才对。
有人针对这个问题向glibc nptl的开发者提了这个bug,然后作者的回答也比较直接拒了这个bug,然后两人就开始撕,单看争论的内容感觉这个确实是一个bug,大概是说文档和标准都没有这么说明,它怎么就崩溃了呢?后面又看了作者为这个问题写的一篇blogpthread_t and similar types,从posix的设计角度谈了一下为什么这么设计,写得也挺有意思。
看完后总体感觉设计者和使用者的角度还是不同的,使用者角度上接口提供的功能应该和文档描述一致,不应该隐藏一些晦涩的逻辑或者至少将功能情景描述清楚;设计者的角度则在于提供更大的灵活性和通用性,接口内不去实现特殊情况下的特定逻辑。这里实现上个人还是比较倾向设计者的角度,但对于man page的说明还是不太认同,确实写得不太清楚,不过后续还是有更新相关说明的
glibc 实现在可以检测到无效线程 ID 的情况下返回此错误。 但还要注意的是,POSIX 表示尝试使用生命周期已结束的线程 ID 会产生未定义的行为,例如,尝试在调用 pthread_kill() 时使用无效的线程 ID 可能会导致分段错误。
针对pthread_kill,其意思是如果内部检测到pthred_t是无效的则返回ESRCH,但这并不表明所有无效的pthread_t内部都能检测到,其原因是因为标准并未对pthread_t的实现类型进行明确的限制。找了glibc的pthread_kill的实现版本,发现只有tid<=0时才返回ESRCH,至于什么实时tid<=0待查(关于tid pthread_t pid tgid的区别可参考**Difference between pid and tid**),同时不同的实现的版本也有可能有区别,因此从这个角度看通过pthread_kill判断线程是否在运行貌似没有意义。。。
最终使用tid表,通过tid表可检查线程是否存活。
使用示例c代码
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void sig_handler(int signo)
{
printf("handle sig %d \n " , signo);
pthread_exit(NULL);
}
int main()
{
pthread_t thread;
int ret=pthread_create(&thread, NULL,FUN,NULL);//创建线程
ret=pthread_kill(thread,0); //判断线程是否存活,如果杀死一个快要死掉的线程会导致段错误
printf("thread=%d\n",ret);
if(ret == 0)
{
printf("thread alive\n");
}
ret=pthread_kill(thread, SIGQUIT);
if(ret == 0)
{
printf("thread kill");
}
}
以上代码会在linux 环境下无问题 但是在ndk编译后会出现段错误,因为篇thread_kill(pthread_t,0)去监听一个快要死掉的或者已经死掉的线程会崩溃crash。
下面通过tid表去解决代码如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include<sys/types.h>
#include <sys/syscall.h>
int tidpthread=0;
void sig_handler(int signo)
{
printf("handle sig %d \n " , signo);
pthread_exit(NULL);
}
void *func(void *arg)
{
signal(SIGUSR1, sig_handler); //安装信号
unsigned int tid = syscall(SYS_gettid);
tidpthread=tid;
while(1)
{
printf("hello\n");
sleep(1);
}
}
pthread_t thread_create()
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_create(&tid,&attr, func,NULL);
return tid;
}
int main()
{
unsigned int pidnum = getpid(); //获取pid tid
pthread_t tid1;
tid1=thread_create(); //线程游离态创建
if(tid1<=0)
{
printf("thread_create error\n");
return -1;
}
char tmpcmd[120] = {0};
sprintf(tmpcmd, "ps -T -p %d | grep %d", pidnum, tidpthread);
FILE *read_fp = popen(tmpcmd, "r");
char ethstr[1024]={0};
int chars_read = fread(ethstr, sizeof(char), BUFSIZ - 1, read_fp);
pclose(read_fp);
if ((chars_read > 0) && (tidpthread > pidnum))
{
pthread_kill(tid1,SIGUSR1);
}
else
{
printf("这个线程已经死亡\n");
}
}