首页 > 系统相关 >Linux系统编程·进程创建及终止

Linux系统编程·进程创建及终止

时间:2022-11-14 14:34:04浏览次数:66  
标签:fork return 函数 编程 进程 exit Linux 终止 拷贝


Linux系统编程·进程创建及终止_子进程

你好,我是安然无虞。

文章目录

  • ​​自学网站​​
  • ​​进程创建​​
  • ​​fork函数​​
  • ​​写时拷贝​​
  • ​​进程终止​​
  • ​​进程退出场景​​
  • ​​练习题​​

自学网站

推荐给老铁们两款学习网站:
面试利器&算法学习:​​​牛客网​​​ 风趣幽默的学人工智能:​​人工智能学习​

进程创建

fork函数

fork函数在Linux中是一个很重要的函数, 它是从已经存在的进程中创建一个新的进程. 新进程为子进程, 而原进程为父进程. 所以, ​​fork函数是用来创建子进程的​​.

Linux系统编程·进程创建及终止_数据_02


返回值: ​​fork 函数比较特殊, 它有两个返回值, 给子进程返回0, 给父进程返回子进程的pid​​, 出错返回-1

调用 fork 函数, 当控制转移到内核中的 fork 代码后, 内核做了几件事:

  • ​分配新的内存块和内核数据结构给子进程(task_struct, mm_struct等)​​;
  • ​将父进程部分数据结构内容拷贝至子进程​​;
  • ​添加子进程到系统进程列表当中​​;
  • ​fork 返回, 开始调度器调度​​.

当一个进程调用 fork 函数之后, 就会有两个二进制相同的代码的进程, 而且它们都运行到相同的地方, 但是每个进程都可以执行自己的代码.
做个小实验:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main()
{
printf("Before: pid is %d\n", getpid());
pid_t id = fork();
if(id == -1)
{
perror("fork()");
exit(1);
}
printf("After: pid is %d, fork return %d\n", getpid(), id);
sleep(1);

return 0;
}

执行程序:

Linux系统编程·进程创建及终止_数据_03


我们看到的现象是:

打印了三行内容, 一行before, 两行after, 注意哦, 进程28187没有打印before, 这是为什么呢?

Linux系统编程·进程创建及终止_运维_04


fork之前只有父进程, 父进程独立执行, fork 之后, 有两个进程, 分子两个执行流分别执行. 注意哦, ​​fork 之后, 谁先执行并没有硬性规定, 完全由调度器决定​​.

​那么 fork 之后, 是否只有 fork 之后的代码是被父子进程共享的? 不是, 一般情况下, fork 之后, 父子进程共享所有的代码, 子进程执行的后续代码 != 共享的所有代码, 只不过子进程只能从这里开始执行​​, 如上图所示.

因为我们知道, CPU的内部是有一些寄存器的, 其中有一个eip寄存器, 叫程序计数器, 也叫PC指针, 它是保存当前正在执行指令的下一条指令. 所以eip程序计数器会拷贝给子进程, 子进程便从eip所指向的代码处开始执行.

写时拷贝

我们知道​​进程是具有独立性的, 通常情况下, 父子进程代码共享, 如若父子不再写入时, 数据也是共享的, 但是当任意一方试图写入, 数据便以写时拷贝的方式各自独立​​.

所以 ​​fork 之后, OS做了什么​​?

​进程 = 进程的内核数据结构 + 进程的代码和数据. 所以 fork 之后创建子进程的内核数据结构(task_struct + mm_struct + 页表等), 代码继承父进程, 数据以写时拷贝的方式, 来共享或独立​​.

为什么要写时拷贝?

