过程的创立

  • 第一个过程:过程0,是在操作系统内核的启动过程中手工形成的。
  • 第二个过程:过程1,是由过程0在内核态下通过fork而来。
  • 其余的过程:在用户态下,通过fork而来。

创立过程(fork)

  • 留神:在不同过程中fork返回值不同

    • 在父过程中,fork返回值为子过程PID
    • 在子过程中,fork返回值为0
    • 在fork外部就曾经开始分流了,依据判断以后是哪个过程,从而返回不同的值。
  • fork之前的代码只有父过程执行,fork之后的代码由父子过程别离独特执行(然而两个过程谁先被调度是不确定的)
  • 在创立子过程后,操作系统会给子过程创立一个过程地址空间和页表,然而此时子过程的过程地址空间所对应的物理地址和父过程的过程地址空间所对应的物理地址是一样的,即父子过程同时应用一块物理内存(代码和数据共用)
  • 然而,当子过程或父过程对数据进行更改时(例如子过程更改数据),子过程会进行 “写时拷贝” 将整个数据段在物理地址中拷贝一份,并且让过程地址空间对应的虚构地址映射到拷贝后的物理地址中。这样不会影响父过程的数据(过程具备独立性)
#include<<unistd.h>pid_t fork(void);  //返回值为过程的PID

fork创立失败的状况

  • 零碎中过程太多。
  • 理论用户的过程超过了限度。

过程终止

  • 程序完结,过程失常终止时,会齐全开释耗费的系统资源(内存、IO等)
  • 如果没有开释,则相应的资源就会失落(异样终止时)
  • Linux零碎设计时规定:每个过程完结时,操作系统会主动回收这个过程的所有资源(例如malloc申请的内存没有free开释时,过程完结后这个内存也会被开释)
  • 然而操作系统的回收只会回收这个过程工作时耗费的内存和IO,而不会回收这个过程自身占用的内存(次要是task_struck和栈内存)

过程终止办法

  • 失常终止:

    • 程序失常完结,从main函数return返回。
    • 调用 exit() 函数退出过程。( void exit(int status); )
    • _exit ( void _exit(int status); )
  • 异样终止:

    • 程序异样
    • 命令行输出ctrl + c 发送退出信号。

应用 return、exit、_exit 终止过程时的区别

  • 应用return 和 exit 终止函数时,都会开释已经占用资源(例如行缓冲区等)
  • 应用_exit 终止程序时 ,是间接终止过程,不会开释系统资源。

过程期待

  • 过程期待个别都是由父过程实现。
  • 父过程通过过程期待的形式,回收子过程资源,获取子过程退出信息。

wait办法

wait函数原型

  • wstatus :用来返回子过程完结时的状态信息(传参一个int类型的变量的地址即可)
  • pid_t :这个是返回本次wait回收的子过程的PID。
#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);

wait工作原理

  • 子过程完结时,操作系统会向父过程发送SIGCHILD信号。
  • 父过程调用wait函数时,会期待SIGCHILD信号(函数会阻塞在这里)
  • 父过程收到信号后,会被唤醒而后去回收僵尸过程(子过程退出,但还未被回收时的状态)
  • 如果父过程没有子过程,然而调用了wait函数,这时wait函数会返回谬误。
  • 如果父过程有多个子过程,wait函数会阻塞到其中一个子过程完结为止。
  • 所以父过程如果调用wait函数,那么某一个子过程肯定会先比父过程完结。

waitpid办法

wait和waitpid差异

  • 基本功能是一样的,都是用来回收子过程。
  • 然而waitpit能够回收指定PID的子过程。
  • waitpit能够抉择阻塞式和非阻塞式两种工作模式。

waitpid函数原型

#include <sys/types.h>#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
  • pid :指定要回收的子过程PID(传递值为 -1 时,示意不指定要回收的子过程)
  • status :用来存储子过程完结时的状态信息(传参一个int类型的变量的地址即可)
  • options :实现某些非凡性能。

    • 传递的参数为 0 时 :示意不应用此项(即应用阻塞式)
    • WNOHANG :如果没有子过程,则立马返回 0(非阻塞式)

退出后果status

  • status 是int 类型,32位,理论应用时只应用低16位。
  • status 的第 8 -- 15 位就是退出码。
  • 异样退出时,status的低 7 位是终止信号(终止信号能够用 kill -l 显示)

检测status退出状态的宏

  • WIFEXITED(status) :查看过程是否失常退出,若是则为真。
  • WEXITSTATUS(status) :若过程失常退出,则查看过程退出码。

过程程序替换(exec函数)

替换原理

  • 用fork创立子过程后,父子过程执行的是雷同的程序,然而为了让子过程执行不同的程序往往须要调用一种 exec 函数来替换掉子过程中原来的程序。
  • 当调用exec函数后,该过程的物理空间的数据会被新程序齐全替换,并且从新建设映射关系,而后让过程从新程序开始执行。
  • 留神:调用exec函数并不创立新过程,所以调用前后过程的PID并未被扭转。
  • 所以,有了exec族函数,子过程中的代码能够独自编写、独自编译链接成一个可执行程序。
  • 当调用exec族函数后,则会加载新的程序,从新程序的启动代码开始执行,不再返回以后代码,那么之前过程的后续代码将不会被执行。

替换函数

execl和execv函数

  • path :要执行的可执行程序的全门路。
  • arg :给可执行程序传递的参数。
  • ... :变参,示意能够传递多个参数,然而最终结尾的参数必须是NULL。
  • argv[ ] :参数数组,要当时将传递的参数写到数组中。
  • 如果调用出错,则返回 -1,调用胜利则执行新程序,不会返回。
int execl(const char *path, const char *arg, .../* (char  *) NULL */);int execv(const char *path, char *const argv[]);
  • 例如:执行 ls 程序,传递参数 -a -l

    • 这里第一个传参ls,貌似没有什么作用,只是占位,第二个开始的参数才会传递到程序中。
execl("/bin/ls", "ls", "-l", "-a",NULL);

execlp和execvp函数

  • 成果与下面的两个函数雷同
  • 然而下面两个只能指定可执行程序的全门路(否则会找不到文件而报错)
  • 而这两个函数能够只传递可执行程序的文件名,也能够是全门路
  • 它会在先去当前目录下找file,如果找到了则间接执行,如果没找到则会去环境变量PATH所指定的目录中去寻找,如果还是没找到则报错。

    int execlp(const char *file, const char *arg, .../* (char  *) NULL */);int execvp(const char *file, char *const argv[]);

execle和execvpe函数

  • 执行可执行程序时会多传一个环境变量的字符串数组 envp 给待执行程序。
  • 传递的这个环境变量会代替默认的环境变量,在找不到file时,会到这个环境变量中去找,而不会到默认环境变量PATH中去找。
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);int execvpe(const char *file, char *const argv[],char *const envp[]);