一 Linux--多线程( 三 )

运行结果如下:

一 Linux--多线程

文章插图
可以看到六个线程的PID是一样的,同属于一个进程,但是它们还有一个表示,LWP(light wighted process),轻量级进程的ID 。下面详细介绍 。
进程ID和线程ID
  • 在Linux下,线程是由Native POSIX Thread Library 实现的 , 在这种实现下,线程又被称为轻量级进程(LWP) 。在用户态的每个进程,内核中都有一个与之对应的调度实体(拥有自己的task_struct结构体) 。
  • 在没有线程之前 , 一个进程对应内核里的一个进程描述符,对应一个进程ID 。引入线程概念之后,一个用户进程下管理多个用户态线程 , 每个线程作为一个独立的调度实体,在内核中都有自己的进程描述符 。进程和内核的描述符变成了1:N的关系 。
  • 多线程的进程,又被称为线程组 。线程组内的每一个线程在内核中都有一个进程描述符与之对应 。进程描述符结构体表面上看是进程的pid,其实它对应的是线程ID;进程描述符中的tpid , 含义是线程组ID,该值对应的是用户层面的进程ID 。
    struct task_struct { ... pid_t pid;// 对应的是线程ID,就是我们看到的lwp pid_t tgid;// 线程组ID,该值对应的是用户层面的进程ID ... struct task_struct *group_leader; ... struct list_head thread_group; ...};
  • 具体关系如下:
用户态系统调用内核进程描述符中对应的结构线程IDpid_t gettid(void)pid_t pid进程IDpid_d getpid(void)pid_t tgid注意: 这里的线程ID和创建线程得到的ID不是一回事,这里的线程ID是用来唯一标识线程的一个整形变量 。
如何查看线程ID?
1.使用ps命令,带-L选项,可以查看到lwp
2.Linux提供了gettid系统调用来返回其线程ID,可是glibc并没有将该系统调用封装起来,在开放接口来供程序员使用 。如果确实需要获得线程ID,可以采用如下方法:
#include <sys/syscall.h>pid_t tid; tid = syscall(SYS_gettid);在前面的一张图片中(如下) , 我们可以发现的是,有一个线程的ID和进程ID是一样的,这个线程就是主线程 。在内核中被称为group leader,内核在创建第一个线程时,会将线程组的ID的值设置成第一个线程的线程ID , group_leader指针则指向自身,既主线程的进程描述符 。所以线程组内存在一个线程ID等于进程ID,而该线程即为线程组的主线程 。
一 Linux--多线程

文章插图
注意: 线程和进程不一样,进程有父进程的概念,但是在线程组中,所有的线程都是对等关系 。
线程ID和进程地址空间布局pthread_create产生的线程ID和gettid获得的id不是一回事 。后者属于进程调度范畴,用来标识轻量级进程 。前者的线程id是一个地址,指向的是一个虚拟内存单元,这个地址就是线程的ID 。属于线程库的范畴,线程库后序对线程操作使用的就是这个ID 。对于目前实现的NPTL而言,pthread_t的类型是线程ID,本质是进程地址空间的一个地址:
一 Linux--多线程

文章插图
这里的每一个线程ID都代表的是每一个线程控制块的起始地址,pthread_create返回的就是线程控制块的起始地址 。这些线程控制块都是struct pthread类型的,所以所有的线程可以看成是一个大的数组,被描述组织起来 。
线程退出在线程中我们可以调用exit函数或者_exit函数来结束进程,在一个线程中我们可以通过以下三种方式在不终止整个进程的情况下停止它的控制流 。
  • 从线程函数return 。这种方法对主线程不适用,从main函数return相当于调用exit 。
  • 线程可以调用pthread_exit终止自己
  • 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程
注意:线程不能用exit(0)来退出,exit是用来退出进程的,如果在线程中调用exit,那么当线程结束的时候 , 该线程的进程也就结束退出了 。
示例1:return退出线程调度函数
#include <stdio.h>#include <pthread.h>#include <unistd.h>void* pthreadrun(void* arg){  int count = 0;  while (1){    printf(" new threaad is running, pid is %d, thread id is %p\n", getpid(), pthread_self());    sleep(1);    if (count++ == 5){      return (void*)10;    }  }}int main(){  pthread_t thread;  pthread_create(&thread, NULL, pthreadrun, NULL);  while (1){    printf("main thread is running, pid is %d, thread id is %p\n", getpid(), pthread_self());    sleep(1);  }  return 0;}

推荐阅读