Linux线程 之 线程 线程组 进程 轻量级进程(LWP)
Thread Local Storage,线程本地存储,大神Ulrich Drepper有篇PDF文档是讲TLS的,我曾经努力过三次尝试搞清楚TLS的原理,均没有彻底搞清楚。这一次是第三次,我沉浸glibc的源码和kernel的源码中,做了一些实验,也有所得。对Linux的线程有了进一步的理解。
线程是有栈的,我们知道,普通的一个进程,它的栈空间是8M,我们可以通过ulmit -a查看:stack size (kbytes, -s) 8192 线程也不例外,线程也是需要栈空间的这句话是废话,呵呵。对于属于同一个进程(或者说是线程组)的多个线程他们是共享一份虚拟内存地址的,如下图所示。这也就决定了,你不能无限制创建线,因为纵然你什么都不做,每个线程默认耗费8M的空间(事实上还不止,还有管理结构,后面陈述)。Ulrich Drepper大神有篇文章《Thread numbers and stacks》,分析了线程栈空间方面的计算。如果我们真的需要很多个线程的话,幸好我们还是可以做一些事情。我们可以通过pthread_attr_setstacksize,设定好stack size属性然后在pthread_create.int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
linux下的sleep函数 线程栈如上图所示,共享进程(或者称之为线程组)的虚拟地址空间。既然多个线程聚集在一起,我怎么知道我要操作的那个线程栈的地址呢。要解决这个问题,必须要领会线程和进程以及线程组的概念。我不想写一堆片汤话,下面我运行我的测试程序,然后结合现象分析原因:#include <stdio.h>#include <pthread.h>#include <sys/syscall.h>#include <assert.h>#define gettid() syscall(__NR_gettid)pthread_key_t key;__thread int count = 2222;__thread unsigned long long count2 ;static __thread int count3;void echomsg(int t){ printf("destructor excuted in thread %x,param=%x\n",pthread_self(),t);}void * child1(void *arg){ int b; int tid=pthread_self(); printf("I am the child1 pthread_self return %p gettid return %d\n",tid,gettid()); char* key_content = malloc(8); if(key_content != NULL) { strcpy(key_content,"ACACACA"); } pthread_setspecific(key,(void *)key_content); count=666666; count2=1023; count3=2048; printf("I am child1 , tid=%x ,count (%p) = %10d,count2(%p) = %10llu,count3(%p) = %6d\n",tid,&count,count,&count2,count2,&count3,count3); asm volatile("movl %%gs:0, %0;" :"=r"(b) /* output */ ); printf("I am child1 , GS address %x\n",b); sleep(2); printf("thread %x returns %x\n",tid,pthread_getspecific(key)); sleep(50);}void
* child2(void *arg){ int b; int tid=pthread_self(); printf("I am the child2 pthread_self return %p gettid return %d\n",tid,gettid()); char* key_content = malloc(8); if(key_content != NULL) { strcpy(key_content,"ABCDEFG"); } pthread_setspecific(key,(void *)key_content); count=88888888; count2=1024; count3=2047; printf("I am child2 , tid=%x ,count (%p) = %10d,count2(%p) = %10llu,count3(%p) = %6d\n",tid,&count,count,&count2,count2,&count3,count3); asm volatile("movl %%gs:0, %0;" :"=r"(b) /* output */ ); printf("I am child2 , GS address %x\n",b); sleep(1); printf("thread %x returns %x\n",tid,pthread_getspecific(key)); sleep(50);}int main(void){ int b; pthread_t tid1,tid2; printf("hello\n"); pthread_key_create(&key,echomsg); asm volatile("movl %%gs:0, %0;" :"=r"(b) /* output */ ); printf("I am the main , GS address %x\n",b); pthread_create(&tid1,NULL,child1,NULL); pthread_create(&tid2,NULL,child2,NULL); printf("pthread_create tid1 = %p\n",tid1); printf("pthread_create tid2 = %p\n",tid2); sleep(60); pthread_key_delete(key); printf("main thread exit\n"); return 0;} 这是一个比较综合的程序,因为我下面要多次从
不同的侧面分析。对于现在,我们要展示的是进程 线程 线程组的关系。在一个终端运行编译出来的test2程序,显示的信息如下: 另一个终端看ps信息,ps显示的信息如下: 直接ps,是看不到我们创建的线程的。只有3658一个进程。当我们采用ps -eLf的时候,我们看到了三个线程3658/3659/3660,或者称之为轻量级进程(LWP)。Linux到底是怎么看待这三者的关系的呢:
Linux下多线程程序,一般都是有一个主进程通过调用pthread_create创建了一个或者多个子线程,如同我们的程序,主进程在main中创建了两个子进程。那么Linux到底是怎么看待这些事情的呢? pid_t pid; pid_t tgid; ... struct task_struct *group_leader; /* threadgroup leader */ 上面三个变量是进程描述符的三个成员变量。pid字面意思是process id,其实叫thread id会更合适。tgid 字面含义是thread group ID。对于存在多个线程的程序而言,每个线程都有自己的pid,没错pid,如同我们例子中的3658/3659/3660,但是都有个共同的线程组ID (TGID):3658 。
好吧,我们再重新说一遍,对于普通进程而言,我们可以称之为只有一个LWP的线程组,pid是它自己的pid,tgid还是它自己,线程组里面只有他自己一个光杆司令,自然grou
p_leader也是它自己。但是多线程的进程(线程组更恰当)则不然。开天辟地的main函数所在的进程会有自己的PID,也会有也TGID,group_leader,都是他自己。注意,它自己也是LWP。后面他使用ptherad_create创建了2个线程,或者LWP,这两个新创建的线程会有自己的PID,但是TGID会沿用创建自己的那个进程的TGID,group_leader也会尊创建自己的进程的进程描述符(task_struct)为自己的group_leader。copy_process函数中有如下代码: p->pid = pid_nr(pid); p->tgid = p->pid;//普通进程 if (clone_flags & CLONE_THREAD) p->tgid = current->tgid;//线程选择叫起它的进程的tgid作为自己的tgid
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论