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

百度推广要多少钱适合seo优化的网站制作

百度推广要多少钱,适合seo优化的网站制作,巧家县城乡建设局网站,施工企业信用综合评价系统文章目录 一、进程创建1.1 fork的使用 二、进程终止2.1 终止是在做什么#xff1f;2.2 终止的3种情况退出码的理解2.3 进程常见退出方法 三、进程等待3.1 为什么要进行进程等待#xff1f;3.2 取子进程退出信息status3.3 宏WIFEXITED和WEXITSTATUS#xff08;获取… 文章目录 一、进程创建1.1 fork的使用 二、进程终止2.1 终止是在做什么2.2 终止的3种情况退出码的理解2.3 进程常见退出方法 三、进程等待3.1 为什么要进行进程等待3.2 取子进程退出信息status3.3 宏WIFEXITED和WEXITSTATUS获取进程终止情况和退出码3.4 进程的阻塞和非阻塞等待 四、进程的程序替换4.1 创建子进程的目的4.2 进程的程序替换4.2.1 单个进程的程序替换4.2.2 父进程派生子进程的程序替换 4.3 进程替换原理 五、替换函数六、自己实现简易shell6.1 shell代码实现6.2 什么是当前路径当前进程的工作目录 cd底层实现用chdir6.3 shell内建/内置命令shell自己执行的命令而不是派生子进程进行程序替换来执行 一、进程创建 1.1 fork的使用 我们可以使用man指令来查看一下 man 2 fork子进程会复制父进程的PCB之间代码共享数据独有拥有各自的进程虚拟地址空间。 这里就有一个代码共享并且子进程是拷贝了父进程的PCB虽然他们各自拥有自己的进程虚拟地址空间数据是拷贝过来的通过页表映射到同一块物理内存中在上篇文章【Linux进程地址空间详解】也讲解过写时拷贝这里就不再过多赘述~大概流程可以看一下下图 在上图中我们还可以看到返回类型是pid_t如果创建子进程失败会返回小于0的数字而如果创建子进程成功该函数则会返回俩个值。它会给子进程返回0值而给父进程返回子进程的pid一个大于0的数创建成功后我们可以对此进行使用if语句进行分流~~ 下面简单验证再来验证一下 #include stdio.h #include stdlib.h #include unistd.hint g_val 100;int main() {pid_t pid fork();if(pid 0) {printf(fork error!\n);exit(-1); }else if(pid 0) {//childg_val 200;printf(This is Child! g_val %d, p %p\n,g_val,g_val);}else {//parentsleep(1);printf(This is Parent! g_val %d, p %p\n,g_val,g_val);}return 0; }很显而易见这里的地址是虚拟的地址空间真正的值是存储在物理内存中的。而这时通过页表的映射本质上内存中已经是指向了不同的物理地址 二、进程终止 顾名思义就是直接让进程终止我们来了解一下 2.1 终止是在做什么 释放内核数据结构—task_structZ僵尸状态 2.2 终止的3种情况退出码的理解 代码运行完毕结果正确代码运行完毕结果不正确代码异常终止 在上面的1和2中可通过退出码进行决定这里什么是退出码呢我们在C语言每次写的时候为什么最后写一个return 0呢这里我们可以实验一下 #include stdio.h int main() {return 1; }这里我们还要了解一个命令 echo $?作用是打印出上一次进程的退出码而我们C语言刚刚最后写的退出码是1最后记录了刚刚的退出码所以打印的是1 第三个是代码执行的时候出现了异常被提前退出了我们可以再来验证一下下面这个代码很明显是野指针的访问 int main() {int* p NULL;*p 10;return 0; }在编译运行的时候出现了异常提前退出了操作系统发现了不该做的事情OS杀死了进程 一旦出现了异常退出码也就没有意义了那么为什么出现了异常是因为进程收到了OS发给进程的信号 在Linux中可以使用kill -l查看所表示的信号可以看到0表示成功~所以一般正常运行完成之后退出码就写成0非0表示失败 2.3 进程常见退出方法 正常退出 从main函数返回调用exit函数调用_exit函数 异常退出 CtrlC信号终止等 在我们平时使用的kill -9就是给OS发送一个信号对程序做出动作 例如使用-9信号杀死进程 刚刚上面的段错误就可以发送11每个对应的编号都有对应的错误描述 exit退出函数和_exit退出函数 可以使用man手册来查看stauts定义了进程的终止状态由用户自己传递父进程可以通过wait来获取该值 exit是库函数_exit是系统调用函数而库函数内部封装了系统调用。 也就是说调用exit函数最终也会调用_exit来使进程退出只不过在其调用_exit之前会将缓冲区进行刷新 return退出 return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返 回值当做 exit的参数。 衡量一个进程退出我们只需要两个数字退出码和退出信号 三、进程等待 3.1 为什么要进行进程等待 如果子进程先于父进程退出而父进程并没有关心子进程的退出状况从而无法回收子进程的资源就会导致子进程变成僵尸进程这里的僵尸状态使用kill也杀不掉会导致内存泄露如果想要解决这个僵尸状态就要进行进程等待等待父进程回收子进程的资源获取子进程的退出状态 在父进程中使用wait或waitpid接口来完成进程等待。 这里的参数是一级指针status它其实是个输出型参数用于获取子进程的退出状态如果不关心则可以设置为NULL成功会返回被等待进程的pid失败则会返回-1 #include stdio.h #include unistd.h #include stdlib.h #include string.h #include sys/types.h #include sys/wait.h int main() {pid_t idfork();if(id0){// child processint cnt10;while(cnt){printf(我是子进程:%d父进程:%d,cnt:%d\n,getpid(),getppid(),cnt--);sleep(1);}exit(0);// 子进程退出}sleep(15);pid_t retwait(NULL);if(ret0){printf(wait success:%d!\n,ret);}sleep(5);return 0; } waitpid 等待成功正常返回则返回被等待进程的pid如果第三个参数options设置成了WNOHANG而此时没有子进程退出没有成功等待到子进程就会返回0而不是阻塞在函数内部调用出错则返回-1 参数 pid设置成-1则表示等待任意一个子进程同wait如果0则表示等待一个指定的子进程pid就是被等待子进程的进程号status获取子进程的退出状态同waitoptions可以设置为0或WNOHANG。设置为0则与wait一样如果没有等待到子进程退出会一直阻塞而设置为WNOHANG则表示非阻塞如果被等待的子进程未退出则会返回0值成功等待到子进程则会返回被等待子进程的pid 这里就不再演示了~~ 3.2 取子进程退出信息status 我们已经知道status是一个出参由操作系统为其赋值用户可以传递NULL值表示不关心而如果传入参数操作系统就会根据该参数将子进程的退出信息反馈给父进程由status最终被赋予的值来体现。 如何通过status来获取子进程的退出信息呢 status是一个int类型的值意味着它应该有32个比特位但它又不能被当初普通的整形来看待因为其高16位的值并不被使用而只使用其低16个比特位 不论是正常退出还是异常退出status的高8个比特位只讨论低16个比特位都表示子进程的退出码而这个退出码一般是return的返回值或者exit的参数正常退出时status的低8个比特位为全0而异常退出时其第8个比特位则为core dump标志位用来标志是否会有core dump文件产生而低7个比特位则是退出信号。 退出码(status 8) 0xFF 低7位检测子进程是否异常退出status 0x7F 结果为0则表示正常退出 不为0则说明是异常退出因为有终止信号 core dump标志位(status 7) 0x1 结果为0则表示没有core dump产生 等于1则说明有core dump产生 #include unistd.h #include stdlib.h #include string.h #include sys/types.h #include sys/wait.h int main() {pid_t idfork();if(id0){// child processint cnt10;while(cnt){printf(我是子进程:%d父进程:%d,cnt:%d\n,getpid(),getppid(),cnt--);sleep(1);}exit(6);// 子进程退出}sleep(15);int status 0;pid_t retwaitpid(id,status,0);if(ret0){printf(wait success:%d!\n,ret);printf(status:%d,退出码是%d,退出信号是%d\n,status,(status8)0xFF,status0x7F);}sleep(5);return 0; } 执行结果 我们还可以演示一下异常退出我们在子进程里写一个野指针访问 int* p NULL; *p 100; 这里也就很显而易见了~~ 3.3 宏WIFEXITED和WEXITSTATUS获取进程终止情况和退出码 WIFEXITED(status)若子进程是正常终止则返回结果为真用于查看进程是否正常退出。 WEXITSTATUS(status)若进程正常终止也就是进程终止信号为0这时候会返回子进程的退出码。 下面我们可以写一个代码来演示一下 #include stdio.h #include sys/types.h #include sys/wait.h #include unistd.h #include stdlib.h #include assert.h int main() {pid_t id fork();assert(id ! -1);if(id 0){// child int cnt 10;while(cnt){printf(child process runningpid:%dppid:%dcnt:%d\n,getpid(),getppid(),cnt--);sleep(1);}exit(10);}// 等待子进程int status0;int ret waitpid(id,status,0);if(ret 0){if(WIFEXITED(status)){printf(child process exit normallyexit code:%d\n,WEXITSTATUS(status));}else{printf(child process dont exit normally\n);}// printf(wait successexit code:%dsignal number:%d\n,(status8)0xFF,status 0x7F);}return 0; }正常退出 异常退出 3.4 进程的阻塞和非阻塞等待 当子进程还没有死的时候也就是没有退出的时候父进程调用的wait或waitpit需要等待子进程退出系统调用接口也不返回这段时间父进程什么都没做就一直等待子进程退出这样的等待方式称之为阻塞式等待。 非阻塞式等待就是不停的检测子进程状态每一次检测之后系统调用立即返回在waitpid中的第三个参数设置为WNOHANG即为父进程非阻塞式等待。 如果等待的子进程状态没有发生变化则waitpid会返回0值。多次非阻塞等待子进程直到子进程退出这样的等待方式又称之为轮询。如果等待的进程不是当前父进程的子进程则waitpid会调用失败。 #include stdio.h #include sys/types.h #include sys/wait.h #include unistd.h #include stdlib.h #include assert.h int main() {pid_t id fork();assert(id!-1);if(id0){// child processint cnt5;while(cnt){printf(child process runningpid:%dppid:%dcnt:%d\n,getpid(),getppid(),cnt--);sleep(3); }exit(10);}int status0;while(1){// WNOHANG是非阻塞等待子进程没有退出父进程检测一次之后立即返回pid_t retwaitpid(id,status,WNOHANG);if(ret 0){// waitpid调用成功子进程没有退出printf(Wait for successbut the child process is still running\n);}else if(ret id){// waitpid调用成功子进程退出printf(wait successexit code:%dsignal number:%d\n,(status8)0xFF,status 0x7F);break;}else{// waitpid调用失败例如等待了一个不属于该父进程的子进程printf(The waitpid call failed\n);break;}sleep(1);}return 0; }非阻塞等待有一个好处就是不会像阻塞式等待一样父进程什么都做不了而是在轮询期间父进程还可以做其他的事情。 下面代码中利用了回调函数的方式来让父进程轮询等待子进程期间还可以处理其他任务。 #include stdio.h #include sys/types.h #include sys/wait.h #include unistd.h #include stdlib.h #include assert.h #include string.h void task1() {printf(Process task1\n); } void task2() {printf(Process task2\n); } void task3() {printf(Process task3\n); } typedef void (*func_t)();// 定义一个函数指针类型。func_t Process_task[10];void loadtask() {memset(Process_task,0, sizeof(Process_task));Process_task[0]task1;Process_task[1]task2;Process_task[2]task3; }int main() {pid_t id fork();assert(id!-1);if(id0){// child processint cnt5;while(cnt){ printf(child process runningpid:%dppid:%dcnt:%d\n,getpid(),getppid(),cnt--);sleep(1); }exit(10); } loadtask();// 加载任务到函数指针数组里面。int status0;while(1){ pid_t retwaitpid(id,status,WNOHANG);// WNOHANG是非阻塞等待子进程没有退出父进程检测一次之后立即返回if(ret 0) {// waitpid调用成功子进程没有退出printf(Wait for successbut the child process is still running\n);for(int i0; Process_task[i]!NULL; i){Process_task[i]();// 回调函数的方式让父进程在轮询期间做其他事情}}else if(ret id){// waitpid调用成功子进程退出printf(wait successexit code:%dsignal number:%d\n,(status8)0xFF,status 0x7F);break;}else {// waitpid调用失败例如等待了一个不属于该父进程的子进程printf(The waitpid call failed\n);break;}sleep(1);}return 0; } 四、进程的程序替换 4.1 创建子进程的目的 创建子进程一般两个目的 让子进程执行父进程代码的一部分也就是执行父进程对应的磁盘上的代码和数据的一部分。 让子进程加载磁盘上指定的程序到内存中使其执行新的程序的代码和数据这就是进程的程序替换。 4.2 进程的程序替换 接下来我们就介绍一些进程的程序的替换函数 4.2.1 单个进程的程序替换 下面函数参数是可变参数列表可以给C语言函数传递不同个数的参数。 int execl(const char* pathconst char* arg...); 通过man指令可以查看 man execl要执行一个程序首先就是找到这个程序然后在执行这个程序执行程序的时候也拥有不同的执行方式通过执行选项的不同便可以使得程序以多种不同的方式执行。 例如 #include stdio.h #include unistd.hint main() {// .c -- .exe -- load into memory -- process -- runningprintf(The process is running...\n);// 传参以NULL结尾来表示传参结束 execl(/usr/bin/ls,ls,-a,-l,--colorauto,NULL);printf(The process finishes running...\n);return 0; }可以看到只打印了一行run紧接着是执行后面替换的程序 exec系列的函数只有在调用失败的时候才有返回值这个返回值是-1那为什么exec系列的函数没有调用成功时的返回值呢 答案没有必要因为exec系列函数调用结束之后代码就全都被替换了就算给你返回值你也使用不了因为代码全都替换为指定程序的代码了所以只要exec系列函数返回那就一定发生调用错误了。 例如我随便写一个命令这个命令是不在这个目录里的 #include stdio.h #include unistd.hint main() {// .c -- .exe -- load into memory -- process -- runningprintf(The process is running...\n);// 传参以NULL结尾来表示传参结束 execl(/usr/bin/lsss,ls,-l,--colorauto,NULL);printf(The process finishes running...\n);return 0; }4.2.2 父进程派生子进程的程序替换 子进程被替换为ls进程不会影响父进程因为进程具有独立性。 #include stdio.h #include unistd.h #include stdlib.h #include assert.h #include sys/types.h #include sys/wait.h int main() {printf(The process is running...\n);pid_t id fork();assert(id ! -1);if(id0){//child processsleep(1);execl(/usr/bin/ls,ls,-l,NULL);exit(1);// 如果调用失败直接让子进程退出}int status 0;pid_t ret waitpid(id,status,0);if(ret id){printf(wait success, exit code:%d , signal number:%d\n,(status8)0xFF,status0x7F);}return 0; }4.3 进程替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并**不创建新进程,**所以调用exec前后该进程的id并未改变。 当父进程派生的子进程发生程序替换时防止父子进程原先共享的代码段和数据段被修改操作系统会进行写时拷贝将代码段和数据段重新复制一份给子进程让子进程程序替换之后不会影响父进程。这就是进程之间的独立性。 虚拟地址空间和页表可以保证进程之间的独立性一旦有执行流要改变代码或数据就会发生写时拷贝。所以不是只有数据可能发生写入代码也是有可能发生写入的这两种情况都会发生写时拷贝。 五、替换函数 l代表list指的是将参数一个一个的传入execl函数 int execl(const char *path, const char *arg, …); int main() {pid_t id fork();if(id 0){// childexecl(/usr/bin/ls, ls, -l, NULL);// 如果执行到这里说明替换失败,让子进程退出exit(-1);}// parentreturn 0; }p是指path不用传程序的路径只需要传程序的名字就够了此函数会自动在PATH环境变量的路径下面去查找对应的程序。execlp中的两个ls是不重复的一个是告诉操作系统要执行什么程序一个是告诉操作系统怎么执行程序。 int execlp(const char *file, const char *arg, …); int main() {pid_t id fork();if(id 0){// childexeclp(ls, ls, -l, --colorauto, NULL);// 如果执行到这里说明替换失败,让子进程退出exit(-1);}// parentreturn 0; }v是指vector指的是该函数可以将所有的执行参数放到数组里面统一进行传参而不是使用可变参数列表的方式来一个一个的传执行参数。 int execv(const char *path, char *const argv[]); int main() {pid_t id fork();if(id 0){// childchar* const argv[] {(char*)ls, (char*)-l, (char*)--colorauto, NULL};execv(/usr/bin/ls, argv);// 如果执行到这里说明替换失败,让子进程退出exit(-1);}// parentreturn 0; }PATH和vector指的是不用传程序路径默认在环境变量中进行查找并且可以将执行参数放到数组里面统一进行传参 int execvp(const char *file, char *const argv[]); int main() {pid_t id fork();if(id 0){// childchar* const argv[] {(char*)ls, (char*)-l, (char*)--colorauto, NULL};execvp(ls, argv);// 如果执行到这里说明替换失败,让子进程退出exit(-1);}// parentreturn 0; }execle中的e代表自定义环境变量。下面定义的env指针数组就是自定义环境变量也就意味着程序替换的时候不用系统环境变量用自己定义的环境变量。 int execle(const char *path, const char *arg,…, char * const envp[]); int main() {pid_t id fork();if(id 0){// childchar* const env[] {(char*)HELLO123456789,NULL};execle(./mybin,mybin, NULL, env);// 如果执行到这里说明替换失败,让子进程退出exit(-1);}// parentreturn 0; }也可以不传自定义环境变量而用系统的环境变量传给子进程替换的程序只不过替换的程序mybin.c没有打印出来全部的环境变量而是只打印了PATH和PWD的值。 int main() {pid_t id fork();if(id 0){// childextern char** environ;execle(./mybin,mybin, NULL, environ);// 如果执行到这里说明替换失败,让子进程退出exit(-1);}// parentreturn 0; }上面那些函数都不在2号手册 int execvpe(const char *file, char *const argv[],char *const envp[]); execvpe其实就是vectorPATHenv我们需要自己传环境变量并且不用可变参数列表的方式传执行参数而是用指针数组的方式来一并将执行参数传递传程序名时可以不带程序路径系统会帮我们找。 带e的函数都需要自己组装环境变量可以选择自己的、或系统的、或系统和自己的环境变量。 真正执行程序替换的其实只有execve这一个系统调用接口其他的6个都是在execve的基础上封装得来的。只有execve在man2号手册其他都在3号手册。 下图exec函数族 一个完整的例子 其中 l 和 v 的区别在于程序运行参数的赋予方式不同l是通过函数参数逐个给与最终以NULL结尾而v是通过字符指针数组一次性给与。其中有没有 p 的区别在于程序是否需要带路径也就是是否会默认到path环境变量指定的路径下寻找程序没有p的需要指定路径有p的会默认到path环境变量指定路径下寻找其中有没有 e 的区别在于程序是否需要自定义环境变量没有e则默认使用父进程环境变量有e则自定义环境变量。 最后在写makefile的时候我们想让两个源文件进行编译我们可以在makefile中添加一个 .PHONY:all all:mybin myprocessmybin:myexec.cgcc -o $ $^ -stdc99myprocess:process.cgcc -o $ $^ -stdc99.PHONY:clean clean:rm -rf myprocess mybin exec函数族代码示例 int main() {char *const argv[] {ps, -ef, NULL};char *const envp[] {PATH/bin:/usr/bin, TERMconsole, NULL};execl(/bin/ps, ps, -ef, NULL);// 带p的可以使用环境变量PATH无需写全路径execlp(ps, ps, -ef, NULL);// 带e的需要自己组装环境变量execle(ps, ps, -ef, NULL, envp);execv(/bin/ps, argv);// 带p的可以使用环境变量PATH无需写全路径execvp(ps, argv);// 带e的需要自己组装环境变量execve(/bin/ps, argv, envp);exit(0); }六、自己实现简易shell 6.1 shell代码实现 #include stdio.h #include stdlib.h #include string.h #include errno.h #include unistd.h #include sys/types.h #include sys/wait.h#define SIZE 512 #define ZERO \0 #define SEP #define NUM 32 #define SkipPath(p) do{ p (strlen(p)-1); while(*p ! /) p--; }while(0)// 为了方便我就直接定义了 char cwd[SIZE*2]; char *gArgv[NUM]; int lastcode 0;void Die() {exit(1); }const char *GetHome() {const char *home getenv(HOME);if(home NULL) return /;return home; }const char *GetUserName() {const char *name getenv(USER);if(name NULL) return None;return name; } const char *GetHostName() {const char *hostname getenv(HOSTNAME);if(hostname NULL) return None;return hostname; } const char *GetCwd() {const char *cwd getenv(PWD);if(cwd NULL) return None;return cwd; }// commandline : output void MakeCommandLineAndPrint() {char line[SIZE];const char *username GetUserName();const char *hostname GetHostName();const char *cwd GetCwd();SkipPath(cwd);snprintf(line, sizeof(line), [%s%s %s] , username, hostname, strlen(cwd) 1 ? / : cwd1);printf(%s, line);fflush(stdout); }int GetUserCommand(char command[], size_t n) {char *s fgets(command, n, stdin);if(s NULL) return -1;command[strlen(command)-1] ZERO;return strlen(command); }void SplitCommand(char command[], size_t n) {(void)n;gArgv[0] strtok(command, SEP);int index 1;while((gArgv[index] strtok(NULL, SEP))); }void ExecuteCommand() {pid_t id fork();if(id 0) Die();else if(id 0){// childexecvp(gArgv[0], gArgv);exit(errno);}else{// fahterint status 0;pid_t rid waitpid(id, status, 0);if(rid 0){lastcode WEXITSTATUS(status);if(lastcode ! 0) printf(%s:%s:%d\n, gArgv[0], strerror(lastcode), lastcode);}} }void Cd() {const char *path gArgv[1];if(path NULL) path GetHome();// path 一定存在chdir(path);// 刷新环境变量char temp[SIZE*2];getcwd(temp, sizeof(temp));snprintf(cwd, sizeof(cwd), PWD%s, temp);putenv(cwd); // OK }int CheckBuildin() {int yes 0;const char *enter_cmd gArgv[0];if(strcmp(enter_cmd, cd) 0){yes 1;Cd();}else if(strcmp(enter_cmd, echo) 0 strcmp(gArgv[1], $?) 0){yes 1;printf(%d\n, lastcode);lastcode 0;}return yes; }int main() {int quit 0;while(!quit){// 1. 我们需要自己输出一个命令行MakeCommandLineAndPrint();// 2. 获取用户命令字符串char usercommand[SIZE];int n GetUserCommand(usercommand, sizeof(usercommand));if(n 0) return 1;// 3. 命令行字符串分割. SplitCommand(usercommand, sizeof(usercommand));// 4. 检测命令是否是内建命令n CheckBuildin();if(n) continue;// 5. 执行命令ExecuteCommand();}return 0; }6.2 什么是当前路径当前进程的工作目录 cd底层实现用chdir 查看进程的指令 ls /proc/进程id可以看到进程有两个路径一个是cwd一个是exeexe路径代表当前进程执行的是磁盘上的哪个路径下的程序可以看到执行的是myproc二进制可执行程序cwd代表current work directory代表当前进程的工作目录所以实际上当前路径就是当前进程的工作目录。 在模拟shell的实现代码中cd到其他目录pwd之后的路径实际上是没有变化的因为pwd实际上pwd的是父进程shell的路径而父进程的cwd路径始终是未改变的而执行cd命令的是子进程所以子进程的cwd路径是会改变的。 系统给我们提供了一个系统调用接口叫做chdir用于改变当前进程的工作目录cwd路径实际上cd能够进入指定路径下的目录底层实现上就是改变了shellbash进程的cwd路径所以pwd时随时随地打印出来的就是shell进程的工作目录。 所以如果我们模拟实现的shell也想实现cd改变路径的功能实际上是不可以创建子进程的因为子进程程序替换执行cd父进程的工作目录是没有改变的所以直接将这一种情况单独拿出来进行判断在这种情况下直接让父进程执行cd命令修改父进程的工作目录即可。 6.3 shell内建/内置命令shell自己执行的命令而不是派生子进程进行程序替换来执行 像上面的cd命令实际上就是shell的内建命令因为这样的命令不需要派生子进程来进行程序替换执行直接让父进程执行就ok这样的指令就是shell自带的命令我们称之为内建命令或内置命令。 这也就能解释为什么echo能够打印本地变量了我们之前将echo理解为一个可执行程序也就是shell的子进程但是我们说子进程只能继承父进程的环境变量而不能继承本地变量所以当时就陷入echo为什么能够打印出本地变量的疑问当中因为如果echo是子进程的话他是没有继承本地变量的。 但现在我们就知道原因了echo实际上不是shell的子进程而是shell的内建命令是shell自己来执行的指令shell当然拥有本地变量了当然也就能够打印本地变量。 好了本文就到这里感谢大家的收看
http://www.pierceye.com/news/647385/

