“技术是时间积淀出来的,你能速成的东西,别人也可以速成,所以需要耐心学习”
猛戳订阅 Linux操作系统详解
图片来自百度百科
输入设备:键盘,话筒,摄像,(磁盘,网卡),C语言可以进行文件操作,所以磁盘可以作为输入设备。
输出设备:显示器,音响,磁盘,网卡,甚至显卡也是输出设备。
运算器+控制器(CPU):运算器进行了算数计算和逻辑计算。
控制器主要协调数据搬到内存,或者从内存搬到外设。
cpu只能被动的完成某种功能,相当于工具人,一般都是需要配合操作系统完成的。
存储器:这里的存储器指的是内存,内存不是磁盘。
除了内存之外的设备几乎都是外存。
CPU内包含了许多寄存器。寄存器的速度是非常快的。
CPU的运算速度 > 寄存器的速度 > L1~L3 > 内存 > 磁盘 > 光盘
一般来说速度越快价格越高。
假如只有输入设备 ——CPU——输出设备。
则效率会很低。效率由最低的设备决定,符合木桶效应。
以为输入设备和输出设备的速度比CPU慢很多。所以这时候冯诺依曼加了一个内存。
内存的速度比外设快,但是比CPU慢,所以可以把数据提前加载到内存,等待CPU读取和输入。
内存先接受输入设备的数据,然后把数据传给CPU计算,CPU再返回给内存,然后内存再输出到输出设备。
内存在我们看来,就是体系结构中的一个大的缓存,用来适配CPU和外设速度不均的问题。
从数据角度来讲:
外设和CPU不直接打交道,而是和内存打交道。
内存存在的最大意义:使用较低的钱,获取较高的性能。
我们都听过一句话,程序在编译好之后,必须先加载内存。
因为CPU需要与内存交互,所以需要先将编译好的代码编译到内存。
我们开机的时候就是把操作系统加载到内存。这就是预加载。
提前把数据加载到内存中,叫做局部性原理。
大概就是假如你现在正在执行第10行代码,则操作系统可以提前把你的第11行以后的代码都提前加载到内存。
这样就可以提高整个计算机的效率。
比如你刚打开计算机他比较卡,但是用了几十分钟后就不卡了,这就是提前加载到内存的好处。
操作系统是一款软件,是一款搞管理的软件。
所谓的管理就是决策者拿到相应的数据通知执行者,来对人进行间接地管理。
决策者就相当于操作系统,中间执行的人是驱动,硬件就是被管理者。
管理的本质就是对数据的管理——对数据的管理再转换为对数据结构的管理。
管理的核心的概念:先描述,再组织。
先描述对应先创建结构体。
再组织对应进行链表链接。
比如写学生信息管理系统,我们先定义结构体来描述对象,然后再定义结构来组织这些对象。
所以我们需要学习 语言+数据结构。
语言是用来描述对象,数据结构是用来组织对象。
操作系统包含四个管理模块:
内存管理 、进程管理、文件管理、驱动管理。
进程概念:进程就是一个运行起来的程序
这是肤浅的说法,实际上是将进程的结构体+进程的代码合起来才叫做进程。
操作系统里面能同时存在大量的进程,操作系统需要将所有的进程管理起来。
对进程的管理,本质就是对进程数据结构的管理。
所以这时候就用到先描述,再组织了。
在Linux中,描述采用的是task_struct这个结构体,这个结构体里面包含了单个进程的所有数据。
组织采用的是链表结构。
在操作系统这门学科中,很多教材叫进程为PCB,叫做进程控制块。
进程 = 对应的内核数据结构+可执行程序
也就是进程控制块=进程结构体+进程代码。
所谓的管理进程就是管理PCB的数据结构。也就是对链表的增删查改。
学进程里面的属性
OS内核需要直接管理驱动软件。
操作系统的管理结构如下
操作系统是一个封闭体,只暴露出一些指定的系统调用接口来供用户使用,这是操作系统为了保护自己。因为操作系统不相信用户。
因为Linux是用C语言写的,所以所谓的接口就是C语言给我们提供函数调用。
比如我们使用cout函数,本质是将数据写到硬件上,因为自己的程序没有资格写到硬件上。因为cout调用的是C++库,也就是用户操作接口,再调用系统调用接口,系统调用接口通过操作系统来管理打印到显示屏上。
操作系统是通过系统调用的方式,对外提供接口服务的。
后面学习的Linux系统编程,本质就是在学习操作系统提供的接口。
因为系统调用的接口对于一些用户来说不会用,所以这时候就在上一层有了shell外壳,C函数库lib,部分指令等,
shell叫做命令行解释器。
还有windows的图形界面,可以让小白使用。
某个语言具有跨平台性,就是因为它的lib库帮你实现了不同平台系统接口的调用。
这个和C++的多态的概念差不多。
可以打,但是Linux系统的用户少,一般人都用windows上打游戏。所以说游戏开发商一般不会在Linux发布开发游戏。毕竟不赚钱。
比如华为的鸿蒙操作系统,因为华为有手机作为资本,所以系统出来后,自动有供应商来找鸿蒙来提供各种软件。
1.ps axj 可以显示所有进程,这是后期最常用的一种方式。
//可以过滤出来关于mytest关键字的进程
ps axj | grep 'mytest'
因为Linux上的命令都算是进程,所以grep也是一个进程,所以以上命令会显示两个进程,一个是grep的一个是mytest的
加上-v grep后则不显示带有grep的进程
只会显示自己的进程。
ps axj | grep 'mytest' | grep -v grep
在/路径下存在一个proc的文件,里面存放的是进程的实时信息。上面的数字叫做PID,(process id)PID是一个进程的唯一标识符。
pid的意义就是快速找到进程。
进程的pid会随着进程的关闭和启动,重新分配pid,所以进程的pid会变化的。
信息中的exe代表进程对应的可执行程序的磁盘文件。
cwd代表的是文件所在的当前路径。
所以说C语言为什么写的文件就在当前路径?
所谓的当前路径就是进程当前所维护的路径。
那么pid和cwd的路径信息都在哪呢?
这些都算是进程的内部属性,内部属性都属于数据,数据存储在进程控制块PCB中。
显示信息列表并显示带有a.out的进程。
ps ajx | head -1 && ps axj | grep 'a.out' | grep -v grep
以下获取pid是系统调用
#include
#include
#include
int main()
{
while(1)
{
printf("process pid :%d\n",getpid());
sleep(1);
}
return 0;
}
假如要杀掉该进程可以按ctrl + c
也可以用命令
kill -9 进程的pid
-9就是发送9信号
1.把你写的程序运行起来,也就是创建进程
不管程序结束和运行,父进程一直不会改变,因为父进程就是bash。
几乎我们在命令行上所执行的所有指令(ls pwd cd等),都是bash的子进程。
fork()函数的功能就是创建一个子进程。
fork给有两个返回值,给父进程返回子进程的pid,给子进程返回0。
这和C语言不同,C语言中不能同时执行if又执行else。
但是fork()函数可以同时运行。因为fork之后,父进程和子进程会共享代码,一般都会执行后续的代码。
所以会打印两次。
因为父子进程的返回值不同,所以可以通过不同的返回值判断,让父子执行不同的代码。
fork如何做到会有不同的返回值?
因为父进程和子进程是一对多的,所以父进程必须有标识子进程的方案,所以fork之后,给父进程返回子进程的pid。
给子进程返回0的原因是因为,来判断子进程是不是创建成功了。
fork有两个返回值,说明曾经被返回了两次。
fork是怎么做到返回两次的?
fork()函数是通过操作系统的system call实现的。
fork()之后,OS做了什么?
进程就是= task_struct+ 进程代码和数据
子进程的task_struct内部的数据基本是从父进程继承下来的。
子进程的代码是和父进程共享代码的。但是数据要各自独立。
虽然fork之后代码共享,但是可以通过不同的返回值执行不同的代码,比如用if,else分流。
如何理解进程被运行?
就是进程在运行队列中被CPU调度。
以后谈到进程必须先想到进程的task_struct
这是结构体里面包含进程的全部信息。
Linux内核对进程的管理就是管理task_sturct的。
计算机学科的哲学科目——操作系统
所以我们具体事情具体分析,以Linux系统为例来学习Linux系统的进程状态。
进程只要在运行队列中,就叫做运行态。
每个CPU都要维护一个runqueue。这个运行队列每个元素就是PCB,也就是Linux中的task_struct,可以通过task_struct可以找到代码和数据。因为结构体里包含了指向代码的指针。
运行态不代表正在运行,而是代表可以随时被CPU调度
所谓的终止状态也就是这个进程还在,只不过永远不会运行了,随时等待被释放。
为什么不立马释放资源?有没有可能操作系统正在忙着别的事情,所以没空来释放。
终止的意思就是我已经结束了,不能被调用了,随时等待释放。
概念:进程等待某种资源(非CPU),资源没有就绪的时候,进程需要在该资源的等待队列中进行排队,此时进程的代码并没有运行,进程所处的状态叫做进程阻塞。
在操作系统学科中,经常会听到阻塞状态。
1.一个进程,使用资源的时候,可不仅仅是在申请CPU资源!
2.进程也可能同时在申请其他资源,比如:
磁盘,网卡,(比如渲染)显卡资源。
如何进程申请CPU资源时,无法得到满足。这时候需要排队。这个队列叫做运行队列。
那么如果我们申请其他慢设备的资源。比如磁盘。也是需要排队的。
总之,进程申请资源是需要排队的。
概念:当进程访问某些资源(磁盘,网卡),该资源如果还没有准备好,或者为其他进程提供服务。此时:操作系统做了以下工作。
1.当前进程要从runqueue中移除
2.将当前进程放入对应设备的描述结构体的等待队列。
此时,我们的进程在外设等待资源时,该进程的代码不会被执行。这时候就叫做进程阻塞
在我们windows界面中,表现出来的状态就是软件卡住了。
通常电脑卡是在打开很多应用的时候会卡。
这时候可以把后台进程关掉一些。
就是进程处在非运行状态。
假如进程过多,内存不足了怎么办?
这时候操作系统就要帮助用户进行辗转腾挪。
短期内不会被调度(你等的资源短期内不会就绪)的进程,**它的代码和数据依旧在内存中。**这就是白白的浪费空间。这时候操作系统就会把该进程的代码和数据置换到磁盘上。
这样的行为就叫做进程挂起。进程挂起在小白看来,也是卡住了的现象。
进程后面加上+加号,叫做前台进程。前台进程可以ctrl+c杀掉。而后台进程需要用kill -9 命令。
R:运行状态,只要在运行队列就行。
S(sleeping):浅度睡眠,对应的就是阻塞状态。也就是在等待某种资源。一旦资源就绪就可以转换为R状态。此时也可以杀掉该进程。
D(disk sleep):这个状态也是一种阻塞状态,在Linux中这个是针对磁盘(disk)资源的等待。也叫深度睡眠状态,操作系统无权利杀掉该进程,只能等待该进程结束。
除非关机,或者拔掉电源。
Z(zombie):叫做僵尸状态。当Linux中的进程退出的时候,一般不会直接进入X状态,而是进入Z状态。
进入Z状态的原因是因为: 该进程退出的时候需要将执行结果告知给父进程。就是为了维护退出信息,可以让父进程或者操作系统来读取。
Z状态就是:如果创建了子进程,子进程退出了,父进程不退出,也不等待子进程,子进程退出后的状态就是Z状态。
假如一个进程正在处于僵尸状态,假如没有人回收,则该进程将一直维护!该进程的相关资源(pcb)不会被释放。这就叫做内存泄漏!
X:就是死亡状态,可以随时将资源回收。
T/t(stopped):大写T叫做暂停状态,比如暂停正在下载。 就是常规的暂停,小写t是进程被调试的时候,遇到断点所处的状态。就会呈现小t,也叫做追踪状态。
孤儿进程是父进程先退出,子进程永远没退出,这时候子进程就变成了孤儿。所以叫做孤儿进程,孤儿进程则由ppid为1的父进程来领养。
父进程的父进程是bash,bash会自动回收父进程。
优先级是进程获取资源的先后顺序。
优先级就是排队。为什么存在优先级?本质原因是因为资源不够。所以才需要排队。
NI是nice值,可以调整优先级
PRI越小则优先级越高。
Linux中更改优先级是更改NI,而不是更改PRI。
改变优先级命令如下
sudo top
NI的取值范围为-20到19。【-20,19】
PRI = PRI+NI;
每次调整NI都PRI初始值都是80.