Linux进程概念(上)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

输入:键盘、话筒、摄像头、磁盘、网卡…

输出:显示器、音响、磁盘、网卡…

(运算器 + 控制器)[CPU]:

存储器:内存

问:为什么要有内存?

答:技术角度:cpu的运算速度 > 寄存器的速度 > L1~L3 Cache > 内存 >> 外设(磁盘)>> 光盘磁带

从数据角度,外设几乎不和CPU直接进行交互,直接和内存直接交互,CPU也同样如此。

成本角度:寄存器 >> 内存 >> 磁盘(外设)

注意:几乎所有的硬件,只能被动的完成某种功能,不能主动的完成某种功能,一般都是要配合软件完成的(OS + CPU)。

操作系统(Operator System)

概念

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(例如函数库,shell程序等等)

设计OS的目的

  • 与硬件交互,管理所有的软硬件资源
  • 为用户程序(应用程序)提供一个良好的执行环境

定位

  • 在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件

image-20220809211343735

总结

计算机管理硬件

  1. 描述起来,用struct结构体
  2. 组织起来,用链表或其他高效的数据结构

系统调用和库函数概念

  • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分 由操作系统提供的接口,叫做系统调用。
  • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统 调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

进程

基本概念

  • 课本概念:程序的一个执行实例,正在执行的程序等
  • 内核观点:担当分配系统资源(CPU时间,内存)的实体。

image-20220809213923913

描述进程-PCB

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct
task_struct-PCB的一种
  • 在Linux中描述进程的结构体叫做task_struct。
  • task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
task_ struct内容分类
  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

PID

每个进程在系统中,都会存在一个唯一的标识符。这个标识就是PID(process ID)

注意:PID每次开启新进程都会随机分配一个PID。

组织进程

  • 可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

查看进程

进程的信息可以通过 /proc 系统文件夹查看(pro的全称是process)

注意:proc是内存文件系统,存放当前系统实时的进程信息。

  • 如:要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹。

image-20220811163301007

注意:上面这些蓝色的数字就是进程的PID。

  • 大多数进程信息同样可以使用top和ps这些用户级工具来获取

首先先编辑一个.c代码:

image-20220810133655100

然后运行:

image-20220810133728548

使用ps ajx命令即可查看进程(all job,x是以特定格式进行显示)

image-20220810134005412

使用ps ajx | grep 'mytest'查看与mytest相关的进程:

image-20220811161702375

注意:我们常常使用的ls、pwd、touch、grep、chgrp、chown、mkdir、rm等命令在启动后也都是一个个进程,这些二进制可执行文件在/usr/bin目录下,我们使用下面的指令可以进行查看,ls /usr/bin/*

使用ps ajx | grep 'mytest' | grep -v grep我们可以查看待用mytest关键词同时又不带有grep关键词的进程:

image-20220811161631546

问:如何查看进程信息和进程信息代表的意义?

答:使用ps ajx | head -1可以查看不同列的进程信息代表的意义:

image-20220811164313285

使用ps ajx | head -1 && ps ajx | grep 'mytest' | grep -v grep

image-20220811164510124

注意:&&是逻辑与,其意义是前面的指令执行成功了,再实行后面的指令。

此时根据PID在proc目录中进行查看:

image-20220811164805965

或者:

image-20220811164948414

使用ls /proc/31745 -al可以查看进程的详细信息:

image-20220811180208113

cwd(current work director):进程当前的工作路径。

exe后面对应的是可执行程序的磁盘文件。

通过系统调用获取进程标示符

  • 进程id(PID)
  • 父进程id(PPID)
PID

使用man getpid来查看getpid介绍

image-20220811182849102

使用举例:

image-20220811183307043

运行:

image-20220811183339712

PPID

使用举例:

image-20220811193941011

运行:

image-20220811194039390

问:为什么每次创建进程时,当前进程的PID每次都会改变,但是PPID却没有发生改变?

答:因为几乎我们在命令行上所执行的所有的指令(cmd),都是bash进程的子进程。

杀掉进程

  • 在程序运行过程中,使用ctrl + c来杀掉程序。

使用举例:

image-20220811183557419

  • 使用kill -9 PID来杀掉进程。

使用举例:

image-20220811193221852

通过系统调用创建进程-fork初识

  • 运行 man fork 认识fork

man 2 fork

image-20220811203606509

  • fork有两个返回值

image-20220811203738709

代码:

image-20220811215837389

运行结果:

image-20220811215816007

代码:

image-20220812113941572

执行结果:

image-20220812114018087

问:为什么会出现这种情况?

答:fork之后,父进程和子进程会共享代码,一般都会执行后续的代码,这就是为什么printf会打印两次的问题。

fork之后,父进程和子进程返回值不同,可以通过不通的返回值,判断,让父子执行不同的代码块。

问:为什么fork会给父进程返回子进程的PID,给子进程返回0?

答:因为父进程必须有标识子进程的PID来方便对子进程进行管理,所以fork之后会给父进程返回子进程的PID。

子进程最重要的是知道自己被创建成功了,因为子进程找父进程成本非常低(getppid())。

问:为什么fork函数会返回两次,有两个返回值?

答:image-20220812202845288

注意:for()之前的代码,在子进程中将不会继续执行。

  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

进程状态

Linux内核源代码

下面的状态在kernel源代码里定义:

/\*
\* The task state array is a strange "bitmap" of
\* reasons to sleep. Thus "running" is zero, and
\* you can test for combinations of others with
\* simple bit tests.
\*/
static const char \* const task_state_array[] = {
"R (running)", /\* 0 \*/
"S (sleeping)", /\* 1 \*/
"D (disk sleep)", /\* 2 \*/
"T (stopped)", /\* 4 \*/
"t (tracing stop)", /\* 8 \*/
"X (dead)", /\* 16 \*/
"Z (zombie)", /\* 32 \*/
};