相关文章:

  • 国外开源 企业网站服务好质量好的网站制作
  • sql网站的发布流程品牌建设是什么意思
  • 营口网站建设价格江苏住房和建设厅网站
  • 网站稳定性不好的原因打金新开传奇网站
  • 做网站怎么上传图片厦门建站网址费用
  • 网站设计方案和技巧做设计有必要买素材网站会员吗
  • 成都制作网站软件网站别人帮做的要注意什么东西
  • 徐州建筑网站建网站要自己买服务器吗
  • 网站订单系统模板专业的做网站公司
  • 怎么做加盟美容院网站黄骅港开发区
  • 品牌高端网站制作官网做网站用的小图标
  • 成都网站设计合理柚v米科技泉州建设公司
  • 网页制作与网站建设完全学习手册软件下载网站怎么做
  • linux系统网站空间如何分析网站关键词
  • 以下属于网站页面设计的原则有查询网站空间商
  • 建设银行网站链接网络推广有哪些常见的推广方法
  • 常州网络公司网站图片在线制作加字
  • 漕泾网站建设建立内部网站
  • 海宁市住房和城乡规划建设局网站北京十大装饰装修公司
  • 创新的常州做网站网站页面设计公司电话
  • 建站公司见客户没话说周年庆网站要怎么做
  • 建设银行网站字体建设官方网站
  • 建设部网站人员查询商城网站 没有什么文章 怎样优化
  • wordpress按标签筛选广州seo网站
  • 南宁手机建站公司常德网站开发服务
  • 智能锁东莞网站建设php网站开发需要学什么软件
  • 扒网站样式中国搜索网站排名
  • 网站空间和云服务器建设建材网站费用
  • 公司网站 正式上线wordpress 移动端不显示
  • 旅行网站排名前十名网站检测报告哪里做