【问题标题】:Are locked pages inherited by pthreads?锁定页面是否由 pthread 继承?
【发布时间】:2019-05-24 08:37:22
【问题描述】:

我的实时系统有一点分页问题,​​我想知道 linux 在我的特定情况下应该如何表现。

除其他外,我的应用程序使用 pthread_create() 生成 2 个线程,它们在一组共享缓冲区上运行。 第一个线程,我们称之为 A,从设备读取数据,对其执行一些计算,并将结果写入其中一个缓冲区。 一旦该缓冲区已满,线程 B 将读取所有结果并通过以太网将它们发送到 PC,而线程 A 将写入下一个缓冲区。

我注意到,每次线程 A 开始写入以前未使用的缓冲区时,我都会错过一些中断并丢失数据(每个数据包的标头中有一个 id,如果增加超过 1,我错过了中断)。 因此,如果我使用 n 个缓冲区,我会在数据采集开始时得到恰好 n 次错过的中断(因此问题肯定是由分页引起的)。

为了解决这个问题,我在所有缓冲区上使用了 mlock() 和 memset() 以确保它们实际被分页。 这解决了我的问题,但我想知道我的代码中的哪个位置是正确的。在我的主应用程序中,还是在一个/两个线程中? (目前我在两个线程中都这样做)

根据 libc 文档(第 3.4.2 节“锁定的内存详细信息”),使用 fork() 创建的子进程不会继承内存锁。 那么pthreads呢?它们的行为方式是否相同,或者它们会从我的主进程继承这些锁?

关于我的系统的一些背景信息,尽管我认为这在这种特殊情况下并不重要:

  • 它是一个嵌入式系统,由具有双核 Cortex-A9 的 SoC 提供支持,运行具有 PREEMPT_RT 的 Linux 4.1.22。
  • 中断频率为4kHz
  • 线程优先级(如 htop 所示)为中断的 -99,线程 A 的 -98(这两者都高于所有其他中断的标准优先级 -51)和线程 B 的 -2李>

编辑:

我做了一些额外的测试,从不同的线程(和主线程)调用我的页面锁定函数。

如果我将页面锁定在 main() 中,然后尝试再次将它们锁定在其中一个线程中,我希望看到 main() 出现大量页面错误,但线程本身没有页面错误(因为页面应该已经被锁定)。然而,htop 讲述了一个不同的故事:我看到每个锁定这些页面的线程都有大量的页面错误(MNFLT 列)。

对我来说,这表明 pthread 实际上确实具有与使用 fork() 生成的子进程相同的限制。如果是这种情况,将它们锁定在两个线程中(但不在主线程中)将是正确的过程。

【问题讨论】:

  • 线程使用相同的地址空间和页表,所以锁应该是通用的。
  • 看来他们不是...我已将我的新发现添加到我的初始帖子中
  • 请显示主线程和线程的锁调用。您确定使用相同的地址吗?
  • 好的,所以我尝试为我的问题创建一个简化的最小示例,并且该示例的行为完全符合应有的要求。我还检查了我实际应用程序上的地址,但它们对于所有线程来说绝对是相同的。我不知道这是怎么发生的,因为我从来没有解锁任何页面(所以我的应用程序的行为应该与最小示例完全相同)。当然,我的应用程序要大得多,无论是在代码大小还是内存消耗方面,但这不应该影响任何这些
  • 锁定 main 后,你确定线程中的页面错误不是来自被分页的代码吗?

标签: c linux pthreads paging


【解决方案1】:

线程共享相同的内存管理上下文。如果一个页面驻留在一个线程中,那么它就驻留在同一进程中的所有线程中。

这意味着内存锁定是每个进程,而不是每个线程。

您可能仍然会在第一次写入时看到小错误,因为错误用于将页面标记为脏。您可以通过在锁定后写入每个页面来避免这种情况。

【讨论】:

  • 这与我的观察不符。我的锁定函数实际上在锁定每个缓冲区后对每个缓冲区执行 memset()(以确保 100% 确保它被分页),这似乎会产生很多页面错误。这正是我所期望的,对我的程序来说完全没问题,因为它在开始时只发生一次。但是,我得到每个线程的页面错误,而不仅仅是第一个锁定页面的线程。而且,最重要的是,所有线程的故障数量都是相同的(并且与我的缓冲区大小相匹配)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 2013-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多