Linux进程控制开发基础——Linux进程状态/fork()/waitpid()/wait()详解

第一部分 Linux进程概述

       在Linux下进程被创建后可以有5种状态。
在这里插入图片描述
在这里插入图片描述

关于Linux进程状态不再赘述,参考下面这2个博客链接即可

不过有一些值得补充的说明:

  • 很多操作系统教科书将正在CPU上执行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在linux下统一为 TASK_RUNNING状态。

  • 进程状态变迁
           进程自创建以后,状态可能发生一系列的变化,直到进程退出。而尽管进程状态有好几种,但是进程状态的变迁却只有两个方向——从TASK_RUNNING状态变为非TASK_RUNNING状态、或者从非TASK_RUNNING状态变为TASK_RUNNING状态
    (1)也就是说,如果给一个TASK_INTERRUPTIBLE状态的进程发送SIGKILL信号,这个进程将先被唤醒(进入TASK_RUNNING状态),然后再响应SIGKILL信号而退出(变为TASK_DEAD状态),并不会从TASK_INTERRUPTIBLE状态直接退出。进程从非TASK_RUNNING状态变为TASK_RUNNING状态,是由别的进程(也可能是中断处理程序)执行唤醒操作来实现的。执行唤醒的进程设置被唤醒进程的状态为TASK_RUNNING,然后将其task_struct结构加入到某个CPU的可执行队列中。于是被唤醒的进程将有机会被调度执行。
    (2)而进程从TASK_RUNNING状态变为非TASK_RUNNING状态,则有两种途径:
           a. 响应信号而进入TASK_STOPED状态、或TASK_DEAD状态;
           b. 执行系统调用主动进入TASK_INTERRUPTIBLE状态 (如nanosleep系统调用)、或TASK_DEAD状态(如exit系统调用);或由于执行系统调用需要的资源得不到满足,而进入TASK_INTERRUPTIBLE状态或TASK_UNINTERRUPTIBLE状态(如select系统调用)。显然,这两种情况都只能发生在进程正在CPU上执行的情况下。

  • 留意SIGSTOP、SIGCONT、SIGKILL信号对于进程状态的影响。另外,像pause()(等待信号的出现才被唤醒)、wait()(等待子进程退出)等函数都会使得当前进程进入可中断睡眠状态。

第二部分 Fork()详解

       在Linux中手动创建一个新进程的唯一方法是使用fork()函数。
       fork()函数用于从已存在的进程中创建一个新进程。新进程成为子进程,原进程成为父进程。子进程是父进程的一个复制品,它从父进程继承了进程的整个地址空间,包括进程上下文、代码段、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等。fork()出来的子进程除了进程标识符pid和父进程不一样以外,其他的都和父进程相同。下面这张图形象的描述了进程的地址空间。
在这里插入图片描述

关于fork()需要注意以下几点
  • 父进程中的返回值是子进程的进程号, 而在子进程中返回0,因此可以通过返回值来判定当前进程是父进程还是子进程。当一个父进程创建多个子进程时(注意此时fork()一定要保证在父进程运行状态下被调用),在代码的不同调用位置可以拿到不同的子进程pid,可参见以下代码,注意留意父进程创建多个子进程的fork()调用位置。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main(){
    pid_t pid;
    pid = fork();
    if(pid < 0){
        printf("Fork1 error!\n");
        exit(1);
    }
    if(pid > 0){
        printf("the father(1) is running, pid is %d, ret value is %d\n", getpid(), pid);
        pid_t pid2 = fork();
        if(pid2 < 0){
            printf("Fork2 error!\n");
            exit(1);
        }
        if(pid2 > 0){
            printf("the father(2) is running, pid is %d, ret value is %d\n", getpid(), pid2);
            exit(0);
        }else if(pid2 == 0){
            printf("the second son is running, pid is %d, ret value is %d\n", getpid(), pid2);
            exit(0);
        }

    }else if(pid == 0){
        printf("the first son is running, pid is %d, ret value is %d\n", getpid(), pid);
        exit(0);
    }
    return 0;
}

       运行效果
在这里插入图片描述

第三部分 wait()/waitpid()详解

(1)wait()和waitpid()区别
  • wait()用于使父进程阻塞等待,直到一个子进程结束或该进程接到了一个信号为止。当该父进程没有子进程或者是子进程结束时会立即返回。
  • waitpid()的作用与wait()相似,但是不一定要等待第一个结束的子进程,它有若干选项,比wait()更加灵活,比如提供一个非阻塞等待的版本。
(2)wait()函数
函数原型 pid_t wait(int *status);
所需头文件 #include<sys/types.h>和#include<sys/wait.h>
成功 返回已结束运行的子进程的进程号
失败 返回-1
参数说明
  • 参数status如果不是一个空指针,则终止进程的终止状态就存放在statloc所指向的单元;
  • 参数status如果是一个空指针,则表示父进程不关心子进程的终止状态。
(3) waitpid()函数
函数原型 pid_t waitpid(pid_t pid, int *status, int options);
所需头文件 #include<sys/types.h>和#include<sys/wait.h>
成功 返回已结束运行的子进程的进程号
options为WNOHANG时 没有子进程退出则立即返回0
失败 返回-1
参数说明
  • pid
pid>0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
  • options
WNOHANG 若由pid指定的子进程未发生状态改变(没有结束),则waitpid()不阻塞,立即返回0
WUNTRACED 返回终止子进程信息和因信号停止的子进程信息
WCONTINUED 返回收到SIGCONT信号而恢复执行的已停止子进程状态信息
0 这个时候跟wait()一样阻塞等待
  • status
    同wait()
用法
  • 一个常见的用法是waitpid(pid, NULL, 0),这样就可以阻塞等待进程号为pid的子进程退出了。注意这个用法和wait()是不一样的,后者只能等待任意一个子进程退出,而waitpid()可以指定等待具体哪个进程。

第四部分 关于返回值pid_t的补充说明

  • pid_t在头文件types.h(sys/types.h)中定义。
  • pid_t是一个typedef定义类型。
    用它来表示进程id类型。
//sys/types.h:
typedef short pid_t;       /* used for process ids */

由此看出,上述定义的pid_t就是一个short类型变量,实际表示的是内核中的进程表的进程号索引。

  • 使用pid_t的一个好处——可移植性更好
    比如对于不同的平台,可以typedef int pid_t,也可以typedef long pid_t
  • 事实上Linux内核源码的数据类型基本都把基本类型进行了一次typedef的封装。

参考文献

https://blog.csdn.net/csdn_kou/article/details/81091191
《嵌入式Linux应用开发标准教程》 华清远见嵌入式培训中心 编著
《深入理解计算机系统》 Randal E.Bryant, David R.O’Hallaron编著

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章