c - Linux中进程和线程的区别

标签 c linux multithreading

阅读 this answer 后和 Robert Love 的“Linux 内核开发”,随后在 clone() 上系统调用时,我发现 Linux 中的进程和线程对于内核来说(几乎)是无法区分的。它们之间有一些调整(在引用的 SO 问题中讨论为“更多共享”或“更少共享”),但我仍然有一些问题有待回答。

我最近开发了一个涉及几个 POSIX 线程的程序,并决定在此前提下进行实验。在创建两个线程的进程上,所有线程当然都会获得 pthread_self() 返回的唯一值。 ,但是,不是 getpid() .

我创建的示例程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>

void* threadMethod(void* arg)
{
    int intArg = (int) *((int*) arg);

    int32_t pid = getpid();
    uint64_t pti = pthread_self();

    printf("[Thread %d] getpid() = %d\n", intArg, pid);
    printf("[Thread %d] pthread_self() = %lu\n", intArg, pti);
}

int main()
{
    pthread_t threads[2];

    int thread1 = 1;

    if ((pthread_create(&threads[0], NULL, threadMethod, (void*) &thread1))
         != 0)
    {
        fprintf(stderr, "pthread_create: error\n");
        exit(EXIT_FAILURE);
    }

    int thread2 = 2;

    if ((pthread_create(&threads[1], NULL, threadMethod, (void*) &thread2))
         != 0)
    {
        fprintf(stderr, "pthread_create: error\n");
        exit(EXIT_FAILURE);
    }

    int32_t pid = getpid();
    uint64_t pti = pthread_self();

    printf("[Process] getpid() = %d\n", pid);
    printf("[Process] pthread_self() = %lu\n", pti);

    if ((pthread_join(threads[0], NULL)) != 0)
    {
        fprintf(stderr, "Could not join thread 1\n");
        exit(EXIT_FAILURE);
    }

    if ((pthread_join(threads[1], NULL)) != 0)
    {
        fprintf(stderr, "Could not join thread 2\n");
        exit(EXIT_FAILURE);
    }

    return 0;
}

(这是在 64 位 Fedora 上编译的 [ gcc -pthread -o thread_test thread_test.c ];由于 pthread_t 使用的 64 位类型源自 <bits/pthreadtypes.h> ,因此需要对代码进行一些细微更改才能在 32 位版本上进行编译。 )

我得到的输出如下:

[bean@fedora ~]$ ./thread_test 
[Process] getpid() = 28549
[Process] pthread_self() = 140050170017568
[Thread 2] getpid() = 28549
[Thread 2] pthread_self() = 140050161620736
[Thread 1] getpid() = 28549
[Thread 1] pthread_self() = 140050170013440
[bean@fedora ~]$ 

通过使用调度程序锁定 gdb ,我可以让程序及其线程保持事件状态,这样我就可以捕获 top 的内容说,仅显示流程,是:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
28602 bean      20   0 15272 1112  820 R  0.4  0.0   0:00.63 top
 2036 bean      20   0  108m 1868 1412 S  0.0  0.0   0:00.11 bash
28547 bean      20   0  231m  16m 7676 S  0.0  0.4   0:01.56 gdb
28549 bean      20   0 22688  340  248 t  0.0  0.0   0:00.26 thread_test
28561 bean      20   0  107m 1712 1356 S  0.0  0.0   0:00.07 bash

当显示线程时,说:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
28617 bean      20   0 15272 1116  820 R 47.2  0.0   0:00.08 top
 2036 bean      20   0  108m 1868 1412 S  0.0  0.0   0:00.11 bash
28547 bean      20   0  231m  16m 7676 S  0.0  0.4   0:01.56 gdb
28549 bean      20   0 22688  340  248 t  0.0  0.0   0:00.26 thread_test
28552 bean      20   0 22688  340  248 t  0.0  0.0   0:00.00 thread_test
28553 bean      20   0 22688  340  248 t  0.0  0.0   0:00.00 thread_test
28561 bean      20   0  107m 1860 1432 S  0.0  0.0   0:00.08 bash

与进程相比,程序(或者可能是内核)有一种独特的方式来定义线程,这似乎很清楚。每个线程都有自己的PID,根据top - 为什么?

最佳答案

这些困惑都源于这样一个事实:内核开发人员最初持有一种非理性和错误的观点,即只要内核提供一种使线程共享内存的方法,线程几乎可以完全在用户空间中以内核进程为原语来实现和文件描述符。这导致了 POSIX 线程的 LinuxThreads 实现出了名的糟糕,这是一个用词不当,因为它没有提供任何与 POSIX 线程语义远程相似的东西。最终 LinuxThreads 被 NPTL 取代,但许多令人困惑的术语和误解仍然存在。

首先要认识到的最重要的一点是“PID”在内核空间和用户空间中意味着不同的东西。内核所谓的 PID 实际上是内核级线程 id(通常称为 TID),不要与 pthread_t 混淆。这是一个单独的标识符。系统上的每个线程,无论是在同一进程还是不同进程中,都有一个唯一的 TID(或内核术语中的“PID”)。

另一方面,POSIX 意义上的“进程”被视为 PID,在内核中被称为“线程组 ID”或“TGID”。每个进程由一个或多个线程(内核进程)组成,每个线程都有自己的 TID(内核 PID),但都共享相同的 TGID,该 TGID 等于初始线程的 TID(内核 PID),其中 main运行。

何时 top显示线程,它显示 TID(内核 PID),而不是 PID(内核 TGID),这就是为什么每个线程都有一个单独的线程。

随着 NPTL 的出现,大多数采用 PID 参数或作用于调用进程的系统调用都被更改为将 PID 视为 TGID 并作用于整个“线程组”(POSIX过程)。

关于c - Linux中进程和线程的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29433973/

相关文章:

java - N次执行后协调多个线程

java - C# 相当于 Java 的同步 (abc.class)

c - rand()/RAND_MAX 是否返回 [0, 1) 或 [0,1]?

c - 换行符或回车符在 C 代码中不起作用

java - Threadpool Executor完成时如何给出消息?

c# - 临时清除Linux中的终端

linux - 如何检查 iptables 状态并在 ubuntu 中允许 ip

c - 在C中初始化一个指向常量对象的常量指针

c - C多维数组是否连续无孔?

linux - 为什么 chroot 不起作用?