Skip to content

C++进程编程完整面试指南

目录

  1. 进程基础概念
  2. 进程创建与管理
  3. 进程继承机制
  4. 进程间通信(IPC)
  5. 进程同步与控制
  6. 进程状态与调度
  7. 高级进程编程
  8. 常见面试题汇总
  9. 实战编程题

1. 进程基础概念

1.1 进程 vs 线程

特性进程线程
内存空间独立的虚拟地址空间共享进程的地址空间
创建开销大(需要复制页表等)相对较小
通信方式IPC机制(管道、共享内存等)直接访问共享变量
安全性进程间相互隔离一个线程崩溃可能影响整个进程
资源消耗内存消耗大内存消耗小
切换成本高(需要切换页表)低(只需切换寄存器)

1.2 进程的内存布局

高地址
┌─────────────┐
│   内核空间   │ <- 内核代码和数据
├─────────────┤
│     栈      │ <- 局部变量、函数参数(向下增长)
│     ↓       │
│             │
│     ↑       │
│     堆      │ <- 动态分配内存(向上增长)
├─────────────┤
│   BSS段     │ <- 未初始化全局变量
├─────────────┤
│   数据段    │ <- 已初始化全局变量
├─────────────┤
│   代码段    │ <- 程序代码(只读)
└─────────────┘
低地址

1.3 进程控制块(PCB)

进程控制块包含的信息:

  • 进程标识符(PID)
  • 进程状态
  • 程序计数器
  • CPU寄存器
  • CPU调度信息
  • 内存管理信息
  • 记账信息
  • I/O状态信息

2. 进程创建与管理

2.1 fork()系统调用

cpp
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>

int main() {
    pid_t pid = fork();
    
    if (pid < 0) {
        // fork失败
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        std::cout << "Child process: PID = " << getpid() 
                  << ", PPID = " << getppid() << std::endl;
        // 子进程的工作
        sleep(2);
        std::cout << "Child process finished" << std::endl;
    } else {
        // 父进程,pid是子进程的PID
        std::cout << "Parent process: PID = " << getpid() 
                  << ", Child PID = " << pid << std::endl;
        
        // 等待子进程结束
        int status;
        wait(&status);
        std::cout << "Child process exited with status: " 
                  << WEXITSTATUS(status) << std::endl;
    }
    
    return 0;
}

2.2 exec族函数

exec函数族用于在当前进程中执行新程序:

cpp
#include <unistd.h>

// 常用的exec函数
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

// 示例:创建子进程执行ls命令
if (fork() == 0) {
    execl("/bin/ls", "ls", "-l", NULL);
    // 如果exec成功,这行代码不会执行
    perror("exec failed");
    exit(1);
}

2.3 wait()和waitpid()

cpp
#include <sys/wait.h>

// 等待任意子进程
pid_t wait(int *status);

// 等待指定子进程
pid_t waitpid(pid_t pid, int *status, int options);

// 示例
int main() {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        sleep(3);
        exit(42);  // 以状态码42退出
    } else {
        // 父进程
        int status;
        pid_t child_pid = wait(&status);
        
        if (WIFEXITED(status)) {
            printf("Child %d exited with status %d\n", 
                   child_pid, WEXITSTATUS(status));
        }
        if (WIFSIGNALED(status)) {
            printf("Child %d killed by signal %d\n", 
                   child_pid, WTERMSIG(status));
        }
    }
    return 0;
}

3. 进程继承机制

3.1 fork()后子进程继承的内容

完全继承:

  • 用户ID和组ID
  • 环境变量
  • 工作目录
  • 根目录
  • 文件创建掩码(umask)
  • 信号处理设置
  • 打开的文件描述符
  • 进程组ID
  • 会话ID

部分继承/修改:

  • 进程ID(子进程获得新的PID)
  • 父进程ID(子进程的PPID是父进程的PID)
  • 进程运行时间(重置为0)
  • 挂起的信号(清空)
  • 文件锁(不继承)

3.2 写时复制(Copy-on-Write)

cpp
#include <unistd.h>
#include <iostream>

int global_var = 100;

