我正在学习这本书,由Mitchell、Jeffrey和Alex编写的高级Linux程序。这是2001年的,所以有点老了。但我觉得它还是不错的。
然而,当它与我的Linux在shell输出中所产生的结果不同时,我得出了这样的结论。在第92页(查看器中的116),第4.5章GNU/Linux线程实现以包含以下语句的段落开头:
POSIX线程在GNU/Linux上的实现在一个重要方面不同于许多其他类似UNIX系统的线程实现:在GNU/Linux上,线程是作为进程实现的。
这似乎是一个关键点,稍后将用C代码加以说明。这本书的产出是:
main thread pid is 14608
child thread pid is 14610在我的Ubuntu 16.04中,它是:
main thread pid is 3615
child thread pid is 3615ps输出支持这一点。
我想从2001年到现在肯定有什么改变了。
下一页的下一章4.5.1信号处理建立在前一条语句的基础上:
信号和线程之间的交互行为因UNIX系统而异。在GNU/Linux中,这种行为是由线程作为进程实现这一事实决定的。
看上去这一点在这本书的后面会更加重要。有人能解释一下这里发生了什么吗?
我看过这个Linux内核线程真的是内核进程吗?,但没有多大帮助。我很困惑。
这是C代码:
#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;
}发布于 2017-05-12 14:11:04
(Userspace)线程不作为进程在Linux上实现,因为它们没有自己的私有地址空间,它们仍然共享父进程的地址空间。
但是,这些线程是为了使用内核进程记帐系统而实现的,因此分配给它们自己的线程ID ( TID ),但是给出了与父进程相同的PID和“线程组ID”( TGID ) --这与创建新的TGID和PID的叉不同,而TID与PID相同。
因此,最近的内核似乎有一个可以查询的单独的TID,这对于线程来说是不同的,在上面的main() thread_function()中显示这一点的一个合适的代码片段是:
long tid = syscall(SYS_gettid);
printf("%ld\n", tid);因此,整个代码如下所示:
#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;
} 给出了一个示例输出:
main TID is 17963
main thread pid is 17963
thread TID is 17964
child thread pid is 17963发布于 2017-05-13 13:28:45
在内部,linux内核中没有进程或线程这样的东西。进程和线程是一个主要是用户的概念,内核本身只看到“任务”,这是一个可调度的对象,可以与其他任务共享任何、部分或全部资源。线程是配置成与父任务共享其大部分资源(地址空间、mmap、管道、打开的文件处理程序、套接字等)的任务,进程是配置成与父任务共享最小资源的任务。
当您直接使用Linux (克隆(),而不是叉()和丝线_创建())时,您可以更灵活地定义要共享或不共享的资源,并且可以创建既不是完全进程也不是完全线程的任务。如果直接使用这些低级调用,也可以使用新的TGID (因此大多数用户工具将其视为一个进程)创建一个任务,该任务实际上与父任务共享其所有资源,反之亦然,从而创建一个具有共享TGID的任务(因此大多数用户工具将其视为线程)与其父任务不共享资源。
Linux2.4实现了TGID,但这主要是为了实现资源核算。许多用户和用户空间工具发现,能够将相关任务分组并一起报告其资源使用情况是非常有用的。
Linux中任务的实现比用户空间工具提供的进程和线程世界观更加流畅。
发布于 2017-05-13 04:17:30
基本上,您书中的信息在历史上是准确的,因为Linux上线程的实现历史非常糟糕。我对有关SO的一个相关问题的回答也是对你问题的回答:
这些混淆都源于内核开发人员最初持有一种非理性和错误的观点,即只要内核提供了一种使线程共享内存和文件描述符的方法,那么线程几乎完全可以在用户空间中使用内核进程作为原语来实现。这导致了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进程)。
https://unix.stackexchange.com/questions/364660
复制相似问题