Linux父、子进程间的竞争条件

2026-10-041

目录

在 Linux 中,fork() 系统调用创建了一个新的子进程,这个子进程是父进程的精确副本。然而,在 fork() 之后,父进程和子进程成为两个独立的进程,并且都可以被系统调度运行。这就引入了一个关键问题:竞争条件(Race Condition)。

竞争条件是指多个进程或线程在没有正确同步的情况下同时访问和操作共享资源,导致程序产生不可预测的行为或结果。在父子进程的场景中,竞争条件可能导致以下问题:

  • 执行顺序的不确定性:在 fork() 之后,父子进程都可以被系统调度运行,但无法确定哪个进程会首先获得 CPU 资源,导致执行顺序不确定。
  • 共享资源的竞争:父子进程可能竞争访问共享的文件描述符、内存区域、或其他资源,这种竞争可能导致数据的不一致或错误。

下面是一个简单的示例程序,演示了竞争条件可能导致的不确定行为。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int global_var = 0;
 
int main() {
    pid_t pid = fork();
 
    if (pid < 0) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        global_var += 5;
        printf("Child process: global_var = %d\n", global_var);
    } else {
        // 父进程
        global_var += 10;
        printf("Parent process: global_var = %d\n", global_var);
        wait(NULL);  // 等待子进程结束
    }
 
    return 0;
}

运行上述代码时,你可能会得到不同的输出结果:

Parent process: global_var = 10
Child process: global_var = 5
或者
Child process: global_var = 5
Parent process: global_var = 10

这取决于系统如何调度父子进程,谁先运行是不可预测的。这种不确定性就是竞争条件的体现。

虽然竞争条件仅导致输出顺序的不同,但在实际应用中,竞争条件可能会导致更加严重的后果,例如:

  • 数据一致性问题:如果父子进程同时修改共享数据,可能导致数据被部分更新或出现错误。
  • 资源锁定:如果两个进程同时尝试锁定同一个资源,可能导致死锁或资源争用。

为了避免竞争条件,必须确保进程或线程之间的操作是正确同步的。以下是几种常见的同步技术。

1、使用 wait()函数

wait() 函数可用于父进程等待子进程结束,确保子进程先运行。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main() {
    pid_t pid = fork();
 
    if (pid < 0) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("Child process running\n");
    } else {
        // 父进程
        wait(NULL);  // 等待子进程结束
        printf("Parent process running after child\n");
    }
 
    return 0;
}

2、使用信号同步

信号(Signals)可以用来同步父子进程。比如可以让父进程在子进程发出特定信号后才继续运行。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
 
volatile sig_atomic_t child_ready = 0;
 
void signal_handler(int sig) {
    child_ready = 1;
}
 
int main() {
    signal(SIGUSR1, signal_handler);
 
    pid_t pid = fork();
 
    if (pid < 0) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("Child process running\n");
        kill(getppid(), SIGUSR1);  // 向父进程发送信号
    } else {
        // 父进程
        while (!child_ready) {
            pause();  // 等待信号
        }
        printf("Parent process running after child signal\n");
    }
 
    return 0;
}

在实际应用中,特别是多进程的服务器或并发处理任务中,必须小心处理竞争条件,以避免不确定行为。通常会使用更复杂的同步机制,如信号量(semaphore)、互斥锁(mutex)等,以确保资源访问的正确性。


来源:Linux父、子进程间的竞争条件(CSDN,原作者“不脱发的程序猿”,CC BY-SA 4.0)

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -