首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >线程是否在Linux上作为进程实现?

线程是否在Linux上作为进程实现?
EN

Unix & Linux用户
提问于 2017-05-12 13:58:51
回答 5查看 33.6K关注 0票数 86

我正在学习这本书,由Mitchell、Jeffrey和Alex编写的高级Linux程序。这是2001年的,所以有点老了。但我觉得它还是不错的。

然而,当它与我的Linux在shell输出中所产生的结果不同时,我得出了这样的结论。在第92页(查看器中的116),第4.5章GNU/Linux线程实现以包含以下语句的段落开头:

POSIX线程在GNU/Linux上的实现在一个重要方面不同于许多其他类似UNIX系统的线程实现:在GNU/Linux上,线程是作为进程实现的。

这似乎是一个关键点,稍后将用C代码加以说明。这本书的产出是:

代码语言:javascript
复制
main thread pid is 14608
child thread pid is 14610

在我的Ubuntu 16.04中,它是:

代码语言:javascript
复制
main thread pid is 3615
child thread pid is 3615

ps输出支持这一点。

我想从2001年到现在肯定有什么改变了。

下一页的下一章4.5.1信号处理建立在前一条语句的基础上:

信号和线程之间的交互行为因UNIX系统而异。在GNU/Linux中,这种行为是由线程作为进程实现这一事实决定的。

看上去这一点在这本书的后面会更加重要。有人能解释一下这里发生了什么吗?

我看过这个Linux内核线程真的是内核进程吗?,但没有多大帮助。我很困惑。

这是C代码:

代码语言:javascript
复制
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void* thread_function (void* arg)
{
    fprintf (stderr, "child thread pid is %d\n", (int) getpid ());
    /* Spin forever. */
    while (1);
    return NULL;
}

int main ()
{
    pthread_t thread;
    fprintf (stderr, "main thread pid is %d\n", (int) getpid ());
    pthread_create (&thread, NULL, &thread_function, NULL);
    /* Spin forever. */
    while (1);
    return 0;
}
EN

回答 5

Unix & Linux用户

发布于 2017-05-12 14:11:04

(Userspace)线程不作为进程在Linux上实现,因为它们没有自己的私有地址空间,它们仍然共享父进程的地址空间。

但是,这些线程是为了使用内核进程记帐系统而实现的,因此分配给它们自己的线程ID ( TID ),但是给出了与父进程相同的PID和“线程组ID”( TGID ) --这与创建新的TGID和PID的叉不同,而TID与PID相同。

因此,最近的内核似乎有一个可以查询的单独的TID,这对于线程来说是不同的,在上面的main() thread_function()中显示这一点的一个合适的代码片段是:

代码语言:javascript
复制
    long tid = syscall(SYS_gettid);
    printf("%ld\n", tid);

因此,整个代码如下所示:

代码语言:javascript
复制
#include <pthread.h>                                                                                                                                          
#include <stdio.h>                                                                                                                                            
#include <unistd.h>                                                                                                                                           
#include <syscall.h>                                                                                                                                          

void* thread_function (void* arg)                                                                                                                             
{                                                                                                                                                             
    long tid = syscall(SYS_gettid);                                                                                                                           
    printf("child thread TID is %ld\n", tid);                                                                                                                 
    fprintf (stderr, "child thread pid is %d\n", (int) getpid ());                                                                                            
    /* Spin forever. */                                                                                                                                       
    while (1);                                                                                                                                                
    return NULL;                                                                                                                                              
}                                                                                                                                                             

int main ()                                                                                                                                                   
{                                                                                                                                               
    pthread_t thread;                                                                               
    long tid = syscall(SYS_gettid);     
    printf("main TID is %ld\n", tid);                                                                                             
    fprintf (stderr, "main thread pid is %d\n", (int) getpid ());                                                    
    pthread_create (&thread, NULL, &thread_function, NULL);                                           
    /* Spin forever. */                                                                                                                                       
    while (1);                                                                                                                                                
    return 0;                                                                                                                                                 
} 

给出了一个示例输出:

代码语言:javascript
复制
main TID is 17963
main thread pid is 17963
thread TID is 17964
child thread pid is 17963
票数 26
EN

Unix & Linux用户

发布于 2017-05-13 13:28:45

在内部,linux内核中没有进程或线程这样的东西。进程和线程是一个主要是用户的概念,内核本身只看到“任务”,这是一个可调度的对象,可以与其他任务共享任何、部分或全部资源。线程是配置成与父任务共享其大部分资源(地址空间、mmap、管道、打开的文件处理程序、套接字等)的任务,进程是配置成与父任务共享最小资源的任务。

当您直接使用Linux (克隆(),而不是叉()丝线_创建())时,您可以更灵活地定义要共享或不共享的资源,并且可以创建既不是完全进程也不是完全线程的任务。如果直接使用这些低级调用,也可以使用新的TGID (因此大多数用户工具将其视为一个进程)创建一个任务,该任务实际上与父任务共享其所有资源,反之亦然,从而创建一个具有共享TGID的任务(因此大多数用户工具将其视为线程)与其父任务不共享资源。

Linux2.4实现了TGID,但这主要是为了实现资源核算。许多用户和用户空间工具发现,能够将相关任务分组并一起报告其资源使用情况是非常有用的。

Linux中任务的实现比用户空间工具提供的进程和线程世界观更加流畅。

票数 16
EN

Unix & Linux用户

发布于 2017-05-13 04:17:30

基本上,您书中的信息在历史上是准确的,因为Linux上线程的实现历史非常糟糕。我对有关SO的一个相关问题的回答也是对你问题的回答:

https://stackoverflow.com/questions/9154671/distinction-between-processes-and-threads-in-linux/9154725#9154725

这些混淆都源于内核开发人员最初持有一种非理性和错误的观点,即只要内核提供了一种使线程共享内存和文件描述符的方法,那么线程几乎完全可以在用户空间中使用内核进程作为原语来实现。这导致了POSIX线程的臭名昭著的LinuxThreads实现,这是一个错误的名称,因为它没有提供任何与POSIX线程语义非常类似的东西。最终,LinuxThreads被NPTL取代,但许多令人困惑的术语和误解依然存在。首先,也是最重要的一点是,在内核空间和用户空间中,PID意味着不同的东西。内核所称的PID实际上是内核级别的线程ids (通常称为TID),不能与单独的标识符pthread_t混淆。系统上的每个线程,无论是在同一个进程中还是在不同的进程中,都有一个唯一的TID (或内核术语中的"PID“)。另一方面,在POSIX意义上被认为是“进程”的PID在内核中被称为“线程组ID”或"TGID“。每个进程由一个或多个线程(内核进程)组成,每个线程都有自己的TID (内核PID),但它们都共享相同的TGID,这等于运行main的初始线程的TID (内核PID)。当top显示线程时,它显示的是TID(内核PID),而不是PID(内核TGID),这就是为什么每个线程都有一个单独的线程。随着NPTL的出现,大多数采用PID参数或对调用进程起作用的系统调用被更改为将PID视为TGID,并作用于整个“线程组”(POSIX进程)。

票数 8
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/364660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档