自己公司内网网站和外网怎么做同步,上海网站建设系,网站免费观看,中铁局哪个局实力最大控制欲强?视奸#xff1f;普通人那才叫视奸#xff0c;您是皇帝#xff0c;天下大事无一逃过您的耳目#xff0c;您想看什么就看什么#xff0c;臣怀疑他在朋友圈私养兵士#xff0c;囤积枪甲#xff0c;蓄意谋反#xff0c;图谋皇位啊#xff01; 哈哈哈哈开个玩笑普通人那才叫视奸您是皇帝天下大事无一逃过您的耳目您想看什么就看什么臣怀疑他在朋友圈私养兵士囤积枪甲蓄意谋反图谋皇位啊 哈哈哈哈开个玩笑这篇就主要讲讲Linux进程的控制吧~
fork( )
由于fork之前也说过啦从已存在进程中创建一个新进程新进程为子进程原进程为父进程所以下面主要讲内核的操作进程调用fork当控制转移到内核中的fork代码后内核做 1.分配新的内存块和内核数据结构给子进程 2.将父进程部分数据结构内容拷贝至子进程 3.添加子进程到系统进程列表当中 4.fork返回开始调度器调度 当一个进程调用fork之后就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以 开始它们自己的旅程
#include unistd.h
#includestdio.h
int main(void)
{pid_t pid;printf(Before: pid is %d\n, getpid());if ((pid fork()) -1)perror(fork()), exit(1);printf(After:pid is %d, fork return %d\n, getpid(), pid);sleep(1);return 0;
} 先来下个定义
进程内核的相关管理数据结构task_struct mm_struct 页表 代码和数据
已知fork函数的返回值是这样的 子进程返回0 父进程返回子进程的pid 那为什么捏
原因其实也很简单爹得知道儿子名杀掉他啊等待他啊爹总要知道的为了方便父进程对紫禁城进行标识进而进行管理
进程具有独立性就在于紫禁城代码数据和父进程共享但因为写时拷贝又不影响父进程
fork常规用法
一个父进程希望复制自己使父子进程同时执行不同的代码段父进程等待客户端请求生成子 进程来处理请求
一个进程要执行一个不同的程序子进程从fork返回后调用exec函数
fork调用失败原因
系统中有太多的进程
实际用户的进程数超过了限制
进程终止
终止是在做什么
进程终止就是在释放曾经的代码和数据所占据的空间也是在释放内核数据结构task_struct当进程状态是Z就要释放对应PCB
终止三种情况
先来看两段代码
#includestdio.h
#includeunistd.hint main()
{printf(hello world!\n);return 0;
}
#includestdio.h
#includeunistd.hint main()
{printf(hello world!\n);return 100;
}
只有返回值不一样对吧对取内容会发现也不一样 echo是内建命令打印的都是bash内部的变量数据
父进程bash获取到的最近一个紫禁城的退出码 0成功0失败
退出码存在意义告诉关心方父进程任务完成如何
因为成功的退出码就是0而0有很多所以不同0值一方面表示失败一方面还表示失败的原因
可以这样打印下错误信息
#includestdio.h
#includeunistd.h
#includestring.hint main()
{int errcode 0;for (errcode 0; errcode 255; errcode){printf(%d:%s\n, errcode, strerror(errcode));}return 0;
} 那么父进程知道紫禁城退出码因为点撒捏
因为要知道紫禁城退出情况正常退出了嘛错误了嘛错哪了呀
错误码可以自己设定
#includestdio.h
#includeunistd.h
#includestring.h
int Div(int x, int y)
{if (0 y){return -1;}else{return x / y;}
}
int main()
{printf(%d\n,Div(-1,1));return 0;
}
但是这样没法判断是y0导致返回错误码-1还是本来的计算结果就是-1
所以可以这样改
#includestdio.h
#includeunistd.h
#includestring.h//自定义枚举常量
enum
{Success 0,Div_Zero,Mod_Zero,
};int exit_code Success;int Div(int x, int y)
{if (0 y){exit_code Div_Zero;return -1;}else{return x / y;}
}
int main()
{printf(%d\n, Div(-1, 1));return exit_code;
}
还可以接着写接口补充错误信息
#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 unknow error!;}
}int Div(int x, int y)
{if (0 y){exit_code Div_Zero;return -1;}else{return x / y;}
}
int main()
{printf(%d\n, Div(-1, 1));printf(%s\n, CodeToErrString(exit_code));return exit_code;
} 来看看进程终止的三种情况吧 1.代码跑完结果正确 2.代码跑完结果不正确正确与否可通过进程退出码决定 3.代码执行时出现了异常提前退出了系统自定义退出码 什么是崩溃
就是编译运行的时候操作系统发现你的进程做了不该做的是事于是OS杀掉了你的进程
那异常了退出码还有意义吗肯定没有啊作弊拿到60和作弊拿到100被抓没区别
进程出现了异常本质是因为进程收到了OS发给进程的信号
比如说来上一份妇孺皆知的代码
#includestdio.h
#includeunistd.h
int main()
{while (1){printf(I am a process,pid:%d\n, getpid());}return 0;
}
这进程能一直运行下去但是我们可以通过kill的方式干掉它
kill -9 pid;
这进程没有出现异常但是由于进程收到了OS发给进程的信号所以进程不得不终止
再来一瓶野指针
#includestdio.h
#includeunistd.h
int main()
{int* p NULL;while (1){printf(I am a process,pid:%d\n, getpid());sleep(1);*p 100; //看好了小登中登老登这是故意哒}return 0;
}
在Linux中运行这段代码会发现出现段错误Segmentation fault 不嘻嘻 段错误OS提前终止进程
我们通过观察进程退出的时候退出信号是多少就可以判断我们的进程为何异常了 判断流程 1.先确认是否异常 2.不是异常就是代码跑完了直接看退出码 衡量一个进程退出只需要两个数字退出码退出信号
进程退出时会把退出码和退出信号写入PCB方便父进程知道
如何进行终止
main函数return就表示进程终止啦非main函数return代表函数结束
代码调用exit函数头文件为stdlib.h
exit(0); //里面数字是return数
还有个东西叫_exit( )
和exit的区别就是它在程序结束的时候并不会冲刷缓冲区
缓冲区必定在_exit()之上 exit在调用_exit前还做了其他工作 1. 执行用户通过 atexit或on_exit定义的清理函数 2. 关闭所有打开的流所有的缓存数据均被写入 3. 调用_exit 除了exitreturn是一种更常见的退出进程方法。执行return n等同于执行exit(n)调用main的运行时函数会将main的返回值当做 exit的参数
进程等待
是什么
任何子进程在退出的情况下一般必须要被父进程进行等待
为什么捏
你想奥如果进程在退出时父进程不管不顾退出进程状态将会变成Z僵尸状态发生内存泄漏进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力因为谁也没有办法杀死一个已经死去的进程就像是永远没办法叫醒一个装睡的人
1.父进程通过等待解决紫禁城退出的僵尸问题回收系统资源一定要考虑的
2.获取紫禁城的退出信息知道紫禁城是什么原因退出的可选功能
怎么办
要来看两个可爱的函数wait、waitpid
wait
返回值等待成功时紫禁城pid
参数等待任意一个紫禁城退出是输出型的参数获取紫禁城退出状态不关心可以置NULL
pid_t wait(int* status); 上代码
#includestdio.h
#includeunistd.h
#includestring.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid ChildRun()
{int cnt 5;while (cnt){printf(I am child process,pid:%d,ppid:%d\n, getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf(I am father,pid:%d,ppid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit ...\n);exit(0);}sleep(3);pid_t rid wait(NULL);if (rid 0){printf(wait success,rid:%d\n, rid);}sleep(3);printf(father quit\n);return 0;
}
一遍运行一边开监控脚本看看怎么个事
while :; do ps ajx | head -1 ps ajx | grep myprocess | grep -v grep; sleep 1;done
可以看到紫禁城在被父进程回收前是处于僵尸状态的 父进程在等待时候也没干其他事只是等
给大家看看单核处理器小猫 polo tiu ~橘域网链接已断开
如果紫禁城没有退出父进程其实一直在进行阻塞等待
紫禁城本身就是软件父进程本质是在等待某种软件条件就绪
阻塞等待
怎么个事
等待硬件or软件本质都是数据结构对象
来康康waitpid 关于这个就改一下就好
pid_t rid waitpid(-1, NULL, 0);
作用和上面的也一样-1是在等任意一个的意思等待紫禁城等待到了哪个就返回哪个那样的还准备俩函数干啥别着急这样就能等待特定的了我是在等可我在等的只是你
#includestdio.h
#includeunistd.h
#includestring.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid ChildRun()
{int cnt 5;while (cnt){printf(I am child process,pid:%d,ppid:%d\n, getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf(I am father,pid:%d,ppid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit ...\n);exit(0);}sleep(3);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;
}
也是可能失败的但基本上不会失败
#includestdio.h
#includeunistd.h
#includestring.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid ChildRun()
{int cnt 5;while (cnt){printf(I am child process,pid:%d,ppid:%d\n, getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf(I am father,pid:%d,ppid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit ...\n);exit(0);}sleep(3);pid_t rid waitpid(id1, NULL, 0);if (rid 0){printf(wait success,rid:%d\n, rid);}else{printf(wait failed\n);}sleep(3);printf(father quit\n);return 0;
}
再来回看这个函数
pid_t waitpid(pid_t pid, int status, int options); 返回值 当正常返回的时候waitpid返回收集到的子进程的进程ID等待成功紫禁城退出 父进程回收成功 若返回值为0那证明检测成功但紫禁城并未退出需要再次进行等待 若设置了选项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子进程存在且正常运行则进程可能阻塞 若不存在该子进程则立即出错返回 退出信息就退出码和退出信号啦可是Status只有一个数哎别猜了人家有特殊格式可以当做位图看待图中表示比特位 退出码0~255最多就那么多
这样可以直接看
#includestdio.h
#includeunistd.h
#includestring.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid ChildRun()
{int cnt 5;while (cnt){printf(I am child process,pid:%d,ppid:%d\n, getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf(I am father,pid:%d,ppid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit ...\n);exit(49);}sleep(3);int status 0;pid_t rid waitpid(id, status , 0);if (rid 0){printf(wait success,rid:%d\n, rid);}else{printf(wait failed\n);}sleep(3);printf(father quit,status:%d,child quit code:%d,child quit signal:%d\n,status,(status8)0xFF, status 0x7F);return 0;
} 退出后会发现是正常退出的 上面的宏和这个位操作差不多使用的话就是结果是紫禁城退出码想知道退出信号就自己去按位与去
if(WIFEXITED(status))
很好正和我意
那假如紫禁城死循环怎么办
看看不就知道了
#includestdio.h
#includeunistd.h
#includestring.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid ChildRun()
{int cnt 5;while (1){printf(I am child process,pid:%d,ppid:%d\n, getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf(I am father,pid:%d,ppid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit ...\n);exit(123);}sleep(3);int status 0;pid_t rid waitpid(id, status , 0);if (rid 0){printf(wait success,rid:%d\n, rid);}else{printf(wait failed\n);}sleep(3);printf(father quit,status:%d,child quit code:%d,child qiut signal:%d\n,status,(status8)0xFF, status 0x7F);return 0;
} 当然是爹一直等了把紫禁城干掉回收
如果紫禁城异常怎么办
#includestdio.h
#includeunistd.h
#includestring.h
#includestdlib.h
#includesys/types.h
#includesys/wait.hvoid ChildRun()
{int cnt 5;int* p NULL;while (cnt){printf(I am child process,pid:%d,ppid:%d\n, getpid(), getppid());sleep(1);cnt--;}*p 10;
}int main()
{printf(I am father,pid:%d,ppid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit ...\n);exit(123);}sleep(3);int status 0;pid_t rid waitpid(id, status , 0);if (rid 0){printf(wait success,rid:%d\n, rid);}else{printf(wait failed\n);}sleep(3);printf(father quit,status:%d,child quit code:%d,child qiut signal:%d\n,status,(status8)0xFF, status 0x7F);return 0;
}
也看看 可以发现直接挂了退出信息告诉程序猿赶紧回去查查你的代码有bug
如果紫禁城没有退出父进程在进行执行waitpid进行等待等待某种条件发生只不过如今的条件恰好是紫禁城退出阻塞等待进程阻塞父进程什么事都没干
但是现在我们使用的大部分进程都是阻塞板的WNOHANG选项就是非阻塞等待如果一直hang住什么都做不了我们把这种情况叫做服务器宕机
讲个小故事来阐述这个故事吧
从前有一只学生名为燃燃子她舍友是个学霸叫挽鸢超级厉害什么都会的那种你问她要课堂笔记没有一个是不记录的平时都不逃课有天燃燃子给挽鸢说宝宝下午C语言要考试了给我画个重点呗考完咱俩出去吃好吃的我请客挽鸢欣然答应但是挽鸢当时正在学cpp的一本书就问燃燃子能不能等她半小时她学完就干燃燃子一听说那好吧你先忙燃燃子在等待挽鸢的过程中一会开局王者一会刷会视频号一会拿出书装样子看看过了差不多半小时燃燃子给挽鸢打电话问她好了没挽鸢说还有两分钟就好怎么可能等待是周而复始的但燃燃子在等待挽鸢的过程中还做了其他事所以这是非阻塞等待打电话的过程是函数调用调用的本质是在检测挽鸢的状态燃燃子和挽鸢说话的过程是函数传参挽鸢告诉燃燃子自己还需要一会的过程就是函数返回值 故事拉长燃燃子在挽鸢的帮助下顺利考过了C语言考试燃燃子狂喜但是先别急着高兴过两天考操作系统了燃燃子我嘞个骚刚操作系统是啥啊于是燃燃子顺理成章找到挽鸢哎嘿能不能再帮我划个操作系统重点这两天饭我包了挽鸢说OK啊但是挽鸢当时在学Linux网络编程就问燃燃子能不能等她一会她还没看完但是燃燃子觉得来回打电话有点麻烦就和挽鸢说你不用挂电话就把手机放旁边好了直接叫我就好这个时候燃燃子墨墨听着电话那头的无尽的翻书声只是沉默着她什么也没干这个时候燃燃子在进行的是阻塞等待同时状态不就绪就不返回这时路过一只笙宝看燃燃子啥也不干就在那扒着手机听听听于是笙宝过去问“干啥呢干在这坐着”燃燃子也不理过会笙宝自讨没趣走了那燃燃子为何要进行这样的苦等呢有很多种可能可能单纯就是想等着还有可能是挽鸢比较受欢迎不太容易约到但是阻塞等待在现实中不太能存在吧应该waitpid检测紫禁城状态变化的
当我们采用非阻塞等待的时候一般要加上循环直到检测到紫禁城退出我们把这种方案叫做非阻塞轮询方案
而阻塞等待优点也很明显了就是简单可靠但非阻塞时父进程可以做其他的事
各有千秋
写段非阻塞轮询的代码吧
#includestdio.h
#includestring.h
#includesys/types.h
#includesys/wait.h
#includestdlib.h
#includeunistd.hvoid ChildRun()
{int cnt 5;while (cnt){printf(I am child process,pid:%d,ppid:%d,cnt:%d\n, getpid(), getppid(), cnt);cnt--;sleep(1);}
}int main()
{printf(I am father process,pid:%d,pid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit\n);exit(123);}while (1){int status 0;pid_t rid waitpid(id, status, WNOHANG); //进行非阻塞等待if (rid 0){printf(child is running, father check next time!\n);//DoOtherThing();}else if (rid 0){if (WIFEXITED(status)){printf(child quit success,child exit code:%d\n, WEXITSTATUS(status));}else{printf(child quit unnormal!\n);}break;}else{printf(waitpid failed!\n);break;}}return 0;
}
刚说在父进程等待的时候还可以做其他事下面来举个栗子基于函数指针级别的对父进程完成任务进行解耦
myprocess.c
#includestdio.h
#includestring.h
#includesys/types.h
#includesys/wait.h
#includestdlib.h
#includeunistd.h
#includetask.htypedef void(*func_t)();#define N 3
func_t tasks[N] { NULL };void LoadTask()
{tasks[0] Printlog;tasks[1] Download;tasks[2] MysqlDataSync;
}void HanderTask()
{for (int i 0; i N; i){tasks[i]();}
}void DoOtherThing()
{HanderTask();
}void ChildRun()
{int cnt 5;while (cnt){printf(I am child process,pid:%d,ppid:%d,cnt:%d\n, getpid(), getppid(), cnt);cnt--;sleep(1);}
}int main()
{printf(I am father process,pid:%d,pid:%d\n, getpid(), getppid());pid_t id fork();if (id 0){ChildRun();printf(child quit\n);exit(123);}LoadTask();while (1){int status 0;pid_t rid waitpid(id, status, WNOHANG); //进行非阻塞等待if (rid 0){printf(child is running, father check next time!\n);//DoOtherThing();}else if (rid 0){if (WIFEXITED(status)){printf(child quit success,child exit code:%d\n, WEXITSTATUS(status));}else{printf(child quit unnormal!\n);}break;}else{printf(waitpid failed!\n);break;}}return 0;
}
task.h
#pragma once
#includestdio.h
#includestring.h
#includesys/types.h
#includesys/wait.h
#includestdlib.h
#includeunistd.hvoid Printlog();
void Download();
void MysqlDataSync();
task.c
#includetask.hvoid Printlog()
{printf(begin Printlog...\n);
}void Download()
{printf(begin Download...\n);
}void MysqlDataSync()
{printf(begin MysqlDataSync...\n);
}
makefile
myprocess:myprocess.c task.c
gcc - o $ $ ^
.PHONT:clean
clean:rm -f myprocess
父进程就完成了在轮询检测时还做其他事
就这些捏到目前为止说的差不多啦再会啦~