为什么线程不能调用fork

探讨并发环境中使用fork()函数引发的问题,包括线程管理、资源释放与内存一致性问题,以及分配内存函数如malloc()在子进程中的行为不确定性。

The general problem with making fork() work in a multi-threaded world is what to do with all of the threads. There are two alternatives. One is to copy all of the threads into the new process. This causes the programmer or implementation to deal with threads that are suspended on system calls or that might be about to execute system calls that should not be executed in the new process. The other alternative is to copy only the thread that calls fork(). This creates the difficulty that the state of process-local resources is usually held in process memory. If a thread that is not calling fork() holds a resource, that resource is never released in the child process because the thread whose job it is to release the resource does not exist in the child process.


If we call fork(2) in a multi-threaded environment the thread doing the call is now the main-thread in the new process and all the other threads, which ran in the parent process, are dead. And everything they did was left exactly as it was just before the call to fork(2).

Now imagine that these other threads were happily doing their work before the call to fork(2)and a couple of milliseconds later they are dead. What if something these now-dead threads did was not meant to be left exactly as it was?

Let me give you an example. Let's say our main thread (the one which is going to call fork(2)) was sleeping while we had lots of other threads happily doing some work. Allocating memory, writing to it, copying from it, writing to files, writing to a database and so on. They were probably allocating memory with something like malloc(3). Well, it turns out that malloc(3) uses a mutex internally to guarantee thread-safety. And exactly this is the problem.

What if one of these threads was using malloc(3) and has acquired the lock of the mutex in the exact same moment that the main-thread called fork(2)? In the new child process the lock is still held - by a now-dead thread, who will never return it.

The new child process will have no idea if it's safe to use malloc(3) or not. In the worst case it will call malloc(3) and block until it acquires the lock, which will never happen, since the thread who's supposed to return it is dead. And this is just malloc(3). Think about all the other possible mutexes and locks in database drivers, file handling libraries, networking libraries and so on.


### 线程 `fork` 的概念解释 #### 子进程创建机制 当在一个进程中某个线程调用了 `fork()` 函数之后,会创建一个新的子进程。这个新创建的子进程几乎完全复制了父进程的状态,但是两者之间仍然存在一些重要的区别[^3]。 #### 地址空间继承特性 子进程通过继承整个地址空间的一个副本而启动,在此过程中也会从父进程那里获得所有互斥量、读写锁以及条件变量状态的一份拷贝。这意味着尽管两个进程拥有独立的内存区域,它们初始时的内容却是相同的。 #### 单一线程的存在形式 值得注意的是,在子进程中仅保留了一个线程——即由发起 `fork()` 调用的那个特定线程所形成的副本。其他任何存在于原生父进程内的额外线程都不会被复制到新的子进程中去。 #### 实际案例分析 考虑一段简单的 C 语言程序示例来展示这一行为: ```c #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<pthread.h> void* fun(void *arg) { for (int i = 0; i < 5; ++i) { printf("Thread function running with PID=%d\n", getpid()); sleep(1); } } int main() { pthread_t tid; pthread_create(&tid, NULL, fun, NULL); fork(); for (int i = 0; i < 5; ++i) { printf("Main thread running with PID=%d\n", getpid()); sleep(1); } return 0; } ``` 在这个例子中,主线程创建了一个辅助线程执行某些操作后立即调用了 `fork()` 。这将导致出现两个不同的进程各自运行自己的版本的剩余代码逻辑。然而,只有最初那个调用了 `fork()` 的线程会在子进程中继续活动,其他的线程则不会出现在子进程中[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值