系统:Fedora 40
使用的库:pthread.h
很抱歉这里没有提供任何代码,因为整个程序有超过 4000 行代码,而且也没有托管在 Github 上。我会尽力用文字来解释。
该程序是一个简单的数据库。这是给我父母的商店的。运行Linux的计算机让程序不断运行。我使用每个客户的文件来存储有关他们的所有信息。还有销售人员,因此该程序必须能够同时为许多销售人员和客户打开并准备好文件。
程序整体结构很简单: 经理线程运行,并为每个客户或销售人员启动新线程。到目前为止,6 个线程是它达到的最大值,但随着销售人员的增多,我认为完全为销售人员提供一个流程是更好的选择。上下文切换会影响程序的速度,因此当需要打开销售人员的文件时,经理线程将创建一个新进程。
每个线程将 4KB 内存映射为 ANONYMOUS 和 PRIVATE,并有一个内存管理器。该映射内存充当任何先前大量计算的缓存。每个线程都有自己的MUTEXES和CONDITION VARIABLES。还有一个由管理线程管理的请求队列和请求处理程序。处理程序也有自己的锁和条件变量。
我的发现:我研究了多处理程序。我发现最好在分叉后重新初始化所有内容,因此我使用 Manager 线程来执行分叉。这样 Manager 线程就被复制了。但我还发现映射的内存、锁和条件变量、分配的内存,基本上所有东西都被继承了。未运行的线程的锁和条件变量也被继承,但它们没有运行,所以我释放了所有分配的内存,销毁了锁并取消映射内存,除了条件变量,每次,第三个线程的条件变量都不能被已释放。由于某种原因,pthread_cond_destroy函数根本不返回。
我向ChatGPT和Gemini提出了问题,并得到了解决方案:不要尝试释放继承的锁和条件变量甚至内存,只需重新初始化所有内容。我无法让自己保留未使用的内存分配。
你的整个设计从根本上被破坏了。
fork()
的 POSIX 文档:
进程应使用单线程创建。如果多线程进程调用 fork(),新进程应包含调用线程及其整个地址空间的副本,可能包括互斥体和其他资源的状态。因此,为了避免错误,子进程只能执行异步信号安全操作,直到调用 exec 函数之一为止。
简而言之:
如果多线程进程调用 fork() ...子进程只能执行异步信号安全操作,直到调用 exec 函数之一为止。
根据 Linux 手册页
fork()
:
请注意以下几点:
• 子进程是用单个线程创建的——即 调用了 fork()。 父级的整个虚拟地址空间 在子进程中复制,包括互斥体的状态, 条件变量和其他 pthreads 对象;指某东西的用途 pthread_atfork(3) 可能有助于处理问题 这可能会导致。
• 在多线程程序中执行 fork() 后,子进程可以 仅安全地调用异步信号安全函数(请参阅 signal-safety(7)) 直到它调用 execve(2)。
Linux 异步信号安全函数列表可以在 Linux signal-safety.7 手册页找到。
子进程中不存在的另一个线程持有的锁上的死锁是未能遵循这些限制的可能结果。