图片 7

tid以及真实pid的关系

By admin in 使用交流 on 2019年11月19日

在Linux的top和ps命令中,默认看到最多的是pid (process
ID),也许你也能看到lwp (thread ID)和tgid (thread group ID for the thread
group leader)等等,而在Linux库函数和系统调用里也许你注意到了pthread
id和tid等等。还有更多的ID,比如pgrp (process group ID), sid (session ID
for the session leader)和 tpgid (tty process group ID for the process
group
leader)。概念太多可能很晕,但是只要对Linux的进程和线程的基本概念有准确的理解,这些ID的含义都迎刃而解。下面将介绍进程和线程的核心概念,并以一个示例程序来验证这些ID之间的关系。

add by zhj: 下面是我对pid,tgid,ppid的个人理解

 

对于ubuntu14.04操作系统,可以在/usr/src/linux-headers-4.4.0-31/include/linux/sched.h文件中看到进程控制块的结构体,如下

Linux的进程和线程

Linux的进程和线程有很多异同点,可以Google下。但只要能清楚地理解一下几点,则足够理解Linux中各种ID的含义。

  • 进程是资源分配的基本单位,线程是调度的基本单位
  • 进程是资源的集合,这些资源包括内存地址空间,文件描述符等等,一个进程中的多个线程共享这些资源。
  • CPU对任务进行调度时,可调度的基本单位
    (dispatchable
    entity)是线程。如果一个进程中没有其他线程,可以理解成这个进程中只有一个主线程,这个主进程独享进程中的所有资源。
  • 进程的个体间是完全独立的,而线程间是彼此依存,并且共享资源。多进程环境中,任何一个进程的终止,不会影响到其他非子进程。而多线程环境中,父线程终止,全部子线程被迫终止(没有了资源)。

上述第一点说明是最基础的,也是最重要的。

 

初步理解各种ID。基本上按照重要程度从高到低,在分割线下方的IDs不太重要。

  • pid: 进程ID。
  • lwp:
    线程ID。在用户态的命令(比如ps)中常用的显示方式。
  • tid:
    线程ID,等于lwp。tid在系统提供的接口函数中更常用,比如syscall(SYS_gettid)和syscall(__NR_gettid)。
  • tgid:
    线程组ID,也就是线程组leader的进程ID,等于pid。
  • ——分割线——
  • pgid: 进程组ID,也就是进程组leader的进程ID。
  • pthread id: pthread库提供的ID,生效范围不在系统级别,可以忽略。
  • sid: session ID for the session leader。
  • tpgid: tty process group ID for the process group leader。

 从上面的列表看出,各种ID最后都归结到pid和lwp(tid)上。所以理解各种ID,最终归结为理解pid和lwp(tid)的联系和区别。

 

下面的图是一张描述父子进程,线程之间关系的图。

图片 1

上图很好地描述了用户视角(user view)和内核视角(kernel
view)看到线程的差别:

  • 从用户视角出发,在pid 42中产生的tid
    44线程,属于tgid(线程组leader的进程ID)
    42。甚至用ps和top的默认参数,你都无法看到tid 44线程。
  • 从内核视角出发,tid 42和tid
    44是独立的调度单元,可以把他们视为”pid 42″和”pid 44″。

需要指出的是,有时候在Linux中进程和线程的区分也是不是十分严格的。即使线程和进程混用,pid和tid混用,根据上下文,还是可以清楚地区分对方想要表达的意思。上图中,从内核视角出发看到了pid
44,是从调度单元的角度出发,但是在top或ps命令中,你是绝对找不到一个pid为44的进程的,只能看到一个lwp(tid)为44的线程。

 

struct task_struct {
        volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
        void *stack;
        atomic_t usage;
        unsigned int flags;     /* per process flags, defined below */
        unsigned int ptrace;

#ifdef CONFIG_SMP
        struct llist_node wake_entry;
        int on_cpu;
        unsigned int wakee_flips;
        unsigned long wakee_flip_decay_ts;
        struct task_struct *last_wakee;

        int wake_cpu;
#endif
        ......
        ......
        pid_t pid;
        pid_t tgid;
        ......
        ......
}

理解pid和lwp(tid)的示例程序

下面利用一个示例程序来进一步理解pid和lwp(tid),以及利用格式化的ps命令打印出各种ID。下面的程序在main函数中创建了2个子线程,加上main函数这个主线程,一共有3个线程。在3个线程中分别打印pthread
id, pid和lwp(tid),来验证pid和lwp(tid)的关系。

 1 #include <unistd.h>
 2 #include <sys/syscall.h>
 3 #include <stdio.h>
 4 #include <pthread.h>
 5 
 6 #define gettidv1() syscall(__NR_gettid) // new form
 7 #define gettidv2() syscall(SYS_gettid)  // traditional form
 8 
 9 void *ThreadFunc1()
