面试题答案
一键面试避免资源竞争问题
- 使用锁机制
- 互斥锁(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; }
- 互斥锁(Mutex):在C语言中,使用POSIX线程库(通常在Linux下开发多线程程序会用到,虽然这里说的是多进程,但原理类似)可以创建互斥锁。在访问共享资源前,进程通过
- 使用原子操作:对于简单的共享变量,可以使用原子操作。在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; }
- 消息队列或管道:通过消息队列(如
msgget
、msgsnd
、msgrcv
等函数)或管道(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; }
进程创建过程中资源分配与释放
- 文件描述符
- 分配:在父进程中打开文件得到文件描述符,然后通过
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)
。
- 分配:在父进程中打开文件得到文件描述符,然后通过
- 内存
- 分配:可以使用
malloc
、calloc
等函数在堆上分配内存。在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
函数释放通过malloc
、calloc
等分配的内存,如上述代码中,父进程和子进程在使用完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; }
- 分配:可以使用