int main() {
    int local_var = 200;
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程
        std::cout << "Child - global_var: " << global_var 
                  << ", local_var: " << local_var << std::endl;
        
        // 修改变量,触发写时复制
        global_var = 300;
        local_var = 400;
        
        std::cout << "Child after modification - global_var: " 
                  << global_var << ", local_var: " << local_var << std::endl;
    } else {
        // 父进程等待子进程完成
        wait(NULL);
        
        std::cout << "Parent - global_var: " << global_var 
                  << ", local_var: " << local_var << std::endl;
        // 父进程中的变量值不受子进程影响
    }
    
    return 0;
}

3.3 文件描述符继承

cpp
#include <fcntl.h>
#include <unistd.h>

int main() {
    // 父进程打开文件
    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    
    if (fork() == 0) {
        // 子进程继承了文件描述符
        write(fd, "Child process\n", 14);
        close(fd);
    } else {
        // 父进程
        write(fd, "Parent process\n", 15);
        close(fd);
        wait(NULL);
    }
    
    return 0;
}

4. 进程间通信(IPC)

4.1 管道(Pipe)

匿名管道:

cpp
#include <unistd.h>

int main() {
    int pipefd[2];  // pipefd[0]读端,pipefd[1]写端
    char buffer[100];
    
    if (pipe(pipefd) == -1) {
        perror("pipe");
        return 1;
    }
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程:写数据
        close(pipefd[0]);  // 关闭读端
        write(pipefd[1], "Hello from child", 16);
        close(pipefd[1]);
    } else {
        // 父进程:读数据
        close(pipefd[1]);  // 关闭写端
        int n = read(pipefd[0], buffer, sizeof(buffer));
        buffer[n] = '\0';
        printf("Parent received: %s\n", buffer);
        close(pipefd[0]);
        wait(NULL);
    }
    
    return 0;
}

命名管道(FIFO):

cpp
#include <sys/stat.h>
#include <fcntl.h>

// 创建命名管道
int mkfifo(const char *pathname, mode_t mode);

// 使用示例
int main() {
    const char* fifo_path = "/tmp/myfifo";
    
    // 创建命名管道
    if (mkfifo(fifo_path, 0666) == -1) {
        perror("mkfifo");
        return 1;
    }
    
    if (fork() == 0) {
        // 子进程:写入数据
        int fd = open(fifo_path, O_WRONLY);
        write(fd, "Hello FIFO", 10);
        close(fd);
    } else {
        // 父进程:读取数据
        char buffer[100];
        int fd = open(fifo_path, O_RDONLY);
        int n = read(fd, buffer, sizeof(buffer));
        buffer[n] = '\0';
        printf("Received: %s\n", buffer);
        close(fd);
        wait(NULL);
        unlink(fifo_path);  // 删除命名管道
    }
    
    return 0;
}

4.2 共享内存

cpp
#include <sys/shm.h>
#include <sys/ipc.h>

struct shared_data {
    int counter;
    char message[100];
};

int main() {
    key_t key = ftok(".", 'a');  // 生成键值
    
    // 创建共享内存段
    int shmid = shmget(key, sizeof(shared_data), IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        return 1;
    }
    
    // 连接共享内存
    shared_data* data = (shared_data*)shmat(shmid, NULL, 0);
    if (data == (void*)-1) {
        perror("shmat");
        return 1;
    }
    
    if (fork() == 0) {
        // 子进程
        data->counter = 42;
        strcpy(data->message, "Hello from child");
        
        // 分离共享内存
        shmdt(data);
    } else {
        // 父进程
        wait(NULL);  // 等待子进程完成
        
        printf("Counter: %d\n", data->counter);
        printf("Message: %s\n", data->message);
        
        // 分离并删除共享内存
        shmdt(data);
        shmctl(shmid, IPC_RMID, NULL);
    }
    
    return 0;
}

4.3 消息队列

cpp
#include <sys/msg.h>

struct msg_buffer {
    long msg_type;
    char msg_text[100];
};

