2023年8月2日发(作者:)
计算机系统fork函数学习⽇志程序的加载和运⾏问题:hello程序的加载和运⾏过程是怎样的?step1:在shell命令⾏提⽰符后输⼊命令:$./hello,然后按下回车键step2:在shell命令⾏解释器构造argv(参数列表)和envp(环境变量列表)step3:调⽤fork()函数,创建⼀个⼦进程,和⽗进程shell完全相同(只读/共享),包括只读代码段、可读写数据段、堆以及⽤户栈等。step4:调⽤execve()函数,在当前进程(新创建的⼦进程)的上下⽂中加载并运⾏hello程序。将hello中的.text节、.data节、.bss节等内容加载到当前进程的虚拟地址空间(仅修改当前进程上下⽂中关于存储映像的⼀些数据结构,不从磁盘拷贝代码和数据等内容)step5:调⽤hello程序的main()函数,hello程序开始在⼀个进程的上下⽂中运⾏。fork的基本知识函数原型:pid_t fork(void);返回值:若成功调⽤⼀次则返回两个值,⼦进程返回0,⽗进程返回⼦进程ID;否则,出错返回-1。⼀个现有进程可以调⽤fork函数创建⼀个新进程。由fork函数创建的新进程被称为⼦进程。fork函数例⼦fork0.c#include
{ if (fork() == 0) { printf("Hello from childn"); } else { printf("Hello from parentn"); } return 0;}输出结果:进程图如下:获取进程ID每个进程都有⼀个唯⼀的正数(⾮零)进程ID(PID)。getpid函数返回调⽤进程的PID。getppid函数返回它的⽗进程的PID(创建调⽤进程的进程)。#include
{ int x = 1; pid_t pid = fork(); if (pid == 0) { printf("Child has x = %dn", ++x); }
else { printf("Parent has x = %dn", --x); } printf("Bye from process %d with x = %dn", getpid(), x); return 0;}运⾏结果:进程图:fork2.c#include
{
printf("L0n"); fork(); printf("L1n");
fork(); printf("Byen"); return 0;}运⾏结果:进程图:fork4.c#include
{ printf("L0n"); if (fork() != 0) { printf("L1n");
if (fork() != 0) { printf("L2n"); } } printf("Byen"); return 0;}运⾏结果:进程图:fork5.c#include
{ printf("L0n"); if (fork() == 0) { printf("L1n");
if (fork() == 0) { printf("L2n"); } } printf("Byen"); return 0;}运⾏结果:进程图:atexit()函数函数声明:int atexit(void (*func)(void));fork6.c#include
使⽤atexit()函数必须包含的头⽂件stdlib.h#include
{ atexit(cleanup); fork(); exit(0);}运⾏结果:进程图:回收⼦进程当⼀个进程由于某种原因终⽌时,内核并不是⽴即把它从系统中清除。相反,进程被保持在⼀种已终⽌的状态,直到被它的⽗进程回收。⼀个终⽌了但还未被回收的进程称为僵死进程。fork7.c#include
{ if (fork() == 0) { /* Child */ printf("Terminating Child, PID = %dn", getpid()); exit(0); } else { printf("Running Parent, PID = %dn", getpid()); while (1); /* Infinite loop */ }}运⾏结果:因为在⽗进程中有while(1); ,所以⽗进程没法结束,⼀直在死循环,回不到shell命令⾏我们⽤CTRL+Z将其挂起在后台运⾏输⼊ps查看进程状态我们发现⼦进程是defunct状态(僵⼫状态),我们尝试⽤kill来杀死它输⼊如下命令$kill -9 4117再⽤ps查看进程状态发现没被杀死所谓斩草要除根,擒贼先擒王我们来杀死它的爸爸(僵⼫进程的⽗进程)发现僵⼫成功扫除⼲净 ~ ( ̄▽ ̄)~*我们再来看⼀个⼦进程死循环的情况#include
{ if (fork() == 0) { /* Child */ printf("Running Child, PID = %dn",getpid()); while (1); /* Infinite loop */ } else { printf("Terminating Parent, PID = %dn", getpid()); exit(0); }}运⾏结果:我们发现⼦进程⼀直在运⾏,没有终⽌爸爸不等⼉⼦直接把⼉⼦抛弃了〒▽〒但是没关系,我们可以叫⽗亲等等他的⼉⼦( · ω · )✧⼀个进程可以调⽤waitpid函数来等待它的⼦进程终⽌或者停⽌。pid_t wait(int *statusp);是waitpid函数的简化版本,调⽤wait(&status)等价于调⽤waitpid(-1,&statusp,0)#include
int child_status;
if (fork() == 0) {
printf("HC: hello from childn");
exit(0);
} else {
printf("HP: hello from parentn");
wait(&child_status); //调⽤wait函数,等到⼦进程终⽌了就执⾏下⼀条语句
printf("CT: child has terminatedn"); //这条语句⼀定是在HC的后⾯出现
}
printf("Byen");}运⾏结果:fork10.c#include
{ int i, child_status; pid_t pid; for (i = 0; i < N; i++) if ((pid = fork()) == 0) { printf("%d已结束n",i); exit(100+i); /* Child */ } while ((pid = wait(&child_status))>0) { /* Parent */ if (WIFEXITED(child_status)) printf("Child %d terminated with exit status %dn", pid, WEXITSTATUS(child_status)); else printf("Child %d terminate abnormallyn", pid); }}运⾏结果:它使⽤waitpid,不按照特定的顺序等待它的所有N个⼦进程终⽌。⽗进程创建 N个⼦进程,每个⼦进程以⼀个唯⼀的退出状态退出。⽗进程⽤ waitpid作为while循环的测试条件,等待它所有的⼦进程终⽌。因为第⼀个参数是-1,所以对waitpid的调⽤会阻塞,直到任意⼀个⼦进程终⽌。在每个⼦进程终⽌时,对waitpid的调⽤会返回,返回值为该⼦进程的⾮零的PID。if (WIFEXITED(child_status)) 检查⼦进程的退出状态。如果⼦进程是正常终⽌的——在此是以调⽤ exit 函数终⽌的——那么⽗进程就提取出退出状态,把它输出到stdout上。我们做⼀个简单的改变,下⾯这个.c⽂件消除了这种不确定性,按照⽗进程创建⼦进程的相同顺序来回收这些⼦进程。fork11.c#include
{ int i, child_status; pid_t wpid,pid[N]; for (i = 0; i < N; i++) if ((pid[i] = fork()) == 0) { printf("%d已结束n",i); exit(100+i); /* Child */ } i = 0; while ((wpid = waitpid(pid[i++],&child_status,0))>0) { /* Parent */ if (WIFEXITED(child_status)) printf("Child %d terminated with exit status %dn", wpid, WEXITSTATUS(child_status)); else printf("Child %d terminate abnormallyn", wpid); }}运⾏结果:持续更新…
发布者:admin,转转请注明出处:http://www.yc00.com/web/1690957688a472827.html
评论列表(0条)