【问题标题】:Why EAGAIN in pthread_key_create happens?为什么会发生 pthread_key_create 中的 EAGAIN?
【发布时间】:2019-04-29 13:32:40
【问题描述】:

有时当我尝试使用 pthread_key_create 创建密钥时,我会收到 EAGAIN 错误代码。有可能确切地知道原因吗?

文档说:

系统缺乏必要的资源来创建另一个特定于线程的数据键,或者系统对每个进程的键总数 [PTHREAD_KEYS_MAX] 施加的限制将被超出。

如何检查它是否是键的限制?或许是某种监控工具之王来检查系统中已经打开了多少个密钥,还有多少个还可以使用?

关于我们的代码有一个重要的事情:我们使用fork() 并且有多个进程在运行。每个进程可以有多个线程。

我发现当我们使用fork()时,我们对线程键没有独立的限制。这是一个小例子。

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

size_t create_keys(pthread_key_t *keys, size_t number_of_keys)
{
    size_t counter = 0;
    for (size_t i = 0; i < number_of_keys; i++)
    {
        int e = pthread_key_create(keys + i, NULL);
        if (e)
        {
            printf("ERROR (%d): index: %ld, pthread_key_create (%d)\n", getpid(), i, e);
            break;
        }
        counter++;
    }

    return counter;
}

int main(int argc, char const *argv[])
{
    printf("maximim number of thread keys: %ld\n", sysconf(_SC_THREAD_KEYS_MAX));

    printf("process id: %d\n", getpid());

    const size_t number_of_keys = 1024;

    pthread_key_t keys_1[number_of_keys];
    memset(keys_1, 0, number_of_keys * sizeof(pthread_key_t));

    printf("INFO (%d): number of active keys: %ld\n", getpid(), create_keys(keys_1, number_of_keys));

    pid_t p = fork();
    if (p == 0)
    {
        printf("process id: %d\n", getpid());

        pthread_key_t keys_2[number_of_keys];
        memset(keys_2, 0, number_of_keys * sizeof(pthread_key_t));

        printf("INFO (%d): number of active keys: %ld\n", getpid(), create_keys(keys_2, number_of_keys));
    }

    return 0;
}

当我在 Ubuntu 16.04 上运行此示例时,我发现如果我使用与限制 (1024) 相同数量的键,子进程将无法创建任何新的线程键。但是,如果我对父进程和子进程使用 512 键,我可以毫无错误地运行它。

【问题讨论】:

  • 假设您的意思是PTHREAD_KEYS_MAX,它在&lt;limits.h&gt; 中定义。您可以通过每次递增一个原子 int 轻松跟踪您调用 pthread_key_create() 的次数,然后在收到该错误后比较这两个值。不过,在我的系统上是 1024。你为什么要创建这么多不同的线程局部变量?似乎不对。显示您的代码;我敢打赌,您尝试使用它们的方式有问题。
  • @Shawn 是的,我们创建了许多进程(Apache + fork),每个进程可以有许多线程。所以使用原子计数器不是一个好主意,因为我需要在所有进程中都有这样的计数器。
  • 嗯?进程数无关紧要;每个人都有自己的一套钥匙。进程中的线程数也无关紧要。重要的是特定进程具有的线程本地存储密钥的数量。对于线程特定数据的概念,您肯定有一些不了解的地方。显示minimal reproducible example
  • @Shawn 我为fork() 创建了一个小例子,看起来进程数很重要。
  • 您的子进程在使用 1024 时无法再创建任何密钥,因为父进程在分叉之前已经创建了最大数量...

标签: linux pthreads


【解决方案1】:

如您所知,fork() 传统上的工作方式是将进程复制到内存中,然后从每个副本中的父进程和子进程中的同一点继续执行。这就是 fork() 的返回码所表示的。

为了执行 fork(),必须复制进程的内部结构。内存、堆栈、打开的文件,可能还有线程本地存储密钥。每个系统的 fork() 实现都不同。某些系统允许您自定义要复制的进程区域(请参阅 Linux clone(2) 界面)。但是,概念保持不变。

因此,继续您的示例代码:如果您在父进程中分配 1024 个键,则每个子进程都会继承一个完整的键表并且没有备用键可以使用,从而导致错误。如果你在父节点中只分配了 512 个键,那么每个子节点都会继承一个半空的键表,并且有 512 个备用键可以使用,因此不会出现错误。

【讨论】:

    【解决方案2】:

    最大值:

    #include <unistd.h>
    #include <stdio.h>
    
    int main ()
    {
      printf ("%ld\n", sysconf(_SC_THREAD_KEYS_MAX));
      return 0;
    }
    

    考虑使用pthread_key_delete

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-23
      • 2013-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-20
      • 2017-09-04
      • 1970-01-01
      相关资源
      最近更新 更多