int main() {
    key_t key = ftok(".", 'b');
    
    // 创建消息队列
    int msgid = msgget(key, IPC_CREAT | 0666);
    if (msgid == -1) {
        perror("msgget");
        return 1;
    }
    
    if (fork() == 0) {
        // 子进程:发送消息
        msg_buffer message;
        message.msg_type = 1;
        strcpy(message.msg_text, "Hello from child process");
        
        if (msgsnd(msgid, &message, sizeof(message.msg_text), 0) == -1) {
            perror("msgsnd");
        }
    } else {
        // 父进程:接收消息
        msg_buffer message;
        
        if (msgrcv(msgid, &message, sizeof(message.msg_text), 1, 0) == -1) {
            perror("msgrcv");
        } else {
            printf("Received: %s\n", message.msg_text);
        }
        
        wait(NULL);
        
        // 删除消息队列
        msgctl(msgid, IPC_RMID, NULL);
    }
    
    return 0;
}

4.4 信号量

cpp
#include <sys/sem.h>

// 信号量操作结构
struct sembuf {
    unsigned short sem_num;  // 信号量编号
    short sem_op;            // 操作值
    short sem_flg;           // 操作标志
};

int main() {
    key_t key = ftok(".", 'c');
    
    // 创建信号量集
    int semid = semget(key, 1, IPC_CREAT | 0666);
    if (semid == -1) {
        perror("semget");
        return 1;
    }
    
    // 初始化信号量值为1
    if (semctl(semid, 0, SETVAL, 1) == -1) {
        perror("semctl");
        return 1;
    }
    
    if (fork() == 0) {
        // 子进程
        struct sembuf sem_op;
        
        // P操作(等待)
        sem_op.sem_num = 0;
        sem_op.sem_op = -1;
        sem_op.sem_flg = 0;
        semop(semid, &sem_op, 1);
        
        // 临界区
        printf("Child in critical section\n");
        sleep(2);
        
        // V操作(信号)
        sem_op.sem_op = 1;
        semop(semid, &sem_op, 1);
    } else {
        // 父进程类似操作
        sleep(1);  // 稍后开始
        
        struct sembuf sem_op;
        sem_op.sem_num = 0;
        sem_op.sem_op = -1;
        sem_op.sem_flg = 0;
        semop(semid, &sem_op, 1);
        
        printf("Parent in critical section\n");
        
        sem_op.sem_op = 1;
        semop(semid, &sem_op, 1);
        
        wait(NULL);
        
        // 删除信号量
        semctl(semid, 0, IPC_RMID);
    }
    
    return 0;
}

5. 进程同步与控制

5.1 信号处理

cpp
#include <signal.h>

// 信号处理函数
void signal_handler(int signum) {
    printf("Received signal %d\n", signum);
    if (signum == SIGINT) {
        printf("Ctrl+C pressed, exiting gracefully...\n");
        exit(0);
    }
}

int main() {
    // 注册信号处理函数
    signal(SIGINT, signal_handler);
    signal(SIGUSR1, signal_handler);
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程
        sleep(2);
        kill(getppid(), SIGUSR1);  // 向父进程发送SIGUSR1信号
        exit(0);
    } else {
        // 父进程
        printf("Parent process PID: %d\n", getpid());
        printf("Press Ctrl+C to exit, or wait for child signal...\n");
        
        // 等待信号
        while (1) {
            pause();  // 暂停直到收到信号
        }
    }
    
    return 0;
}

5.2 进程组和会话

cpp
#include <unistd.h>

int main() {
    printf("Initial: PID=%d, PGID=%d, SID=%d\n", 
           getpid(), getpgrp(), getsid(0));
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程创建新的进程组
        setpgid(0, 0);  // 将自己设为进程组长
        printf("Child: PID=%d, PGID=%d, SID=%d\n", 
               getpid(), getpgrp(), getsid(0));
        
        // 创建新会话
        if (setsid() != -1) {
            printf("New session: PID=%d, PGID=%d, SID=%d\n", 
                   getpid(), getpgrp(), getsid(0));
        }
    } else {
        wait(NULL);
        printf("Parent: PID=%d, PGID=%d, SID=%d\n", 
               getpid(), getpgrp(), getsid(0));
    }
    
    return 0;
}

6. 进程状态与调度

6.1 进程状态

