C语言编程中pid, tid以及真实pid的关系

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

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和tgid,其中pid就是这个轻量级进程lwp的id,而tgid是lwp组的id,即主lwp的id

原文:http://blog.csdn.net/u012398613/article/details/52183708

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

进程pid: getpid()
线程tid: pthread_self()     //进程内唯一,但是在不同进程则不唯一。
线程pid: syscall(SYS_gettid)     //系统内是唯一的

#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=%d\n",pthread_self(),syscall(SYS_gettid));
    printf("the arg.i is %d, arg.j is %d\n",str->i,str->j);
    printf("child, getpid()=%d\n",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=%d\n",pthread_self(),syscall(SYS_gettid));
    printf("parent, getpid()=%d\n",getpid());
    pthread_join(thread_id,NULL);
    return 0;
}

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=%ld\n",getpid(),pthread_self(),syscall(SYS_gettid));
}

void *thread_two()
{
    printf("thread two:int %d main process, the tid=%lu,pid=%ld\n",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;
}

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

引用原文

The four threads will have the same PID but only when viewed from above. What you (as a user) call a PID is not what the kernel (looking from below) calls a PID.
In the kernel, each thread has it‘s own ID, called a PID (although it would possibly make more sense to call this a TID, or thread ID) and they also have a TGID (thread group ID) which is the PID of the thread that started the whole process.
Simplistically, when a new process is created, it appears as a thread where both the PID and TGID are the same (new) number.
When a thread starts another thread, that started thread gets its own PID (so the scheduler can schedule it independently) but it inherits the TGID from the original thread.
That way, the kernel can happily schedule threads independent of what process they belong to, while processes (thread group IDs) are reported to you.

关于线程继承关系图如下:

              USER VIEW
 <-- PID 43 --> <----------------- PID 42 ----------------->
                     +---------+
                     | process |
                    _| pid=42  |_
                  _/ | tgid=42 | \_ (new thread) _
       _ (fork) _/   +---------+                        /                                        +---------+
+---------+                                    | process |
| process |                                    | pid=44  |
| pid=43  |                                    | tgid=42 |
| tgid=43 |                                    +---------+
+---------+
 <-- PID 43 --> <--------- PID 42 --------> <--- PID 44 --->
                     KERNEL VIEW

在这里你可以清晰的看到,创建一个新的进程会给一个新的PID和TGID,并且2个值相同,
当创建一个新的线程的时候,会给你一个新的PID,并且TGID和之前开始的进程一致。

Linux通过进程查看线程的方法 1).htop按t(显示进程线程嵌套关系)和H(显示线程) ,然后F4过滤进程名。2).ps -eLf | grep java(快照,带线程命令,e是显示全部进程,L是显示线程,f全格式输出) 3).pstree -p <pid>(显示进程树,不加pid显示所有) 4).top -Hp <pid> (实时) 5).ps -T -p <pid>(快照)推荐程度按数字从小到大。

原文地址:https://www.cnblogs.com/ajianbeyourself/p/8146449.html

时间: 2024-08-15 03:53:29

C语言编程中pid, tid以及真实pid的关系的相关文章

R语言编程中的常见错误

R语言编程中的常见错误有一些错误是R的初学者和经验丰富的R程序员都可能常犯的.如果程序出错了,请检查以下几方面.? 使用了错误的大小写.help().Help()和HELP()是三个不同的函数(只有第一个是正确的).? 忘记使用必要的引号.install.packages("gclus")能够正常执行,然而Install.packages(gclus)将会报错.? 在函数调用时忘记使用括号.例如,要使用help()而非help.即使函数无需参数,仍需加上().? 在Windows上,路

[C] 在 C 语言编程中实现动态数组对象

对于习惯使用高级语言编程的人来说,使用 C 语言编程最头痛的问题之一就是在使用数组需要事先确定数组长度. C 语言本身不提供动态数组这种数据结构,本文将演示如何在 C 语言编程中实现一种对象来作为动态数组. /* Author: [email protected] */ 基本的 C 数组 C 语言编程中声明一个基本数组如下: int main() { // 声明一个容纳 3000 个整数的数组 int my_array[3000]; } 以上代码做了两件事: ● 在栈区开辟内存空间.准确说来是在

C语言编程中的指针

单片机编程中,涉及指针的地方很多,需要多看多练习多总结.#include <string.h>#include <stdio.h>#include <stdlib.h>int main(void){unsigned int pInt32;//指向32位无符号整数的指针unsigned int UINT32_1;//32位的无符号整数unsigned int Array1_UINT32[]={1,9,0,1};//存储4个32位无符号整数的数组unsigned short

各种语言编程中的注释总结

JAVA: 单行注释 // 多行注释 /*..........*/ 多行注释快捷键:Ctrl+/ 或者 Ctrl+Shift+C 或者 Ctrl+Shift+/ HTML: <!-- 注释内容 --> JSP中的HTML注释: <!-- 注释内容 --> JSP页面中的普通注释 <% // 注释内容 %>    <% /* 注释内容 */ %> .....

PL/SQL语言编程中异常分类及其处理

--预定义异常:系统已经声明的异常DECLARE V_NAME EMP.ENAME%TYPE;BEGIN SELECT ENAME INTO V_NAME FROM EMP WHERE EMPNO = 1234; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('未查到数据'); WHEN OTHERS THEN dbms_output.put_line('其他异常');END; --非预定义异常:系统异常,系统没有对其进行声明DEC

C#编程中的Image/Bitmap与base64的关系

最近用base64编码传图片遇到了点问题,总结下. 首先总结下base64编码的逻辑,来自网络:https://www.cnblogs.com/zhangchengye/p/5432276.html Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编码.它将需要编码的数据拆分成字节 数组.以3个字节为一组.按顺序排列24 位数据,再把这24位数据分成4组,即每组6位.再在每组的的最高位前 补两个0凑足一个字节.这样就把一个3字节为一组的数据重新编码成了4个字节.当所要编码

在 JNI 编程中避免内存泄漏

JAVA 中的内存泄漏 JAVA 编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏:JVM 内存中 native memory 的内存泄漏. Java Heap 的内存泄漏 Java 对象存储在 JVM 进程空间中的 Java Heap 中,Java Heap 可以在 JVM 运行过程中动态变化.如果 Java 对象越来越多,占据 Java Heap 的空间也越来越大,JVM 会在运行时扩充 Java Heap 的容量.如果 Java Heap 容量

解析Java的JNI编程中的对象引用与内存泄漏问题

JNI,Java Native Interface,是 native code 的编程接口.JNI 使 Java 代码程序可以与 native code 交互--在 Java 程序中调用 native code:在 native code 中嵌入 Java 虚拟机调用 Java 的代码.JNI 编程在软件开发中运用广泛,其优势可以归结为以下几点: 利用 native code 的平台相关性,在平台相关的编程中彰显优势. 对 native code 的代码重用.native code 底层操作,更

C语言编程入门——动态内存分配

内存管理,是指软件运行时对计算机内存资源的分配和使用的技术.其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源.内存管理是C语言编程中重要的组成部分,C语言中的内存需要手动分配,手动释放,一般遵循分配多少,释放多少,以免造成内存泄漏.内存管理是一项重要且复杂的事情,理解内存管理,对后面课程及项目的学习会有很大的作用. 之前创建变量,是系统自动分配的内存,放在栈内存中,销毁后被系统自动回收,手动分配的内存,放在堆内存中,需要手动释放.千万不要忘记销毁对象手动将内存释放并将指针