linux 判断指针是否可读_Linux进程详解(一)

1. 进程的代码结构

1.1 进程控制块PCB与TASK_STRUCT

进程是一个资源封装的单位,资源指占用的内存,文件系统,信号及处理方法。线程是调度执行的单元。一个进程区别与另一个进程的标记就是资源。linux操作系统是可以做到进程与进程之间的资源隔离。进程的描述就是资源的描述。PCB (PROCESS CONTROL BLOCK) 在不同操作系统中用于描述进 程,在Linux的 PCB 就是用task_struct来描述。如图 1-1 所示,图中列出了主要对应包含的资源种类及作用.

79e31e93245d06dcb910b659327aa840.png

mm 内存资源: 进程的内存

fs 文件系统资源 1: 根路径和当前路径指针

files 文件系统资源 2: 进程打开的文件,文件描述符数组

signal 信号资源: 不同进程可以针对同一信号挂不同的处理方法

pid 属性资源: 描述进程的属性

1.2 TASK_STRUCT 的属性特点

linux的pid与tgid

一个进程fork出子进程后,从linux内核的角度看,对应的pid肯定不一样。但是为了符合POSIX的标准要求,POSIX 要求规定同一个父进程fork出的子进程,调用getpid返回的pid的号必须是一样的,我们用top命令查看进程可以看到fork出的子进程与父进程的Pid号是一样的。linux实现的原理就是通过增加一个tgid来实现父子进程调用 getpid 时返回值都一样的效果。

linux进程task_struct的三种数据结构

在linux代码中会涉及各种对task_struct的引用关系,比如调度算法中会将task_struct挂在链表上,父子进程的关系用树来描,CFS调度算法会用到红黑树,通过pid查找进程则是用hash表的结构。其对应的数据结构如图1-2所示

ea56574d06d9bbdb0d27a0ce90a08745.png

2. 进程的状态特征

2.1 进程状态切换

进程运行时的3个基本状态

操作系统包括实时系统对应进程一般都有3个状态,进程在有CPU时对应运行态,无CPU时对应就绪态和睡眠态。就绪态指所有资源都准备好,只要有CPU就可以运行了。睡眠指有资源还未准备好,比如读串口数据时,数 据还未发送。此时有 CPU 也无法运行,需要等资源准备好后变成就绪态,然 后得到 CPU 后才能变成运行态,其转换关系如图 2-1 所示。

d3fe67d88225d7cfd62b7f8fdf2f6e5f.png

2.2 linux进程扩展的6个状态

1. 僵尸态:子进程退出后,所有资源都消失了,只剩下 task_struct,父进 程在 wait 函数中可以得到子进程的死亡原因。在 wait 之前子进程的状态就是僵尸态。

2. 深度睡眠:等待资源到位后才醒过来

3. 浅度睡眠: 等待资源到位或收到信号后都会醒过来

4. 暂停: stop 状态是被外部命令作业控制等强制进程进入的状态。

5. 就绪:未占用 CPU,等待调度算法调度到运行态的进程

6. 运行:占有 CPU,正在运行的线程。

79aff237c5fd59b3dcd369872b88aa69.png

暂停状态是进程在运行过程中,通过外部bash命令强制让进程进入的状态。通过这种方法可以指定进程的CPU占用率。后面我们通常用 cgroup 的方法来实现,这里仅作了解。

进入stop状态的方法:

ctrl + z, fg/bg

cpulimit -l 20 -p 10111: 限制 pid 为 10111 程序的 CPU 使用率不超过 20%

linux进程状态的联系和区别:

就绪 VS 运行 linux 的调度算法只管理就绪和运行态中的进程,只对应中的就绪和占有状态的进程,这两个状态都称为task_running。

深度睡眠 VS 浅度睡眠 深度睡眠只有资源到位才醒,收到信号也不醒,浅度睡眠资源到位或收到信号都会醒

睡眠 VS 暂停 睡眠是代码中未得到资源主动进入的状态,暂停是程序外部强 制进程进入的状态。

2.3进程的内存泄露