我们说了这么多次写时拷贝, 为什么要发生写时拷贝呢, 创建子进程的时候, 就把数据分开不行吗?
当然不行, 原因有三:

  • 父进程的数据, 子进程不一定会用, 即便使用, 也不一定全部写入, 所以会有浪费空间的嫌疑;
  • 最理想的情况, 只有会被父子修改的数据, 进行分离拷贝, 不需要修改的共享即可, 但是这样的话, 技术角度实现复杂;
  • 如果fork的时候就无脑拷贝数据给子进程, 会增加 fork 的成本, 比如内存和时间等.

所以​​采用写时拷贝的方式, 只会拷贝父子修改的数据, 是拷贝数据的最小成本, 但是拷贝的成本依然是存在的, 这也可以认为是一种延时拷贝策略 (只有你真正使用的时候才给你, 你想要, 但是不立马使用的空间, 先不给你, 这也就意味着可以先给别人) 变相的提高内存的使用率​​.

写时拷贝本身就是由OS的内存管理模块完成的.

Linux系统编程·进程创建及终止_运维_05

fork 函数的常规用法:

  • 一个父进程希望复制自己, 使父子进程同时执行不同的代码段;
  • 一个进程要执行不同的程序(后面程序替换时详细讲解)

fork 函数调用失败的原因:

  • ​系统之中有太多的进程;​
  • 实际用户的进程数超过了限制.

进程终止

进程退出场景

进程退出总共有三种场景:

  • ​代码跑完, 结果正确​​;
  • ​代码跑完, 结果不正确​​;
  • ​代码没跑完, 程序异常​​.

1,2两种情况关注的是退出码, 第3种情况关注的是退出信号, 这个进程等待的时候详细介绍.

关于进程终止的正确认识:
我们之前在编写 C/C++ 代码的时候, main函数是入口函数, 进程return 0. 下面有几个问题, 看看你知道吗:

  1. return 0, 给谁return?
  2. 为何是0? 其他值可以吗?

进程代码执行完毕, 我们想要知道结果是否正确, 常用0表征成功, 用非零表征失败.

为什么用非零表征失败呢? 因为当结果错误, 我们最想知道的是失败的原因, 所以用非零标识不同的原因, main函数中, ​​return x 代表的是进程退出码, 用来表征进程退出的信息, 让父进程读取的. 所以return 0, 是给父进程return 的​​.

一般而言, 失败的非零值我该如何设置呢, 以及默认表达的含义?
错误码, 退出码可以对应不同的错误原因, 方便我们定位问题, 这个放在后面讲解.

关于进程终止的常见做法:

1.​​在main函数中return​​​, 代表进程退出, 为什么其他函数中不行呢?
其他函数中return , 代表的是函数调用结束, 函数返回.

2.​​在自己的代码任意地点中, 调用exit()​​​ 其中exit() 用的多.
exit() 终止进程, 刷新缓冲区;
_exit() 终止进程, 不会刷新缓冲区.

Linux系统编程·进程创建及终止_数据_06


exit 函数最后也会调用_exit 函数, 但在此之前, 还做了其他动作:

Linux系统编程·进程创建及终止_运维_07

关于进程终止, 内核做了什么?

进程 = 进程的内核数据结构 + 进程的代码和数据

进程终止后, 代码和数据会被释放, 当时进程的内核数据结构, 如task_struct, mm_struct等, 操作系统可能并不会释放该进程的内核数据结构, 因为用task_struct, mm_struct 这些内核数据结构创建对象的时候, 要开辟空间和初始化啥的, 这些都是需要花时间的.

了解部分:
OS将不同的数据结构全部维护到一个链表中, 空间并没有释放, 只是设置为无效, 当再次创建进程时, OS会直接从这里拿出来相关的task_struct 和 mm_struct这些内核数据结构, 由此省去了开辟空间所花费的时间, 这样一来, 只要处理新进程的代码和数据的初始化工作即可.这里会提到一个概念, 叫做内核的数据结构缓冲池(slab分派器)

练习题

1.如何使一个进程退出,以下错误的是
A.在程序的任意位置调用return
B.在main函数中调用return
C.在程序的任意位置调用exit接口
D.在程序的任意位置调用_exit接口

