网络编程:多进程

46 阅读3分钟

进程:占用内存空间的正在运行的程序

PID:进程ID, linux下用ps au查看

fork 函数

//成功时返回进程ID,失败返回-1
pid_t fork(void);

当前运行的进程调用fork(),复制正在运行的,调用fork函数的进程,内存相同的内存空间也被复制,包括各种变量值,但是,调用fork之后的程序流要根据fork函数的返回值加以区分。

  • 父进程:fork函数返回子进程ID
  • 子进程:fork函数返回0

示例程序

int gval = 10;
int main(int argc, char *argv[]) {
    pid_t pid;
    int lval = 20;
    gval++; //11
    lval += 5; //25

    pid = fork(); //调用后程序已经复制了一份,创建了子进程
    // 注意:fork函数会返回两次,一次是父进程返回,一次是子进程返回
    // 父进程:返回值是子进程ID
    // 子进程:返回值是 0
    if (pid == 0) { //子进程
        gval += 2; //13
        lval += 2; //27
    } else { //父进程
        gval -= 2; //9
        lval -= 2; //23
    }
    if (pid == 0) {
        printf("child proc: [%d %d] \n", gval, lval);
    } else {
        printf("pid: %d, Parent proc: [%d %d] \n", pid, gval, lval);
    }
    return 0;
}

结果如下:

F1182F10-0720-4BC3-82F9-7973D482F362.png

僵尸进程

产生原因

调用fork函数产生的子进程的终止方式

  • 传递参数并调用exit函数
  • main函数中执行return语句并返回值

向exit传递的参数值和main函数return返回值最终都会传递给操作系统,但操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程。处在这种状态下的进程就是僵尸进程。

僵尸进程何时销毁?

向创建子进程的父进程传递子进程的exit参数值或return语句的返回值

如何传递这些值?

操作系统不会主动传递,只有父进程主动发起请求(函数调用)时,操作系统才会传递该值。

销毁僵尸进程

wait函数

调用wait函数,调用此函数时如果已有子进程终止,那么子进程终止时传递的返回值将保存在该函数参数所指内存空间。

#include <sys/wait.h>
// 成功时返回终止的子进程的pid,失败时返回-1
pit_t wait(int *statloc);
  • WIFEEXITED:子进程正常终止时返回true
  • WEXITSTATUS:返回子进程的返回值 调用代码如下:
if(WIFEEXITED(status)) {
  puts("正常终止");
  printf("child pass num :%d", WEXITSTATUS(status));
}

注意:调用wait时,如果没有已终止的子进程,那么程序将阻塞,直到有子进程终止。

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    int status;
    pid_t pid = fork();
    if (pid == 0) {
        return 3;
    } else {
        printf("child pid : %d \n", pid);
        pid = fork();
        if (pid == 0) {
            exit(7);
        } else {
            printf("child pid : %d \n", pid);
            wait(&status);
            if (WIFEXITED(status)) {
                printf("child send one: %d \n", WEXITSTATUS(status));
            }
            wait(&status);
            if (WIFEXITED(status)) {
                printf("child send two: %d \n", WEXITSTATUS(status));
            }
            sleep(30);
        }
    }
    return 0;
}

971E6085-3EAB-48E5-BF4C-FE8E7863CDB0.png

waitpid 函数

可以防止阻塞

// 成功时返回终止的子进程的ID,失败返回-1
pid_t waitpid(pid_t pid, int *statloc, int options);
pid: 等待终止的子进程的ID,若传递-1,可以等待任意子进程终止
statloc:同wait函数的statloc
options:一般传递 WHOHANG
int main(int argc, char *argv[]) {
    int staus;
    pid_t pid = fork();
    if (pid == 0) {
        sleep(15);
        return 24;
    } else {
        while (!waitpid(-1, &staus, WNOHANG)) {
            sleep(3);
            puts("sleep 3sec");
        }
        if (WIFEXITED(staus)) {
            printf("child send  %d \n", WEXITSTATUS(staus));
        }
    }
    return 0;
}

➜  hello_server gcc waitpid.c -o waitpid
➜  hello_server ./waitpid
sleep 3sec
sleep 3sec
sleep 3sec
sleep 3sec
sleep 3sec
child send  24