内存泄露指随着时间的增长,进程的内存使用呈现线性增长的情况,指的是进程一直在运行,运行中申请了内存,但使用完后并没有释放,运行期间每次都申请内存而不释放导致系统内存越来越少的情况。这里要理解内存泄露的 原因不可能是进程死了,内存没释放。因为进程死了之后就变成僵尸,Linux 会自动将进程中申请的资源全部释放,只留下task_struct让父进程wait来查看状态。不可能再占用内存。

3. 进程的出生、运行、死亡

3.1 进程出生时资源处理

fork 出子进程后,子进程的资源就直接从父进程的进程结构task_struct拷贝出同样的信息,如图3-1所示。进程P2刚创建后,其资源是一模一样的。Linux 内核的调度算法,是根据task_struct结构体来进行调度的。

8c86030aa5c9cc58c199554afb485303.png

所有资源的结构体都会被copy。之后随着进程变化,本着谁修改谁分裂的原则进行资源变化的处理。

3.2 写实拷贝技术

当p1把p2创建出来时,会把task_struct里描述的资源结构体对拷给p2。

区分进程的标志,就是 p2的资源不是p1的资源。两个task_struct 的资源都相同,那就不叫两个进程了。

执行一个copy,但是任何修改都造成分裂,如:chroot,open,写memory,

最难copy的是 mm 这个部分,因为要做写时拷贝。

Linux通过MMU进行虚拟地址到物理地址的转换,当进程执行fork()后,会把页表中的权限设置为RD-ONLY,当P1,P2去写该页时,CPU会收到page fault,申请新的内存。Linux再将页表中的virt1指向新的物理地址。

5f17ea1b785968e3c2d9fc08ca815223.png

下面我们具体分析程序背后采用 COW 的原理和流程。

fork前第1阶段: 全局变量data对应数据段内存vir和phy都在数据段,权限为可读可写。

fork后: vir和Phy的权限全部变成只读权限,读内存正常,写内存会进入page fault缺页中断。

fork后写内存: 写内存后,发生缺页中断,Linux会重新申请一个4k内存,将新物理内存指向更改了内存地址的进程vir。同时将老的 4k内存拷贝给新的内存,同时将权限改为R+W,这样父子进程的同一个vir虚拟地址就分别对应2个独立的可读可写的物理地址。总之谁先写谁拿到新的物理内存,原内存留给剩下的进程。

3.3 无法用COW的情况:VFORK

COW 技术必须借助 MMU(内存管理单元)来实现。COW 是通过改变虚 拟内存和物理内存的映射关系来实现,没有 MMU 的系统,无法实现虚拟内存 和物理内存的映射。也无法调用 fork 函数,无 MMU 系统对应调用的是 vfork 函数,其资源变化对比 fork 如图 3-3 所示:

abb0f1373842dcdb15c9685c0a078e20.png

vfork 的特点:vfork 会阻塞父进程,只有等子进程完全退出后才执行父进程。

1) exit

2) exec

CoW,严重依赖CPU的MMU。Mmu-less Linux 无copy-on-write, 没有fork,而使用vfork。

3.4强制共享资源–线程

当P1和P2都用同一个资源,资源结构体不进行拷贝,P2的资源指针直接指向P1,这样就体现出线程的特征:可以调度又共享一样的资源。Linux中也是这样来实现线程的,调用pthread_create时,会调到CLONE的API,这样就会让P2的资源指针指向P1。注:最终调用的API为CLONE,pthread_create

6e7525f2191def029d348fa1aa72dc7d.png

3.5第1个进程,进程0与进程1

开机后进程0创建出进程1,开机后进程0会退化成idle进程,idle进程的优先级最低。此进程运行的原则是所有其他的进程不运行时它就开始运行,当运行idle进程时,cpu就设置成低功耗模式。(注:与开机键中的suspend的区别是idle状态时只有cpu是低耗,suspend时显示器电源等其他设备也会进入 低功耗)。此设计的精妙之处在于,如果不用进程0,进程进入低功耗模式的判断标准就变成了所有进程退出后要检查一下是否是最后一个进程,如果是最后 一个就进入低功耗模式。这样的设计就会把检查状态耦合到了每个进程之中。 增加进程0设计的好处在于,设计就简化只要判断是否在idle进程就可以了。实现了去耦合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值