简介
- pid(process identifier),进程id。在整个操作系统中,每个进程的id唯一。
- tid(thread identifier),线程id。在一个进程中,每个线程的id唯一,不同的进程可能有tid相同的线程。
这只是简单地描述,能够应付一般的应用场景,比如只是简单地区别进程和线程。
但是当使用htop/top
查看所有线程时,PID一列显示的值会让你疑惑不解。因为它对线程依然显示的是pid。
因为对于pid和tid的真实含意,从用户视角(user view)和从内核视角(kernel view)看到的是不同的结果。
用户视角
在程序中获取这两个id并不难:
#include <iostream>
#include <thread>
#include <pthread.h>
//#include <sys/types.h>
//#include <sys/syscall.h>
int main(void)
{
using std::cout;
using std::endl;
cout << "getpid: " << getpid() << endl;
cout << "gettid: " << pthread_self() << endl;
cout << "gettid: " << std::this_thread::get_id() << endl; // same as the last line, c++11
return 0;
}
输出如下:
getpid: 15439
gettid: 139708860868480
gettid: 139708860868480
对于用户来说,一般的应用就足够了。
除非,你有了一个需求,需要在一个进程中向另一个进程中的某个线程发送信号,接收信号的线程的id是哪个?
这就要说到内核视角了。
内核视角
对于操作系统内核来说,线程才是调度单元的最小单位。
因此,内核并没有严格区分进程和线程,而是把所有的线程都看作是进程,都有一个pid,只是把所有的线程进行了分组,同属一个组的线程可以共享内存,fd等。
可见,一个组里的进程就是用户视角的一个进程里的线程。
于是,这里没有tid的概念,只有pid。
那么,对于用户应用来说,如何获取到内核层面所谓的线程pid呢?
有一个函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。
代码就一行:
#include <unistd.h>
#include <sys/syscall.h>
cout << "gettid: " << syscall(SYS_gettid) << endl; // 这个值就对应了top中的pid列
理解了上面的内容,用户视角和内核视角的区别可以用下图示例:
看到了吧,用户视角看到的new thread的pid还是42的时候,内核已经把它作为44对待了。
小结
理解了pid、tid的异同之后,能够方便程序调试。
比如在编程时,启动线程的时候记录一下它的内核态pid,这样在top显示线程pid时就能对应上。
当线程出现问题,如内存使用过多,cpu使用率过高等时,能够快速定位到当事线程。