Linux tgkill(),真的可以看到两个tid相同的线程吗?

问题描述 投票:0回答:1

在 Ubuntu Linux 20.04.4(Linux 内核 5.13)上,

man tgkill
说:

int tgkill(int tgid, int tid, int sig);

tgkill() 将信号 sig 发送到具有线程 ID 的线程 线程组中的tidtgid

我的问题是,系统真的可以同时拥有两个具有相同tid的线程(由

gettid()
获取)吗?

如果不能,那为什么

tgkill
强制用户提供tgid参数呢?系统应该能够自己从特定的
tgid
查询对应的
tid

顺便说一句:我知道,要从

tgid
(例如 1554725)手动查询
tid
,我们可以
cat /proc/1554725/status
并抓取
Tgid
字段。

上面的说法可以用下面的

threadtid.cpp
来验证:

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

void* threadFunc(void *arg)
{
    (void)arg;
    int child_tid = (int)gettid();
    printf("Child:  tid=%d\n", child_tid);
    sleep(30);
    return nullptr;
}

int main(int argc, char *argv[])
{
    pthread_t t1;
    int err = pthread_create(&t1, nullptr, threadFunc, nullptr);
    if (err != 0)
        exit(4);

    int parent_tid = (int)gettid();
    
    printf("Parent: tid=%d\n", parent_tid);

    void* childres = 0;
    err = pthread_join(t1, &childres);
    if (err != 0)
        exit(4);

    printf("Done.\n");
    exit(EXIT_SUCCESS);
}

我们看到:

Name:   threadtid.out   
Umask:  0002            
State:  T (stopped)     
Tgid:   1554724  
Ngid:   0        
Pid:    1554725          
PPid:   37645      
...           

linux-kernel pthreads
1个回答
0
投票

系统真的可以同时拥有两个具有相同tid(由gettid()获取)的线程吗?

不。线程 ID 在系统中在任何给定时间都是唯一的,但它们确实会被回收。

线程组 ID(和进程 ID)是线程 ID 的子集(请参阅线程 ID 和进程 ID 之间的关系),根据情况进行选择:新进程的第一个线程的 TID 被分配为该线程的 TGID 和该进程的 TGID PID。当在任何其他上下文中创建新线程时,它们都会继承创建它们的线程的 PID 和 TGID。 PID 和 TGID 之间的区别表明 Linus* 可能已经预料到允许一个进程拥有多个线程组,但迄今为止,这还没有实现。

为什么

tgkill
强制用户提供tgid参数?系统应该能够自己从特定的
tgid
查询对应的
tid

tgid参数本身并不用于识别要终止的线程。 TID 足以让系统识别要发出信号的线程(如果存在)。 TGID 是为了最大限度地减少程序因终止线程而发出错误信号的可能性,并且其 TID 被回收用于新线程。 对于 TGID,只有当该 TGID 所属的进程本身生成大量线程时才会发生这种情况。如果没有,它可能是由于系统上其他进程的行为而发生的,而这些其他进程不需要特殊权限。我可以想象这是一个可利用的弱点。我不确定是否创建了实际的漏洞利用程序,但我注意到 Linux 有一个过时的系统调用

TKILL

,它实际上与

tgkill()
做同样的事情,而不需要指定 TGID。 Linux 很少(如果有的话)实际上删除系统调用,但是不应该使用
TKILL
,并且 Glibc 没有为其提供包装函数。


*

这都是 Linux 特定的。

© www.soinside.com 2019 - 2024. All rights reserved.