10 {
11         printf("the pthread_1 id is %ldn", pthread_self());
12         printf("the thread_1's Pid is %dn", getpid());
13         printf("The LWPID/tid of thread_1 is: %ldn", (long int)gettidv1());
14         pause();
15 
16         return 0;
17 }
18 
19 void *ThreadFunc2()
20 {
21         printf("the pthread_2 id is %ldn", pthread_self());
22         printf("the thread_2's Pid is %dn", getpid());
23         printf("The LWPID/tid of thread_2 is: %ldn", (long int)gettidv1());
24         pause();
25 
26         return 0;
27 }
28 
29 int main(int argc, char *argv[])
30 {
31         pid_t tid;
32         pthread_t pthread_id;
33 
34         printf("the master thread's pthread id is %ldn", pthread_self());
35         printf("the master thread's Pid is %dn", getpid());
36         printf("The LWPID of master thread is: %ldn", (long int)gettidv1());
37 
38         // 创建2个线程
39         pthread_create(&pthread_id, NULL, ThreadFunc2, NULL);
40         pthread_create(&pthread_id, NULL, ThreadFunc1, NULL);
41         pause();
42 
43         return 0;
44 }

注意编译的时候要利用-l指定library参数。

# gcc threadTest.c -o threadTest -l pthread

 

执行程序,结果如下:

# ./threadTest
the master thread's pthread id is 140154481125184
the master thread's Pid is 20992
The LWPID of master thread is: 20992
the pthread_1 id is 140154464352000
the thread_1's Pid is 20992
The LWPID/tid of thread_1 is: 20994
the pthread_2 id is 140154472744704
the thread_2's Pid is 20992
The LWPID/tid of thread_2 is: 20993

上述结果说明pthread
id是pthread库提供的ID,在系统级别没有意义。pid都是线程组leader的进程ID,即20992。而lwp(tid)则是线程ID,分别是20993和20994。

 

同时利用ps来查看结果,注意ps默认只打印进程级别信息,需要用-L选项来查看线程基本信息。

# ps -eo pid,tid,lwp,tgid,pgrp,sid,tpgid,args -L | awk '{if(NR==1) print $0; if($8~/threadTest/) print $0}'
  PID   TID   LWP  TGID  PGRP   SID TPGID COMMAND
20992 20992 20992 20992 20992 30481 20992 ./threadTest
20992 20993 20993 20992 20992 30481 20992 ./threadTest
20992 20994 20994 20992 20992 30481 20992 ./threadTest

从上述结果中可以看到:

  • PID=TGID: 20992
  • TID=LWP: 20993 or 20994
  • 至于SID,30481是bash shell的进程ID。

 

可以看到,里面定义了两个字段,pid和tgid,其中pid就是这个轻量级进程lwp的id,而tgid是轻量级进程组的id,当创建进程时,我们可以为自己指定

Linux用户态命令查看线程

进程的tgid字段,貌似可以随便指定,只要存在就行,另外在父进程中,可以为子进程设置进程组id,如果没有指定,它会继承父进程的进程组id。

top

默认top显示的是task数量,即进程。

图片 2

可以利用敲”H”,来切换成线程。如下,可以看到实际上有96个线程。也可以直接利用top
-H命令来直接打印线程情况。

图片 3

 

还有一个概念ppid,我没在这个结构体中找到,但操作系统肯定是会记录的,在Python中,通过os.get_ppid()就可以获取当前进程的父进程。tgid与ppid,

ps

ps的-L选项可以看到线程,通常能打印出LWP和NLWP相关信息。如下命令即可查看线程信息:

ps -eLf

 

这两者其实没有任何关系,因为tgid是可以自己来指定的,平时基本不用,不用管它。

pidstat

pidstat -t [-p pid号] 可以打印出线程之间的关系。

 

 

htop

要在htop中启用线程查看,开启htop,然后按<F2>来进入htop的设置菜单。选择“设置”栏下面的“显示选项”,然后开启“树状视图”和“显示自定义线程名”选项。按<F10>退出设置。
注:MAC的F2按fn+F2。

 

 

参考

Linux进程与线程的区别

原文:

1、pid,tid,真实pid的使用

