误区:
之前一直认为,linux的主线程只是人为强加的概念(当一个进程创建了一些线程后,我们就把此进程主观当做主线程),在操作系统实现上并没有相关的机制或代码,今天查了一些资料,发现并不是如此。
如何标识一个进程:(以下内容多引自《Understanding linux kernel》3.2.2节)
通常来说,每个可以独立被调度的执行上下文都有一个进程描述符,除了进程,轻量级进程也如此(为什么叫轻量级进程而不是线程,因为这是站在操作系统
的角度,而不是编程人员的角度,也是为了区分与pthread相关的概念),所以我们可以简单的认为,不管是进程还是线程都有自己的
task_struct结构。在task_struct字段中,有下面一些结构我们应该关心:
在linux系统中,我们用pid区分每一个进程,linux给每一个进程和轻量级进程都分配一个pid,但是linux程序员希望由一个进程产生的轻量级进程具有相同的pid,这样当我们向进程发送信号时,此信号可以影响进程及进程产生的轻量级进程。
为了做到这一点,linux用了线程组(可以理解为轻量级进程组)的概念,在线程组内,每个线程都使用此线程组内第一个线程(thread group leader)的pid,并将此值存入tgid,当 我们使用getpid()函数得到进程ID时,其实操作系统返回的是task_struct的tgid字段,而非pid字段(这很重要)。如果我们调用某 个函数返回线程的ID,就应该返回的是pid,但这个函数是什么呢?不知道,也许是gettid(),但没有试验成功。
通过线程组的概念我们做到了以下两点
1.每个线程的task_struct中,pid都不同,方便系统进行调度等操作
2.每个线程的task_struct中,tgid都相同,都是第一个线程的pid,这样方便了编程人员对一个进程产生的线程进行统一管理,当然我们在线程组的每个线程内用getpid()函数时,返回相同的值。
这里有个“线程组内第一个线程(thread group leader)”的概念,怎么理解?我认为,就是创建线程的进程,也就是那个不轻量的进程,它的pid和tgid是相同的,在线程组内其他线程的pid和tgid都是不同的。
说到这,我们就可以很自然的引出“主线程”的概念,如上面所说,主线程即是“线程组内第一个线程”。
那在“误区”中为什么又说之前的理解也就是“主线程”是人为的想出来的是不对的呢?因为linux在task_struct中引入了tgid地段,用来明显的标识,若pid和tgid相同,那它就是主线程,是实实在在存在的。
下面我们用进程和pthread线程概念演示一下线程号和进程号区别,pthread线程应该在实现上和上面所说的线程,即轻量级进程还是有区别的,但具体有什么区别待研究。
程序演示:
程序输出,运行了两次:
如上程序,在进程和线程内分别调用了getpid()和pthread_self()函数,getpid()返回了相同的值,而pthread_self()返回的不同。
按照之前的讲解main即为主线程,那么它pid和线程id应该是一样的,可这里我们看到pthread_self()输出的是一串十六进制数,并非如我
们所想和pid相同。查了相关资料,说phtead_self()其实返回的是phtread线程描述符的地址,即此线程struct
pthread结构(在此结构中包含pid_t tid字段为线程ID)的地址,但在我的实验环境中,并不存在此结构,所以没办法验证。
另外还有一个函数gettid()用来返回线程的id,在资料中多次提到,但在我的环境中也没有。
以上理解不一定正确,欢迎讨论并指正。
看过评论后的补充说明看了rucku关于gettid()函数使用的说明,,分别更改第5行,第9行代码如下:
此时输出为
正好验证了第一处红色文字关于线程id的猜想。