文章目录
一、 冯诺依曼体系结构
计算机硬件上的结构:
输入设备:键盘,鼠标,话筒,摄像头……网卡,磁盘
输出设备:显示器、磁盘、网卡,打印机……
输入/输出设备称为外设,Input/Output->IO,站在内存的角度理解IO
CPU = 运算器 + 控制器
存储器:内存
磁盘是外部存储
二、操作系统(Operator System)
1、概念
任何计算机系统都包含⼀个基本的程序集合,称为操作系统(OS)。
操作系统一款进行软硬件管理的系统软件。
操作系统包括:
• 内核(进程管理,内存管理,⽂件管理,驱动管理)
• 其他程序(例如函数库,shell程序等等)
2、设计OS的目的
- 操作系统向下对软硬件资源管理。(不是目的,是手段)
- 操作系统对上,为用户程序(应用程序)提供一个良好的执行环境。(目的)
- 软硬件体系结构是层状结构
- 访问操作系统,必须使用系统调用——其实就是函数调用,只不过是系统提供的
- 我们的程序,只要你判断出它访问了硬件,那么它必须贯穿整个软硬件体系结构
- 库可能在底层封装了系统调用
操作系统核心:管理(先描述,再组织)
3、系统调用
操作系统要向上提供对应的服务
操作系统,不相信任何用户
用户不能直接访问操作系统内部的信息,必须通过系统调用返回信息。
三、进程的基本概念与基本操作
1、概念
-
课本概念:程序的一个执行实例,正在执行的程序等。
-
内核观点:担当分配系统资源(CPU时间,内存)的实例。
-
进程 = 内核数据结构对象 + 自己的代码和数据
2、描述进程——PCB
- 进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct
- 进程的所有属性,都可以直接或者间接通过进程控制块找到
在linux具体的进程:
- 进程 = PCB(task_struct) + 自己的代码和数据
3、task_struct
4、getpid 获取进程ID
谁调用这个函数就获取谁的进程ID
5、ps axj 所有的以特定格式的进程
也可以使用 top 命令来查,但查看全部太多了不方便我们看
只查看当前我们的进程:
显示进程对应的信息:
连续执行两条指令,用分号依次隔开:
也可以使用 && 隔开两条指令,效果跟分号一样
6、kill - 9 + pid 杀掉进程
-9是信号
7、ls /proc 通过目录结构查看进程
对应的每个数字目录都是一个进程,数字对应的是进程的pid
目录里面存的是进程对应的各种属性
cdw(current work dir) : 存放当前路径
exe :存放可执行程序所在的路径
函数 chdir修改当前路径
8、getppid 获取父进程
Linux系统所以的进程都是被它的父进程创建的
一个父进程可以有多个子进程,进程也是进程树
-bash是命令行解释器:本质也是一个进程
- OS会给每一个登录的用户,分配一个bash(-bash代表远程登录)!
9、fork创建子进程
本来是一个执行流的遇到fork会变成两个执行流
- 子进程没有自己独立的代码和数据,因为目前没有程序新加载
让父子执行不同的代码
创建失败返回值小于0
在父进程里的代码中fork()返回值是子进程的pid
在子进程里的代码中fork()返回值是等于0的
- 进程具有独立性
- 数据默认是共享的,但父子如何一方,进行修改数据,OS就会把修改的数据在底层拷贝一份,让目标进程修改这个拷贝!(写时拷贝)
四、进程的状态
进程状态就是task_struct内的一个整数
- 运行:进程在调度队列中,进程的状态都是running!
- 阻塞:等待某种设备或者资源就绪,键盘、显示器、网卡、磁盘、摄像头、话筒……
- 进程状态的变化,表现之一,就是要在不同的队列中进行流动。本质都是数据结构的增删查改!
- 挂起:当操作系统的内存资源不足时,把进程的代码和数据换入到磁盘中,真正运行进程时再换入到内存里
1、Linux内核中进程的状态
R:运行状态
S:阻塞状态,可中断休眠,浅睡眠
D:深度睡眠,不可中断休眠
t:gdb时进程被debug暂停
T:用户Ctrl+z,进程暂停
X:结束状态
Z:僵尸状态,为了获取退出信息
2、孤儿进程
- 父进程先结束,子进程的退出信息没有获取,此时子进程就称为孤儿进程
- 父子进程关系中,如果父进程先退出,子进程要被1号进程领养,这个被领养的进程(子进程),叫做孤儿进程
1号进程是操作系统(init/systemd进程)
孤儿进程自动变成后台进程
五、进程的优先级
- 优先级是进程得到CPU资源的先后顺序
- 目标资源稀缺,导致要通过优先级确认谁先谁后的问题
1、ps -al 查看所有进程优先级的全部信息
UID:user id
PRI:进程的优先级,默认:80
NI:进程优先级的修正数据,nice值
进程真实的优先级 = PRI(默认) + NI
系统怎么知道访问文件的时候是用用于者,所属组还是other
Linux系统中,访问任何资源,都是进程访问,进程就代表用户
2、调整优先级
首先top,然后r,输入对应要调整的pid,最后输入调整NI的值
其他方法:nice,renice命令
优先级的极值:nice:[-20,19]
Linux进程的优先级范围[60,99]
3、竞争、独立、并行、并发
六、进程切换
- 进程切换,最核心的,就是保存和恢复当前进程的硬件上下文的数据,即CPU内寄存器的内容
- 当前进程要把自己的进程硬件上下文数据保存到task_struct里面,找到TSS任务状态段
七、Linux2.6内核进程O(1)调度队列
调度和切换共同构成了调度器
- ⼀个CPU拥有⼀个runqueue
- 如果有多个CPU就要考虑进程个数的负载均衡问题
queue[140]是调度队列:
1、其中0~99是实时优先级
2、而剩下的,100~139这 40 个优先级对应的进程优先级范围为[60,99],是一个哈希,在计算映射关系时只需要加40
bitmap是一个位图:
1、一个有 5 * 32 = 160个比特位,用来对应着调度队列
2、比特位的内容:1/0 表示是否为空
*active 指针指向一个调度队列
*expired 也指向一个调度队列
当active里的队列的进程执行一个时间片后,将进程放入到expired队列中,保证每个进程都能分到资源
当active队列没有进程后 swap(&active,&expired)
调度器快速挑选一个进程:1、挑队列 2、挑进程, 挑选进程的时间复杂度O(1)
实时操作系统:工业领域、制造类领域(来了个进程执行完)
实时操作系统:根据时间片来执行
八、环境变量
1、基本概念
- 环境变量(environment variables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进⾏查找。
- 环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性
2、命令行参数
argv:是一个变长的char*类型的指针数组,指向一个一个字符串的地址
argc:代表数组当中元素的个数
然后打印里面的内容
把命令行参数以空格分隔给argv
main的命令行参数,是实现程序不同子功能的方法!
指令选项实现的实现原理
- 进程拥有一张表,argv表,用来支持实现选项功能!
3、常见的环境变量
-
要执行一个程序,必须先找到它
-
系统中存在环境变量,来帮助系统找到目标二进制文件
-
环境变量:PATH :系统中搜索指令的默认搜索路径
-
env(environment) 可以把系统中所以的环境变量挪列出来
-
环境变量 = 名字 + 内容
-
根据一个环境变量的名字查看对应的内容
-
往PATH里加路径
-
bash 形成一张表,环境变量表
-
bash内部有两张表,命令行参数表、环境变量表
-
环境变量最开始是从系统的相关配置文件来的
配置文件存放地方:
- HOME环境变量:保存当前用户家目录
- SHELL环境变量:当前Shell,它的值通常是/bin/bash,当前登录时用的那一个版本的shell
- USER:当前登录用户
- HOSTNAME:当前主机名
- SSH_TTY:对应的终端设备
- OLDPWD:上一次访问的目录
cd - :回到上一次访问的目录
4、获取环境变量的方法
- env:查看所以环境变量
- export:新导入环境变量
- unset :取消环境变量
通过代码的方式获取环境变量:
方法1:main函数获取环境变量,获取环境变量是父进程的,环境变量可以被子进程继承
char *env[]:环境变量表
遍历环境变量表:
方法2:getenv系统调用
根据环境变量的名字获取内容
方法3:environ 全局变量
5、理解环境变量的特性
- 环境变量具有全局特性
- 本地变量,不会被子进程继承,只在bash内部使用
- set 命令显示环境变量和本地变量
bash 会记录两套变量:1.环境 2.本地
6、shell脚本
创建一个.sh的文件
文件里面的内容是命令行
批量化处理
九、程序地址空间
- 程序地址空间不是内存
- 程序地址空间叫做进程地址空间(虚拟地址空间),它是一个系统的概念,不是语言层的概念
1、虚拟地址空间
地址相同值不同,虚拟地址空间不是内存,不是物理内存
- 一个进程,一个虚拟空间
- 一个进程,一套页表
- 页表是用来做虚拟地址和物理地址映射的
2、进程地址空间
进程里用于分配地址空间的数据结构
进程加载内容:
1、在虚拟地址空间中申请指定大小的空间
2、加载程序,申请物理空间
3、经过页表进行映射
3、为什么要有虚拟地址空间
- 1、将地址从无序,变有序
- 2、地址转换过程中,也可以对你的地址和操作进行合法判定,进而保护物理内存
- 3、让进程管理和内存管理,进行一定程度的解耦合
4、如何解决堆区不连续的问题
在mm_struct中有一个vm_area_struct的链表把每一段堆区的起始地址和结束地址通过链表维护起来
- 进程具有独立性:
- 1、内核数据结构独立
- 2、加载进入内存的代码和数据独立