目录
- 谈谈硬件
- 冯诺依曼体系结构
- 数据流向
- 谈谈软件(操作系统)
- 什么是操作系统?
- 为什么需要操作系统?
- 操作系统如何管理?
- 谈谈进程
- 管理进程
- PCB
- 查看进程
- ps ajx
- proc
- kill -9 PID
- 系统调用
- getpid()
- getppid()
- fork()
- 进程状态
- linux下的进程状态
- R
- S
- D
- T/t
- X
- Z
- 进程优先级
- 什么是优先级?
- 为什么要有优先级?
- 设置了什么样的优先级?
- 查看优先级
- 更改优先级
- 环境变量
- 介绍几个环境变量
- $PATH
- $HOME
- $SHELL
- 查看环境变量
- 获取环境变量
- 什么是环境变量?
- 命令行参数
- main的第三个参数
- 本地变量,内建命令
谈谈硬件
冯诺依曼体系结构
存储器:指的是内存。
输入设备:键盘,鼠标,摄像头,话筒,网卡,磁盘等一些输入数据的东西。
输出设备:显示器,播放器,磁盘,网卡等。输入输出设备叫做外部设备,简称外设。
运算器:对数据进行计算任务(算数运算,逻辑运算)。
控制器:控制计算硬件的流程。运算器和控制器一起叫做中央处理器(CPU)。
这五个都是独立的个体,所以需要一些“线”连接起来,系统总线,IO总线。
数据流向
为什么CPU不直接连接输入输出设备?
因为CPU和输入输出设备的速度差距太大,所以引入一个速度适中的介质作为缓冲,可以把存储器看作是硬件级别的缓冲空间。
不过数据并不是单纯串行的,因为CPU和存储器交互的时候,存储器也可以同时和输入输出设备交互,可以把这一条线分成并行的三部分。
一个程序要运行,必须得先加载到内存中运行。为什么?
因为程序的代码和数据要让CPU去运行,又因为CPU只和内存交互,所以要把程序从外设加载到内存,这也是体系结构规定的。
谈谈软件(操作系统)
什么是操作系统?
操作系统是一款软件,负责管理软硬件资源。
操作系统包括:内核(进程管理,内存管理,文件管理,驱动管理),其他程序(例如函数库,shell程序等等)。
为什么需要操作系统?
因为需要通过操作系统帮助用户管理好底层的软硬件资源(手段),从而为用户提供一个良好(稳定,高效,安全)的执行环境(目的)。
但是操作系统不相信用户,为了保证自己的数据安全同时也能给用户提供服务,操作系统给用户提供了接口,用户通过接口调用操作系统内部的函数,这叫作系统调用,所以访问操作系统的行为都只能通过系统调用。
用户操作接口比如shell外壳,语言库,指令本质都是直接或间接进行系统调用,只不过多封装了一层。
操作系统如何管理?
操作系统通过对软硬件资源数据的管理来间接实现对软硬件资源的管理,数据由驱动程序提供。
软硬件资源数据太大太多,把它们用结构体描述起来,然后将这些结构体组织成某种数据结构,所以,对这些数据的管理就转化成对这个数据结构的增删改查。先描述,再组织。
谈谈进程
一个已经加载到内存的程序就叫做进程也叫任务。
管理进程
操作系统可以同时运行多个进程,如何管理进程呢?
先描述,再组织。
程序加载到内存变成进程的时候,操作系统会为该进程创建一个PCB(进程控制块)结构体来描述这个进程,并将里面的属性值初始化。
进程 = 该进程的PCB + 该进程的代码与数据。
操作系统有多个进程就意味着有多个PCB,操作系统这些PCB组织成某种数据结构,通过对数据结构的增删查改来对PCB进行管理从而间接管理进程。
PCB
PCB是指描述进程的东西,在linux下的PCB是task_struct,其他操作系统也会有自己的PCB。
task_struct结构体保存了进程的所以属性。
查看进程
ps ajx
PID:进程唯一ID值。
proc
proc目录里面存放了进程文件,每当你创建一个进程,这个目录就会创建一个以进程PID为名字的文件。
kill -9 PID
杀死进程
系统调用
getpid()
获取调用进程的ID。
getppid()
获取调用进程的父进程ID。
fork()
作用是创建一个子进程。
fork给父进程返回子进程的ID,给子进程返回0。
fork函数之后会分裂成两个执行流。
创建进程有两种方式,./是指令层面的,fork是代码层面的。
为什么给父子进程的返回值不同?给子进程返回0,给父进程返回子进程PID。
因为fork配合循环可以创建多个子进程,父进程想找到某个子进程就需要该进程的PID,而子进程找父进程直接getppid。
另外,创建子进程的目的是让父子进程做不同的事情,也就是让父子进程执行不同的代码块,所以需要fork有不同的返回值进行区分。
fork做了什么?
fork创建了一个子进程,一个进程包含PCB和代码与数据,子进程的PCB就是拷贝父进程的PCB然后修改部分属性,子进程没有代码,所以就指向父进程的代码,这也就是为什么fork之后父子进程代码共享。
fork是怎么做到返回两个值的?
因为fork本身也是函数,里面的return属于父子共享的,父进程会return一次,子进程也会return一次。
fork返回到一个变量,这个变量怎么做到有不同的内容?
之前提到子进程共享父进程的代码,其实子进程连数据都一起共享,只不过当子进程要修改数据时,操作系统会另开空间保存子进程修改的数据,从而不影响父进程的数据。所以保证了变量有不同的内容,因为这个变量父进程有一个空间,子进程也有一个空间。
进程状态
运行状态
CPU会维护一个运行队列,运行队列管理着PCB,谁到了就去CPU运行,其他的排队。
凡是处在运行队列的进程,它们的状态叫作运行态®,表示可以随时被调度。
阻塞状态
凡是处在等待队列的进程,它们的状态叫作阻塞状态。
每一个设备都有自己的等待队列,比如键盘设备,当某个进程需要读取键盘的数据时,此时键盘没输入,该进程就要在键盘的等待队列中。
挂起状态
当操作系统内存资源不足时,为了节省资源,会把一些正在队列排队的进程进行挂起。
进程排队时,操作系统就只留下对应PCB,把代码和数据放到磁盘中(换出),此时该进程的状态就是挂起状态,当排到该进程时,就把对应的代码和数据从磁盘拿回来(换入)。
linux下的进程状态
R
R是运行状态,+代表前台运行,也就是说这个进程运行的时候不能输入指令了。
加一个&可以变成后台运行。
S
S是阻塞状态,等待某种资源就绪,比如等待键盘输入。
D
D是深度睡眠,S是浅度睡眠可以响应外部的变化。
D也叫磁盘休眠。
假如你有数据要给磁盘,由于这段时间比较久你需要等待,此时操作系统内存不够把你进程杀了,过了一会,磁盘来反馈了,结果发现等待的进程不见了,那么磁盘数据可能会丢失,所以我们要让进程在等待磁盘写入完毕期间保证这个进程不会被任何人杀掉,这个状态就是D状态。
T/t
T代表暂停状态,给进程发19号信号就是让进程暂停。
S和T的区别,S一定是在等待某种资源,T不一定,可能只是被其他进程控制。
场景:gdb遇到断点停下,gdb控制目标进程停下。
X
X表示这个进程死了,意味着要回收资源了。
Z
Z表示僵尸状态。
子进程退出时,如果父进程没有主动回收子进程信息,子进程会进入僵尸状态,子进程的相关资源不会被释放比如PCB。
孤儿进程
如果子进程在等待父进程回收时父进程先退出了,此时子进程会变成孤儿进程,该进程由操作系统领养,父进程变成1号进程。
僵尸进程的危害
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。
维护退出状态本身就是要用数据维护,也属于进程基本信息,保存在task_struct(PCB)中,所以Z状态一直不退出,PCB一直都要维护。
一个父进程创建了很多子进程就是不回收,会造成内存资源的浪费,因为结构体对象本身就要占用内存。
内存泄漏。
进程优先级
什么是优先级?
拿优先级和权限做个对比,优先级表示对于资源的访问,谁先访问谁后访问,权限表示你能不能访问。
为什么要有优先级?
因为资源有限,多个进程之间要竞争。操作系统为了保证良性竞争,所以要确认优先级。
如果不设置合适的优先级会造成某个进程或某些进程长时间得不到CPU资源,该进程代码长时间无法推进,导致该进程饥饿问题。
设置了什么样的优先级?
查看优先级
ps查看进程优先级。
PRI:优先级,数字越小优先级越高。
NI:进程nice值。
一个进程的优先级是PRI = PRI + NI,所以NI越小优先级越高。
NI取值范围:-20到19。
每次变化old都是80。PRI = 80 + NI
更改优先级
top + r + pid + 要修改的NI值
环境变量
介绍几个环境变量
$PATH
为什么运行指令不用带./,运行程序就要带./?
因为有环境变量。echo $PATH查看环境变量,它们都是路径用冒号分隔,当你使用指令时系统会在这些路径一个一个寻找。自己的程序在这些路径里面找不到。
PATH是linux系统的指令搜索路径。
把自己程序的当前路径加入到环境变量中,这样运行自己的程序就不用加./了,加的时候前面要加美元符合和PATH表示这是在之前的环境变量后面继续添加,不然就变成覆盖了。
不加./也能跑了。
不过这种修改是内存级的。
$HOME
shell登录时会识别用户,然后填充对应的HOME环境变量,也就是把自己放在这个目录下。
$SHELL
shell对应的可执行程序。
查看环境变量
env
这个表示保留1000条历史命令
终端设备,进程默认输出时,输出在这个设备。
cd - 是返回上一次所在路径,其实是被OLDPWD保存起来了。
LOGNAME代表当前登录的用户,USER代表当前使用的用户。
获取环境变量
系统调用getenv
程序内可以判断使用者是谁,所以有了这个环境变量,操作系统就可以知道你是谁,从而判断你的权限。
什么是环境变量?
环境变量是系统提供的一组name=value形式的变量,通常具有全局属性。
命令行参数
main函数是可以带参的。
main函数也是函数也会被调用,所以它的参数也可以被传递。
命令行字符串以空格为分隔打散,argv存放每个字符串的首地址,argc存放字符串个数,然后把这两个参数传给main。
argv最后一个元素的下一个位置会设置成NULL。
为什么要这么做?
可以为指令,工具,软件提供命令行选项。
main的第三个参数
env的结构和argv的结构一样,argv是命令行参数表,env是环境变量表。
当我们进程启动时,一定要有人调我们的main函数,然后把这两张表传过来。
打印环境变量
bash本身在启动时,会从操作系统的配置文件中读取环境变量信息,我们自己运行的进程都是bash的子进程,子进程会继承父进程的环境变量信息。所有子进程都会继承下去这也就是为什么环境变量具有全局属性。
增加环境变量。
取消环境变量。
本地变量,内建命令
像这种直接定义的就是本地变量。
set可以查系统内的所有变量,比如环境变量,本地变量。
本地变量只会在本BASH内有效,不会继承。
这个本地变量控制命令行提示符的格式。
这个本地变量控制着续行提示符。
两批命令
- 常规命令:通过创建子进程完成。
- 内建命令:不创建子进程,bash调用自己写的函数或系统提供的函数完成。