进程pid: getpid()                // 相当于os.getpid() 
线程tid: pthread_self()          //进程内唯一,但是在不同进程则不唯一。相当于thread.get_ident()
线程pid: syscall(SYS_gettid)     //系统内是唯一的。python中没有现成的方法,需要手动调用动态链接库ctypes.CDLL('libc.so.6').syscall(xx)

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/syscall.h>

struct message
{
    int i;
    int j;
};

void *hello(struct message *str)
{
    printf("child, the tid=%lu, pid=%dn",pthread_self(),syscall(SYS_gettid));
    printf("the arg.i is %d, arg.j is %dn",str->i,str->j);
    printf("child, getpid()=%dn",getpid());
    while(1);
}

int main(int argc, char *argv[])
{
    struct message test;
    pthread_t thread_id;
    test.i=10;
    test.j=20;
    pthread_create(&thread_id,NULL,hello,&test);
    printf("parent, the tid=%lu, pid=%dn",pthread_self(),syscall(SYS_gettid));
    printf("parent, getpid()=%dn",getpid());
    pthread_join(thread_id,NULL);
    return 0;
}

图片 4

getpid()得到的是进程的pid,在内核中,每个线程都有自己的PID,要得到线程的PID,必须用syscall(SYS_gettid);

pthread_self函数获取的是线程ID,线程ID在某进程中是唯一的,在不同的进程中创建的线程可能出现ID值相同的情况。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

void *thread_one()
{
    printf("thread_one:int %d main process, the tid=%lu,pid=%ldn",getpid(),pthread_self(),syscall(SYS_gettid));
}

void *thread_two()
{
    printf("thread two:int %d main process, the tid=%lu,pid=%ldn",getpid(),pthread_self(),syscall(SYS_gettid));
}

int main(int argc, char *argv[])
{
    pid_t pid;
    pthread_t tid_one,tid_two;
    if((pid=fork())==-1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if(pid==0)
    {
        pthread_create(&tid_one,NULL,(void *)thread_one,NULL);
        pthread_join(tid_one,NULL);
    }
    else
    {
        pthread_create(&tid_two,NULL,(void *)thread_two,NULL);
        pthread_join(tid_two,NULL);
    }
    wait(NULL);
    return 0;
}

图片 5

2、pid与tid的用途

Linux中,每个进程有一个pid,类型pid_t,由getpid()取得。Linux下的POSIX线程也有一个id,类型pthread_t,由pthread_self()取得,该id由线程维护,其id空间是各个进程独立的(即不同进程中的线程可能有相同的id)。你可能知道,Linux中的POSIX线程库实现的线程其实也是一个进程(LWP),只是该进程与主进程(启动线程的进程)共享一些资源而已,比如代码段,数据段等。
  有时候我们可能需要知道线程的真实pid。比如进程P1要向另外一个进程P2中的某个线程发送信号时,既不能使用P2的pid,更不能使用线程的pthread
id,而只能使用该线程的真实pid,称为tid。
  有一个函数gettid()可以得到tid,但glibc并没有实现该函数,只能通过Linux的系统调用syscall来获取。使用syscall得到tid只需一行代码,但为了加深各位看官的印象,简单提供下面场景。
  有一簇进程,其中一个进程中另外启了一个线程。各进程共享一个数据结构,由shared_ptr指明,其中保存有线程的tid。在各个进程的执行过程中,需要判断线程是否存在,若不存在则(重新)创建。
  首先,在线程函数的开始,需要将自己的tid保存至共享内存,

点击(此处)折叠或打开

  1. #include <sys/syscall.h>
  2. #include <sys/types.h>
  3. void*
  4. thread_func(void *args)
  5. {
  6.     //~ lock shared memory
  7.     shared_ptr->tid = syscall(SYS_gettid); //~ gettid()
  8.     //~ unlock shared memory
  9.     //~ other stuff
  10. }

  在各进程中判断进程是否存在,

点击(此处)折叠或打开

  1. //~ lock shared memory
  2. pthread_t id;
  3. if (shared_ptr->tid == 0) { //~ tid is initialized to 0
  4.     pthread_create(&id, NULL, thread_func, NULL);
  5. } else if (shared_ptr->tid > 0) {
  6.     int ret = kill(shared_ptr->tid, 0); //~ send signal
    0 to thread
  7.     if (ret != 0) { //~ thread already died
  8.         pthread_create(&id, NULL, thread_func, NULL);
  9.     }
  10. }
  11. //~ unlock shared memory

3、linux 系统中查看pid,tid的方法

线程进程都会有自己的ID,从操作系统来讲,这个ID就叫做PID

图片 6

 

 图片 7

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 大奖888网页版登陆 版权所有