找不到自己做的dw网站,百度账号登录入口,ui培训班一般学费多少,iis端口相同不同网站建设目录
前言
进程创建
fork()函数
写时拷贝
进程终止
退出场景
退出方法
进程等待
等待原因
等待方法
1.wait函数
2.waitpid函数
等待结果#xff08;status介绍#xff09;
进程替换
替换原理
替换函数
进程替换例子
shell简易实现
后记 前言 学习完操作…目录
前言
进程创建
fork()函数
写时拷贝
进程终止
退出场景
退出方法
进程等待
等待原因
等待方法
1.wait函数
2.waitpid函数
等待结果status介绍
进程替换
替换原理
替换函数
进程替换例子
shell简易实现
后记 前言 学习完操作系统中进程部分的入门介绍之后大家应该进程有了个初步了解那么下面就可以很好地进军进程控制部分了包括进程创建、进程终止、进程等待、进程替换等重点部分其中的细节很多也比较难以理解但是没有关系在介绍完进程控制之后会简单实现一个shell程序也就是类似Xshell的一个软件也可以执行相关命令进行各种操作来综合理解一下四个重点部分快往下看吧 进程创建 fork()函数 在进程入门理解章节中我们介绍到了fork()函数可以创建一个新进程此进程称为子进程原进程称为父进程函数信息如图所示 还知道fork失败时返回-1成功时有两个返回值给父进程返回子进程的pid给子进程返回0所以fork()之后由此分流使得父子进程去做不同的事。 1fork()深入理解 由于进程内核数据结构进程的代码和数据其中内核数据结构由os搞定而进程的代码和数据一般从磁盘来也就是c/c运行的可执行文件。所以fork()之后os创建子进程为其分配对应内核数据结构必须子进程独有因为进程具有独立性理论上子进程也要有自己的代码和数据这如何拥有 对于代码都是不可写的所以父子共享对于数据可写可读所以不能共享必须分离。这里先针对于代码数据的分离会在下面的写时拷贝讲到。 见上图想一下fork()之后父子进程是共享after的代码还是共享所有的代码所有的但子进程从after那里开始执行而不是从头开始执行。下面先提一下两个认知 ①代码汇编以后会有很多行代码在加载到内存之后每行代码都有自己对应的地址 ②cpu中有一个寄存器叫做EIP也叫做pc指针、程序计数器记录当前正在执行代码的下一行代码的地址属于进程的上下文数据。 创建子进程时EIP的值无需给子进程因为父子进程各自调度会修改EIP就算给了子进程也用不到在子进程中会将after的第一行代码的地址赋给EIP进程就从after开始执行了。 值得注意的是fork()之后父子进程两个执行流分别各自执行谁先执行完全是由调度器决定的。 2fork()常规用处 ①一个父进程希望复制自己使父子进程同时执行不同的代码段。例如父进程等待客户端请求生成子进程来处理请求。 ②一个进程要执行一个不同的程序。例如子进程从fork返回后调用exec系列函数进行进程替换。 3fork()调用失败的原因 ①系统中有太多的进程 ②实际用户的进程数超过了限制。 写时拷贝 在上文讲到对于代码都是不可写的所以父子共享对于数据可写可读所以不能共享必须分离。那如何分离呢直接拷贝一份然后修改 不行这样的话会存在子进程不会用到的空间造成内存浪费即使有用到的空间也可能只是读取所以数据分为不会被访问或指挥读取的数据和将来会被父或子进程写入的数据但一般而言os无法得知哪些数据不会被访问或者只会读取亦或会被写入所以os选择了写时拷贝技术。 也就是说父子进程两方都没有数据写入操作时数据也是共享的当任意一方试图写入时便以写时拷贝的方式重新分配内存并将原来内存上的内容拷贝到新内存上再进行修改。 好处 ①使父子进程彻底分离保证了进程的独立性 ②写时拷贝是一种延时申请技术提高了整机内存的使用率。 进程终止 退出场景 在写c/c程序时我们写main函数都要返回一个int且都返回0这个操作到底是什么意思呢实际上main函数作为一个进程在结束时是要返回一个结果给操作系统的。对于main函数返回0代表成功返回结果正确而返回非0值代表结果不正确或异常因为非0值有很多所以错误结果或异常结果就对应很多此时这个0或者非0值叫做进程退出码返回给上一层评判进程的执行结果的。 但是我们写完一个main函数也不知道结果到底正不正确进而也不知道该返回什么啊其实是可以的看看下面的例子我们可以使用if语句判断是否为期望结果决定返回值结果不正确返回1正确返回0当然也可以预判出其他错误返回不同的非0值。 通过main函数可以总结出一个进程退出有三种情况 ①正常退出结果正确 ②正常退出结果不正确 ③异常退出。 退出方法 1正常终止的方法 ①在main函数中return ②调用系统接口_exit函数 ③调用库函数exit函数 注意 ①必须在main函数中返回才是终止进程普通函数返回只是在返回调用结果 ②正常终止都会返回进程退出码给os可以通过【echo $?】查看最近一次的进程退出码同时可以通过函数【strerror(退出码)】查看对应的退出原因比如 2异常终止 ①ctrlc ②通过信号终止。 注意通过信号终止在下面即将要学到的信号章节中讲解这里重点讲上面的正常退出的方法。 exit、_exit介绍与对比 1_exit函数 参数status存储着进程的终止信息包括进程退出码父进程通过wait函数接收该值这里在进程等待部分重点讲解。 2exit函数 这里的参数status与_exit函数中的一样。 3对比 ①exit函数与_exit函数在代码的任何地方调用都表示结束进程无论在main中还是调用的某个函数中。 ②其实exit库函数是_exit系统接口的一个封装在exit函数内部也会调用_exit函数但在这之前还会执行清理函数并且清理缓冲区然后再调用_exit函数如图。 注意return结束进程更为常见return n相当于exit(n)。 进程等待 等待原因 在前面说过僵尸进程的问题即子进程退出但父进程不管不顾就会造成内存泄漏。按照正常情况父进程创造出一个子进程肯定是要其完成一个任务然后子进程去完成父进程等待子进程终止以后返回的结果这就是进程等待。通过这种方式父进程回收子进程的资源及获取子进程退出信息比如进程退出码。 等待方法
1.wait函数 返回值成功接受到被等待进程返回该进程的pid失败返回-1 参数status是一个输出型参数即传进此参数函数内会将进程信息放进这个指针中函数返回后父进程可通过此值查看子进程信息若不想得到父进程的结束信息就传入NULL关于此参数的构成会在下文提到。 eg 2.waitpid函数 参数 pid①传入指定被等待的进程pid②传-1代表等待任一个进程与wait等效 status与wait函数一致 options①传WNOHANG代表若指定进程没有结束则函数直接返回0不再等待若进程已经结束则函数直接返回子进程pid即非阻塞等待②传0代表当子进程没有结束父进程处于阻塞状态去等待其结束与wait等效。 注意WNOHANG是一个宏定义一般大写的标记位都是宏定义。 返回值 与wait一致但要注意设置了WNOHANG选项的返回值。 eg 等待结果status介绍 对于wait/waitpid函数都有一个输出型参数statusos将子进程信息填入其中带给父进程。status不能简单的当作一个整形来看要分开看它的比特位目前只关心status的低16个比特位如图 可以看到低八位存放终止信号此低八位存放退出码对于异常终止时的core dump标志暂时不说明后面信号章节会说到。明显地当wait/waitpid函数接受完子进程退出结果之后正常退出可以通过【(status8)0xFF】获取退出码异常退出可以通过【status0x7F】获取终止信号有点C语言地基础都可以看的懂不多赘述。因为比较麻烦所以Linux也提供了可以关于此的宏定义 ①WIFEXITED(status)查看进程是否正常退出若正常退出返回真否则返回假 ②WEXITSTATUS(status)查看进程的退出码。 eg除了else部分其他部分与上张截图一样 eg增加了子进程睡眠时间中间通过kill指令杀掉进程 进程替换 替换原理 通过特定的接口加载磁盘上的一个全新的程序代码和数据到内存中并和当前进程的页表重新建立映射这就叫做替换而其中加载的方法就是使用exec系列函数。当进程调用一种exec函数时该进程的代码和数据完全被新程序替换从新程序的开头开始执行原理图如下。 注意 ①调用exec函数并没有创建新进程所以调用前后的进程id并没有变化 ②当子进程加载新数据时代码和数据就会被替换对于代码而言这正是一种写入即写时拷贝此时父子进程彻底分离虽然曾经并不冲突之前说过父子进程代码共享数据采用写时拷贝的手段。 替换函数 如图一替换函数有6种统称为exec函数而图二的一个exec函数是系统调用函数图一的6个函数都是基于这个系统调用函数封装的函数以满足不同的需求这里我们也是重点介绍上面6个函数。 参数 path参数是个指针需要传入一个路径字符串 arg参数也是个指针需要传入一个指令而后面的省略号是可变参数列表可以传入指令的选项 file参数指针变量传入一个文件名 envp指针数组里面存放环境变量 argv参数指针数组里面存放命令行参数即全部arg参数。 返回值 如果调用成功则加载新的程序从新程序的启动代码开始执行不再返回如果调用出错就返回-1。 注意 l(list) : 表示参数采用列表 v(vector) : 参数用数组 p(path) : 有p自动搜索环境变量PATH e(env) : 表示自己维护环境变量 eg 注意可变参数列表列出所有的选项后要以NULL结尾命令行参数数组也是如此。
进程替换例子
1.如何替换自己的c/c程序 自己通过vim或者其他编辑器编写一个c/c程序这里我编写了一个cmd.c的文件其中main函数的参数可以传入命令行参数个数命令行参数数组及环境变量数组这里我没有传入函数体根据命令行参数传入所构成具体如下图一所示。 之前说过我们可以创建一个子进程让其执行其他的事父进程等待回收以接受结果图二就是在这样一个情况下我们将子进程替换成自己写的如图一所示的子程序如图二。 2.如何替换其他语言的程序 替换其他语言的程序与c/c语言的程序并无二质都先编译成可执行程序然后将子进程替换成自己写的程序就行这里举例python程序和shell程序具体如下图一 如图二则是替换python程序的结果替换shell程序一样。 shell简易实现 在学习Linux的过程中离不开xshell的帮助这个软件可以远程访问服务器通过指令管理服务器上的文件等比如ls、pwd等。思考一下我们可不可以通过程序控制来简易实现一个shell程序步骤如下 ①获取命令行 ②解析命令行 ③创建子进程 ④替换子进程 ⑤父进程等待接收子进程 ⑥循环以上步骤。 代码
#include stdio.h
#include stdlib.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/wait.h#define CMD_LINE_SIZE 1024
#define ARGC_MAX 32char cmd_line[CMD_LINE_SIZE];
char* _argv[ARGC_MAX];int main()
{//程序不退出while(1){//用户名主机当前目录//[phan9iZf8z8xmdh7b2erpqis8sxZ test_os_8_21]$printf([rootlocalhost shell]# );//初始化memset(cmd_line,\0,sizeof(cmd_line));//获取用户输入的指令if(fgets(cmd_line,sizeof(cmd_line),stdin)NULL)continue;cmd_line[strlen(cmd_line)-1]\0;//将指令选项导入指令数组int index0;_argv[0]strtok(cmd_line, );//将指令选项导入指令数组while(_argv[index]){index;_argv[index]strtok(NULL, );}//创建进程pid_t idfork();if(id0){//子进程execvp(_argv[0],_argv);exit(1);}//父进程继续int status0;int reswaitpid(-1,status,0);//阻塞等待if(res0)printf(退出码%d\n,WEXITSTATUS(status));}return 0;
}以上是基本的shell框架可以自行加入一些功能比如【ls -l】指令简写成【ll】指令文件名变色如图地方加入 后记 本篇文章的知识点加上上篇进程入门介绍文章的知识点大家应该对操作系统中的进程所涉及的知识点有了比较全面的认识了相信反复阅读两篇文章再加上自己尝试实现一个简易的shell程序可以更加的深入认识两篇文章有不懂的地方可以私我或者发在评论区有大伙共同解答哦加油拜拜