找网络公司做的网站可以出售吗,百度创建网站,做视频网站带宽要求,响应式旅游网站模板下载前言#xff1a;前面了解完前面的Linux进程基础概念后#xff0c;我们算是解决了Linux进程中的一大麻烦#xff0c;现在我们准备更深入的了解Linux进程——Linux进程控制#xff01; 我们主要介绍的Linux进程控制内容包括#xff1a;进程终止#xff0c;进程等待与替换前面了解完前面的Linux进程基础概念后我们算是解决了Linux进程中的一大麻烦现在我们准备更深入的了解Linux进程——Linux进程控制 我们主要介绍的Linux进程控制内容包括进程终止进程等待与替换进程程序替换 本篇主要内容 重识进程创建 进程退出场景 错误码和退出码 进程终止 1. 重识进程创建1.1 fork函数1.2 写时拷贝1.3 fork创建进程与进程终止 2. 进程终止2.1 main的退出码2.2 C语言的错误码2.3 进程异常终止 3. 总结拓展拓展exit与_exit的区别总结 1. 重识进程创建
1.1 fork函数
在我们之前提到过创建进程使用的是fork函数它从已存在进程中创建一个新进程。新进程为子进程而原进程为父进程。
#include unistd.hpid_t fork(void);// 返回值自进程中返回0父进程返回子进程id出错返回-1进程调用fork当控制转移到内核中的fork代码后内核做 分配新的内存块和内核数据结构给子进程将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中fork返回开始调度器调度 fork函数返回值 子进程返回0父进程返回的是子进程的pid 这些我们在之前都提到过我们复习一下今天我们重点要讲的是fork的写时拷贝 1.2 写时拷贝
通常父子代码共享父子再不写入时数据也是共享的当任意一方试图写入便以写时拷贝的方式各自一份副本。 当父进程形成子进程之后子进程写入发生写时拷贝重新申请空间进行拷贝修改页表(OS) 但是我们怎么知道发生了写时拷贝呢写时拷贝的内容都是由操作系统来完成的 其实父进程创建子进程的时候首先将自己的读写权限改成只读然后再创建子进程此时是操作系统在做用户并不知道而且用户可能会对某一数据进行写入这时页表转换就会出现问题操作系统就会介入就触发了我们重新申请内存拷贝内容的策略机制 1.3 fork创建进程与进程终止
接下来我们看一下这段代码 #includestdio.h#includeunistd.h#includestdlib.h#define N 10typedef void (*callback_t)(); // 函数指针void Worker(){int cnt 10;while(cnt){printf(i am child process, pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt);sleep(1);cnt--;}}void createSubProcess(int n, callback_t cb){int i 0;for(i 0; i n; i){sleep(1);pid_t id fork();if(id 0){printf(create child process success: %d\n, i);// childcb(); exit(0);}}}int main(){createSubProcess(N, Worker);// 只有父进程走到这里 sleep(100);return 0;}
而代码中的exit函数可以让进程终止
这段代码涵盖了fork创建进程与进程终止让我们来执行以下 fork创建进程与进程的终止 下面将让我们真正来了解进程终止 2. 进程终止 进程退出场景 代码运行完毕结果正确代码运行完毕结果不正确代码异常终止 前面二者都是正常退出而最后则是异常退出 2.1 main的退出码
我们之前写代码中main函数只能return 0吗答案是肯定不是! 在多进程环境中我们创建子进程的目的就是协助父进程办事但是父进程怎么知道子进程把事情办得怎么样所以父进程要知道子进程办的怎么样就有了退出码而main函数的返回值就是进程的退出码 返回 0 就表示成功其他数字就表示进程失败的原因每个不同的数字代表不同的原因
我们可以通过函数来直接查看每个数字代表的意义strerror 它可以返回描述错误码的字符串 #includestdio.h
#includestring.hint main()
{for(int i 0; i 200; i){printf(%d: %s\n, i, strerror(i));}return 0;
}退出码 0 正好对应的是成功 当我们134位置处时发现已经没有错误信息了。 注意错误码我们可以自己自定义 main函数的退出码是可以被父进程获取的用来判断子进程的运行结果 我们可以直接用指令查看进程的退出码 指令echo $? int main()
{return 31;
}我们在执行完这段代码后再用指令查看退出码 我们可以发现指令echo $?返回的是上一个进程的错误码 2.2 C语言的错误码 错误码和退出码 错误码通常是衡量一个库函数或者是一个系统调用一个函数的调用情况退出码通常是一个进程退出的时候他的退出结果二者都是当失败的时候用来衡量函数、进程出错是的出错详细原因 我们用代码来测试一下
#includestdio.h
#includeerrno.hint main(){// errno 可以返回最近一次错误的错误码int ret 0;printf(before: %d\n, errno);FILE *fp fopen(csdn.txt, r);if(fp NULL){printf(after: %d, error string: %s\n, errno, strerror(errno)); ret errno;}return ret;} 一旦出现错误错误码errno就会被系统自动赋值
可以通过错误码来判断代码运行的情况 2.3 进程异常终止
当我们的代码出异常了也就是进程异常终止了退出码还有意义吗
// 当我们在运行这样的代码时int a 100;
a / 0;
......
int *p NULL;
*p 100;
......第一种情况 Floating point exception 第二种情况 Segmentation fault
当然不止这两个情况但是它们都会让程序进程异常终止
其实一旦程序出现了异常操作系统就是通过信号的方式来杀掉这个进程 而我们的前面两种情况正好对应了kill -8 和 kill -11
我们来试试通过使用信号能不能还原之前这几种错误情况
while(1)
{printf(i am a process, pid: %d\n, getpid());sleep(1);
}通过信号终止进程 进程出异常本质是收到了对应的信号因此一个进程是否出异常我们只要看有没有收到信号即可 3. 总结拓展
拓展exit与_exit的区别
在之前讲到正常退出时我们有3种情况。 正常终止 从main返回 调用exit _exit 而exit与_exit两个有什么区别和联系呢
首先他们二者都可以让进程终止并且使用方法也一样那他们到底有什么区别呢我们用代码来一探究竟
//代码一
int main()
{printf(Hello);exit(0);
}......
//代码二
int main()
{printf(Hello);_exit(0);
}代码一 代码二 为什么会出现这种情况呢 exit最后也会调用exit, 但在调用exit之前还做了其他工作 执行用户通过 atexit或on_exit定义的清理函数。 关闭所有打开的流所有的缓存数据均被写入 调用_exit printf打印如果不使用\n换行的话,数据会被存储到缓冲区里,exit函数会帮助我们 刷新缓冲区的数据,然而_exit函数不会。因为exit函数在调用exit之前将所有缓存数据都写入了所以在终止进程时会将数据打印在屏幕上 总结
进程终止相对于其他的进程控制来说是很好理解的。我们主要介绍了进程终止在明白错误码和退出码之后相信能理解父进程是怎么得到子进程信息的。让我们期待下一节——进程等待与替换
谢谢大家支持本篇到这里就结束了