Skip to the content.

homepage

Linux 进程操作

进程创建 pid_t fork(void);

系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。

#include <sys/types.h>
#include <unistd.h>
/*
  返回值:
    - 成功:
        子进程中返回      0
        父进程中返回      子进程 ID 
    - 失败:
        返回 -1
  失败的两个主要原因:
    1. 当前系统的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN
    2. 系统内存不足,这时 errno 的值被设置为 ENOMEM
*/
pid_t fork(void);
int main(){
  pid_t pid = fork();
  if(pid > 0){
    //父进程
    printf("i am parent process, pid : %d, ppid : %d\n",getpid(),pid);
  }else if(pid == 0){
    //子进程
    printf("i am child process, pid : %d, ppid: %d\n", getpid(), getppid());
  }
  
  for(int i = 0; i < 3; ++i)
    cout << i <<" "<< getpid() << endl;
  return 0;
}

父子进程虚地址空间

image

子进程栈中也复制了父进程的栈,但两者对各自栈里的变量修改,是不会对彼此产生影响的

实际上,更准确来说,Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。

父子进程之间的关系:

GDB多进程调试

进程退出

exit 与 _exit


#include <stdlib.h>
void exit(int status);

#include <unistd.h>
void _exit(int status);

status参数:是进程退出时的一个状态信息。父进程回收子进程资源的时候可以获取到。

孤儿进程 Orphan

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {

        printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());

    } else if(pid == 0) {
        sleep(1);   //父进程已经结束
        // 当前是子进程 变成孤儿进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
       
    }

    // for循环
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
    }

    return 0;
}

僵尸进程 Zombie

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {
        // 父进程持续不结束,导致子进程变成僵尸进程
        while(1) {
            printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
            sleep(1);
        }

    } else if(pid == 0) {
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
        // 子进程结束
    }

    // for循环
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
    }

    return 0;
}

进程回收

pid_t wait(int * wstatus)

功能:阻塞等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收子进程的资源。

调用wait函数的进程会被挂起(阻塞),直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒(相当于继续往下执行) :

#include <sys/types.h>
#include <sys/wait.h>
/*
  参数:int *wstatus
      用来接收子进程退出时的状态信息,传入的是一个int类型的地址,传出参数。exit(int);
  返回值:
      - 成功:返回被回收的子进程的id
      - 失败:-1 (所有的子进程都结束,调用函数失败) 
*/
pid_t wait(int *wstatus);
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {

    // 有一个父进程,创建5个子进程(兄弟)
    pid_t pid;

    // 创建5个子进程
    for(int i = 0; i < 5; i++) {
        pid = fork();
        if(pid == 0) {//子进程退出循环,不要继续不要创建
            break;
        }
    }

    if(pid > 0) {
        // 父进程
        while(1) {
            printf("parent, pid = %d\n", getpid());

            // int ret = wait(NULL); //不需要获得子进程退出状态
            
            int st;
            int ret = wait(&st);  // ret == childId 或者 -1

            if(ret == -1) {
                break;
            }
            //WIFEXITED() 是否正常退出
            if(WIFEXITED(st)) {
                // 是不是正常退出
                printf("退出的状态码:%d\n", WEXITSTATUS(st)); 
                //WEXITSTATUE()获取状态码7
            }
            //WIFSIGNALED()是否异常终止
            if(WIFSIGNALED(st)) {
                // 是不是异常终止
                printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                //WTERMSIG()获取终止子进程的状态码
            }

            printf("child die, pid = %d\n", ret);

            sleep(1);
        }

    } else if (pid == 0){
        // 子进程
         while(1) {
            printf("child, pid = %d\n",getpid());    
            sleep(1);       
         }

        exit(0);
    }

    return 0; // exit(0)
}

退出信息相关宏函数

pid_t waitpid(pid_t pid, int * wstatus, int options)

功能:回收指定进程号的子进程,可以设置是否阻塞

#include <sys/types.h>
#include <sys/wait.h>
/*
    
    参数:
        - pid:
            pid > 0 : 某个子进程的pid
            pid = 0 : 回收当前进程组的所有子进程 (可能会将子进程给别的进程组,此时就不会释放给出去的)
            pid = -1 : 回收所有的子进程,相当于 wait()  (最常用)(即使将子进程给别的组,仍然会释放)
            pid < -1 : 某个进程组的组id的绝对值,回收指定进程组中的子进程
        - options:设置阻塞或者非阻塞
            0 : 阻塞
            WNOHANG : 非阻塞
        - 返回值:
            > 0 : 返回子进程的id
            = 0 : options=WNOHANG, 表示还有子进程或者
            = -1 :错误,或者没有子进程了
*/
pid_t waitpid(pid_t pid, int *wstatus, int options);
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {

    // 有一个父进程,创建5个子进程(兄弟)
    pid_t pid;

    // 创建5个子进程
    for(int i = 0; i < 5; i++) {
        pid = fork();
        if(pid == 0) {
            break;
        }
    }
    if(pid > 0) {
        // 父进程
        while(1) {
            printf("parent, pid = %d\n", getpid());
            sleep(1);

            int st;
            // int ret = waitpid(-1, &st, 0); 等价于wait(&st)
            int ret = waitpid(-1, &st, WNOHANG);
            // 没有子进程
            if(ret == -1) {
                break;
            // 此次没有需要释放的子进程,但仍存在子进程
            } else if(ret == 0) {
                // 说明还有子进程存在
                continue;
            // 释放了子进程
            } else if(ret > 0) {

                if(WIFEXITED(st)) {
                    // 是不是正常退出
                    printf("退出的状态码:%d\n", WEXITSTATUS(st));
                }
                if(WIFSIGNALED(st)) {
                    // 是不是异常终止
                    printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                }

                printf("child die, pid = %d\n", ret);
            }
           
        }

    } else if (pid == 0){
        // 子进程
         while(1) {
            printf("child, pid = %d\n",getpid());    
            sleep(1);       
         }
        exit(0);
    }

    return 0; 
}