网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
输入:键盘、话筒、摄像头、磁盘、网卡…
输出:显示器、音响、磁盘、网卡…
(运算器 + 控制器)[CPU]:
存储器:内存
问:为什么要有内存?
答:技术角度:cpu的运算速度 > 寄存器的速度 > L1~L3 Cache > 内存 >> 外设(磁盘)>> 光盘磁带
从数据角度,外设几乎不和CPU直接进行交互,直接和内存直接交互,CPU也同样如此。
成本角度:寄存器 >> 内存 >> 磁盘(外设)
注意:几乎所有的硬件,只能被动的完成某种功能,不能主动的完成某种功能,一般都是要配合软件完成的(OS + CPU)。
操作系统(Operator System)
概念
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库,shell程序等等)
设计OS的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的执行环境
定位
- 在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件
总结
计算机管理硬件
- 描述起来,用struct结构体
- 组织起来,用链表或其他高效的数据结构
系统调用和库函数概念
- 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分 由操作系统提供的接口,叫做系统调用。
- 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统 调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
进程
基本概念
- 课本概念:程序的一个执行实例,正在执行的程序等
- 内核观点:担当分配系统资源(CPU时间,内存)的实体。
描述进程-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
这个文件夹。
注意:上面这些蓝色的数字就是进程的PID。
- 大多数进程信息同样可以使用top和ps这些用户级工具来获取
首先先编辑一个.c代码:
然后运行:
使用
ps ajx
命令即可查看进程(all job,x是以特定格式进行显示)
使用
ps ajx | grep 'mytest'
查看与mytest
相关的进程:注意:我们常常使用的
ls、pwd、touch、grep、chgrp、chown、mkdir、rm
等命令在启动后也都是一个个进程,这些二进制可执行文件在/usr/bin
目录下,我们使用下面的指令可以进行查看,ls /usr/bin/*
使用
ps ajx | grep 'mytest' | grep -v grep
我们可以查看待用mytest
关键词同时又不带有grep
关键词的进程:
问:如何查看进程信息和进程信息代表的意义?
答:使用
ps ajx | head -1
可以查看不同列的进程信息代表的意义:
使用
ps ajx | head -1 && ps ajx | grep 'mytest' | grep -v grep
注意:&&是逻辑与,其意义是前面的指令执行成功了,再实行后面的指令。
此时根据PID在proc目录中进行查看:
或者:
使用
ls /proc/31745 -al
可以查看进程的详细信息:cwd(current work director):进程当前的工作路径。
exe后面对应的是可执行程序的磁盘文件。
通过系统调用获取进程标示符
- 进程id(PID)
- 父进程id(PPID)
PID
使用
man getpid
来查看getpid介绍
使用举例:
运行:
PPID
使用举例:
运行:
问:为什么每次创建进程时,当前进程的PID每次都会改变,但是PPID却没有发生改变?
答:因为几乎我们在命令行上所执行的所有的指令(cmd),都是bash进程的子进程。
杀掉进程
- 在程序运行过程中,使用
ctrl + c
来杀掉程序。使用举例:
- 使用
kill -9 PID
来杀掉进程。使用举例:
通过系统调用创建进程-fork初识
- 运行 man fork 认识fork
man 2 fork
- fork有两个返回值
代码:
运行结果:
代码:
执行结果:
问:为什么会出现这种情况?
答:fork之后,父进程和子进程会共享代码,一般都会执行后续的代码,这就是为什么printf会打印两次的问题。
fork之后,父进程和子进程返回值不同,可以通过不通的返回值,判断,让父子执行不同的代码块。
问:为什么fork会给父进程返回子进程的PID,给子进程返回0?
答:因为父进程必须有标识子进程的PID来方便对子进程进行管理,所以fork之后会给父进程返回子进程的PID。
子进程最重要的是知道自己被创建成功了,因为子进程找父进程成本非常低(getppid())。
问:为什么fork函数会返回两次,有两个返回值?
答:
注意: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上运行,还是进程只要在运行队列中叫作运行态?
答:
运行态表示当前进程的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):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
运行:R
终止:Z和X
阻塞:S或者D
挂起:S或者T
挂起状态:
进程状态查看
ps aux / ps axj 命令
使用举例:
process.c文件:
运行之后查看进程状态:
问:此时进程的状态是S,说明程序处于休眠状态或者阻塞状态,为什么此时的程序处于休眠状态而不是运行状态?
答:因为CPU执行的速度很快,大部分时间,进程在等待外设即输出设备,所以大部分时间是阻塞状态
对源程序进行下面的改变,进程的状态就会发生改变:
注意:在上图中的就绪和执行状态在操作系统中都对应的是R状态。
Z(zombie)-僵尸进程
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
模拟僵尸进程:
代码:
问:长时间的保持僵尸状态会出现什么问题?
答:如果没有人回收僵尸子进程,该状态会一直维护,并且该进程的相关资源(task_struct)也不会被释放,进而出现内存泄漏问题。
孤儿进程
- 父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
- 父进程先退出,子进程就称之为“孤儿进程”
- 孤儿进程被1号init进程领养,当然要有init进程回收喽。
process.c代码:
执行结果:
最终只剩下一个子进程。
最后的话
最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!
资料预览
给大家整理的视频资料:
给大家整理的电子书资料:
如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
0815172229437](https://i-blog.csdnimg.cn/blog_migrate/de20b55c69bfe06270803efb46971989.png)
最终只剩下一个子进程。
最后的话
最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!
资料预览
给大家整理的视频资料:
[外链图片转存中…(img-en6fpv9N-1715830785776)]
给大家整理的电子书资料:
[外链图片转存中…(img-h45o2A7X-1715830785776)]
如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!