如何为昆明地区的企业寻找合适的网站建设公司,以下载安卓应用商店?

摘要:网站建设公司昆明,安卓应用商店下载,网站代理怎么设置,Wordpress query 参数hello,大家好,这里是bang___bang_,在上两篇
网站建设公司昆明,安卓应用商店下载,网站代理怎么设置,Wordpress query 参数hello#xff0c;大家好#xff0c;这里是bang___bang_#xff0c;在上两篇中我们讲解了进程的概念、状态和进程地址空间#xff0c;本篇讲解进程的控制#xff01;#xff01;包含内容有进程创建、进程等待、进程替换、进程终止#xff01;#xff01; 附上前2篇文章…        hello大家好这里是bang___bang_在上两篇中我们讲解了进程的概念、状态和进程地址空间本篇讲解进程的控制包含内容有进程创建、进程等待、进程替换、进程终止 附上前2篇文章链接 Linux——操作系统进程详解建议收藏细品_bang___bang_的博客-CSDN博客 [Linux]环境变量 进程地址空间虚拟内存与物理内存的关系_bang___bang_的博客-CSDN博客 目录 1️⃣计算机四个重要概念 2️⃣进程创建 fork函数初识 fork返回值 fork调用失败原因 3️⃣进程终止 进程终止的场景 退出码 查看退出码echo 字符串格式查看错误信息strerror 退出码讲解 进程常见退出方法 _exit函数——系统调用接口 exit函数——C库函数 return退出 4️⃣进程等待 进程等待必要性 进程等待的方法 wait方法 waitpid方法 5️⃣进程程序替换 替换原理 替换函数 execl函数 execv函数 execlp函数 execle函数 execvp函数 execvpe函数 execve函数系统调用 1️⃣计算机四个重要概念 ✦竞争性系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级 ✦独立性多进程运行需要独享各种资源多进程运行期间互不干扰 ✦并行多个进程在多个CPU下分别同时进行运行这称之为并行 ✦并发多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发 2️⃣进程创建 fork创建子进程操作系统都做了什么 fork创建子进程系统多了一个进程。 进程内核数据结构进程代码和数据 fork函数初识 创建子进程失败返回-1成功返回子进程PID给父进程0返回给子进程 进程调用fork当控制转移到内核中的fork代码后内核做 ★分配新的内存块和内核数据结构给子进程 ★将父进程部分数据结构内容拷贝至子进程 ★添加子进程到系统进程列表当中 ★fork返回开始调度器调度 在进程详解篇我提到过父子进程代码共享。 那么有个问题是父进程所有的代码子进程都共享呢还是在fork函数之后的代码才共享 代码共享猜想图 写一段fork代码看看结果提示眼见不一定为实 #includestdio.h #includeunistd.h #includesys/types.h #includestdlib.h int main() {int num0;int*pnum;printf(Begin:pid:%d,num:%p\n,getpid(),p);pid_t idfork();if(id0){perror(fork);}printf(After:pid:%d,fork return %d,num:%p\n,getpid(),id,p);return 0; }fork验证结果图 根据结果我们发现Begin在子进程并没有执行但这能表示子进程没有共享父进程的Begin代码吗 答案是不是的实际上子进程也共享到了父进程的Begin语句只不过CPU中有个EPI寄存器他保存了进程的上下文信息使得子进程以为fork是他的开始从fork语句后开始执行 实际代码共享图 创建子进程给子进程分配对应的内核空间结构必须子进程自己独有因为进程具有独立性理论上子进程也要有自己的代码和数据可是一般而言我们创建的子进程没有加载的过程代码和数据一般是从磁盘上加载到的程序也就是说子进程没有自己的代码和数据所以子进程只能“使用”父进程的代码和数据 代码都是不可被写的只能读取所以父子共享 数据可能被修改的所以必须分离 fork返回值 ★子进程返回0 ★父进程返回子进程的pid ★出错返回-1 为什么fork会有2个返回值是因为写时拷贝 在进程地址空间中我有讲解这里再简单讲解一下 基于进程的独立性父子进程的数据必须分离但是对于只读的数据我们也进行拷贝的话对内存太过于浪费所以出现了一种新的技术写时拷贝技术 写时拷贝是一种延时申请技术可以提高整机内存的使用率。 写时拷贝 子进程执行读权限的时候父子进程页表映射到同一物理内存当执行写权限时OS重新拷贝一份数据到物理内存上同时子进程的页表断开原来的映射关系映射到拷贝数据的物理地址。 fork调用失败原因 ★系统中有太多的进程 ★实际用户的进程数超过了限制 3️⃣进程终止 进程终止时操作系统做了什么 答释放进程申请的相关内核数据结构和对应的数据和代码。本质释放系统资源。 进程终止的场景 ✦代码运行完毕结果正确 ✦代码运行完毕结果不正确 ✦代码异常退出 退出码 查看退出码echo //获取最近一个进程执行完毕的退出码 echo $? echo示例图 问题main函数返回值的意义是什么为什么总是0 并不是总是0返回值是进程的退出码0表示进程运行结果正确非0表示进程运行结果错误。 返回值的意义返回给上一级进程用来评判该进程执行结果用的可以忽略。让上层能根据程序的退出码定位代码出错的原因。非0值有无数个不同的非0值就可以标识不同的错误原因从而给我们的程序在运行结束之后结果不正确时方便定位错误的原因细节 字符串格式查看错误信息strerror 为了方便我们查看对应的错误是什么C库提供了一个函数strerror将错误以字符串的形式打印。 strerror结果 退出码讲解 退出码在status参数中 status并不是按照整数来整体使用的而是按照bit位的方式将32个bit位进行划分位图 status低16位 上图是status的低16位示意图。 次低8位表示子进程退出的退出码。获取status0xFF008 低7位表示进程收到操作系统的信号表示退出信号。获取 status 0x7F 系统提供了2个宏来获取退出码和退出信号 WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出 WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码 WIFEXITED(status) //是否正常退出 WEXITSTATUS(status) //若正常退出获取退出码 使用grep -ER xxxx /usr/include 进行查找  进程异常退出或者崩溃本质是操作系统通过发送信号杀掉了你的进程 进程常见退出方法 _exit函数——系统调用接口 参数status 定义了进程的终止状态父进程通过wait来获取该值。 测试status次低8位作退出码 #includestdio.h #includeunistd.h int main() { _exit(-1); } status次低8位测试结果图 将status设为-1补码为全1但是只有8位所以退出码显示为2551111 1111 exit函数——C库函数 exit最后也会调用_exit但在调用前还刷新了缓冲区。 观察_exit和exit的区别 /*_exit测试*/ #includestdio.h #includeunistd.h int main() { printf(hello); _exit(0); }/*exit测试*/ #includestdio.h #includestdlib.h int main() { printf(hello); exit(0); } _exit和exit对比结果图 printf输出是对stdout标准输出文件写入stdout文件的缓冲区刷新策略是行刷新即遇到\n刷新 测试中没有\n也就不会刷新缓冲区也就不会显示hello。 但是通过结果图我们可以看到系统调用_exit没有打印C库函数exit有打印也就是说exit还刷新了缓冲区。 return退出 return是一种更常见的退出进程方法。 执行return n等同于执行exit(n) , 因为调用 main 的运行时函数会将 main 的返回值当做exit 的参数。 return等同于执行exit #includestdio.h int main() { printf(hello); return 0; } return测试图 现象和exit一样main返回值当做exit的参数。 4️⃣进程等待 进程等待必要性 ✦ 子进程退出父进程如果不管不顾就可能造成‘僵尸进程’的问题进而造成内存泄漏。 ✦ 进程一旦变成僵尸状态kill -9 也无能为力因为谁也没有办法杀死一个已经死去的进程。 ✦ 父进程派给子进程的任务完成的如何我们需要知道。 ✦ 父进程通过进程等待的方式回收子进程资源获取子进程退出信息。 进程等待的方法 wait方法 参数输出型参数获取子进程退出状态不关心则可以设置为NULL 返回值成功返回被等待进程的pid失败则返回-1。 wait系统接口的阻塞式测试 不关心子进程退出状态 获取子进程退出状态 waitpid方法 pid_ t waitpid(pid_t pid, int *status, int options); 返回值当正常返回的时候waitpid返回收集到的子进程的进程ID如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在 pidpid-1,等待任一个子进程。与wait等效。pid0.等待其进程ID与pid相等的子进程。 status:输出型参数WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码 options:默认为0表示阻塞等待WNOHANG(非阻塞等待): 若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进程的ID。waitpid(pid,NULL,0)wait(NULL) waitpid系统接口的阻塞式测试 waitpid阻塞式测试 结果与wait一致。 等待回收僵尸进程 未等待 等待 waitpid非阻塞式轮询检测测试 #includestdio.h #includestdlib.h #includeunistd.h #includesys/wait.h #includesys/types.h//非阻塞等待测试代码int main() {pid_t idfork();if(id0){//子进程int cnt5;while(cnt){printf(我是子进程:%d\n,cnt--);sleep(1);}exit(11); //退出码11仅用来测试}else{int quit0;while(!quit){int status0;pid_t reswaitpid(-1,status,WNOHANG);//以非阻塞方式等待if(res0){//等待成功子进程退出printf(等待子进程退出成功退出码:%d\n,WEXITSTATUS(status));quit1;}else if(res0){//等待成功但子进程并未退出printf(子进程还在运行中暂时还没有退出父进程可以再等一等,处理其他事情\n);}else {//等待失败printf(wait失败\n);quit1;}sleep(1);}} }问题父进程通过wait/waitpid可以拿到子进程的退出结果为什么要用wait/waitpid函数呢直接全局变量不行吗 答不行因为进程具有独立性数据就要发生写实拷贝父进程无法拿到正确的退出结果。 问题既然进程是具有独立性的进程退出码不也是子进程的数据吗父进程又凭什么拿到呢 答僵尸进程至少要保留该进程的PCB信息task_struct里面保留了任何进程退出时的退出结果信息 wait/waitpid本质是读取子进程的task_struct结构中的退出码exit_code退出信号exit_signal 5️⃣进程程序替换 之前fork()之后父子各自执行父进程代码的一部分父子代码共享数据写时拷贝各自一份 现在如果子进程就想执行一个全新的程序呢 想有自己的代码就需要进程的程序替换来完成这个功能。 替换原理 用fork 创建子进程后执行的是和父进程相同的程序 ( 但有可能执行不同的代码分支 ) 子进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种exec 函数时 该进程的用户空间代码和数据完全被新程序替换 从新程序的启动例程开始执行。调用exec 并不创建新进程 所以调用 exec 前后该进程的 id 并未改变。 程序替换原理图 替换函数 函数名参数格式是否带路径是否使用当前环境变量execl列表不是是execlp列表是是execle列表不是不是需自己组装环境变量execv数组不是是execvp数组是是execvpe数组是不是需自己组织环境变量 下面我们对各函数进行测试测试使用ls命令替换程序。模板如下 测试函数模板 execl函数 execl的使用测试 #includestdio.h #includestdlib.h #includeunistd.h #includesys/wait.hint main(int argc,char* argv[],char* env[]) {pid_t idfork();if(id0){//子进程printf(子进程开始运行pid:%d\n,getpid());sleep(3);char* const _argv[]{(char*) ls,(char*) -a,(char*) -l,NULL};//execl函数,传递参数列表execl(/usr/bin/ls,ls,-a,-l,NULL);exit(1);}else{//父进程printf(父进程开始运行pid:%d\n,getpid());int status0;pid_t idwaitpid(-1,status,0);//阻塞等待if(id0){printf(wait seccess,exit code:%d\n,WEXITSTATUS(status));}}return 0; }execl结果图 execl是程序替换调用该函数成功后会将当前进程的所有的代码和数据都进行替换包括以及执行的和没有执行的一旦调用成功后面的所有代码都不会被执行  execv函数 execv的使用测试 //execv函数传递数组 execv(/usr/bin/ls,_argv); execv结果图 execlp函数 execlp的使用测试 //execlp函数传递文件名无需路径和参数列表 execlp(ls,ls,-a,-l,NULL); execlp结果图 execle函数 execle的使用测试 //execle函数传递参数列表和组装的环境变量 execle(/usr/bin/ls,ls,-a,-l,NULL,env); execle结果图 execvp函数 execvp的使用测试 //使用execvp函数传递文件名和数组 execvp(ls,_argv); execvp结果图 execvpe函数 execvpe的使用测试 //execvpe函数传递文件名数组需组织环境变量 execvpe(ls,_argv,env); execvpe结构图 execve函数系统调用 上面是exec系列的函数事实上他们都调用execve只有execve是真正的系统调用 函数名参数格式是否带路径是否使用当前环境变量execve数组不是不是需自己组织环境变量 关系图 execve的使用测试 exec.c: #includestdio.h #includestdlib.h #includeunistd.h #includesys/wait.hint main(int argc,char* argv[],char* env[]) {pid_t idfork();if(id0){//子进程printf(子进程开始运行pid:%d\n,getpid());sleep(3);char* const _argv[]{(char*) ls,(char*) -a,(char*) -l,NULL};char* const _argv_mycmd[]{(char*)mycmd,(char*)-a,NULL};char* const _env_mycmd[]{(char*)My_Path11111,NULL};//系统调用execveexecve(./mycmd,_argv_mycmd,_env_mycmd);exit(1);}else{//父进程printf(父进程开始运行pid:%d\n,getpid());int status0;pid_t idwaitpid(-1,status,0);//阻塞等待if(id0){printf(wait seccess,exit code:%d\n,WEXITSTATUS(status));}}return 0; } mycmd.c: #includestdio.h #includestring.h #includestdlib.hint main(int argc,char* argv[]) {if(argc!2){printf(can not execute!\n);exit(1);}printf(获取环境变量:My_Path:%s\n,getenv(My_Path));if(strcmp(argv[1],-a)0){printf(hello a!\n);}else if(strcmp(argv[1],-b)0){printf(hello b!\n);}else{printf(default!\n);}return 0; }execve结构图 成功将子进程程序替换为mycmd。 问题为什么要创建子进程在子进程中进行进程替换 ——如果不创建那么我们替换的进程只能是父进程如果创建了替换的进程就是子进程而不影响父进程。为了不想影响父进程我们想让父进程聚焦在读取数据解析数据指派进程执行代码的功能 文末结语本篇结合前2篇内容详细讲解了进程控制包含进程创建进程终止终止码_exit进程等待的必要性以及方法waitwaitpid阻塞式等待和非阻塞式等待进程程序替换的替换原理以及6大替换函数和系统调用execve替换子进程程序图文并茂使用例子测试代码。