解析:
退出进程的方式咱们讲到了三种:

  • ​在main函数中return​
  • ​主任意位置调用库函数 exit​
  • ​在任意位置调用系统调用 _exit​

2.关于进程退出返回值的说法中,正确的有
A.进程退出的返回值可以随便设置
B.进程的退出返回值可以在父进程中通过wait/waitpid接口获取
C.程序异常退出时,进程返回值为-1
D.进程的退出返回值可以在任意进程中通过wait/waitpid接口获取

解析:
进程的退出返回值也不能随意设置,因为进程的退出返回值实际上只用了一个字节进行存储,因此随意设置可能会导致实际保存的数据与设置的数据不同的情况,因为过大会导致数据截断存储;
waitpid(int pid, int *status, int options); 函数中 status参数 用于父进程获取退出子进程的返回值;
程序异常退出时,意味着程序并没有运行到return/exit去设置返回值,则返回值不做评判标准,因为返回值的存储位置的数据是一个未知随机值


标签:fork,return,函数,编程,进程,exit,Linux,终止,拷贝
From: https://blog.51cto.com/u_15495449/5849096

相关文章

  • Linux系统编程·进程概念
    你好,我是安然无虞。文章目录​​自学网站​​​​上文回顾​​​​进程控制块—PCB​​​​查看进程​​​​初识系统调用​​​​初始fork函数​​​​练习题​​自学网站......
  • SELinux
    1.SELinux基本架构与原理 SELinux是典型的MAC-MandatoryAccessControls实现,对系统中每个对象都生成一个安全上下文(SecurityContext),每一个对象访问系统的资源都......
  • Linux系统编程·进程地址空间
    你好,我是安然无虞。文章目录​​自学网站​​​​三个验证​​​​虚拟地址引入​​​​进程地址空间​​​​练习题​​自学网站推荐给老铁们两款学习网站:面试利器&算法......
  • C基础学习笔记——01-C基础第02天(用户权限、VI操作、Linux服务器搭建)
    在学习C基础总结了笔记,并分享出来。01-C基础第02天(用户权限、VI操作、Linux服务器搭建) 打开终端:ctrl+alt+t清屏:ctrl+l或clear在终端中退出锁定:ctrl+c 目录3常用命令4......
  • Linux系统编程·进程优先级
    你好,我是安然无虞。文章目录​​自学网站​​​​基本概念​​​​查看系统进程​​​​PRIvsNI​​​​其他概念​​​​竞争性​​​​独立性​​​​并行​​​​并发......
  • 响应式编程(反应式编程)的来龙去脉(同步编程、多线程编程、异步编程再到响应式编程)
    响应式编程的来龙去脉(同步编程、多线程编程、异步编程再到响应式编程)文章目录​​响应式编程的来龙去脉(同步编程、多线程编程、异步编程再到响应式编程)​​​​简介​​​​......
  • 在安卓手机上运行arm汇编程序
      效果图手机安装gcc.datamsg:.asciz"hello,gnuasm\n"len=.-msg.text.globalmainmain:push{r0,r1,r2,lr}ldrr1,=msgmov......
  • Linux命令之sar命令用例
    一、命令简介  sar(SystemActivityReporter系统活动情况报告)命令用于收集、报告或保存系统活动信息。sar命令将操作系统中选定的累积活动计数器的内容写入标准输出。会计......
  • Linux sar命令详解:分析系统性能
    sar命令很强大,是分析系统性能的重要工具之一,通过该命令可以全面地获取系统的CPU、运行队列、磁盘读写(I/O)、分区(交换区)、内存、CPU中断和网络等性能数据。sar命令的基本......
  • Linux配置node环境
    1.下载安装包https://nodejs.org/en/download/releases/官网下载需要的版本,我这里下的16版本mac本通过电脑自带远程传输sftp进行本地上传安装包,window需要自行百度一下......