首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux 下網絡編程 Server 端進階:使用 fork()、waitpid() 與 sigaction() 處理僵屍進程

     編輯:關於C語言

在前一篇基礎服務器代碼基礎上,本文加入 Linux 系統特有的進程管理機制,解決僵屍進程問題。

核心問題

fork() 只有 Unix/Linux 才有(無線程概念,只有子進程)。使用 fork() 必須配合 waitpid() 回收子進程,否則會產生僵屍進程。

waitpid() 原型

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
  • pid = -1:等待任意子進程。
  • status = NULL:不關心退出狀態。
  • options = WNOHANG:非阻塞模式,無子進程退出時立即返回。

sigaction() 設置信號處理

為了只在子進程退出時調用 waitpid(),需使用 sigaction() 捕獲 SIGCHLD 信號。

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum = SIGCHLD:子進程停止或退出時觸發。
  • act:指向 sigaction 結構,設置 sa_handler、sa_mask、sa_flags。

完整服務器代碼(支持 IPv4/IPv6、僵屍進程回收)

/** 
 * Version: 0.2
 * Description: Add signal handling for zombie process reaping
 */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>   /* exit */
#include <unistd.h>   /* fork */

#define SERVPORT "2349"

void sigchld_handler() {
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main(int argc, char *argv[])
{
    struct addrinfo hints, *res;
    int status;
    int sockfd;
    int connFd;
    struct sockaddr_storage clientAddr; /* 支持 IPv4 和 IPv6 */
    struct sigaction sa;
    int sendSta;
    char *msg;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s \n", argv[0]);
        return 1;
    }

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    status = getaddrinfo(NULL, SERVPORT, &hints, &res);
    if (status != 0) {
        fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
        return 2;
    }

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd == -1) {
        perror("socket");
        return 3;
    }

    if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
        perror("bind");
        return 4;
    }

    if (listen(sockfd, 5) == -1) {
        perror("listen");
        return 5;
    }

    printf("======== Please Wait Client =========\n");

    /* 設置信號處理 */
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;  /* 系統調用被信號中斷後自動重啟 */

    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(3);
    }

    while (1) {
        char ipstr[INET_ADDRSTRLEN];
        void *addr;
        int len = sizeof(clientAddr);
        connFd = accept(sockfd, (struct sockaddr *)&clientAddr, &len);
        if (connFd == -1) {
            perror("accept");
            continue;
        }

        /* 顯示客戶端 IP (假設 IPv4) */
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)&clientAddr;
        addr = &(ipv4->sin_addr);
        inet_ntop(AF_INET, addr, ipstr, sizeof(ipstr));
        printf("Client: %s\n", ipstr);

        if (!fork()) {  /* 子進程 */
            close(sockfd);  /* 子進程不需要監聽套接字 */
            msg = "The server simply displays a message!";
            sendSta = send(connFd, msg, strlen(msg), 0);
            if (sendSta == -1)
                fprintf(stderr, "send fail!\n");
            close(connFd);
            exit(0);
        }

        close(connFd);  /* 父進程關閉已連接套接字 */
    }

    close(sockfd);
    return 0;
}

摘自 xiaobin_HLJ80 的專欄

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved