MST

星途 面试题库

面试题:C语言多进程创建中的资源管理问题

在Linux下用C语言创建多个进程后,不同进程可能会共享一些资源。请详细说明如何避免资源竞争问题,以及在进程创建过程中,如何合理分配和释放资源,比如文件描述符、内存等。
25.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

避免资源竞争问题

  1. 使用锁机制
    • 互斥锁(Mutex):在C语言中,使用POSIX线程库(通常在Linux下开发多线程程序会用到,虽然这里说的是多进程,但原理类似)可以创建互斥锁。在访问共享资源前,进程通过pthread_mutex_lock函数获取互斥锁,访问完成后通过pthread_mutex_unlock函数释放互斥锁。例如:
    #include <pthread.h>
    #include <stdio.h>
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    int shared_variable = 0;
    void *thread_function(void *arg) {
        pthread_mutex_lock(&mutex);
        shared_variable++;
        printf("Shared variable in thread: %d\n", shared_variable);
        pthread_mutex_unlock(&mutex);
        return NULL;
    }
    int main() {
        pthread_t thread;
        pthread_create(&thread, NULL, thread_function, NULL);
        pthread_join(thread, NULL);
        pthread_mutex_destroy(&mutex);
        return 0;
    }
    
    • 信号量(Semaphore):对于进程间同步,可以使用semaphore。通过sem_open打开信号量,sem_wait获取信号量(相当于获取锁),sem_post释放信号量(相当于释放锁)。例如:
    #include <fcntl.h>
    #include <semaphore.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    int main() {
        sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 1);
        pid_t pid = fork();
        if (pid == 0) {
            sem_wait(sem);
            // 访问共享资源
            sem_post(sem);
            sem_close(sem);
            exit(0);
        } else if (pid > 0) {
            sem_wait(sem);
            // 访问共享资源
            sem_post(sem);
            wait(NULL);
            sem_close(sem);
            sem_unlink("/mysem");
        } else {
            perror("fork");
            return 1;
        }
        return 0;
    }
    
  2. 使用原子操作:对于简单的共享变量,可以使用原子操作。在C11标准中,<stdatomic.h>头文件提供了原子类型和操作函数。例如:
    #include <stdio.h>
    #include <stdatomic.h>
    #include <pthread.h>
    atomic_int shared_variable = ATOMIC_VAR_INIT(0);
    void *thread_function(void *arg) {
        atomic_fetch_add(&shared_variable, 1);
        printf("Shared variable in thread: %d\n", atomic_load(&shared_variable));
        return NULL;
    }
    int main() {
        pthread_t thread;
        pthread_create(&thread, NULL, thread_function, NULL);
        pthread_join(thread, NULL);
        return 0;
    }
    
  3. 消息队列或管道:通过消息队列(如msggetmsgsndmsgrcv等函数)或管道(pipe函数)进行进程间通信,将共享资源的访问通过消息传递来控制,避免直接竞争。例如使用管道:
    #include <stdio.h>
    #include <unistd.h>
    int main() {
        int pipefd[2];
        if (pipe(pipefd) == -1) {
            perror("pipe");
            return 1;
        }
        pid_t pid = fork();
        if (pid == 0) {
            close(pipefd[1]);
            // 从管道读数据,这里模拟获取访问共享资源的许可
            close(pipefd[0]);
            exit(0);
        } else if (pid > 0) {
            close(pipefd[0]);
            // 向管道写数据,这里模拟允许访问共享资源
            close(pipefd[1]);
            wait(NULL);
        } else {
            perror("fork");
            return 1;
        }
        return 0;
    }
    

进程创建过程中资源分配与释放

  1. 文件描述符
    • 分配:在父进程中打开文件得到文件描述符,然后通过fork创建子进程时,子进程会继承父进程的文件描述符表。例如:
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    int main() {
        int fd = open("test.txt", O_RDONLY);
        if (fd == -1) {
            perror("open");
            return 1;
        }
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程使用继承的文件描述符fd
            close(fd);
            exit(0);
        } else if (pid > 0) {
            close(fd);
            wait(NULL);
        } else {
            perror("fork");
            return 1;
        }
        return 0;
    }
    
    • 释放:在进程使用完文件描述符后,通过close函数关闭文件描述符。如上述代码中,父进程和子进程在使用完fd后都调用close(fd)
  2. 内存
    • 分配:可以使用malloccalloc等函数在堆上分配内存。在fork后,子进程会复制父进程的内存空间(写时复制机制)。例如:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main() {
        int *arr = (int *)malloc(10 * sizeof(int));
        if (arr == NULL) {
            perror("malloc");
            return 1;
        }
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程使用分配的内存arr
            free(arr);
            exit(0);
        } else if (pid > 0) {
            free(arr);
            wait(NULL);
        } else {
            perror("fork");
            return 1;
        }
        return 0;
    }
    
    • 释放:使用free函数释放通过malloccalloc等分配的内存,如上述代码中,父进程和子进程在使用完arr后都调用free(arr)。对于共享内存(如使用shmat映射的共享内存),使用完后通过shmdt分离,最后使用shmctl删除共享内存段。例如:
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <unistd.h>
    int main() {
        key_t key = ftok(".", 'a');
        int shmid = shmget(key, 1024, IPC_CREAT | 0666);
        if (shmid == -1) {
            perror("shmget");
            return 1;
        }
        char *shmaddr = (char *)shmat(shmid, NULL, 0);
        if (shmaddr == (void *)-1) {
            perror("shmat");
            return 1;
        }
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程使用共享内存
            shmdt(shmaddr);
            exit(0);
        } else if (pid > 0) {
            shmdt(shmaddr);
            wait(NULL);
            shmctl(shmid, IPC_RMID, NULL);
        } else {
            perror("fork");
            return 1;
        }
        return 0;
    }