普通营业执照有做网站条件吗,郑州建网站371,做房产网站能赚钱吗,东风地区网站建设价格低进程创建 fork
1.fork 之后发生了什么 将给子进程分配新的内存块和内核数据结构#xff08;形成了新的页表映射#xff09;将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中fork 返回#xff0c;开始调度器调度 这样就可以回答之前返回两个值#xff1f…进程创建 fork
1.fork 之后发生了什么 将给子进程分配新的内存块和内核数据结构形成了新的页表映射将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中fork 返回开始调度器调度 这样就可以回答之前返回两个值
发生了写实拷贝形成了两个物理空间块
测试
#include stdio.h
#include unistd.h
#include stdlib.hint main(void)
{printf(Before - pid: %d\n, getpid());fork();printf(After - pid: %d\n, getpid());sleep(1);return 0;
} 创建了一个子进程
一般来说子进程创建之后会共享父进程的所有代码
是怎么知道的呢没关系eip 程序计数器会出手 eip 叫做 程序计数器用来保存当前正在执行的指令的下一条指令。eip 程序计数器会拷贝给子进程子进程便从该 eip 所指向的代码处开始执行。 我们再来重新思考一下 fork 之后操作系统会做什么 进程 进程的数据结构 进程的代码和数据 创建子进程的内核数据结构 struct task_struct struct mm_struct 页表 代码继承父进程数据以写时拷贝的方式来进行共享或者独立。 代码共享写实拷贝确保了进程的独立性
写实拷贝
当任意一方试图写入就会按照写时拷贝的方式各自拷贝一份副本出来。写时拷贝本身由操作系统的内存管理模块完成的。
选择暂时先不给你等你什么时候要用什么时候再给。这就变相的提高了内存的使用情况。
fork
fork 之后利用 if-else 进行分流 让父子执行不同的代码块。我们做网络写服务器的时候会经常采用这样的编码方式例如父进程等待客户端请求生成子进程来处理请求。
继承大纲后又有所区别
fork 肯定不是永远都成功的fork 也是有可能调用失败的。
测试
#include stdio.h
#include unistd.h
#include stdlib.hint main(void)
{for (;;) {//写了一个死循环pid_t id fork();if (id 0) {printf(子进程创建失败\n);break;}if (id 0) {printf(I am a child... %d\n, getpid());sleep(2); // 给它活2秒后 exitexit(0); // 成功就退出}}return 0;
} 进程终止 exit
为什么要使用return?
思考代码运行完的结果一般有以下三种 代码运行完毕结果正确 代码运行完毕结果不正确 代码异常中止 成功了就是成功了失败了就会有各种原因
我们重点来对不正确进行思考
进程中谁会关心我运行的情况呢 父进程
怎么表示不同的出错原因呢 A: 进程的退出码 return 0 表示正确main函数的返回值本质表示进程运行完成时是否是正确的结果如果不是我们可以用不同的数字表示出错的原因 模拟一个逻辑的实现
$? : 保存的是最近的一次进程退出的时候的退出码 我们想要进步不再是随便无脑 return 了我该怎么办呢
一般而言失败的非零值我该如何设置呢非零值默认表达的含义又是什么呢 首先失败的非零值是可以自定义的我们可以看看系统对于不同数字默认的 错误码 是什么含义。C 语言当中有个的 string.h 中有一个 strerror 接口 如果感兴趣可以看看 2.6.32 的内核代码中的 /usr/include/asm-generic/errno.h 及 errno-base.h输出错误原因定义归纳整理如下
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
查看错误码 strerror(i)
我们可以在 Linux 下写个程式去把这些错误码给打印出来
结果如下 系统提供的错误码和
添加图片
const char *errString[]{
success,
error 1,
error 2,
error 3
};
实践
对于 ls myfile.txt 的查看
$?: 2
其中0 表示 success1 表示权限不允许2 找不到文件或目录。 我们刚才 ls 一个不存在的再 echo $? 显示对应的错误码就是 2 所以无论正确还是不正确统一会采用进程的退出码来进行判定 不同的错误码方便我们定位问题出在哪里 我们可以通过以下方法来查看
C 语言中 errno - number of last error
打印错误 strerror(errno) 如果代码异常了退出码还有意义吗 本质可能就是代码没有跑完都跑不到那个地方了 进程的退出码无意义了因为可能都到不了 return ? : 那么如何知道发生了什么异常呢 发信号 eg. 野指针 举例 8 11 表示异常 kill -8 出现野指针 -11 段错误 终止进程的做法
#includestdlib.h
exit 的退出码 12
exit 在任意地方被调用都表示调用进程直接退出
return 只表示当前函数的返回
测试
#include stdio.h
#include stdlib.hvoid func() {printf(hello func\n);exit(111);
}int main(void)
{func(); return 10;
} 注意只有在 main 函数调 return 才叫做 进程退出其他函数调 return 叫做 函数返回。
_exit 和 exit
exit 会清理缓冲区关闭流等操作而 _exit 什么都不干直接终止。
void func() {printf(hello exit);exit(0);
}int main(void)
{func(); printf(hello _exit);_exit(0);
} 是否冲刷缓冲区的区别 缓冲区Buffer的概念在计算机科学中非常广泛但在你提供的上下文中缓冲区指的是一种用于临时存储数据的内存区域。在介绍内核的数据结构缓冲池时缓冲区的概念主要与提升性能和合理利用内存资源有关。
具体来说你提供的描述强调了以下几点
开辟空间和初始化有成本 在操作系统中每次为新进程或新数据结构开辟内存空间并初始化都需要花费时间和资源这会影响系统性能。
废弃的数据结构链表 为了优化这种开销Linux 操作系统会维护一张废弃的数据结构链表。这个链表上存放的是那些已经被标记为“无效”的数据结构但其内存空间并没有被立即释放。这些数据结构包括 task_struct 和 mm_struct 等。
重用策略 当一个进程被释放即终止后它的相关数据结构不会立即被完全删除而是被标记为无效并加入废弃的数据结构链表中。当有新的进程创建时操作系统会首先检查这个链表从中取出一个合适的、已经存在的但无效的数据结构如 task_struct 和 mm_struct进行必要的初始化然后再使用。
内核的数据结构缓冲池 这种方法本质上是一种内存池也即“缓冲池”专门用来存放和重用数据结构的内存。Slab 分配器Slab Allocator即是实现这种内存池概念的机制之一。通过使用 slab 分配器系统能有效减少重复的内存分配和释放操作提高运行效率并减少内存碎片化的问题。
它的原理是将内存按照大小分成不同的块然后将这些块以页的形式进行分配和管理。当需要分配内存时slab 分配器会从对应大小的块中选择一个可用的块分配给程序当内存不需要时这块内存又会被返回到对应的块中以便后续重复使用从而降低了内存的分配和释放开销。这种方式可以提高内存分配的性能减少内存碎片化
sum: 缓冲区在这个背景下的概念是指一块预分配的内存用来存储和重复利用特定类型的数据结构。这种做法可以显著减少频繁的内存分配和释放所带来的开销提高系统性能这也是 slab 分配器背后的核心思想。 slab 分配器根据合适的内存拿不要就放回去
我们 printf 一定是把数据写入缓冲区中合适的时候在进行刷新
这个缓冲区绝对不在哪里
绝对不在内核里在用户空间要不然一定会被刷新