┌─────────┐    fork()    ┌─────────┐
│ 不存在   │ ─────────→  │ 创建    │
└─────────┘             └─────────┘


                        ┌─────────┐
                    ┌─→ │ 就绪    │ ←─┐
                    │   └─────────┘   │
                    │         │       │
                    │ 时间片  │ 调度  │ I/O完成
                    │ 用完    ↓       │ 或事件
                    │   ┌─────────┐   │
                    └── │ 运行    │ ──┘
                        └─────────┘

                              │ 等待I/O或事件

                        ┌─────────┐
                        │ 阻塞    │
                        └─────────┘

                              │ exit()

                        ┌─────────┐
                        │ 终止    │
                        └─────────┘

6.2 优先级和调度策略

cpp
#include <sched.h>
#include <sys/resource.h>

int main() {
    // 获取和设置进程优先级
    int priority = getpriority(PRIO_PROCESS, 0);
    printf("Current priority: %d\n", priority);
    
    // 设置更高优先级(需要权限)
    if (setpriority(PRIO_PROCESS, 0, priority - 1) == -1) {
        perror("setpriority");
    }
    
    // 获取调度策略
    int policy = sched_getscheduler(0);
    switch (policy) {
        case SCHED_OTHER:
            printf("Scheduling policy: SCHED_OTHER\n");
            break;
        case SCHED_FIFO:
            printf("Scheduling policy: SCHED_FIFO\n");
            break;
        case SCHED_RR:
            printf("Scheduling policy: SCHED_RR\n");
            break;
    }
    
    return 0;
}

7. 高级进程编程

7.1 守护进程(Daemon)

cpp
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

void create_daemon() {
    pid_t pid = fork();
    if (pid < 0) exit(1);      // fork失败
    if (pid > 0) exit(0);      // 父进程退出
    
    // 子进程继续
    if (setsid() < 0) exit(1); // 创建新会话
    
    // 忽略信号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);
    
    // 再次fork
    pid = fork();
    if (pid < 0) exit(1);
    if (pid > 0) exit(0);
    
    // 设置文件权限掩码
    umask(0);
    
    // 改变工作目录
    chdir("/");
    
    // 关闭文件描述符
    for (int fd = sysconf(_SC_OPEN_MAX); fd >= 0; fd--) {
        close(fd);
    }
    
    // 重定向标准输入输出到/dev/null
    open("/dev/null", O_RDWR);  // stdin
    dup(0);                     // stdout
    dup(0);                     // stderr
}

int main() {
    create_daemon();
    
    // 守护进程工作循环
    while (1) {
        // 执行守护进程任务
        sleep(30);
    }
    
    return 0;
}

7.2 进程池

cpp
#include <vector>
#include <queue>
#include <sys/socket.h>

class ProcessPool {
private:
    std::vector<pid_t> workers;
    int listen_fd;
    int worker_count;
    
public:
    ProcessPool(int count, int port) : worker_count(count) {
        // 创建监听socket
        listen_fd = create_listen_socket(port);
        
        // 创建工作进程
        for (int i = 0; i < worker_count; i++) {
            pid_t pid = fork();
            if (pid == 0) {
                // 子进程:工作进程
                worker_process();
                exit(0);
            } else if (pid > 0) {
                workers.push_back(pid);
            }
        }
    }
    
    void worker_process() {
        while (true) {
            int client_fd = accept(listen_fd, NULL, NULL);
            if (client_fd > 0) {
                handle_client(client_fd);
                close(client_fd);
            }
        }
    }
    
    void handle_client(int client_fd) {
        // 处理客户端请求
        char buffer[1024];
        int n = read(client_fd, buffer, sizeof(buffer));
        if (n > 0) {
            // 处理数据
            write(client_fd, "HTTP/1.1 200 OK\r\n\r\nHello\n", 26);
        }
    }
    
    ~ProcessPool() {
        // 终止所有工作进程
        for (pid_t pid : workers) {
            kill(pid, SIGTERM);
            waitpid(pid, NULL, 0);
        }
        close(listen_fd);
    }
};

7.3 僵尸进程和孤儿进程处理

cpp
#include <signal.h>

