MST

星途 面试题库

面试题:C语言prefork模型中的进程管理与资源分配

在C语言实现的prefork多进程服务器模型里,假设系统资源有限,如何合理地分配文件描述符、内存等资源给各个子进程,以避免资源耗尽或冲突?请详细阐述实现思路和关键代码片段。
42.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 资源分配策略
    • 文件描述符
      • 在父进程中打开需要共享的文件描述符(如监听套接字),然后通过fork传递给子进程。子进程继承父进程的文件描述符表,这样可以保证多个子进程共享这些文件描述符。对于每个子进程需要独占的文件描述符(如日志文件),在子进程内单独打开。
      • 使用fcntl函数设置文件描述符为FD_CLOEXEC标志,这样在执行exec系列函数时,该文件描述符会自动关闭,避免资源泄露。
    • 内存
      • 对于需要共享的内存区域,使用mmap创建共享内存段。父进程创建共享内存后,通过fork,子进程可以继承对该共享内存的访问权。
      • 对于子进程独有的内存需求,使用malloc等函数在子进程内分配。为了避免内存碎片,可以采用内存池等技术来管理内存。
  2. 资源管理与避免冲突
    • 文件描述符
      • 维护一个文件描述符使用表,记录哪些文件描述符已经被分配使用,哪些可以分配。当一个子进程退出时,将其使用的文件描述符标记为可用。
      • 使用selectpollepoll等多路复用技术管理文件描述符,提高文件描述符的使用效率,减少资源占用。
    • 内存
      • 对于共享内存,使用信号量(如semgetsemop等函数)或互斥锁(pthread_mutex_t)进行同步访问,避免多个子进程同时修改共享内存导致数据冲突。
      • 定期检查子进程的内存使用情况,当发现某个子进程内存使用过高时,可以采取如终止该子进程并重新启动等措施。

关键代码片段

  1. 文件描述符相关
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>

int main() {
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
        perror("socket error");
        exit(1);
    }
    // 设置监听套接字为FD_CLOEXEC
    int flags = fcntl(listenfd, F_GETFD, 0);
    fcntl(listenfd, F_SETFD, flags | FD_CLOEXEC);

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(8080);

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind error");
        close(listenfd);
        exit(1);
    }

    if (listen(listenfd, 10) < 0) {
        perror("listen error");
        close(listenfd);
        exit(1);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        close(listenfd);
        exit(1);
    } else if (pid == 0) {
        // 子进程
        int connfd = accept(listenfd, NULL, NULL);
        if (connfd < 0) {
            perror("accept error");
            close(listenfd);
            exit(1);
        }
        // 子进程处理连接
        close(listenfd); // 子进程不再需要监听套接字
        // 处理业务逻辑
        close(connfd);
        exit(0);
    } else {
        // 父进程
        close(listenfd); // 父进程也可以关闭监听套接字,由子进程处理连接
        // 父进程继续其他逻辑
        wait(NULL);
    }
    return 0;
}
  1. 共享内存相关
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <semaphore.h>

#define SHM_SIZE 1024

int main() {
    int shm_fd;
    void *ptr;
    sem_t *sem;

    // 创建共享内存对象
    shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    if (shm_fd < 0) {
        perror("shm_open error");
        exit(1);
    }

    // 设置共享内存大小
    if (ftruncate(shm_fd, SHM_SIZE) == -1) {
        perror("ftruncate error");
        close(shm_fd);
        shm_unlink("/shared_memory");
        exit(1);
    }

    // 映射共享内存到进程地址空间
    ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap error");
        close(shm_fd);
        shm_unlink("/shared_memory");
        exit(1);
    }

    // 创建信号量
    sem = sem_open("/semaphore", O_CREAT, 0666, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open error");
        munmap(ptr, SHM_SIZE);
        close(shm_fd);
        shm_unlink("/shared_memory");
        exit(1);
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        sem_close(sem);
        sem_unlink("/semaphore");
        munmap(ptr, SHM_SIZE);
        close(shm_fd);
        shm_unlink("/shared_memory");
        exit(1);
    } else if (pid == 0) {
        // 子进程
        sem_wait(sem);
        // 使用共享内存
        sprintf((char *)ptr, "Hello from child");
        sem_post(sem);
        sem_close(sem);
        munmap(ptr, SHM_SIZE);
        close(shm_fd);
        exit(0);
    } else {
        // 父进程
        wait(NULL);
        sem_wait(sem);
        printf("Data from child: %s\n", (char *)ptr);
        sem_post(sem);
        sem_close(sem);
        sem_unlink("/semaphore");
        munmap(ptr, SHM_SIZE);
        close(shm_fd);
        shm_unlink("/shared_memory");
    }
    return 0;
}