当前位置: 首页 > news >正文

做网站的工作流程wordpress赞助

做网站的工作流程,wordpress赞助,logo查询有没有注册过,深圳seo优化推广业务员进程控制 文章目录进程控制一、进程创建1.fork函数认识2.写时拷贝3.fork常规用法4.fork调用失败的原因二、进程终止1.进程退出场景2.进程退出码3.进程退出的方式三、进程等待1.进程等待是什么#xff1f;2.进程等待的必要性3.进程等待的方法3.1.wait函数3.2.waitpid函数4.如何…进程控制 文章目录进程控制一、进程创建1.fork函数认识2.写时拷贝3.fork常规用法4.fork调用失败的原因二、进程终止1.进程退出场景2.进程退出码3.进程退出的方式三、进程等待1.进程等待是什么2.进程等待的必要性3.进程等待的方法3.1.wait函数3.2.waitpid函数4.如何获取子进程status4.1.如何理解status参数4.2.获取退出码和退出信号3.阻塞等待和非阻塞等待四、进程替换4.1 替换原理4.2 替换函数4.4 exec*接口介绍4.5.替换函数使用实例一、进程创建 1.fork函数认识 在Linux中fork函数非常的重要它的作用是在一个已经存在的进程中创建一个新进程。新进程叫做子进程原来的进程叫做父进程。 函数名称fork函数功能创建子进程头文件#includeunistd.h函数原型pid_t fork(void);参数无返回值-1成功(其中子进程返回0父进程返回子进程的id) -1失败 进程调用fork当控制转移到内核中的fork代码后内核要做的是 分配新内存和数据结构给子进程将父进程部分数据结构内容拷贝至子进程中添加子进程到系统进程列表中fork返回调度器开始调度 当一个进程调用了fork之后父子进程代码是共享的虽然他们都运行到了相同的地方但是每个进程都可以开始自己的旅程 #includestdio.h #includeunistd.h #includestdlib.h #includesys/types.h int main() {printf(Before:pid is: %d\n,getpid());pid_t pid fork();if(pid -1){perror(fork());exit(1);}printf(after:pid is: %d,return is %d\n,getpid(), pid);sleep(1);return 0; }结果展示 我们可以看到第一行输出是fork之前只有父进程在执行打印了before信息fork创建子进程后打印了两行after信息分别由父子进程打印注意到进程29404打印了before的pid而另外一个after却没有打印这是为啥呢 所以fork之前父进程独立执行fork之后两个父子进程执行流分别执行。 注意fork之后谁先执行完全由调度器决定。(父子都有可能先执行) 2.写时拷贝 通常父子代码共享父子不再写入时数据也是共享的当任意一方试图写入时便以写时拷贝的方式各自一份副本(在物理内存中)。 3.fork常规用法 一个父进程希望复制自己使父子进程同时执行不同的代码段。例如父进程等待客户端请求生成子进程来处理请求。一个进程要执行一个不同的程序。例如子进程从fork返回后调用exec函数。 4.fork调用失败的原因 以下两种原因可能会导致fork调用失败 系统中有太多的进程实际用户的进程数超过了限制 我们写一个死循环创建子进程的程序测试我们当前操作系统最多能创建多少个进程 注意上面这个程序可能会导致服务器或虚拟机直接挂掉虚拟机的话大家只需要使用shotdown命令关机重启即可服务器则需要去服务器控制台进行重启。 二、进程终止 1.进程退出场景 代码运行完毕结果正确代码运行完毕结果不正确代码异常终止(vs下叫做程序崩溃) 2.进程退出码 我们先前写C/C代码的时候都会在入口函数main函数开始写我们总是喜欢在结尾的时候给上一个return 0继而引发出了如下的两个问题 return 0给谁return为何是0其它值可以吗 下面一次解决 1、return 0给谁return 给父进程具体理由在下面会有讲解。 2、为何是0其它值可以吗 返回值代表的是进程代码跑完结果是否正确如果是0则成功非零则失败。所以我们在写一个程序的时候如果测试结果正确这里我们可以给上return返回值0可如果不正确我们return的应该是其他值以此表示结果失败只不过我们平时都无脑return 0了准确说是不太正确的。 此外失败虽是用非零值表示可也是有讲究的结果成功都是用0表示结果失败反倒用不同的数字来表示以此表示失败的不同原因。所以我们把main函数的return返回值称之为进程退出码进程退出码表征了进程退出的信息此信息是要给父进程去读取的。 示例 我们可以通过如下的指令查看退出码 echo $?$?表示在bash中最近一次执行完毕时对应进程的退出码(说的简单点就是上一条指令执行完毕后的退出码) 再比如我们平时在命令行输入的指令诸如ls、cd……类的其退出码均为0表示结果正确可是当你随便输入一条错误指令的时候其退出码则是某一数字表示结果错误 问一般而言失败的的非零值我该如何设置呢以及默认表达的含义 C语言当中的strerror函数可以通过错误码获取该错误码在C语言当中对应的错误信息。**总结**错误码退出码可以对应不同的错误原因方便定位问题这里就可以提出我们退出码的意义 它能够表示结果的正确与否正确用0表示因为那么多个数字0只有一个但是错误却有多个用非0数字表示错误的原因也是有多种可能的 退出码也是不能够随意乱写的每一个退出码对应的数字代表不同的错误我们可以利用函数接口strerror观察有哪些错误码 #includestdio.h #includestring.h int main() {for(int i0;i150;i){printf(%d:%s\n,i,strerror(i));}return 0; }结果展示部分错误码对应的错误原因 3.进程退出的方式 正常终止 1、main函数中return返回代表退出进程而非main函数中return返回表示的就是普通的函数返回/结束调用。 2、调用exit函数它在程序的任意地方调用都是代表终止进程参数是退出码exit函数会完成一些收尾工作例如资源的清理和释放刷新缓冲区等。 3、调用_exit函数它的作用是强制终止进程不要进行后续收尾工作比如刷新缓冲区(用户级别的缓冲区) 异常终止 【ctrl c】信号终止 介绍return退出、exit函数和_exit函数 return退出 return退出是一种最为常见的一种退出进程的方法执行return n等于执行exit(n)因为调用main函数运行时会将main的返回值当做exit的参数。 exit函数 exit函数是标准C库中的一个库函数。 函数名称exit函数功能正常终止一个进程头文件#inlcudestdlib.h函数原型void exit(int status)参数status程序退出的状态返回值无执行并查看退出码 在调用exit之前还会做一些其他的工作 执行用户通过atexit或者on_exit定义的清理函数。关闭所有打开的流所有的缓冲数据均被写入。调用_exit函数。 结果展示 _ exit函数 _ exit也是标准C库中的一个库函数它和_ Exit函数调用同义。 函数名称_exit函数功能正常终止一个进程头文件#includeunistd.h函数原型void _exit(int status)参数status 定义了进程的终止状态父进程通过wait来获取该值返回值无 _exit是强制退出进程并不进行后续的收尾工作 注意status定义了进程的终止状态父进程通过wait来获取该值虽然status是int但是仅有低八位可以被父进程所用。所以_exit(-1)时在终端执行echo $?时发现返回值是255。 _exit和exit的作用都是终止进程但是还是有点区别的区别如下 结果演示 exit和_exit函数的区别 exit函数退出进程前exit函数会执行用户定义的清理函数、刷新缓冲区关闭流等操作然后再终止进程而_exit函数会直接终止进程不会做任何收尾工作。 三、进程等待 1.进程等待是什么 让父进程fork之后需要通过wait或者waitpid等待子进程退出父进程想要知道子进程完成的任务情况如何了。 2.进程等待的必要性 之前讲过子进程退出父进程如果不管不顾就可能造成‘僵尸进程’的问题进而造成内存泄漏问题需要通过父进程wait,释放子进程占用的资源。另外进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力因为谁也没有办法杀死一个已经死去的进程。最后父进程派给子进程的任务完成的如何我们需要知道。如子进程运行完成结果对还是不对或者是否正常退出。父进程需要获得子进程的退出状态。父进程通过进程等待的方式回收子进程资源获取子进程退出信息(status)能够得知子进程的执行结果。可以保证“时序问题”那就是保证子进程先退出父进程后退出。 3.进程等待的方法 利用系统级别的函数wait和waitpid 3.1.wait函数 函数名称wait函数功能暂停当前进程直至子进程结束并取回子进程结束时的状态头文件#includesys/wait.h函数原型pid_t wait(int *status)参数status子进程终止状态的地址返回值等待成功返回被等待进程pid失败返回-1 说明输出型参数status获取子进程状态不关心时可以设置为NULL 作用等待任意子进程 我们写一段代码来看一看fork之后我们先让子进程运行5秒之后子进程退出而让父进程一直在等待(调用wait函数)我们就能看到进程等待的现象。 #includestdio.h #includestring.h #includesys/types.h #includesys/wait.h #includeunistd.h #includestdlib.hint main() {pid_t id fork();if (id 0){//childint cnt 5;while (cnt)//5秒后子进程退出{printf(child is running!,pid:%d, cnt:%d\n, getpid(), cnt);cnt--;sleep(1);}exit(0);//退出子进程}printf(father wait begin!\n);sleep(5);//父进程休眠5秒pid_t ret wait(NULL);if (ret 0){printf(father wait:%d,sucess\n, ret);}else{printf(father wait failed!\n);}sleep(5);//子进程被回收之后让父进程再运行5秒钟return 0; }我们可以使用以下监控脚本对进程进行实时监控 while :; do ps axj | head -1 ps axj | grep proc1 | grep -v grep;echo —————————————————————————————————————————————————————————————————;sleep 1;done运行结果如下 这时我们可以看到当子进程退出后父进程读取了子进程的退出信息子进程也就不会变成僵尸进程了。 由此得知我们可以通过wait()的方案解决回收子进程Z状态让子进程进入X。 3.2.waitpid函数 函数名称waitpid函数功能获取子进程结束时的状态头文件#includesys/wait.h函数原型pid_t waitpid(pid_t pid, int *status, int options)参数pid指定的子进程PID status子进程终止状态的地址 options控制操作方式的选项返回值1、 0 等待子进程成功当正常返回的时候waitpid返回收集到的子进程的进程ID 2、 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0 3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在 对于三个参数进一步说明 pid 1、pid-1等待进程组识别码为pid绝对值的任何子进程. 2、pid-1 等待任何子进程,相当于wait(). 3、pid0 等待进程组识别码与目前进程相同的任何子进程. 4、pid0 等待任何子进程识别码为pid的子进程. status 用下面的常用的两个宏: WIFEXITED(status): 若为正常终止子进程返回的状态则为真。(查看进程是否是正常退出)WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。(查看进程的退出码) options: 0默认行为阻塞等待(父进程什么都不做就是等待子进程退出)WNOHANG:设置等待方式为非阻塞等待 注意当一个进程非正常退出时说明该进程是被信号所杀那么该进程的退出码也就没有意义了 如果子进程已经退出调用wait/waitpid时wait/waitpid会立即返回并且释放资源获得子进程退出信息。如果在任意时刻调用wait/waitpid子进程存在且正常运行则进程可能阻塞。如果不存在该子进程则立即出错返回。 下面再来强调下第二个参数status status是一个输出型参数通过调用该函数从函数内部拿出来特定的数据也就是从操作系统拿到特定数据。 子进程退出的时候会将自己的退出信息写入自己的task_struct随后变成Z状态随后父进程调用wait / waitpid接口通过status把子进程的退出码拿到。 4.如何获取子进程status 4.1.如何理解status参数 wait和waitpid都有一个status参数该参数是一个输出型参数由操作系统填充。如果传递NULL表示不关心子进程的退出状态信息。否则操作系统会根据该参数将子进程的退出信息反馈给父进程。status不能简单的当作整形来看待可以当作位图来看待具体细节如下图(只研究status低16比特位) 解释上图 在status的低16比特位当中高8位表示进程的退出状态即退出码。进程若是被信号所杀则低7位表示终止信号而第8位比特位是core dump标志。 我们通过一系列位操作就可以根据status得到进程的退出码和退出信号。 exitCode (status 8) 0xFF; //退出码 exitSignal status 0x7F; //退出信号对于此系统中提供了两个宏来获取退出码和退出信号。 WIFEXITED(status)用于查看进程是否是正常退出本质是检查是否收到信号。WEXITSTATUS(status)用于获取进程的退出码。 exitNormal WIFEXITED(status);//是否正常退出 exitCode WEXITSTATUS(status);//获取退出码需要注意的是当一个进程非正常退出时说明该进程是被信号所杀那么该进程的退出码也就没有意义了。 4.2.获取退出码和退出信号 我们通过位运算可以根据status得到退出码和退出信号 #includestdio.h #includestring.h #includesys/types.h #includesys/wait.h #includeunistd.h #includestdlib.h int main() {pid_t idfork();if(id0){//childint cnt5;while(cnt){printf(child is running! pid:%d, cnt:%d\n,getpid(), cnt);cnt--;sleep(1);}exit(10);}printf(father wait begin!\n);sleep(10);//pid_t ret wait(NULL);int status0;pid_t retwaitpid(id,status,0);if(ret0){printf(father wait:%d sucess,status wait code:%d,status exit signal:%d\n,ret,(status8)0xFF,status0x7F);}else{printf(father wait failed!\n);}sleep(10);return 0; }结果如下 status的最低7位表示进程退出时收到的信号进程如果异常退出是因为这个进程收到了特定的信号我们先前kill -9 pid就是在进程异常时退出而发出的信号。下面来模拟下进程的异常退出 #includestdio.h #includestring.h #includesys/types.h #includesys/wait.h #includeunistd.h #includestdlib.h int main() {pid_t idfork();if(id0){//childwhile(1){printf(child is running! pid:%d, cnt:%d\n,getpid(), cnt);sleep(1);}exit(10);}printf(father wait begin!\n);sleep(10);//pid_t ret wait(NULL);int status0;pid_t retwaitpid(id,status,0);if(ret0){printf(father wait:%d sucess,status wait code:%d,status exit signal:%d\n,ret,(status8)0xFF,status0x7F);}else{printf(father wait failed!\n);}sleep(10);return 0; }这里子进程是在无限循环的父进程只能阻塞等待现在我们把子进程kill掉结果如下 如果kil -3 pid退出信号就是3……当进程收到信号的时候就代表进程异常了。 综上退出信号代表进程是否异常退出码代表程序跑完后的结果正确与否。 问一个进程退出的时候父进程会拿到退出码和退出信号那到底先看谁呢 一旦进程出现异常只关心退出信号退出码没有任何意义。 强调系统中的两个宏 status:WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码我们对于位操作是不是有点太过复杂和麻烦了我们可以用上文提到的宏来代替位操作 #includestdio.h #includestring.h #includesys/types.h #includesys/wait.h #includeunistd.h #includestdlib.h int main() {pid_t id fork();if (id 0){//childint cnt 5;while (cnt){printf(child[%d] is running! cnt is %d\n, getpid(), cnt);cnt--;sleep(1);}exit(11);}printf(father wait begin!\n);sleep(10);//pid_t ret wait(NULL);int status 0;pid_t ret waitpid(id, status, 0);if (ret0){if (WIFEXITED(status))//没有收到任何退出信号{ //正常结束获取对应的退出码printf(exit code:%d\n, WEXITSTATUS(status));}else{printf(error get a signal!\n);}}/*if (ret 0){printf(father wait:%d sucess,status wait code:%d,status exit signal:%d\n, ret, (status 8) 0xFF, status 0x7F);}else{printf(father wait failed!\n);}*/sleep(10);return 0; }结果展示 3.阻塞等待和非阻塞等待 再来强调下waitpid函数中的最后一个参数options当其值为0时就是阻塞等待当为WNOHANG时就是非阻塞等待。下面展开讨论 阻塞等待 如果子进程就是不退出(如死循环)怎么办呢我的父进程只能阻塞等待。 当我们调用某些函数的时候因为条件不就绪可能是任意的软硬件条件需要我们阻塞等待本质就是当前进程自己变成阻塞状态当条件就绪的时候再被唤醒。 详细说明 就是一个进程在系统层面上因为要等待某种事件发生如果这个事件并没有发生那么当前进程的代码和数据将无法运行此时就要进入阻塞状态也就是将父进程的task_struct的状态由R-S从运行队列投入到等待队列等待子进程退出当子进程退出了本质就是条件就绪那么就会逆向执行上述操作将进程从等待队列搬到运行队列并将状态由S-R。 举例 假设你叫李四是个大混子天天不学习马上就要考数据结构了为了能够及格你打电话给了你班的一位学霸朋友张三来让他教我整个过程我就是一个进程打电话的过程就是在调用接口张三就是所谓的OS操作系统当电话接通了但是张三说他在忙于是我让张三别挂电话我在电话这头一直等待你此时这个等待过程中我什么也没干只是等待张三也就是说父进程在等待期间不做任何事情这个过程就是阻塞等待。 非阻塞等待 举例 此时重复上述场景当电话接通后张三表示还在忙那么我直接挂电话此时我做些自己的事情忙了一会后又给张三打了个电话张三还在忙那么我又挂电话继续做自己的事情while(1)重复循环直至张三说自己ok了。 详细说明 整个过程我依旧是用户张三是OS操作系统打电话就等价于调用waitpid函数相当于是用户问操作系统子进程是否退出当OS回应没有时此时waitpid直接返回此时用户不会调用wait而将自己阻塞住此时用户在空闲时间段内做自己的事情做一会之后再去问OS操作系统好了没这个过程就叫做非阻塞等待。这种多次调用非阻塞接口就是轮询检测。 总结 1、阻塞等待死等就是上述的情况父进程一直等待子进程父进程不做任何事情。 2、非阻塞等待我们可以不要让父进程死等而是在等待期间父进程去做自己的事情等子进程退出时再来检测子进程的运行状态 代码示例 #includestdio.h #includeunistd.h #includestdlib.h #includesys/types.h #includesys/wait.hint main() {pid_t id fork();assert(id ! -1);if (id 0){//子进程while (1){printf(我是子进程我的pid%d, 我的ppid%d\n, getpid(), getppid());sleep(3);}exit(10);}else if (id 0){//父进程//基于非阻塞的轮询等待方案int status 0;while (1){pid_t ret waitpid(id, status, WNOHANG);// WNOHANG:非阻塞-子进程没有退出父进程检测时候立即返回if (ret 0){//waitpid调用成功子进程退出了printf(等待成功%d退出信号是%d退出码是%d\n, ret, status 0x7F, (status 8) 0xFF);break;}else if (ret 0){//(waitpid调用成功了)等待成功了但是子进程没有退出//子进程没有退出我的waitpid没有等待失败仅仅是检测到了子进程没有退出printf(子进程完成没还没有那么我父进程就做其他事情啦...\n);sleep(1);}else{//waitpid调用失败printf(waitpid调用失败了\n);}}}else{//do nothing }return 0; }运行结果就是父进程每隔一段时间就去查看子进程是否退出若未退出则父进程先去忙自己的事情过一段时间再来查看直到子进程推出后读取子进程的退出信息。 现在增加点设计模拟让父进程在等待的过程中做些自己的事情如下的代码 #includestdio.h #includeunistd.h #includestdlib.h #includesys/types.h #includesys/wait.h #define NUM 10 typedef void (*func_t)(); //函数指针 func_t handlerTask[NUM]; //样例任务 void task1() {printf(handler task1\n); } void task2() {printf(handler task2\n); } void task3() {printf(handler task3\n); } void loadTask() {memset(handlerTask, 0, sizeof(handlerTask));handlerTask[0] task1;handlerTask[1] task2;handlerTask[2] task3; } int main() {pid_t id fork();assert(id ! -1);if (id 0){//子进程while (1){printf(我是子进程我的PID%d, 我的PPID%d\n, getpid(), getppid());sleep(3);}exit(10);}else if (id 0){//父进程//基于非阻塞的轮询等待方案loadTask();int status 0;while (1){pid_t ret waitpid(id, status, WNOHANG);// WNOHANG:非阻塞-子进程没有退出父进程检测时候立即返回if (ret 0){// waitpid调用成功子进程退出了printf(等待成功%d退出信号是%d退出码是%d\n, ret, status 0x7F, (status 8) 0xFF);break;}else if (ret 0){//(waitpid调用成功了)等待成功了但是子进程没有退出//子进程没有退出我的waitpid没有等待失败仅仅是检测到了子进程没有退出printf(子进程完成没还没有那么我父进程就做其他事情啦...\n);for(int i 0; handlerTask[i] ! NULL; i){handlerTask[i](); // 采用回调的方式执行我们想让父进程在空闲的时候做的事情}sleep(1);}else{//waitpid调用失败printf(waitpid调用失败了\n);}}}else{//do nothing }return 0; }过程描述父进程每隔一秒就去看看自己进程退出没有若没有就继续等待若退出了就获取退出码。 四、进程替换 4.1 替换原理 程序替换的概念 用fork创建子进程后子进程执行的是和父进程相同的程序但有可能执行不同的代码分支若想让子进程执行另一个程序这就需要用到程序替换而完成程序替换需要用到exec函数。当进程调用一种exec函数时该进程的用户空间代码和数据完全被新程序替换并从新程序的启动李成开始执行。 为什么要完成程序替换 我们一般在服务器设计linux变成的时候往往需要子进程干两件种类的事情 让子进程执行父进程的代码片段服务器代码让子进程执行磁盘中一个全新的程序shell想让客户端执行对应的程序通过我们的进程。执行其他人写的进程代码等待如C/C自己写的 - C/C/Python/Shell/Php/java……别人写的 上述的第二点需求就是用我自己写的C/C代码调用别人不同语言的代码程序完成这一项需求就需要用到程序替换。 程序替换的原理 如下图所示 前面我们学习到当fork创建子进程的时候子进程的PCB、虚拟地址空间都以父进程为模板页表中的代码段指向的是父进程中的代码段数据也以写时拷贝的方式来和父进程进行共享如果现在有一个全新的程序b.exe并且我现在不想让子进程执行任何父进程相关的代码以及访问父进程的数据并执行的是b.exe程序此时把b.exe的程序加载到物理内存上让子进程重新调整自己的页表映射使其指向新的b程序的代码和数据这种过程就叫做程序替换。 总结程序替换的原理 将磁盘中的程序加载入内存结构重新建立页表映射谁执行程序替换就重新建立谁的映射最终达到的效果就是让父进程和子进程彻底分离并让子进程执行一个全新的程序 问1当进行程序替换时有没有创建新的进程 进程程序替换后该进程对应的PCB、进程地址空间以及页表等数据结构均没有发生改变只是重新建立了一下物理内存中的映射关系罢了它的内核对应的数据结构没有发生变化他的pid也没有发生变化也就没有创建新的进程只不过是让进程执行不同的程序罢了 问2子进程进行进程程序替换后会影响父进程的代码和数据吗 子进程刚被创建时与父进程共享代码和数据但当子进程需要进行进程程序替换时也就意味着子进程需要对其数据和代码进行写入操作这时便需要将父子进程共享的代码和数据进行写时拷贝此后父子进程的代码和数据也就分离了因此子进程进行程序替换后不会影响父进程的代码和数据。 问3为什么要加载到内存中呢 因为数据是存储在外设磁盘上的CPU距离内存最近读取是最方便的 代码演示 #includestdio.h #includeunistd.h int main() {printf(I am a process! pid:%d\n, getpid());execl(/usr/bin/ls,ls,-a,-l,NULL);//本该执行下面的hello程序的但是调用execl替换了代码和数据将会执行ls等命令printf(process done, pid:%d\n, getpid());return 0; }结果展示: 程序替换的本质就是把程序的代码和数据加载到特定的进程的上下文中 我们平时写的C/C程序要运行必须先加载到内存中如何加载呢——加载器(封装的底层就是exec系列的程序替换函数) 现在我们想让子进程也进行程序替换 #includestdio.h #includeunistd.h #includestdlib.h #includesys/wait.h int main() {if(fork()0){//childprintf(command begin...\n);execl(/usr/bin/ls,ls,-a,-l,-i,NULL);printf(command end...\n);exit(1);}//fatherwaitpid(-1,NULL,0);//等待任意一个子进程printf(father wait success!\n);return 0; }结果展示  当我们在执行子进程的替换时我们的父进程照常执行自己的任务两者之间互不影响呢那是因为进程之间具有独立性 4.2 替换函数 其实有6种以exec开头的函数统称为exec函数 六种替换函数实际上七种换汤不换药 上述过程中的程序替换是我们通过调用接口函数来让OS操作系统帮助我们完成的此接口就是替换函数替换函数有六种以exec开头的函数他们统称为exec函数 #include unistd.h int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ...,char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);只要程序替换成功就不会执行后续的代码意味着exec* 系列的函数执行成功的时候就不需要返回值检测只要exec* 返回了那就意味着替换失败调用函数也失败了(返回-1)所以exec函数只有出错的返回值而没有成功的返回值 4.4 exec*接口介绍 函数名参数格式是否带路径是否使用当前环境变量execl列表不带是execlp列表带是execle列表不带不是需要自己组装环境变量execv数组不带是execvp数组带是execve数组不带不是需要自己组装环境变量 函数解释 这些函数如果调用成功则加载新的程序从启动代码开始执行不再返回如果调用出错则返回-1所以exec函数只有出错的返回值而没有成功的返回值 命名理解 这六个exec系列函数的函数名都以exec开头其后缀的含义如下 l(list)表示参数可用列表的形式一一列出v(vector)表示参数采用数组的形式p(path)表示能自动搜索环境变量PATH进行程序查找e(env)表示可以传入自己设置的环境变量 事实上只有execve是真正的系统调用其它五个函数最终都是调用的execve所以execve在man手册的第2节其它五个函数在man手册的第3节也就是说其它五个函数实际上是对系统调用execve进行了封装以满足不同用户的不同调用场景的需求。 下图为exec系列函数族之间的关系 4.5.替换函数使用实例 我们如果要执行一个全新的程序需要做2件事情 先找到这个程序在哪里 程序可能携带选项进行执行也可以不携带要明确告诉OS操作系统我想怎么执行这个程序要不要带选项 下面几个替换函数均是按照上述逻辑执行的。 1、execl int execl(const char *path, const char *arg, ...);第一个参数是要执行程序的路径第二个参数是可变参数列表我们可以按照用户的意愿传入数量大小不等的参数表示你要如何执行这个此程序命令行怎么写ls -l -a这个参数就怎么填 “ls”“-l”“-a”并以NULL结尾。 示例 假设我执行的是ls -l -a程序 当我make之后会生成myexec可执行程序现在运行此程序 当我执行top、pwd命令也亦是同样的操作 此时一个现象就产生了当我调用execl替换程序函数成功后后面的printf语句并没有执行原因就在于一旦execl替换成功是将当前进程的代码和数据全部替换了后面的printf自然就被替换了即该代码就不存在了。 问程序替换用不用判断返回值 程序替换不用判断返回值这就是我们前面提到的因为一旦程序替换成功了就不会有返回值而失败的时候必然会向后执行最多通过返回值得到什么原因导致的替换失败。 下面演示进程替换失败的场景只需随便执行一个不存在的程序即可 我们的上述操作是一个单进程程序我没有创建子进程相当于是把父进程的代码给替换了现在想让子进程完成替换操作如下 上述操作就完成了让子进程执行全新的程序以前是执行父进程的代码片段运行结果如下 问子进程执行程序替换会不会影响父进程呢 答案是不会的这个我上面详细讲解过总结就是进程具有独立性当程序替换的时候代码和数据都发生了写时拷贝完成父子的分离。 #include stdio.h #include unistd.hint main() {printf(我是一个进程我的pid:%d\n, getpid());// ls -l -a; execl(usr/bin/ls, ls, -l, -a, NULL);printf(我执行完了我的pid:%d\n, getpid()); return 0; } 2、execv int execv(const char *path, char *const argv[]);第一个参数是要执行程序的路径第二个参数是一个字符指针数组数组当中的内容表示你要如何执行这个程序数组以NULL结尾 例如要执行的是ls -l -a -i char *const argv_[] { (char*)ls, (char*)-a, (char*)-l, (char*)-i, NULL }; execv(/usr/bin/ls, argv_);前面的execl是一个一个参数传过去的这里execv是直接传一个指针数组示例如下 补充vim批量化注释、取消注释小技巧 注释小写ctrlvhjkl选中区域切换大写输入I//esc取消注释小写ctrlvhjkl选中区域注释区域输入d 3、execlp int execlp(const char *file, const char *arg, ...);第一个参数是要执行程序的名字第二个参数是可变参数列表表示你要如何执行这个程序并以NULL结尾 例如要执行的是ls -a -l execlp(ls, ls, -a, -l, NULL );它和execl唯一的区别就是多了一个p在我们执行指令的时候默认的搜索路径是在环境变量PATH搜索的而execl中的p就是此PATH环境变量前面我们的execl和execv都是要指明路径的而这里execlp命名带p了因此就可以不带路径只说出你要执行哪一个程序即可。示例 这里出现了两个ls含义是不一样的第一个参数是供系统去找你要执行谁的第二个是你想怎么执行它。 4、execvp int execvp(const char *file, char *const argv[]);第一个参数是要执行程序的名字第二个参数是一个字符指针数组数组当中的内容表示你要如何执行这个程序数组以NULL结尾 例如要执行的是ls -a -l -i char *const argv_[] { (char*)ls, (char*)-a, (char*)-l, (char*)-i, NULL }; execvp(ls, argv_);此替换函数同样是带了p所以它是从PATH路径里头找我们只需要程序名即可还带了v我们就可以将命令行参数字符串统一放入数组中即可完成调用。示例 5、execle 在正式讲解此替换函数execle前先来看这样一个问题 目前我们执行的程序全部都是系统命令如果我们要执行自己写的C/C程序呢如果我们要执行其它语言写的程序呢 首先一次makefile默认只能生产一个可执行程序但是按如下修改一次可生产多个可执行程序 现在有两个可执行程序我现在的目标是让myexec把mycmd调起来代码如下 execl(/home/xzy/dir/date16/mycmd, mycmd, NULL);//绝对路径 //execl(./mycmd, mycmd, NULL);相对路径详情如下 上述实现了C可执行程序调C可执行程序现在来实现C可执行程序调用python可执行程序如下一个python小脚本 现在想让C可执行程序myexec来调用python只需如下操作 execl(/usr/bin/python3, python3, test.py, NULL); 解决了刚刚那个问题现在再回过头来看execle替换函数 int execle(const char *path, const char *arg, ..., char * const envp[]);第一个参数是要执行程序的路径第二个参数是可变参数列表表示你要如何执行这个程序并以NULL结尾第三个参数是你自己设置的环境变量 以我设置的MYPATH环境变量为例 char* const env_[] { (char*)1314520, NULL }; execle(./mycmd, mycmd, NULL, env_);execle的第三个参数表示的是如果你想给你的程序传入对应的环境变量信息也是一个字符指针数组那就可以传入对应的环境变量参数示例 如下我们的mycmd.cpp文件 如上我输出的是系统PATH中的环境变量现在我想自定义环境变量MYPATH并交给子进程我的myexec.c程序如下 这里有个错误 execle(./mycmd, mycmd, NULL, env_);当我make后并运行myexec可执行程序会发现有个问题 当我自定义一个环境变量时运行myexec我PATH环境变量就输出不了了当我把mycmd.cpp中的PATH语句那块给注释掉再运行myexec此时就能看到我自定义的环境变量MYPATH了 为什么我把PATH的注释取消了却输出不了我的PATH环境变量相反的把PATH注释掉才能打印我的MYPATH 根本原因自己添加环境变量给目标进程是覆盖式的 解决办法如下 使用environ系统环境变量的指针声明利用export将MYPATH添加到系统环境变量中 6、execve int execve(const char *filename, char *const argv[], char *const envp[]);第一个参数是要执行程序的路径第二个参数是一个指针数组表示你要如何执行这个程序数组以NULL结尾第三个参数是你自己设置的环境变量 例如你设置了MYPATH环境变量在myexec程序内部就可以使用该环境变量 char* my_argv[] { myexec, NULL }; char* my_envp[] { MYPATHhelloWorld, NULL }; execve(./myexec, my_argv, my_envp);7、execvpe int execvpe(const char *file, char *const argv[], char *const envp[]);第一个参数是要执行程序的路径第二个参数是一个指针数组表示你要如何执行这个程序数组以NULL结尾第三个参数是你自己设置的环境变量 此替换函数在我们前面讲解的基础上已经不难理解了就不给出测试用例了。 问为什么会有这么多接口execve为什么是单独的 其实替换函数一共有7个只不过execve是单独的 有多个接口的原因在于调用替换函数的场景各不相同所以有不同的接口execve是单独的原因其实我在命名理解那也说明了这里再强调下 事实上只有execve是真正的系统调用其它五个函数最终都是调用的execve所以execve在man手册的第2节其它五个函数在man手册的第3节也就是说其它五个函数实际上是对系统调用execve进行了封装以满足不同用户的不同调用场景的需求。
http://www.w-s-a.com/news/311805/