// 处理SIGCHLD信号,避免僵尸进程
void sigchld_handler(int sig) {
    pid_t pid;
    int status;
    
    // 回收所有可用的子进程
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        printf("Child process %d terminated\n", pid);
    }
}

int main() {
    // 注册SIGCHLD信号处理函数
    signal(SIGCHLD, sigchld_handler);
    
    // 创建多个子进程
    for (int i = 0; i < 5; i++) {
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程工作
            printf("Child %d working...\n", getpid());
            sleep(rand() % 10 + 1);  // 随机工作时间
            printf("Child %d finished\n", getpid());
            exit(i);
        }
    }
    
    // 父进程继续工作
    printf("Parent process continues...\n");
    sleep(15);
    
    return 0;
}

8. 常见面试题汇总

8.1 基础概念题

Q1: 进程和线程的区别是什么?

  • 内存空间:进程独立,线程共享
  • 创建开销:进程大,线程小
  • 通信方式:进程需要IPC,线程直接共享内存
  • 安全性:进程隔离性好,线程安全性差

Q2: fork()函数的返回值是什么?

  • 父进程中返回子进程的PID(>0)
  • 子进程中返回0
  • 失败时返回-1

Q3: 什么是写时复制(COW)?

  • fork()后父子进程共享物理内存页
  • 只有在写入时才复制页面
  • 节省内存,提高fork效率

8.2 进程通信题

Q4: IPC方式有哪些?各有什么特点?

IPC方式特点适用场景
管道简单,单向,有血缘关系父子进程通信
命名管道双向,无血缘关系限制任意进程通信
共享内存速度最快,需要同步大量数据交换
消息队列有格式,异步结构化数据传递
信号量用于同步进程同步控制
信号异步,简单进程控制

Q5: 管道的读写特性?

  • 当管道为空时,读操作阻塞
  • 当管道满时,写操作阻塞
  • 当写端关闭时,读操作返回0
  • 当读端关闭时,写操作收到SIGPIPE信号

8.3 进程控制题

Q6: 什么是僵尸进程?如何避免?

  • 子进程已终止但父进程未调用wait()回收资源
  • 避免方法:调用wait()、处理SIGCHLD信号、使用SIGIGN忽略

Q7: 什么是孤儿进程?

  • 父进程先于子进程终止
  • 子进程被init进程收养

Q8: exec族函数的作用?

  • 在当前进程中执行新程序
  • 替换当前进程的内存映像
  • 进程ID不变,但程序代码完全替换

8.4 高级应用题

Q9: 如何实现守护进程?

  1. fork()创建子进程,父进程退出
  2. 子进程调用setsid()创建新会话
  3. 再次fork(),避免获得控制终端
  4. 改变工作目录到根目录
  5. 重置文件权限掩码
  6. 关闭不需要的文件描述符

Q10: 多进程vs多线程如何选择?

  • CPU密集型:多进程(避免GIL限制)
  • I/O密集型:多线程(减少内存开销)
  • 需要容错性:多进程(进程隔离)
  • 需要共享状态:多线程(共享内存)

9. 实战编程题

9.1 生产者消费者模型

cpp
#include <sys/shm.h>
#include <sys/sem.h>
#include <iostream>

#define BUFFER_SIZE 10

struct shared_buffer {
    int buffer[BUFFER_SIZE];
    int in;   // 生产者位置
    int out;  // 消费者位置
    int count; // 当前元素数量
};

// 信号量操作函数
void sem_wait(int semid, int sem_num) {
    struct sembuf sb = {sem_num, -1, 0};
    semop(semid, &sb, 1);
}

void sem_signal(int semid, int sem_num) {
    struct sembuf sb = {sem_num, 1, 0};
    semop(semid, &sb, 1);
}

