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