注意:进程的状态定义在进程的task_struct中。

  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。 (操作系统中叫执行)

问:运行态是进程在CPU上运行,还是进程只要在运行队列中叫作运行态?

答:image-20220812222541094

运行态表示当前进程的task_struct在运行队列runqueue中,已经准备好了,可以随时被调度到CPU中进行执行。(参考分时操作系统)

  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。(操作系统中叫阻塞)

定义:当进程访问某些资源(磁盘网卡),该资源如果暂时没有准备好,或者正在给其它进程提供服务,此时:1. 当前进程要从runqueue中移除 2. 将当前进程放入对应设备的描述结构体中的等待队列。此时进程就处于阻塞状态。

注意:处于阻塞状态的进程的代码并没有被执行。

  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。 (操作系统中叫阻塞)

例如下面代码举例:

struct disk\_div
{
    //磁盘属性
    task_struct \*wait_queue;
}

在等待队列中的进程就处于阻塞状态。

问:S和D有什么区别吗?

答:S可以被中断,即可以被操作系统强制回收,但是D只能等待程序自己结束或者醒来,操作系统无法强制回收。

  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。 (操作系统中叫终止态)

问:这个进程已经被释放,就叫终止态?还是该进程还在,只不过永远不运行了,随时等待被释放?

答:该进程还在,只不过永远都不运行了,这种状态叫作终止态。为什么这种状态才叫作终止态?因为释放是要花费时间的,有时候操作系统很忙,所以没法立即回收我们的进程以及进程所占用的资源,回收进程和进程占用的资源与修改task_struct的状态位相比,显然前者的开销是要更小一些的。

问:如何使程序处于暂停状态?

答:使用kill -19 PID命令就可以使程序处于暂停状态。

问:如何使程序继续?

答:使用kill -18 PID命令就可以使程序继续。

  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

image-20220812213923600

运行:R

终止:Z和X

阻塞:S或者D

挂起:S或者T

挂起状态:

image-20220813110312580

进程状态查看
ps aux / ps axj 命令

使用举例:

process.c文件:

image-20220813120241730

运行之后查看进程状态:

image-20220813120421384

问:此时进程的状态是S,说明程序处于休眠状态或者阻塞状态,为什么此时的程序处于休眠状态而不是运行状态?

答:因为CPU执行的速度很快,大部分时间,进程在等待外设即输出设备,所以大部分时间是阻塞状态

对源程序进行下面的改变,进程的状态就会发生改变:

image-20220813203814689

image-20220813203900576

image-20220813213315180

注意:在上图中的就绪和执行状态在操作系统中都对应的是R状态。

Z(zombie)-僵尸进程
  • 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

模拟僵尸进程:

代码:

image-20220813224312659

image-20220813224212777

image-20220813224152795

问:长时间的保持僵尸状态会出现什么问题?

答:如果没有人回收僵尸子进程,该状态会一直维护,并且该进程的相关资源(task_struct)也不会被释放,进而出现内存泄漏问题。

孤儿进程
  • 父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
  • 父进程先退出,子进程就称之为“孤儿进程”
  • 孤儿进程被1号init进程领养,当然要有init进程回收喽。

process.c代码:

image-20220815172129232

执行结果:

image-20220815172229437

image-20220815172242688

最终只剩下一个子进程。

最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

资料预览

给大家整理的视频资料:

给大家整理的电子书资料:

如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

0815172229437](https://i-blog.csdnimg.cn/blog_migrate/de20b55c69bfe06270803efb46971989.png)

image-20220815172242688

最终只剩下一个子进程。

最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

资料预览

给大家整理的视频资料:

[外链图片转存中…(img-en6fpv9N-1715830785776)]

给大家整理的电子书资料:

[外链图片转存中…(img-h45o2A7X-1715830785776)]

如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值