您现在的位置是:主页 > news > cms适合做什么网站/吸引人气的营销方案
cms适合做什么网站/吸引人气的营销方案
admin2025/4/22 6:25:21【news】
简介cms适合做什么网站,吸引人气的营销方案,怎么去管理好一个团队,主角重生做代购网站发家目录1.进程创建1.1fork1.1.1fork函数1.1.2fork写实拷贝1.1.3fork常规用法1.1.4fork调用失败的原因1.2进程终止2. 进程等待2.1相关概念2.2进程等待方法2.3进程等待中"waitpid"处status参数2.4重新解读单个进程的pid和ppid2.5进程等待中waitpid的第三个参数options3. 进…
目录
- 1.进程创建
- 1.1fork
- 1.1.1fork函数
- 1.1.2fork写实拷贝
- 1.1.3fork常规用法
- 1.1.4fork调用失败的原因
- 1.2进程终止
- 2. 进程等待
- 2.1相关概念
- 2.2进程等待方法
- 2.3进程等待中"waitpid"处status参数
- 2.4重新解读单个进程的pid和ppid
- 2.5进程等待中waitpid的第三个参数options
- 3. 进程程序替换, 微型shell,重新认识shell运行原理
- 3.1简单了解程序替换
- 3.2基本程序替换函数的使用
- 3.4样例测试
- 3.5接口总结
- 3.6模拟实现一个简单shell
1.进程创建
1.1fork
1.1.1fork函数
linux下创建进程的方式:
- ./可执行程序,
- 调用fork,创建成功子进程给父进程返回子进程id,给子进程返回0,当然还有创建失败的情况
进程调用fork,当控制转移到内核中的fork代码后,内核做:
分配新的内存块和内核数据结构给子进程
将父进程部分数据结构内容拷贝至子进程
添加子进程到系统进程列表当中
fork返回,开始调度器调度
1.1.2fork写实拷贝
1.1.3fork常规用法
一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数
1.1.4fork调用失败的原因
系统中有太多的进程
实际用户的进程数超过了限制
创建进程是有成本的
1.2进程终止
程序退出的三种情况
- 代码跑完了,结果是对的
- 代码跑完了,结果是不对的
- 代码出现异常,程序崩溃,退出码也变得没有意义了
- 进程卡住了,没有响应了,不算退出。属于操作系统操作不过来
- 什么main函数要return 0;意义是什么?
2.1这个叫做进程退出码,这个是用来衡量我们进程退出的情况
2.2可以通过这个进程return 返回的值,来判定这个进程执行的结果
2.3echo $? 用来查看进程的退出码
2.4用0表示成功
echo $? 输出最近一次进程退出时的退出码
此时我们接下来继续执行这个就会发现不是123?为什么?
此时输出的时echo这个命令的退出码。
从上图中可以得出,当ls使用错误的时候,退出码是2
正确使用时,是0
总结:
我们用退出码来表示这个程序是否正常运行并结束
0:success
!0:failed
当代码不正确的运行结束后,我们想知道为什么不正确,有多种可能
就可以用不同的数字来表示错误
下面有很多就不截完了,一共0-133个
- main函数return,代表进程退出,非main函数呢?函数返回
- exit在任意位置调用都代表进程退出
exit(EXIT_SUCCESS)和return都可以达到终止程序的目的
exit和return本身就会要求系统进行缓冲区刷新
_exit终止程序,强制终止进程,不要进行进程的后续收尾工作,比如:刷新缓冲区
缓冲区:用户级缓冲区
问题:进程退出,OS层面做了什么?
进程退出,系统层面少了一个进程,系统就要free PCB,mm_struct,页表和各种映射关系,代码+数据曾经申请的
2. 进程等待
2.1相关概念
进程等待是什么?
进程等待:让父进程fork之后,需要通过wait/waitpid等待子进程退出
为什么要父进程等待呢?
- 通过获取子进程退出的信息,能够得知子进程执行结果
- 可以保证:时序问题,子进程先退出,父进程后退出
- 进程退出的时候,进程会先进入僵尸状态,会造成内存泄露的问题,需要通过父进程wait,释放该子进程占用的资源!
- 当进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法
杀死一个已经死去的进程。
当一个进程进入僵尸,那么这个进程就已经死掉了。
2.2进程等待方法
代码一:wait小测试
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h> int main()
{ pid_t id = fork(); if(id == 0) { int cnt = 5; while(cnt) { printf("child[%d] is running :cnt is : %d\n",id,cnt); cnt--; sleep(1); } exit(0); } pid_t ret = wait(NULL); if(ret>0) printf("father wait:%d, success\n",ret); else { printf("father wait failed!\n"); } return 0;
}
监视:
监视指令:
while :; do ps ajx | head -1 && ps ajx | grep a.out | grep -v grep; sleep 1;echo"#############################################################"; done
代码二:wait执行过程中进程状态深入体现
#include<stdio.h> #include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h> int main()
{ pid_t id = fork(); if(id == 0) { int cnt = 5; while(cnt) { printf("child[%d] is running :cnt is : %d\n",id,cnt); cnt--; sleep(1); } exit(0); } sleep(10); printf("father wait begin!\n"); pid_t ret = wait(NULL); if(ret>0) printf("father wait:%d, success\n",ret); else printf("father wait failed!\n"); sleep(10); return 0;
}
代码和下图分析:
子进程执行五秒,
父进程先等待10秒,前五秒子进程正常运行,后五秒子进程变成了僵尸状态,
father开始等待
僵尸状态此时没了
父进程在等待10秒 (此时的进程两个变一个,此时只剩下父进程一个)
即:wait是可以回收僵尸进程的
代码三:waitpid
代码和代码二类似
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h> int main()
{ pid_t id = fork(); if(id == 0) { int cnt = 5; while(cnt) { printf("child[%d] is running :cnt is :%d\n",getpid(),cnt); cnt--; sleep(1); } exit(0); } sleep(10); printf("father wait begin!\n"); pid_t ret = waitpid(id,NULL,0); if(ret>0) printf("father wait:%d,success\n",ret); else printf("father wait failed!\n"); sleep(10); return 0;
}
返回值:
如果成功,就会返回你等待的那个子进程,那个子进程退出了,我们返回的就是那个子进程的id
如果失败了,就是-1
子进程是11314等待的也是,,所以没毛病
2.3进程等待中"waitpid"处status参数
pid_t ret = waitpid(id,NULL,0)//等待指定一个进程
pid_t ret = waitpid(-1,NULL,0)//等待任意一个子进程,等价于wait
pid_ t waitpid(pid_t pid, int *status, int options);//第二个参数是输出性参数
将我们的测试代码中一部分不需要的删除
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h> int main()
{ pid_t id = fork(); if(id == 0) { int cnt = 3; while(cnt) { printf("child[%d] is running :cnt is :%d\n",getpid(),cnt); cnt--; sleep(1); } exit(0); //第一次这里是0,第二次随便传入一个值 } printf("father wait begin!\n"); int status = 0; //我们探究的就是这个值pid_t ret = waitpid(id,&status,0); if(ret>0) printf("father wait:%d,success,status:%d\n",ret,status); else printf("father wait failed!\n"); return 0;
}
我们可以推出:
父进程拿到什么status结果,一定和子进程如何退出强相关!!!
子进程退出无非三种结果, 则我们可以通过status反馈出子进程退出的情况
status构成:
获取退出码和终止信号:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h> int main()
{ pid_t id = fork(); if(id == 0) { int cnt = 3; while(cnt) { printf("child[%d] is running :cnt is :%d\n",getpid(),cnt); cnt--; sleep(1); } exit(11); } printf("father wait begin!\n"); int status = 0; pid_t ret = waitpid(id,&status,0); if(ret>0) printf("father wait:%d,success,status exit code :%d,status exit signal:%d\n",ret,(status>>8)&0xFF,status&0x7F); else printf("father wait failed!\n"); return 0;
}
~
上图表示:我们的代码是正常退出的
11是我们设置的,0是代表没有接受到异常终止信号
关于异常终止信号我们可以:
kill -2 子进程
这行指令的意思是,我们让子进程直接退出,退出的原因是2
那么我们执行上面的代码时(在子进程进行的时候我们kill)
最后显示的异常终止信号就是2
当我们写的代码出现问题时:1/0
这个明显是错误的
那么我们最终显示的就是浮点数错误:8号信号SIGFPE
2.4重新解读单个进程的pid和ppid
可以得出:
bash是命令行启动的所有进程的父进程!
bash一定是通过wait方式得到子进程的退出结果(退出码)
所以我们能看到echo $?能够查到子进程的退出码
2.5进程等待中waitpid的第三个参数options
阻塞本质:进程的PCB被放入了等待队列,并将进程的状态改为S状态
返回的本质:进程的PCB等待队列拿到运行队列,从而被CPU调度
看到某些应用或者OS本身,卡住了长时间不动,应用或者程序hang住了
WNOHANG:非阻塞
- 子进程根本就没有退出
- 子进程退出,waitpid(调用成功or失败)
基于非阻塞的轮询方案
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h> int main()
{ pid_t id = fork(); if(id == 0) { int cnt = 3; while(cnt--) { printf("child[%d] is running ;cnt is:%d\n",getpid(),cnt); sleep(1); } exit(1); } int status = 0; while(1){ pid_t ret = waitpid(id,&status,WNOHANG); if(ret == 0) { //子进程没有退出,但是waitpid等待是成功的,需要父进程重复进行等待 printf("do father thing!\n"); } else if(ret>0) { //子进程推出了,waitpid也成功了,获取到了对应的结果 printf("father wait:%d,success,status exit code:%d ,status exit signal:%d\n",ret,(status>>8)&0xFF,status&0x7F); } else{ //等待失败, perror("waitpid"); break; }
3. 进程程序替换, 微型shell,重新认识shell运行原理
3.1简单了解程序替换
引入:
目前我们创建子进程的目的是什么:
if else 让子进程执行父进程代码的一部分!
如果我让子进程执行一个全新的程序呢?
进程不变,仅仅替换当前进程的代码和数据的技术,叫做进程的程序替换
程序本身就是一个文件!!!
文件=程序代码+程序数据
问题:程序在进行程序替换的时候有没有创建新的进程?
没有,只是把我们老程序的壳子不变,新程序的文件替换进来
#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[]);
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h> int main()
{ printf("i am a process! pid: %d\n",getpid()); execl("/usr/bin/ls","ls","-a","-l",NULL); //ececl执行程序替换, printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); return 0;
}
问题:
程序替换的本质是不是就是吧特定的进程代码+数据,加载到特定进程的上下文中?
我们知道C/C++程序要运行,必须要先加载到内存中
如何加载?(就是讲程序和数据拷贝到内存中)加载器!
加载器-> exec*程序替换函数!!!
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h> int main()
{ pid_t id = fork(); if(id == 0) { printf("i am a process! pid: %d\n",getpid()); sleep(3); execl("/usr/bin/ls","ls","-a","-l",NULL); //ececl执行程序替换, printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); printf("Hello sakeww!!!!!\n"); } while(1) { printf("i am a father\n"); sleep(1); } waitpid(id,NULL,0); printf("wait success!\n"); return 0;
}
问题:
为什么父进程没有受影响呢?
进程具有独立性!
但是我们又知道:父子代码是共享的吗?
进程程序替换会更改代码区的代码,也要发生写时拷贝!
问题:
只要进程的程序替换成功,就不会执行后续代码,意味着exec函数,成功的时候,不需要返回值检测?
换一种问法:
只要exec返回了,就一定时因为调用失败了?
程序替换失败时:
总计:
1.为什么要进行程序替换?
想让子进程去执行一个全新的程序
2.什么是程序替换?原理:
用新的代码和数据替换 原始的代码和数据
3.进程的程序替换使用–怎么办?? --阶段一
a.现象
b.fork()
c.exec* 返回值
3.2基本程序替换函数的使用
#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[]);
int execl(const char *path, const char *arg, …);
path->你要执行的目标程序的全路径,所在路径/文件名。
… -> 可变参数链表
从第二个参数开始,要执行的目标程序,在命令行怎么执行,这里参数就怎么一个一个的传递
必须以NULL作为参数的结束
即:ececl("/usr/bin/ls","ls",-a","-l",NULL);
在vim中查询“execl”相关信息
3.4样例测试
ececl("/usr/bin/ls","ls",-a","-l",NULL)
内容解释:
./myexec
变成一个进程,在内部就会执行被替换的代码
printf("执行结束!\n);
没有被执行,是因为:
我们进行了程序替换,就叫做替换所有的代码
只要程序执行exec返回了必定出错了!
帮助理解:
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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
这些函数原型看起来很容易混,但只要掌握了规律就很好记。
l(list) : 表示参数采用列表
列表:一个一个的传参,
v(vector) : 参数用数组
char *const argv[]–>指针数组:放你在命令行传的一个一个的参数
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
int execv(const char *path, char *const argv[]);
int execv(const char *path, char *const argv[]);
int execl(const char *path, const char *arg, ...);
这两个无任何效果上的区别,
第二个是可变参数列表
第一个是数组,里面的元素可以由自己定
argv类似我们的main函数的参数列表
int execlp(const char *file, const char *arg, ...);
因为l:所以传参都是列表形式
const char *file,只要告诉这个程序的名字即可,不用告诉这个程序在哪里
可以在环境变量PATH帮你搜索这个程序
int execle(const char *path, const char *arg, ...,char *const envp[]);
在c中执行py程序
3.5接口总结
所有的接口,看起来是没有太大区别的,只有一个就是参数的不同!
为什么会由这么多接口?
是为了满足不同的应用场景
区别图解:
3.6模拟实现一个简单shell
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<sys/wait.h> #include<string.h> #define NUM 128 #define CMD_NUM 64 int main() { char command[NUM]; for(;;){ char *argv[CMD_NUM] = {NULL}; //1.打印提示符 command[0] = 0;//用这种方式,可以做到O(1)时间复杂度,清空字符串 printf("[who@myhostname mydir]# "); fflush(stdout); //2.获取命令字符串 fgets(command,NUM,stdin); command[strlen(command)-1] = '\0';//我们的输入中,最后会以回车结尾,导致换行两次 //3.解析命令字符串,解析成char *argc[]; //strtok(); const char *sep = " "; argv[0] = strtok(command,sep); int i = 1; while(argv[i] = strtok(NULL,sep)) { i++; } //4.检测命令是否是需要shell本身执行的,内建命令 if(strcmp(argv[0],"cd") == 0) { if(argv[1] != NULL) chdir(argv[1]); continue;}//5.执行第三方命令if(fork() == 0){execvp(argv[0],argv);exit(1);//上面一行执行成功就不会执行本行代码}waitpid(-1,NULL,0);}}