企业网站 开源php,久久营销网站,目前好的外贸网站,nas服务器 做网站进程控制下篇
1.进程创建
1.1认识fork / vfork 在linux中fork函数时非常重要的函数#xff0c;它从已存在进程中创建一个新进程。新进程为子进程#xff0c;而原进程为父进程 #includeunistd.h
int main()
{pid_t i fork;return 0;
}当前进程调用fork#xff0c;…进程控制下篇
1.进程创建
1.1认识fork / vfork 在linux中fork函数时非常重要的函数它从已存在进程中创建一个新进程。新进程为子进程而原进程为父进程 #includeunistd.h
int main()
{pid_t i fork;return 0;
}当前进程调用fork当控制转移到内核中的fork代码后内核做 分配新的内存块和内核数据结构给子进程 将父进程部分数据结构内容拷贝至子进程 添加子进程到系统进程列表当中 fork返回开始调度器调度 #include iostream
#include unistd.h
#include cstdioint main()
{
pid_t pid;
printf(Before: pid is %d\n, getpid());
if ( (pidfork()) -1 )perror(fork()),exit(1);
printf(After:pid is %d, fork return %d\n, getpid(), pid);
sleep(1);
return 0;
}简单来说在fork之后会有两个进程有两个进程地址空间子进程拷贝了父进程的地址空间以及页表然后在进行自己的程序运行。具体在产生两个进程的时候谁先谁后这个就全取决于调度器
调度器就是根据确定的算法对进程以及os需要进行的任务进行调度的一个程序
1.2fork函数返回值 父进程返回的是子进程的pid 子进程返回的是0 1.3写时拷贝
通常父子代码共享父子再不写入时数据也是共享的当任意一方试图写入便以写时拷贝的方式各自一份副 本。具体见下图: 这也就是为什么我们上篇提到的同样的变量在父子进程中得到的数据不同
1.4fork常规用法 一个父进程希望复制自己使父子进程同时执行不同的代码段。例如父进程等待客户端请求生成子 进程来处理请求。 一个进程要执行一个不同的程序。例如子进程从fork返回后调用exec函数 1.5fork调用失败的原因 系统中有太多的进程 实际用户的进程数超过了限制 2.进程终止
2.1进程退出场景 代码运行完毕结果正确 代码运行完毕结果不正确 代码异常终止 2.2进程常见退出方法
2.2.1正常终止 可以通过 echo $? 查看进程退出码 从main返回 调用exit _exit 2.2.2异常退出 ctrl c信号终止 2.2.3_exit函数 说明虽然status是int但是仅有低8位可以被父进程所用。所以_exit(-1)时在终端执行$?发现返回值 是255 2.2.4exit函数 执行用户通过 atexit或on_exit定义的清理函数。 关闭所有打开的流所有的缓存数据均被写入 调用_exit int main()
{
printf(hello);
exit(0);
}int main()
{
printf(hello);
_exit(0);
}2.2.5return退出
return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返 回值当做 exit的参数。
3.进程等待
3.1进程等待必要性 之前讲过子进程退出父进程如果不管不顾就可能造成‘僵尸进程’的问题进而造成内存泄漏。 另外进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力因为谁也没有办法 杀死一个已经死去的进程。 最后父进程派给子进程的任务完成的如何我们需要知道。如子进程运行完成结果对还是不对 或者是否正常退出。 父进程通过进程等待的方式回收子进程资源获取子进程退出信息 3.2进程等待的方法
3.2.1wait方法 返回值 成功返回被等待进程pid失败返回-1。 参数 输出型参数获取子进程退出状态,不关心则可以设置成为NULL 3.2.2waitpid方法 返回值 当正常返回的时候waitpid返回收集到的子进程的进程ID 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在 参数 pid Pid-1,等待任一个子进程。与wait等效。 Pid0.等待其进程ID与pid相等的子进程。 status: WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出 WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码 options: WNOHANG: 若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进 程的ID。 如果子进程已经退出调用wait/waitpid时wait/waitpid会立即返回并且释放资源获得子进程退 出信息。 如果在任意时刻调用wait/waitpid子进程存在且正常运行则进程可能阻塞。 如果不存在该子进程则立即出错返回。 3.3获取子进程status wait和waitpid都有一个status参数该参数是一个输出型参数由操作系统填充。 如果传递NULL表示不关心子进程的退出状态信息。 否则操作系统会根据该参数将子进程的退出信息反馈给父进程。 status不能简单的当作整形来看待可以当作位图来看待具体细节如下图只研究status低16比特 位 #include iostream
#include unistd.h
#include cstdio
#include error.h
#include cstdlib
#include sys/wait.h
int main()
{pid_t i fork();if(i 0){//childint a 5;while(a--){sleep(1);printf(child::pid:%d\n,getpid());}//sleep(20);//休眠20秒正常退出exit(10);}else{//parentint st;int ret wait(st);//父进程等待子进程退出并获取退出码if(ret 0 ( st 0X7F ) 0){printf(get the exit code :%d\n,(st8)0XFF);}else if(ret0){printf(get the sig code :%d\n,st0X7F);} i fork();if(i 0){//childint a 5;while(a--){sleep(1);printf(child::pid:%d\n,getpid());}}else{//parentint st;int ret wait(st);//父进程等待子进程退出并获取退出码if(ret 0 ( st 0X7F ) 0){printf(get the exit code :%d\n,(st8)0XFF);}else if(ret0){printf(get the sig code :%d\n,st0X7F);} }}return 0;
}3.4等待方式
3.4.1进程的阻塞等待方式
int main()
{pid_t pid;pid fork();if(pid 0){printf(%s fork error\n,__FUNCTION__);return 1;} else if( pid 0 ){ //childprintf(child is run, pid is : %d\n,getpid());sleep(5);exit(10);} else{int status 0;pid_t ret waitpid(-1, status, 0);//阻塞式等待等待5Sprintf(this is test for wait\n);if( WIFEXITED(status) ret pid ){printf(wait child 5s success, child return code is :%d.\n,WEXITSTATUS(status));}else{printf(wait child failed, return.\n);return 1;}}return 0;
}3.4.2非阻塞等待方式
int main()
{pid_t pid;pid fork();if(pid 0){printf(%s fork error\n,__FUNCTION__);return 1;} else if( pid 0 ){ //childprintf(child is run, pid is : %d\n,getpid());sleep(5);exit(10);} else{int status 0;//pid_t ret waitpid(-1, status, 0);//阻塞式等待等待5Spid_t ret 0;while(ret 0){sleep(1);ret waitpid(-1, status, WNOHANG);if(ret 0){printf(child is runing\n);}}printf(this is test for wait\n);if( WIFEXITED(status) ret pid ){printf(wait child 5s success, child return code is :%d.\n,WEXITSTATUS(status));}else{printf(wait child failed, return.\n);return 1;}}return 0;
}4.进程程序替换
4.1替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变 4.2替换函数exec系列函数 4.3函数解释 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。 如果调用出错则返回-1 所以exec函数只有出错的返回值而没有成功的返回值 4.4更好的记忆方式 l(list) : 表示参数采用列表 v(vector) : 参数用数组 p(path) : 有p自动搜索环境变量PATH e(env) : 表示自己维护环境变量 5.简易shell命令行
#include stdio.h
#include stdlib.h
#include iostream
#include unistd.h
#include string.h
#include fcntl.h
#include ctype.h
#include wait.h
#include string
#define MAX_CMD 1024
char command[MAX_CMD];
int workername()
{memset(command, 0, MAX_CMD);printf([ minishell] $ );fflush(stdout);if (scanf(%[^\n]%*c,command) 0) {getchar();return -1;}return 0;
}
char **minshell(char *buff)
{int argc 0;static char *argv[32];char *ptr buff;while(*ptr ! \0) {if (!isspace(*ptr)) {argv[argc] ptr;while((!isspace(*ptr)) (*ptr) ! \0) {ptr;}}else {while(isspace(*ptr)) {*ptr \0;ptr;}}}argv[argc] NULL;return argv;
}
int exec(char *buff)
{char **argv {NULL};int pid fork();if (pid 0) {argv minshell(buff);if (argv[0] NULL) {exit(-1);}execvp(argv[0], argv);}else {waitpid(pid, NULL, 0);}return 0;
}
int main(int argc, char *argv[])
{while(1) {if (workername() 0)continue;exec(command);}return 0;
}