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

怎么样网站开源如何做百度竞价推广

怎么样网站开源,如何做百度竞价推广,展馆设计的主题有哪些,黄页号码怎么删除掉目录 一、 进程创建 fork函数 二、进程的终止#xff1a; 1. 想明白#xff1a;终止是在做什么#xff1f; 2.进程终止的3种情况#xff1f; a.退出码是什么#xff1f;存在原因#xff1f;为什么int main#xff08;#xff09;return 0? b.第三种进程终止的情况…目录 一、 进程创建 fork函数 二、进程的终止 1. 想明白终止是在做什么 2.进程终止的3种情况 a.退出码是什么存在原因为什么int mainreturn 0? b.第三种进程终止的情况异常-- 代码执行时出现了异常提前退出 一个进程退出原因只需要知道退出码、退出信号 三、如何终止 a. main 函数return 表示进程终止。 (非main函数return表示什么?叫做函数结束不一定代表进程结束) b. exit函数引起一个正常的进程终止。 任意位置调用 exit() 都表示进程终止 c. _exit()   --- system call(系统调用) exit() c库函数vs _exit()系统调用 进程退出时内核会执行以下操作 释放流程总结详细版 四.怎么避免成为僵尸进程而导致内存泄漏 进程等待必要性 wait() 等待的时候解决子进程的僵尸状态 如何理解阻塞等待子进程 waitpid() 获取子进程status 可以不使用位操作来提退出信息。 非阻塞等待 五、进程程序替换 替换原理 1. 通过代码看现象 2. 解释原理 3. 将代码改成多进程版 4. 使用所有的替换方法并且认识函数参数的含义 1.exec ---- l -- list 代表的是命令列表 2.execv -- v --- vector类似于c中所学到的动态数组 vector, 使用的时候就是现将要执行的选项放进数组里然后再进行传参 3.execvp -- p ---- 用户可以不穿要执行文件的路径要传文件名直接告诉exec*, 我要执行谁就行p: 查找这个程序系统会自动在环境变量PATH中进行查找。 4.execlp - 程序替换 --- 替换自己写的程序 调用其他语言的程序 5.execvpe   ---- e  ---- evironment --- 环境变量允许我们自定义 6.execve ---真正的系统调用 一、 进程创建 fork函数 在linux中fork函数时非常重要的函数它从已存在进程中创建一个新进程。新进程为子进程而原进程为父进程。 #include unistd.h pid_t fork(void);进程内核的相关管理数据结构(tast_struct mm_struct 页表)  代码和数据 进程调用fork当控制转移到内核中的fork代码后内核做 1.分配新的内存块和内核数据结构给子进程 2.将父进程部分数据结构内容拷贝至子进程 3.添加子进程到系统进程列表当中 4.fork返回开始调度器调度 写时拷贝 fork函数具体使用和写时拷贝可以查看我的上一篇博客 linux操作系统内存管理核心解析地址空间、页表与进程调度原理-CSDN博客 返回值子进程中返回0父进程返回子进程id出错返回-1 因为return的本质就是向父进程进行写入写入发生写时拷贝。 为什么给父进程返回的是子进程的PID子进程返回的是0 方便父进程对子进程进行标识从而对子进程进行管理通过0值知道子进程创建成功 fork常规用法 1.一个父进程希望复制自己使父子进程同时执行不同的代码段。例如父进程等待客户端请求生成子进程来处理请求。 2.一个进程要执行一个不同的程序。例如子进程从fork返回后调用exec函数。 fork调用失败的原因 1.系统中有太多的进程 2.实际用户的进程数超过了限制 二、进程的终止 1. 想明白终止是在做什么 创建进程是先创建好内核的相关管理数据结构还是先创建好代码和数据          先 创建好内核的相关管理数据结构  再 创建好代 码和数据 终止是在释放曾经的代码和数据所占据的空间。再释放内核数据结构 2.进程终止的3种情况 a.退出码是什么存在原因为什么int mainreturn 0? 使用vim编译器写一段代码 #includestdio.h#includeunistd.hint main() {printf(im a process, pid: %d, ppid: %d\n,getpid(),getppid());sleep(1);return 0; } 这是我两次运行打印出的结果 im a process, pid: 562487, ppid: 553748im a process, pid: 562539, ppid: 553748现在将main函数的返回值改为100 再次运行程序的结果 im a process, pid: 563668, ppid: 553748im a process, pid: 563689, ppid: 553748此时在命令行输入echo $?,输出结果100 echo $? 100变量?  在shell中存在一个变量?    ----使用‘$’ 查看 echo 是 内建命令打印的都是bash内部的变量数据。 ?   ----  父进程bash获取到的最近一个子进程退出的退出码退出码为0表示成功!0表示失败。 进程的退出码是告诉关心方父进程任务完成的情况失败的原因是什么。 不是说 echo $ 是查看最近一次进程的退出码吗 因为 echo $? 也是一个进程 不同的非0值一方面表示失败一方面表示失败原因每一个数字都有对应的错误描述字符串类型。 运行以下代码 #includestdio.h#includeunistd.h#includestring.hint main() { for(int errcode 0; errcode 255; errcode){printf(errcode%d, %s\n,errcode, strerror(errcode));}printf(im a process, pid: %d, ppid: %d\n,getpid(),getppid());sleep(1);return 0; }其中的strerror:把错误码转换成一段描述错误信息的字符串。 运行程序一共有133条错误码的提示。我们发现在进行指令操作或者运行程序失败后系统会直接告诉我们错误的原因。 因此进程的退出码是告诉关心方父进程任务完成的情况失败的原因是什么。bash是为用户负责 运行以下代码 #includestdio.h#includeunistd.h#includestring.hint Div(int x, int y) {if( 0 y){return -1;}else{return x / y;} }int main() { int result 0;result Div(10, 0);printf(result %d\n, result);sleep(1);return 0; } 运行结果 result -1虽然我知道代码是什么但看到result -1, 会认为是结果为-1还是因为错误的 -1 再写以下代码 #includestdio.h#includeunistd.h#includestring.h//自定义枚举常量 enum {Success 0,DIV_ZERO,Mod_Zero, }; int exit_code Success;const char* CodeToErrString(int code) {switch(code){case Success:return Success!;case DIV_ZERO:return div zero!;case Mod_Zero:return mod zero!;default:return unknown error!;} }int Div(int x, int y) {if( 0 y){exit_code DIV_ZERO;return -1;}else{return x / y;} }int main() {int result 0;result Div(10, 5);printf(result %d [%s]\n, result, CodeToErrString(exit_code));result Div(10, 0);printf(result %d [%s]\n, result, CodeToErrString(exit_code));sleep(1);return 0; }输出结果 result 2 [Success!] result -1 [div zero!]此时的结果就是正确的 进程终止的前两种情况 1.代码跑完结果正确 2.代码跑完结果不正确 main函数的返回值是给父进程的这叫做进程的退出码用来标明进程退出结果的正确与否 b.第三种进程终止的情况异常-- 代码执行时出现了异常提前退出 直接除0并返回 运行结果 vs 编译运行的时候崩溃了操作系统发现进程做了不该做的事情因此杀掉了进程 异常之后退出码还是否有意义 没有意义。只关心异常的原因 进程出异常是因为操作系统发现后给进程发送了信号。 当我们只运行以下代码 运行结果正常情况下一直运行 也可以直接用kill -9 pid 命令杀掉进程 又运行代码 输出结果 segmentation fault  --- 段错误也就是野指针os提前终止进程。这就是一种异常。 退出信号 因为进程出现异常后退出码失去了意义但是还可以看退出信号判断进程异常的原因是什么。               一个进程退出原因只需要知道退出码、退出信号 退出码退出信号进程终止情况00成功00没异常但结果不对00进程出现异常00 这些信号告诉父进程如bash, 一个进程退出后会将进程的退出码、退出信号写入到进程的pcb当中当一个进程成为了僵尸进程已经删释放了代码和数据但是会维持一段时间pcb不释放编程z状态这就是因为要让父进程知道进程退出的情况退出信号、退出码保存到了进程的pcb中 int sig_code、int exit_code 当中 看下面几张图 进程的task_struct 把所有的进程用一个双链表链接在一起   tast_struct中的 exit_code、exit_signal  三、如何终止 a. main 函数return 表示进程终止。 (非main函数return表示什么?叫做函数结束不一定代表进程结束) b. exit函数引起一个正常的进程终止。 头文件stdlib.h, 参数status 在代码中 运行程序后echo $? 执行代码 运行后结果此时exit()是写在非main函数中的这是函数返回呢还是进程终止 任意位置调用 exit() 都表示进程终止 c. _exit()   --- system call(系统调用) 运行下面代码 运行结果  将_exit()写入函数内部 运行结果 作用和exit()类似区别是  运行下面代码 结果 去掉\n: 结果运行程序时前3秒这个还没有打印出来运行结束时才打印出来没打印出来的时候存在缓冲区 exit()作用是在进程退出时冲刷缓冲区 改成_exit() 运行 运行了但是没打印退出结果也是3 exit() c库函数vs _exit()系统调用 exit()作用是在进程退出时帮我们冲刷缓冲区, _exit()不会因为缓冲区不是内核缓冲区不在系统调用和操作系统内部因此刷新不了。而实际上exit()在底层上就调用的_exit()。杀掉进程让进程退出。 进程退出时内核会执行以下操作 释放用户空间资源 代码段、数据段、堆、栈等内存空间。 用户空间的缓冲区由 exit() 处理_exit() 不处理。 释放内核空间资源 关闭所有打开的文件描述符由内核自动处理无论是否调用 exit()。 释放进程的页表、信号处理结构、定时器等内核数据结构。 PCB进程控制块内核会保留 PCB 直到父进程通过 wait() 读取子进程的退出状态ZOMBIE 状态。之后 PCB 才会被彻底释放。 通知父进程通过 SIGCHLD 信号和退出状态码。 task_struct进程控制块PCB作用描述进程的所有信息是内核管理进程的核心结构。 包含内容 进程状态运行、就绪、僵尸等。 进程标识符PID、PPID。 调度信息优先级、时间片、调度策略。 内存管理指针指向 mm_struct描述进程的虚拟内存空间代码段、堆、栈等。 打开的文件描述符表指向 files_struct管理进程打开的文件。 信号处理表、资源限制、父子进程关系等。    释放流程总结详细版  用户空间资源释放 代码、数据、堆栈内存通过释放 mm_struct 和 vm_area_struct 描述进程的每一段虚拟内存如代码段、堆、栈、内存映射文件等回收。  内核资源释放 关闭文件描述符释放 files_struct 和 file 对象。 取消信号处理和定时器。  保留 PCBtask_struct 等待父进程调用 wait() 后释放。  缓存和共享资源 dentry、inode 等由内核缓存机制延迟释放。 进程等待 任何子进程在退出的情况下一般必须要由父进程等待进程在退出时。父进程不管就会变成Z(僵尸进程)一直占用系统内核内存 --- 内存泄漏。 为什么进程等待 四.怎么避免成为僵尸进程而导致内存泄漏 进程等待必要性 之前讲过子进程退出父进程如果不管不顾就可能造成‘僵尸进程’的问题进而造成内存泄漏。另外进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力因为谁也没有办法杀死一个已经死去的进程。 最后父进程派给子进程的任务完成的如何我们需要知道。如子进程运行完成结果对还是不对或者是否正常退出。 父进程通过进程等待的方式 1.回收子进程资源 2. 获取子进程退出信息。 wait() pid_t wait(int* status); wait函数成功后会返回进程的pid等待父进程中任意一个子进程退出。 如果子进程已经退出调用wait/waitpid时wait/waitpid会立即返回并且释放资源获得子进程退出信息。 如果在任意时刻调用wait/waitpid子进程存在且正常运行则进程可能阻塞。 如果不存在该子进程则立即出错返回。 等待的时候解决子进程的僵尸状态 在vim编译器中写入以下代码 ​​​​​等子进程运行完毕并且进程退出父进程休眠10s,再对子进程进行回收让我们能短暂的看到子进程退出之后所形成的僵尸状态当父进程进行回收的时候子进程的僵尸状态消失3秒之后父进程退出。 #includestdio.h#includeunistd.h#includestring.h#includestdlib.h#includesys/types.h#includesys/wait.hvoid ChildRun {int cnt 5;while(cnt){printf(im child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid());sleep(2);cnt--;} }int main() {printf(I am father, pid: %d, ppid: %d\n, getpid(), getppid());pid_t id fork();if(id 0){//childChildRun();printf(child quit ...\n);exit(0);}//fathersleep(10);pid_t rit wait(NULL);if(rid 0){printf(wait Success, rit: %d\n, rit);}sleep(3);return 0; } 每隔一秒显示进程列表的表头列名,查找名为 myprocess 的进程并排除 grep 自身。 while : ; do ps ajx | head -1 ps ajx | grep myprocess | grep -v grep; sleep 1; done 运行 就算没有sleep(10)(只是为了便于我们观察), 如果子进程没有退出父进程其实一直在进行阻塞等待。在这个过程中子进程本身就是软件父进程本质是在等待某种软件条件继续。 如何理解阻塞等待子进程 把父进程的状态设置为非运行状态将父进程的pcb链入到子进程当中当子进程执行完毕再唤醒父进程。 waitpid() 此时这样写所表示的作用和wait(NULL)完全相同所表示的是等待任何一个子进程退出那个子进程退出就返回这个子进程所对应的rid返回 此时所表示的是等待的是指定id的子进程这就是为什么需要给父进程返回子进程id的原因 运行代码 void ChildRun() {int cnt 5;while(cnt){printf(im child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt);sleep(2);cnt--;} }int main() {printf(I am father, pid: %d, ppid: %d\n, getpid(), getppid());pid_t id fork();if(id 0){//childChildRun();printf(child quit ...\n);exit(0);}//fathersleep(15);//pid_t rid wait(NULL);//pid_t waitpid(pid_t pid, int* wstatus, int options) ; pid_t rid waitpid(id, NULL, 0);if(rid 0){printf(wait Success, rid: %d\n, rid);}sleep(3);printf(father quit ...\n);return 0; } 运行结果 等待失败的案例写一个不存在的子进程id 运行结果 获取子进程status 如何获得子进程退出的信息 -- 参数status status -- 输出型参数  -- 表示的是子进程的退出信息  修改代码   运行结果status 256 子进程退出的信息需要什么 进程退出码 进程退出信号 只能使用waitpid()的方式来拿到子进程的退出信号 难道不能使用全局变量,exit_code, exit_signal?  不能 在子进程退出的时候将这两个全局变量设置成退出码和退出信号就能让父进程拿到吗不可以  设置全局变量子进程退出的时候值也设置了但是父进程根本就看不到因为父子进程具有独立性在子进程退出的时候临时修改os都会发生写时拷贝。 想要看到退出码和退出信号需要使用正确的查看方式status是有自己的特殊格式的 status不能简单的当作整形来看待可以当作位图来看待具体细节如下图只研究status低16比特位                 次低八位退出状态        低七位退出时如果发生异常所收到信号 打印出位图status按位与的时候不会改变其值  运行结果子进程退出码为1退出信号为0符合我们的预期。 再将子进程写成死循环 父进程只能一直阻塞等待 使用  kill -9 杀掉子进程得到退出码 0,退出信号 9 当子进程有异常 运行退出码 0 退出信号11 11是因为野指针退出的 可以不使用位操作来提退出信息。 status:         WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出         WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码 options:         WNOHANG: 若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进程的ID。 修改代码 #includestdio.h#includeunistd.h#includestring.h#includestdlib.h#includesys/types.h#includesys/wait.hvoid ChildRun() {int cnt 5;while(cnt){printf(im child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt);sleep(2);cnt--;} }int main() {printf(I am father, pid: %d, ppid: %d\n, getpid(), getppid());pid_t id fork();if(id 0){//childChildRun();printf(child quit ...\n);exit(1);}//fathersleep(15);//pid_t rid wait(NULL);//pid_t waitpid(pid_t pid, int* wstatus, int options) ; int status 0;pid_t rid waitpid(id, status, 0);if(rid 0){if(WIFEXITED(status)){printf(child quit success, child quit code: %d\n, WEXITSTATUS(status));}else{printf(child quit unnormal\n);}printf(wait Success, rid: %d\n, rid);}else{printf(wait fail!\n);}sleep(3); }运行结果子进程退出码是1 现在写一个异常代码 运行结果子进程非正常退出 父进程等待子进程是必须的获取子进程退出信息不是必须的 现在让父进程不sleep(15)了 如果子进程没有退出二父进程在执行waitpid进行等待。 阻塞等待   ---- 进程阻塞了 ---  进程状态从 r 状态 变成非 r 状态。在等待某种条件发生例如今上文说的子进程退出。 如何不阻塞等待因为此时父进程除了等待没做其他事 非阻塞等待 pid_t waitpid(pid_t pid, int *status, int options) ---当 options 0就默认是阻塞等待 宏WNOHANG  ---  以非阻塞等待    HANG -- hang(服务器卡住了服务器hang住了) 服务器hang很久计算机挂掉  ---- 服务器宕机了。WNOHANG --wait no hang等待的时候不要hang住。 非阻塞等待的特点优点 1. 检测子进程状态的变化是否就绪 阻塞等待 pid_t 0 : 等待成功子进程退出了并且父进程回收成功并退出 pid_t 0 :等待失败了 非阻塞等待 pid_t    0:检测是成功的只不过子进程还没有退出需要你下一次进行重复等待。 非阻塞等待接口 循环    非阻塞轮询方案 2. 在非阻塞轮询的时候允许父进程做其他的事情 实现以上描述的代码  子进程开始进行父进程检测子进程状态变化当子进程还没有结束时父进程会再次检查子进程状态直到id0即子进程退出父进程再检测子进程退出状态检测完成后父进程退出。 void ChildRun() {int cnt 5;while(cnt){printf(im child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt);sleep(3);cnt--;} }int main() {printf(I am father, pid: %d, ppid: %d\n, getpid(), getppid());pid_t id fork();if(id 0){//childChildRun();printf(child quit ...\n);exit(123);}//father//循环对子进程的状态进行访问while (1) {int status 0;pid_t rid waitpid(id, status, WNOHANG);if (rid 0) {printf(child is running, father check next time!\n);sleep(1); // 避免高频轮询适当休眠} else if (rid id) { // 或 rid 0if (WIFEXITED(status)) {printf(child quit success, code: %d\n, WEXITSTATUS(status));} else {printf(child quit abnormally\n);}break;} else { // rid -1出错printf(waitpid failed!\n);break;}} } 运行结果 实际状况下父进程的检测更为频繁。可以自己使用这个速度来观测一下。 当前我们设计一个父进程在检测子进程状态时去做其他任务的场景这个代码加入到上面的代码中 typedef void(*func_t)();//定义一个函数指针类型//想让父进程完成的任务 #define N 3 func_t task[N] {NULL};//加载任务 void LoadTask() { }//处理凡是在任务列表中的任务 void HanderlTask() { }//做除了等待子进程外的其他任务 void DoOtherThing() {HandlerTask(); }再创建两个文件作为我们的任务 task.c #includetask.hvoid PrintLog() {printf(开始打印我的 LOG...\n);}void Download() {printf(开始下载软件...\n);} //数据库同步void MysqlDataSync() {printf(数据正在同步...\n);} task.h #pragma once#includestdio.hvoid PrintLog();void Download();void MysqlDataSync();然后在myprocess.c中 #includestdio.h#includeunistd.h#includestring.h#includestdlib.h#includesys/types.h#includesys/wait.h#includetask.htypedef void(*func_t)();//定义一个函数指针类型//想让父进程完成的任务 #define N 3 func_t task[N] {NULL};//加载任务 void LoadTask() //函数名就是函数的地址此时将地址交给这个函数指针此时相当于有一张函数表 {task[0] PrintLog;task[1] Download;task[2] MysqlDataSync; }//处理凡是在任务列表中的任务 void HandlerTask() {for(int i 0; i N; i){task[i]();} }//做除了等待子进程外的其他任务 void DoOtherThing() {HandlerTask(); }void ChildRun() {//int* p NULL;int cnt 5;while(cnt){printf(im child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt);sleep(1);cnt--;// *p 100;} }int main() {printf(I am father, pid: %d, ppid: %d\n, getpid(), getppid());pid_t id fork();if(id 0){//childChildRun();printf(child quit ...\n);exit(123);}//启动父进程之前先加载任务LoadTask();//father//循环对子进程的状态进行访问while(1){int status 0;pid_t rid waitpid(id, status, WNOHANG);//非阻塞 non block//查询到子进程状态但是子进程还没有退出if(rid 0){usleep(1000);printf(child is running, father check next time!\n);DoOtherThing();}//等待子进程退出成功else if( rid id){//检测是否是正常退出if(WIFEXITED(status)){printf(child quit success, child quit code: %d\n, WEXITSTATUS(status));}else{printf(child quit unnormal\n);}break;}else{printf(waitpid failed!\n);}}运行程序上面省略 child is running, father check next time! 开始打印我的 LOG... 开始下载软件... 数据正在同步... child is running, father check next time! 开始打印我的 LOG... 开始下载软件... 数据正在同步... child is running, father check next time! 开始打印我的 LOG... 开始下载软件... 数据正在同步... child is running, father check next time! 开始打印我的 LOG... 开始下载软件... 数据正在同步... child quit ... child is running, father check next time! 开始打印我的 LOG... 开始下载软件... 数据正在同步... child quit success, child quit code: 123这是为了证明在非阻塞状态当父进程在进行轮询检测时还能够做其他的事情 编译文件之前记得修改makefile 从此以后我们可以直接修改task.c中的代码来让父进程做任何事情基于函数指针级别的对于父进程完成任务进行解耦。有了多进程将来就可以创建很多进程将来可以让不同子进程进行不同的任务让不同的程序来跑其他的任务。 五、进程程序替换 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。 1. 通过代码看现象 替换函数 其实有六种以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[]) 参数中的 ‘...’ 表示可变参数 这里的execl就类似于printf函数。 如何快捷的在vim编译器中修改替代一个字符串 %s/要修改的字符串/修改后的字符串/g,如修改makefile文件经常使用 代码 运行结果  请注意ls命令就在 usr/bin/ls 处这里也没有打印出我写的testexec ... end!这个字符串 这个代码已经被替换 通过 file /usr/bin/ls 实际上ls就是用c语言写的一个可执行二进制程序 exec*函数的作用就是让我们(我们写的程序):执行起来新的程序来替换我们写的程序 2. 解释原理 进程 内核数据结构 代码和数据 程序替换在执行进程的时候保留原有的内核数据结构结构本身不变(部分属性值更改)再将新程序的代码和数据和原有的在物理内存上的代码和数据进行替换覆盖取代原有的代码和数据。 在进行替换的时候没有创建新的进程。 查看此图 程序运行之前会先被加载到内存里根据冯诺依曼定理我的程序最总是会被cpu访问的cpu只能就近访问内存是通过 exec* 函数类似于一种Linux上的加载函数加载的。 exec* 函数的返回值不需要关心只要替代成功就不会运行后面的代码反之只要后面的代码运行了就没有替换成功因此不需要通过返回值来判断是否替换成功。 想要进行程序替换但是不想要影响父进程本身 3. 将代码改成多进程版 检测一次失败的替换  #includestdio.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/wait.hint main() {printf(testexec ... begin\n);pid_t id fork();if(id 0){//childsleep(2);execl(/usr/bin/lsss, lsss, -all, NULL);exit(1);}//fatherint status 0;pid_t rid waitpid(id, status, 0);if(rid id){printf(father wait success, child exit code : %d\n, WEXITSTATUS(status));}printf(testexec ... end\n);return 0; }运行结果 修改代码后ls的名字的修改 创建子进程让子进程完成任务 1.让子进程执行父进程代码的一部分 2.让子进程执行一个全新的程序 3.在子进程要执行新程序的时候进行程序替换的时候不仅数据发生了写时拷贝代码也发生了写时拷贝不再是仅仅是共享而不能写入。至此父子进程在数据结构和代码层面彻底分开。因此不会对父进程产生影响。 4. 使用所有的替换方法并且认识函数参数的含义 程序替换的接口都是什么意思 int execl(const char *pathname, const char *arg, .../* (char *) NULL */);int execlp(const char *file, const char *arg, .../* (char *) NULL */);int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);int execv(const char *pathname, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[],char *const envp[]); 1.exec ---- l -- list 代表的是命令列表 execl( /usr/bin/ls , ls, -a , -l , -i, -n, NULL); path要执行的程序需带路径。函数需要知道怎么找到程序需要告诉函数 arg ---- 选项 在命令行中怎么执行就怎么传参例如命令后的选项带多少个都可以 ls -a -l -i -n 标准写法以NULL结尾可以省略但是建议不省略 int execv(const char *pathname, char *const argv[]); 2.execv -- v --- vector类似于c中所学到的动态数组 vector, 使用的时候就是现将要执行的选项放进数组里然后再进行传参 #includestdio.h #includestdlib.h #includeunistd.h #includesys/types.h #includesys/wait.hint main() {printf(testexec ... begin\n);pid_t id fork();if(id 0){//childchar* const argv[] {ls,-l,-a,--color,NULL};sleep(2);// execl(/usr/bin/ls, ls, -all, NULL);execv(/usr/bin/ls, argv);exit(1);}//fatherint status 0;pid_t rid waitpid(id, status, 0);if(rid id){printf(father wait success, child exit code : %d\n, WEXITSTATUS(status));}printf(testexec ... end\n);return 0; }3.execvp -- p ---- 用户可以不穿要执行文件的路径要传文件名直接告诉exec*, 我要执行谁就行p: 查找这个程序系统会自动在环境变量PATH中进行查找。 int execvp(const char *file, char *const argv[]); 使用方法 execvp(ls, argv); 4.execlp - int execlp(const char *file, const char *arg, .../* (char *) NULL */); 使用方法 execlp(ls, ls, -l, -a, NULL); 程序替换 --- 替换自己写的程序 上面的程序替换我们替换的都是系统命令可不可以替换成我们自己写的程序呢当让可以。 提供一个新文件mypragma.cc #includeiostreamusing namespace std;int main() {cout hello c, im c pragma! endl;cout hello c, im c pragma! endl;cout hello c, im c pragma! endl;cout hello c, im c pragma! endl;cout hello c, im c pragma! endl;cout hello c, im c pragma! endl;return 0; }修改makefile文件让他能一次性执行两个程序 运行后却只执行了mypragma,因为makefile在执行程序时从上至下匹配时只会默认形成第一个目标文件所对应的可执行程序推导他的依赖关系。 解决办法以及实现原理 不能让其中的一个成为第一个目标文件使用.PHONY定义一个伪目标allall所依赖的是这两个目标文件有依赖关系不写依赖方法makefile在从上往下扫描时先扫描到的是all这个all 就成为第一个目标文件其中并没有依赖方法makefile就会推all其中mypragmatestexec的依赖关系 .PHONY:all all:mypragma testexecmypragma:mypragma.ccg -o $ $^ -stdc11testexec:testexec.cgcc -o $ $^ .PHONY:clean clean:rm -f testexec mypragma现在学习如何替换成自己写的程序 1.使用execl做测试 ./mypragma --- 通过相对路径找到要执行的文件程序mypragma  --- 在命令行需要输入的命令因为已经找到程序了就不用再写 ./  此时执行程序自己写的程序被调度起来了 细节提及在之前我们有说过在程序替换的时候并没有形成新的进程是代码和数据被写时拷贝到原来的內核数据结构当中了。 验证 1.先在testexec.c代码中添加打印子进程pid: 2.再在mypragma.cc需执行的替换文件中添加打印子进程pid验证两pid是否相等来看是否是同一个进程 #includeiostream #includeunistd.h using namespace std;int main() {cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;return 0; }运行结果 验证成功两者的子进程pid并没有发生变换。 调用其他语言的程序 验证除了替换c语言程序还是否能替换其他语言的程序呢 首先验证一下ubuntu系统中是否有python: 出现后使用quit()退出 创建一个python文件test.py,  成功运行 或者是shell脚本语言都可以 #!/usr/bin/bashcnt0 while [ $cnt -le 10 ] # 在 10 和 ] 之间添加空格 doecho hello shell, cnt: ${cnt}let cnt done 运行成功 进行替换同理 运行结果 python也同理。 所有的语言在运行的时候在系统中都会变成进程因此就可以被调用。 想使用 “./” 去运行其他语言的程序可以给他们加入可执行权限 5.execvpe   ---- e  ---- evironment --- 环境变量允许我们自定义 int execvpe(const char *file, char *const argv[],char *const envp[]); 在环境变量的参数位置写NULL 程序虽然有个报错但是依然能执行请忽略这个报错 添加环境变量 此时将参数显示传入main函数中并打印传入的命令行参数和环境变量 #includeiostream #includeunistd.h using namespace std;int main(int argc, char *argv[], char *env[]) {int i 0;//打印命令行参数for(; argv[i]; i){printf(argv[%d] : %s\n, i, argv[i]);}printf(------------------------\n);//打印环境变量for(i 0; env[i]; i){printf(env[%d] : %s\n, i, env[i]);} printf(------------------------\n);cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;cout hello c, im c pragma!, mypid: getpid() endl;return 0; }运行结果 选项也能传 运行结果 环境变量是父进程给子进程的父进程现有一张命令行参数和环境变量的表再传给子进程。 那么子进程的父进程的父进程是谁 --- bash,父进程本身就有一批环境变量。 实际上可以不传自定义的环境变量直接传bash本身有的环境变量给传入 运行结果 而当我们传的是自定义的环境变量的时候只会出现我们自定义的环境变量这种现象表明了我们整体替换了所有的环境变量。 使用老的环境变量记得定义 extern char **environ 或者想要适当修改新增环境变量到旧的环境变量当中 谁调用这个函数就会导出一个新的环境变量 这样就会直接在环境变量的表里面直接新增而不是替换整个环境变量的表 最后运行结果 6.execve ---真正的系统调用 事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示 上图所说的所有接口都是最终系统调用execve()的封装用来支持不同的应用场景 结语        随着这篇关于题目解析的博客接近尾声我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战但正是这些挑战让我们不断成长和进步。我在准备这篇文章时也深刻体会到了学习与分享的乐趣。              在此我要特别感谢每一位阅读到这里的你。是你的关注和支持给予了我持续写作和分享的动力。我深知无论我在某个领域有多少见解都离不开大家的鼓励与指正。因此如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处都欢迎你慷慨赐教。                        你的每一条反馈都是我前进路上的宝贵财富。同时我也非常期待能够得到你的点赞、收藏关注这将是对我莫大的支持和鼓励。当然我更期待的是能够持续为你带来有价值的内容让我们在知识的道路上共同前行。
http://www.zqtcl.cn/news/303061/

