如何使用克隆系统调用分配新的TLS区域

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用克隆系统调用分配新的TLS区域相关的知识,希望对你有一定的参考价值。

问题的简短版本:如果我想为我正在创建的线程分配一个新的TLS区域,我需要将哪个参数传递给x86_64 Linux系统上的clone系统调用。

长版:

我正在研究一个研究项目,对于我正在尝试的东西,我想使用clone系统调用而不是使用pthread_create来创建线程。但是,我也希望能够使用线程本地存储。我现在不打算创建多个线程,所以我可以为我使用克隆系统调用创建的每个线程创建一个新的TLS区域。

我正在查看clone的手册页,它有关于TLS参数标志的以下信息:

CLONE_SETTLS (since Linux 2.5.32)
   The newtls argument is the new TLS (Thread Local Storage) descriptor.
   (See set_thread_area(2).)

所以我查看了set_thread_area的手册页,注意到以下内容看起来很有希望:

 When  set_thread_area()  is  passed  an  entry_number  of -1, it uses a 
 free TLS entry. If set_thread_area() finds a free TLS entry, the value of
 u_info->entry_number is set upon return to show which entry was changed.

然而,在尝试了一些之后,似乎set_thread_area没有在我的系统中实现(在x86_64平台上的Ubunut 10.04)。当我运行以下代码时,我收到一条错误消息:set_thread_area() failed: Function not implemented

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h> 
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/unistd.h>

 #include <asm/ldt.h>

int main()
{
  struct user_desc u_info;
  u_info.entry_number = -1; 
  int rc = syscall(SYS_set_thread_area,&u_info);
  if(rc < 0) {
    perror("set_thread_area() failed");
    exit(-1);
  }

  printf("entry_number is %d",u_info.entry_number);
}

我也看到当我使用strace时,看看当调用pthread_create时会发生什么,我看不到任何对set_thread_area的调用。我一直在查看nptl pthread源代码,试图了解它们在创建线程时所做的工作。但我还没有完全理解它,我认为它比我正在尝试做的更复杂,因为我不需要在pthread实现中具有强大功能的东西。我假设set_thread_area系统调用是针对x86的,并且有一种不同的机制用于x86_64。但目前我还没弄清楚它是什么,所以我希望这个问题可以帮助我了解一些我需要了解的内容。

答案

我正在研究一个研究项目和我正在尝试的东西我想使用克隆系统调用而不是使用pthread_create来创建线程

在极不可能的情况下,你的新线程永远不会调用任何libc函数(或直接调用其他调用libc的东西;这也包括通过PLT的动态符号解析),那么你可以传递你想要的任何TLS存储作为new_tls clone的参数。

你应该忽略对set_thread_area的所有引用 - 它们只适用于32位/ ix86情况。

如果你打算在新创建的线程中使用libc,你应该放弃你的方法:libc期望TLS以某种方式设置,当你直接调用clone时,你无法安排这样的设置。当libc发现您没有正确设置TLS时,您的新线程将间歇性地崩溃。调试此类崩溃非常困难,唯一可靠的解决方案是......使用pthread_create

另一答案

另一个答案是绝对正确的,因为在libc的控制之外设置一个线程肯定会在某一点上造成麻烦。你可以这样做,但你不能再依赖于libc的服务,绝对不能依赖任何pthread_*函数或线程局部变量(使用__threadthread_local定义)。

话虽这么说,你甚至可以在x86-64上设置一个用于TLS(GS和FS)的段寄存器。要查找的系统调用是prctl(ARCH_SET_GS, ...)

您可以看到一个示例,比较在i386上设置TLS寄存器和在this piece of code中设置x86-64。

以上是关于如何使用克隆系统调用分配新的TLS区域的主要内容,如果未能解决你的问题,请参考以下文章

php 多进程

Linux操作系统下如何调用动态分配显存?

Forge Viewer - 如何在场景中访问(或获取渲染/片段代理)克隆的网格?

如何在多个页面使用同一个HTML片段

函数调用的过程-栈

如何在 Android Volley 中判断 TLS 版本