0


Linux——进程(下)

一.进程创建

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

二.进程终止

1.为什么终止

释放曾经的代码和数据所占据的空间。释放内核数据结构。


2.终止的三种情况

  • 代码跑完,结果正确
  • 代码跑完,结果不正确
  • 代码执行时,出现了异常,提前退出了。

echo $?指令:父进程bash获取到最近一个的子进程的退出码,要知道子进程的退出情况。

结果是否正确可以通过进程的退出码决定,****退出码为0表示退出成功,非0则表示失败

非0的情况有多种,每个退出码都代表失败的不同原因,可以使用下述函数,通过退出码来打印退出信息:

**char *strerror(int errnum); **

父进程bash要通过退出码来获取子进程的退出情况(成功或失败,失败的原因),为用户负责。

进程的异常退出,本质是进程收到了OS发出的进程信号

想知道进程如何异常退出,可以看进程退出时的退出信号是多少

衡量一个进程退出的情况,只需要两个数字,退出码和退出信号


3.如何终止

  • main函数中直接return,表示进程终止(非main函数,return表示函数结束,而非进程退出)。
  • 代码调用exit函数。在代码的任意位置调用exit,都代表进程终止。
  • _exit() -- 系统指令。

exit和_exit的区别:exit会在进程退出时冲刷缓冲区,_exit不会。


三.进程等待

1.为什么等待

任何子进程,在退出情况下,一般必须要被父进程进行等待。等待的原因如下:

  • 父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(必须考虑)。
  • 获取子进程的退出信息,知道子进程是因为什么原因退出的(非必须)。

2.怎么等待

函数

  • *pid_t wait(int status):等待父进程中,任意一个子进程退出,等待成功返回子进程的pid。
  • *pid_t waitpid(pid_t pid,int status,int options)

在子进程退出之前,父进程会一直进行阻塞等待,而不会执行自己的代码


参数

pid:

  • pid = -1 ,表示等待任一个子进程,与wait等效。
  • pid>0,等待其进程ID与输入的pid相等的子进程。

status:

输出型参数,用于得到子进程的退出信息。退出信息包括:退出码和退出信号

如果父进程不关心子进程的退出信息,则可以设为NULL

int类型的status由32位组成,其中高16位不使用,对于低16位,高8位为进程的退出码,低七位为进程的退出信号

  • WIFEXITED(status)函数:若为正常终止子进程返回的状态,则为真。(查看进程是否正常退出)
  • WEXITSTATUS(status)函数:若WIFEXITED函数返回值非零,提取子进程退出码。(查看进程退出码)

options:

子进程没有退出,而父进程在执行waitpid等待子进程,就会导致阻塞等待,即options默认为0。

将options改为WNOHANG,则为非阻塞等待

阻塞等待下,父进程调用waitpid函数会一直等待子进程的状态,直到其达到某种情况(如退出)才会停止等待

非阻塞等待下,父进程调用waitpid函数只会确认一次子进程的状态,而不进行等待,可以干其他事。此时,父进程需要循环调用waitpid函数来判断子进程是否退出,

**非阻塞等待 + 循环 = 非阻塞轮询 **


返回值

阻塞等待:

  • 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  • 如果调用中出错,则返回-1,这时erron会被设置成相应的值以示错误所在。

非阻塞等待:

  • pid_t > 0:等待成功的,子进程退出了,并且父进程回收成功。
  • pid_t < 0:等待失败了。
  • pid_t == 0:检测是成功了,只不过子进程还没完全退出,需要你下一次进行重复等待。

四.进程替换

使用exec*系列的函数在原本的程序中执行新的程序,称为程序替换

其本质是exec*系列的函数类似于linux系统上的加载函数将新的程序加载到内存中

exec*系列的函数,执行完之后,后边的代码不会再被执行,因为被替换了。

这些函数的返回值不用关心只要替换成功,就不会向后运行,如果向后运行,代表替换失败

进程 = 内核数据结构 + 代码和数据,而进程替换即替换****掉代码和数据,没有创建新的进程,而是复用原本的虚拟地址空间和页表,重新建立页表映射关系

当父进程创建子进程,通过进程替换让子进程去执行一个新的进程时,父进程的代码和数据都将进行写时拷贝,子进程将享有新的代码和数据,实现与父进程的完全独立


1.替换函数

exec*是整个替换函数系列的开头,后边追加的字符则代表不同的含义


(1)execl

**int execl(const char path,const char arg,...)

  • “l”代表list,即列表,表示该函数可以按列表方式执行程序。
  • path是程序所在的路径,执行程序必须要给出其所在的路径。
  • arg表示程序名(指令名)。
  • "..."表示该函数为变参函数,用于追加多个后缀指令。
  • 参数必须以NULL结尾。

例子:

execl("/usr/bin/ls","ls","-l","-a",NULL);

ls -l -a即为一个列表

执行ls -l -a指令。


(2)execv

**int execv(const char path, char const argv[])

  • "v"表示数组vector,说明该函数调用的指令需要从数组中获取。
  • argv即为要存放指令的数组。

例子:

*char const argv[] = ("ls","-l","-a",NULL);

execv("/usr/bin/ls",argv);


execlp/execvp

**int execlp(const char flie,const char arg,...)

**int execvp(const char file, char const argv[])

"p"表示环境变量PATH。

这两个函数与上述两种函数功能完全相同,但是使用这两个函数,第一个参数可以不传要执行的程序所在的路径,而只需传入该程序的名字,而后函数会从系统的PATH环境变量下找到该程序并执行


execle/execvpe

**int execle(const char *flie,const char arg,...,char const envp[])

**int execvpe(const char *file, char const argv[],char const envp[])

e表示环境变量,使用这两种函数,用户替换程序的环境变量

本质是用新的环境变量替换程序原本的环境变量, 使其成为新环境变量下的程序

标签: linux 笔记 运维

本文转载自: https://blog.csdn.net/2303_78442132/article/details/137244896
版权归原作者 很楠不爱 所有, 如有侵权,请联系我们删除。

“Linux——进程(下)”的评论:

还没有评论