相关文章:

  • 内蒙古工程建设协会网站sem优化策略
  • Linux网站建设总结建设电子商务平台
  • 公司网站背景图片课程网站如何建设
  • 用js做简单的网站页面互联网技术对人力资源管理的影响有哪些
  • 银川做网站贵德县wap网站建设公司
  • 深圳网站建设zvge山西省煤炭基本建设局网站
  • 佛山网页网站设计线上怎么做推广和宣传
  • 多个域名绑定同一个网站案例
  • 建设网站都需要准备什么代理加盟微信网站建设
  • 网站备案没有了wordpress 添加按钮
  • 湖南建设银行宣传部网站福田蒙派克空调滤芯安装位置图
  • wap网站搜索wordpress工作室模板
  • 青岛金融网站建设如何提交网站地图
  • 制作简单门户网站步骤网站建设论文的摘要
  • 可以直接进入网站的正能量照片学做静态网站
  • 织梦做社交网站合适吗网站的市场如何制作
  • 阳曲网站建设价格多少四川佳和建设工程网站
  • 免费注册店铺位置sem seo什么意思
  • 建筑网站搜图电子商务网站建设渠道
  • 学校网站内容四川手机网站开发
  • 网站制作公司违法商业网站运营成本
  • 显示佣金的网站是怎么做的广告设计主要做哪些
  • 做阿里网站的分录济南seo网站排名关键词优化
  • 北京建设银行纪念钞预定官方网站wordpress中文优化版
  • 宝安做棋牌网站建设找哪家效益快创意设计师个人网站
  • 做线上网站需要多少钱系统开发板价格
  • 建筑企业登录哪个网站wordpress feed地址
  • 网站建设流程百科提升seo搜索排名
  • 杭州网站建设 巴零做销售怎么和客户聊天
  • 北京自己怎样做网站wordpress oauth2插件