int main() {
    key_t shm_key = ftok(".", 's');
    key_t sem_key = ftok(".", 'e');
    
    // 创建共享内存
    int shmid = shmget(shm_key, sizeof(shared_buffer), IPC_CREAT | 0666);
    shared_buffer* buffer = (shared_buffer*)shmat(shmid, NULL, 0);
    
    // 初始化缓冲区
    buffer->in = buffer->out = buffer->count = 0;
    
    // 创建信号量集:0-mutex, 1-empty, 2-full
    int semid = semget(sem_key, 3, IPC_CREAT | 0666);
    semctl(semid, 0, SETVAL, 1);           // mutex = 1
    semctl(semid, 1, SETVAL, BUFFER_SIZE); // empty = BUFFER_SIZE
    semctl(semid, 2, SETVAL, 0);           // full = 0
    
    if (fork() == 0) {
        // 生产者进程
        for (int i = 0; i < 20; i++) {
            sem_wait(semid, 1);  // wait(empty)
            sem_wait(semid, 0);  // wait(mutex)
            
            // 生产
            buffer->buffer[buffer->in] = i;
            buffer->in = (buffer->in + 1) % BUFFER_SIZE;
            buffer->count++;
            printf("Produced: %d, count: %d\n", i, buffer->count);
            
            sem_signal(semid, 0);  // signal(mutex)
            sem_signal(semid, 2);  // signal(full)
            
            usleep(100000);  // 0.1秒
        }
    } else {
        // 消费者进程
        for (int i = 0; i < 20; i++) {
            sem_wait(semid, 2);  // wait(full)
            sem_wait(semid, 0);  // wait(mutex)
            
            // 消费
            int item = buffer->buffer[buffer->out];
            buffer->out = (buffer->out + 1) % BUFFER_SIZE;
            buffer->count--;
            printf("Consumed: %d, count: %d\n", item, buffer->count);
            
            sem_signal(semid, 0);  // signal(mutex)
            sem_signal(semid, 1);  // signal(empty)
            
            usleep(150000);  // 0.15秒
        }
        
        wait(NULL);
        
        // 清理资源
        shmdt(buffer);
        shmctl(shmid, IPC_RMID, NULL);
        semctl(semid, 0, IPC_RMID);
    }
    
    return 0;
}

9.2 简单的Shell实现

cpp
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>

class SimpleShell {
private:
    std::vector<std::string> parse_command(const std::string& input) {
        std::vector<std::string> tokens;
        std::istringstream iss(input);
        std::string token;
        
        while (iss >> token) {
            tokens.push_back(token);
        }
        
        return tokens;
    }
    
    char** vector_to_argv(const std::vector<std::string>& tokens) {
        char** argv = new char*[tokens.size() + 1];
        for (size_t i = 0; i < tokens.size(); i++) {
            argv[i] = const_cast<char*>(tokens[i].c_str());
        }
        argv[tokens.size()] = nullptr;
        return argv;
    }
    
public:
    void run() {
        std::string input;
        
        while (true) {
            std::cout << "simple_shell> ";
            std::getline(std::cin, input);
            
            if (input.empty()) continue;
            if (input == "exit") break;
            
            // 内置命令
            if (input.substr(0, 2) == "cd") {
                handle_cd(input);
                continue;
            }
            
            // 执行外部命令
            execute_command(input);
        }
    }
    
private:
    void handle_cd(const std::string& input) {
        auto tokens = parse_command(input);
        if (tokens.size() < 2) {
            chdir(getenv("HOME"));
        } else {
            if (chdir(tokens[1].c_str()) != 0) {
                perror("cd");
            }
        }
    }
    
    void execute_command(const std::string& input) {
        auto tokens = parse_command(input);
        if (tokens.empty()) return;
        
        char** argv = vector_to_argv(tokens);
        
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程
            if (execvp(argv[0], argv) == -1) {
                perror("Command not found");
                exit(1);
            }
        } else if (pid > 0) {
            // 父进程
            int status;
            waitpid(pid, &status, 0);
        } else {
            perror("fork");
        }
        
        delete[] argv;
    }
};

int main() {
    SimpleShell shell;
    shell.run();
    return 0;
}

9.3 多进程排序

cpp
#include <algorithm>
#include <vector>
#include <sys/shm.h>

#define ARRAY_SIZE 1000
#define PROCESS_COUNT 4

struct shared_data {
    int array[ARRAY_SIZE];
    int sorted_parts[PROCESS_COUNT][ARRAY_SIZE / PROCESS_COUNT];
    bool part_ready[PROCESS_COUNT];
};