相关文章:

  • 做网站的毕业设计网站没备案怎么做淘宝客
  • 百度申诉网站建设银行住房租赁代表品牌是什么
  • 网站初期推广方案虚拟服务器搭建wordpress
  • jeecms可以做网站卖吗山西网络推广专业
  • 2017 如何做网站优化育儿哪个网站做的好
  • 网站制作容易吗青岛网站建设公司报价
  • 淘宝建设网站的好处网站制作结构
  • 网站开发网站建设公司临沂网站建设找谁
  • 咋么做网站在电脑上潍坊免费模板建站
  • 苏州网站建设推广咨询平台做网站的公司图
  • 北京企业网站怎么建设免费给我推广
  • 网站制作价钱多少专业的咨询行业网站制作
  • 做百度网站每年的费用多少交换友情链接时需要注意的事项
  • 怎么在百度网站上做自己的网站百度开户渠道
  • php技术的网站建设实录方案做二手手机的网站有哪些
  • 做网站店铺装修的软件怎么做淘课网站
  • 百度一下官方网站wordpress连接代码
  • 什么网站详情页做的好仿唧唧帝笑话门户网站源码带多条采集规则 织梦搞笑图片视频模板
  • 平原网站建设费用少儿编程加盟店倒闭
  • 企业网站建设专业公司蜜淘app在那个网站做的
  • 市住房城乡建设部网站大学生课程设计网站
  • 广州大石附近做网站的公司外包服务公司是干什么的
  • 做的新网站网上搜不到做的网站百度搜索不出来的
  • 电商网站后台报价公司如何建站
  • 查网站有没有做推广企业网站建设的目标
  • 北京网站维护公司专业外贸网站建设_诚信_青岛
  • 网站自己做还是用程序制作网站一般使用的软件有哪些
  • 晨雷文化传媒网站建设济南互联网品牌设计
  • 怎样给自己的网站做防红连接梵客装饰公司官网
  • 甘肃省城乡与住房建设厅网站纪检网站建设动态主题