TCP/IP 网络编程 (抄书笔记 3) – 僵尸进程和多任务并发服务器
僵尸进程的产生
- 子进程先退出, 父进程没有退出 ==> 僵尸进程
- 父进程先退出, 子进程没有退出 ==> 子进程被 0 号进程回收, 不会产生僵尸进程
pid_t pid = fork(); if (pid == 0) { // child printf("child: %d\n", getpid()); printf("child exited\n"); exit(0); } else { printf("parent: %d\n", getpid()); sleep(30); }
避免僵尸进程
子进程退出的时候, 通知父进程:
父进程调用
int status; wait(&status); WIFEXITED(status); WEXITSTATUS(status);
这样就可以等待子进程的退出, 并获取他的返回值
如果父进程先调用了 exit 等命令自己退出, 那么子进程会被 0 号进程回收
调用 wait 的话, 父进程会阻塞自己, 直到子进程退出, 如果不想阻塞可以使用
waitpid(-1, &status, WNOHANG);
信号
void keyboard(int sig) { if (sig == SIGINT) { printf("Ctrl + C pressed\n"); } } void timeout(int sig) { if (sig == SIGALRM) { printf("time out ...\n"); exit(0); } } int main() { ... signal(SIGINT, keyboard); signal(SIGALRM, timeout); alarm(3); // 时间结束, 会产生一个信号, 然后自动调用 timeoutu 函数 ... }
使用 sigaction, signal 过时
struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = timeout; sigaction(SIGALRM, &act, 0);
利用信号消灭僵尸进程
原理: 只要做到子进程退出的时候告诉父进程一声, "我要退出了" 就可以消灭僵尸进程, 也就是调用 wait/waitpid
, 子进程退出的信号是 SIGCHLD
多任务的并发服务器
void read_childproc(int sig) { int status; if (sig == SIGCHLD) { if (WIFEXITED(status)) { printf("child quit ... %d\n", WEXITSTATUS(status)); } } } int main(int argc, char *argv[]) { int server_sock; int client_sock; struct sockaddr_in server_addr; struct sockaddr_in client_addr; // socket server_sock = socket(AF_INET, SOCK_STREAM, 0); // bind memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(atoi(argv[1])); bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); // listen listen(server_sock, 5); // sigaction struct sigaction act; act.sa_handler = read_childproc; char buf[BUF_SIZE]; int str_len; pid_t pid; while (1) { socklen_t client_size = sizeof(client_addr); client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_size); pid = fork(); if (pid == 0) { // 子进程复制, 这时候打开了 4 个 fd, 需要子进程关一个, 父进程关一个, 看下图 close(server_sock); while ((str_len = read(client_sock, buf, BUF_SIZE)) != 0) { write(client_sock, buf, str_len); } close(server_sock); printf("client disconnected ...\n"); } else { // 父进程将 client 交给子进程处理, 自己继续监听 server fd close(client_sock); } } close(server_sock); close(client_sock); return 0; }