void merge_arrays(int* result, int* arr1, int size1, int* arr2, int size2) {
    int i = 0, j = 0, k = 0;
    
    while (i < size1 && j < size2) {
        if (arr1[i] <= arr2[j]) {
            result[k++] = arr1[i++];
        } else {
            result[k++] = arr2[j++];
        }
    }
    
    while (i < size1) result[k++] = arr1[i++];
    while (j < size2) result[k++] = arr2[j++];
}

int main() {
    // 创建共享内存
    key_t key = ftok(".", 'a');
    int shmid = shmget(key, sizeof(shared_data), IPC_CREAT | 0666);
    shared_data* data = (shared_data*)shmat(shmid, NULL, 0);
    
    // 初始化数组
    srand(time(NULL));
    for (int i = 0; i < ARRAY_SIZE; i++) {
        data->array[i] = rand() % 1000;
    }
    
    // 初始化标志
    for (int i = 0; i < PROCESS_COUNT; i++) {
        data->part_ready[i] = false;
    }
    
    printf("Original array (first 20 elements): ");
    for (int i = 0; i < 20; i++) {
        printf("%d ", data->array[i]);
    }
    printf("\n");
    
    // 创建子进程进行并行排序
    for (int i = 0; i < PROCESS_COUNT; i++) {
        if (fork() == 0) {
            // 子进程:排序指定部分
            int start = i * (ARRAY_SIZE / PROCESS_COUNT);
            int end = (i + 1) * (ARRAY_SIZE / PROCESS_COUNT);
            int size = end - start;
            
            // 复制到本地数组进行排序
            int local_array[size];
            for (int j = 0; j < size; j++) {
                local_array[j] = data->array[start + j];
            }
            
            // 排序
            std::sort(local_array, local_array + size);
            
            // 复制回共享内存
            for (int j = 0; j < size; j++) {
                data->sorted_parts[i][j] = local_array[j];
            }
            
            data->part_ready[i] = true;
            printf("Process %d finished sorting\n", i);
            exit(0);
        }
    }
    
    // 等待所有子进程完成
    for (int i = 0; i < PROCESS_COUNT; i++) {
        wait(NULL);
    }
    
    // 合并排序结果
    int part_size = ARRAY_SIZE / PROCESS_COUNT;
    std::vector<int> result(ARRAY_SIZE);
    
    // 简单的合并算法(可以优化为k路归并)
    int* current_result = new int[ARRAY_SIZE];
    int current_size = part_size;
    
    // 复制第一部分
    for (int i = 0; i < part_size; i++) {
        current_result[i] = data->sorted_parts[0][i];
    }
    
    // 逐步合并其他部分
    for (int i = 1; i < PROCESS_COUNT; i++) {
        int* temp = new int[current_size + part_size];
        merge_arrays(temp, current_result, current_size, 
                    data->sorted_parts[i], part_size);
        
        delete[] current_result;
        current_result = temp;
        current_size += part_size;
    }
    
    printf("Sorted array (first 20 elements): ");
    for (int i = 0; i < 20; i++) {
        printf("%d ", current_result[i]);
    }
    printf("\n");
    
    // 验证排序结果
    bool is_sorted = true;
    for (int i = 1; i < ARRAY_SIZE; i++) {
        if (current_result[i] < current_result[i-1]) {
            is_sorted = false;
            break;
        }
    }
    
    printf("Array is %s\n", is_sorted ? "sorted correctly" : "NOT sorted");
    
    // 清理资源
    delete[] current_result;
    shmdt(data);
    shmctl(shmid, IPC_RMID, NULL);
    
    return 0;
}

总结

C++进程编程涉及操作系统的核心概念,掌握这些知识点对于系统编程和面试都至关重要。重点要理解:

  1. 进程生命周期:创建、执行、终止的完整过程
  2. 进程间通信:各种IPC机制的特点和适用场景
  3. 进程同步:如何协调多个进程的执行
  4. 资源管理:避免资源泄漏和僵尸进程
  5. 实际应用:守护进程、进程池等高级技术

在面试中,不仅要掌握理论知识,还要能够编写实际的代码来解决问题。建议多做练习,理解每个系统调用的工作原理和使用场景。