1.操作系统(OS)
(1)基本结构的认识
任何计算机系统都包含一个基本的程序集合,用于实现计算机最基本最底层的操作,这个软件称为操作系统。操作系统大部分使用C语言编写,少量使用汇编语言。
从较大的层面来看,操作系统分为内核(Kernel)和外部程序。内核主要进行进程/任务/线程管理,文件系统管理,内存管理、驱动程序管理;外部程序包括Shell(通常指的是命令行界面CLI)、图形化界面(GUI)、glibc、系统调用的库、预装软件等。
我们要理解:我们平时用图形化界面操作Windows实际上是在GUI的运行基础上进行的,同样,使用Linux的命令行窗口也是在CLI这个程序运行的基础上进行的,我们都是在外部程序里进行的操作
(2)OS软硬件资源管理的理解
操作系统的定位就是控制和管理计算机上软硬件资源,让计算机的使用成本降低,能够走进家家户户。在软件层面,有编译器对语言进行翻译;在硬件层面,有文件系统对磁盘数据进行管理,而操作系统则是夹在软硬件之间的软件,起到沟通、协调作用。总的来说,操作系统对软硬件资源高效且安全地管理,是一种以给用户提供更好的使用体验为目的的手段(以人为本)。
(3)OS的结构和软硬件管理方式
①用户调用接口
我们日常使用的软件都是在这个层面上的,包括图形化界面、命令行。像安卓也是以Linux为底层,在用户调用接口上建立起来的
②系统调用接口(System Call)
对于操作系统而言,它不相信任何人,因为如果它相信用户,自己就危险了,因此它规定用户只能通过操作系统对外提供的接口来进行调用,同时,它也不允许用户跳过它直接向硬件访问。
尽管规定了只能通过接口来访问系统,但是这些接口同样复杂,因此系统调用接口上面还建立了我们日常接触到的软件(用户调用接口),当我们执行操作时,由软件来帮我们调用系统对应的接口,如printf就是通过库来调用对应接口,最后将数据输出到内存文件中。
当我们涉及到任何软硬件沟通时,都需要借助系统调用接口来调用内核。如当我们执行简单数学运算时,运算本身只会占用CPU资源,但运算程序的进程是在内存创建的,因此同样需要调用系统调用接口。
系统调用接口是C语言写的,这就意味着我们凡事要调用系统接口,必须使用C语言。进一步讲,所有编程语言、程序的底层,只要涉及系统接口调用,通通是C语言,包括我们熟知的Java、Python、Rust等
③Kernel
操作系统内核就像是一个管家,向下买菜做饭打扫清洁,向上询问主人的需求。在很多如银行、派出所、医院等也都能见到类似的影子,即用户通过对外开放的窗口提出自己的需求,一层一层向下传递,紧凑有序地完成任务。
④驱动程序
底层硬件都是采用冯诺依曼结构集成在主板上的,能够直接或间接被CPU控制器控制,每一种硬件都要有自己的驱动程序(操作系统自带或需要自己安装),驱动程序不可或缺,因为不同硬件功能不同,实现不同,迭代速度快,如果让操作系统直接访问硬件,那就要频繁修改操作系统和硬件之间的交互方式。为了避免操作系统频繁修改,在操作系统和硬件上做一层抽象,同样以接口形式进行访问,使得开发成本降低。
(4)OS如何控制软硬件资源?先描述,再组织
①硬件层面
Kernel中有大量C语言的数据结构,其中能描述对象的就是结构体。针对不同的硬件,Kernel会在其开辟的内存中存储描述硬件信息的结构体(Linux中一般是struct device),并且将这些结构体通过一定数据结构联系起来(如链表,构成device_list)。这些结构体里面存有硬件基本信息,以及调用驱动程序接口的操作方法。当要和硬件沟通时,Kernel就直接通过struct device调用驱动程序接口,进而和硬件沟通。我们发现,Kernel对硬件的管理转变为对device_list、struct device的管理
②软件层面
由冯诺依曼体系结构出发可以得到,当有程序开始运行时,在SSD(或HDD)中的数据就会被拷贝一份至DRAM中,内存中存储的就是硬盘代码和数据的备份。不仅如此,拷贝的数据还需要进行管理,因为可能同时有多份文件被拷贝进内存。因此,在计算机启动时,DRAM为Kernel分配一定大小的内存中,就有一部分空间是用于描述数据的结构体,称为PCB(进程管理块,PCB是总称,Linux中是struct task_struct)。
PCB底层就是结构体,其功能和前面说的struct device一样,是用于描述载入内存的代码数据的详细信息的。并且这些PCB也以某种数据结构联系在一起,Kernel通过管理这个数据结构,就能够间接管理程序的运行,这也涉及到进程的概念了。
③控制软硬件资源带来的启发-----数据结构、面向对象的思想
通过上面对软硬件管理的方法可以看出,要实现对某对象的管理,需要先对要管理的对象进行描述,将它们的信息和调用方式提取出来放一起,同时不同对象之间用某种结构联系起来(数据结构),进而实现对这些对象的管理(面向对象的思想OOP,体现了管理的本质是对数据和属性的管理)。
OS管理软硬件的资源的核心思想是先描述,再组织。面向对象的思想提供了描述的方法,一系列数据结构提供了组织的方法。这也是C++、Java这种面向对象编程语言出现并在当今作为主流的原因。因为几乎所有的程序逻辑都可以用先描述、再组织的思想来进行管理,而面向对象的语言拥有面向对象设计的类,也有一系列数据结构组成的库。很符合管理对象的条件。
包括生活中,上层对我们的管理也是面向对象的思想,他们不需要和我们见面,他们只需要拿到我们的描述信息,一天里干了什么没干什么,再通过传话就可以实现对我们的管理。
2.进程
(1)进程的概念和组成
运行起来的程序就叫进程(进行中的程序),进程是操作系统管理软件的相关概念,是操作系统调度的实体。
当我们启动一个程序时,操作系统首先向硬盘访问,读取程序相关的代码和数据到DRAM中,这相当于冯诺依曼结构中输入设备 -> 存储器的步骤。但是只有代码和数据还不能管理它,还需要一个结构体来管理,Kernel中的PCB结构体就是完成这项工作的,将我们的代码和数据描述起来。每一项进程对应一个PCB、一块内存空间。换句话说,进程 = PCB(包含进程的属性) + 内存中的代码和数据。PCB通过某种数据结构联系起来(以链表为例,PCB_list),由Kernel管理。这样,操作系统对软件、对进程的管理就转换为对PCB_list管理(对进程属性的管理),这也体现出先描述、再组织的思想。
进程创建其实是先生成PCB,再加载代码,这样加载的代码才能够及时地被管理,可以节约时间。
注意只有操作系统生成了PCB并和代码数据联系起来后,才被操作系统认为是一个进程。
PCB开辟空间的操作是Kernel进行的,当操作系统启动时,DRAM最先为Kernel分配内存。
PCB是一类结构体的总称,在Linux中具体为struct task_struct
(2)并行处理和并发处理
当程序被操作系统管理起来后,就要解决如何执行的问题了。CPU有两种处理进程的方式,一种是并行处理,一种是并发处理。并行处理是将多个任务列表(struct runqueue = PCB组成的数据结构 + 其他属性)分配给多个CPU,同时并行处理。并发处理是指CPU执行进程代码时,会给每一个进程预分配一个时间片,CPU基于时间片进行轮转执行(非常快),每当执行时间达到时间片,不管有没有执行完都切换为下一个。
并行处理加快处理进程的速度,并发处理保证调度任务能较为公平地得到处理。这两种处理方式适用于绝大多数民用系统。但除此之外还有实时操作系统,这只在特殊领域使用。如车载操作系统就是实时操作系统,它不遵循公平处理的原则,必须要求汽车一旦发生紧急情况,操作系统一定要最高优先级处理紧急状况。
(3)调度顺序
这些任务列表会根据并行的处理方式交给CPU每个核心处理,CPU的每个核心都配有一个调度器,任务列表会先交给调度器,由调度器决策如何并发处理,适时将对应的PCB交给运算器进行处理。进程排队的本质是PCB在排队(进程的属性在排队),同样体现先描述、再组织的思想。
(4)查看进程
当我们创建一个进程时,操作系统会为这个进程分配唯一的pid,pid的生成用到了时间戳,因此不连续以及pid前后两次不同是正常的。
我们可以在代码中使用pid_t getpid()来查看运行代码的进程的pid,pid_t本质是int
当我们运行程序时,就能看到进程对应的pid了
我们还可以通过ps ajx | head -1 && ps ajx | grep (name)查看进程来验证
指令解读:
ps是显示进程的状态,一般需要搭配选项;ajx,a意味着所有进程,j意味着打印进程的pid和ppid等信息,x显示没有控制终端的进程,j可以替换为u,这时就会显示进程的用户、CPU使用率、内存使用率等。
但ps ajx获取到的信息太多了,我们将所有获取的信息通过管道,让head取第一行,也就是每一列的说明。&&意味着第二条指令,当我们想要在一行中执行多条指令时,需要使用&&或;
之后同理,将获取到的信息通过管道传给grep,grep的功能是筛选出所传信息中包含规定字符串的行,日常使用方式是grep (字符串,不能以符号开头) (文件路径)
如此我们就能筛选出我们想要看到的进程了
grep指令的执行本质也是一个进程,所以当我们筛选的时候,ps ajx | grep (name)作为一个指令也会将自己筛选出来,要避免这种情况,我们可以再使用一条管道,将含grep的行筛选出去,即grep -v grep
ps ajx | head -1 && ps ajx | grep (name) | grep -v grep指令执行结果是
(5)/proc内存文件
当进程被创建时,/proc目录下会创建内存文件(不存入磁盘,临时文件,随着进程的消亡而消亡),名字和进程的pid一致。进入这个目录,我们就能看到所有关于这个进程的信息。
ps的底层就是对/proc里面的信息做解读
其中有两个文件很特殊,一个是cwd,一个是exe
exe指向可执行程序的位置,实时监控进程启动的可执行文件的状态。cwd是指向可执行程序当前的工作目录。当我们在代码中有创建文件的行为,如果使用的是相对地址,那么程序运行起来后相对地址前面会拼上cwd构成绝对进程。
我们以下面的代码来进行验证
运行起来后在proc里面可以查看到cwd已经修改
因此最终生成的文件会在cwd目录下生成
当程序开始执行时,我们就可以删掉可执行程序,因为程序相关代码已经被加载到内存中了,此时exe就会检测到