一.冯诺依曼体系

-
cpu:由运算器和控制器组合的统称
-
存储器:特指内存,而磁盘(外存)是充当输入和输出设备
-
不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备),总结就是所有设备只和内存打交道(这也是当初进度条为什么会被缓存起来)
-
冯诺依曼体系都是独立的个体,而个体又是由"线"连接起来的,分成系统总线和IO总线
用冯诺依曼体系结构来表示数据的流动过程

二.操作系统
概念:
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库,shell程序等等)
设计OS的目的是什么
与硬件交互,管理所有的软硬件资源
为用户程序(应用程序)提供一个良好的执行环境
定位
在整个计算机软硬件架构中,操作系统的定位是:一款纯正的"搞管理"的软件
为什么需要操作系统

根据这张图知道,操作系统是管理下面的软硬件资源,通过管理这些资源才能为用户提供一个良好的运行环境。
但是操作系统是不信任用户的(里面由大量数据怕被损坏)但是操作系统也要供人使用才能发挥作用,**因此操作系统以接口的方式给用户来获取使用操作系统的数据,这个接口就是系统调用接口,因此所有对操作系统的行为,都只能通过它来完成,**但是因为使用复杂因此又嵌套一层也就shell外壳(封装系统调用接口让人方便使用)
操作系统管理软硬件的本质
先描述,后组织:描述要组织对象的性质,然后使用容器(数据结构)来组织,因此操作系统需要先学习数据结构
系统调用和库函数概念
在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
三.进程宏观理解
3.1 进程的概念
- 一个已经加载到内存的程序,叫进程(任务)
- 进程 = 内核PCB数据结构体对象(描述这个进程的属性值) + 该文件的代码和数据

而操作系统根据PCB就可以找到code和data,从而管理,因此操作系统只需要管理进程的PCB对象即可
当有多个进程时,我们要用到数据结构(容器)来组织它们,然后我们对,对象的管理就变成对数据结构的管理

3.2 进程的理解
-
有的操作系统一次只能跑一个程序,这叫单进程(单任务);可以同时跑很多程序,这叫多进程(多任务)。
-
操作系统如何管理进程(先描述,在组织)
-
任何一个进程,在记载到内存的时候形成真正的进程时,操作系统要先创建描述进程的结构体对象------PCB(进程控制块)
3.3 为什么需要PCB
我们描述一个人是不是只要属性(标签)够多那么我们就可以通过这个来认识这个人,同理一个进程是不是只要属性够多我们就能认识这个进程从而管理。
四.进程的微观理解
4.1 描述进程PCB
进程信息被放在一个叫做进程控制块(process ctrl block)的数据结构中,可以理解为进程属性的集合。
Linux操作系统下的PCB是:task_struct
4.2 task_struct
task_struct是Linux内核的一种数据类型,它会被装载到RAM(内存)里并且包含着进程的信息
4.3 task_struct内容分类
标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
五.详细介绍task_struct内容
5.1 查看进程(标识符)
- 查看进程可以通过/proc系统文件 or ps查看,如果/proc后面跟PID可以查看该进程对应的目录
- 查看指令:ps axj | grep 可执行文件
- 查看指令:ls -l /proc/PID
- **cwd:**当前进程的工作目录
- **exe:**可执行文件的路径
- 查看函数:getpid()可以查看该进程的PID
- 查看函数:getppid()可以查看该父进程的PPID
补充知识:
进程如果重启后PID会改变(相当于DHCP分配地址池)
命令行输的所有指令都是bash子进程,子进程出问题不会影响父进程
5.2 创建子进程
认识fork:
fork有两个返回值
fork后创建的父子进程共享代码段。当任一进程修改数据时,操作系统会通过写时复制机制为其创建独立的数据副本,确保数据隔离。
fork的返回值类型是pid_t
fork使用的是<sys/types.h>,<unistd.h> 库
了解fork:
为什么fork要给子进程返回值为0,给父进程返回值为子进程的PID
返回不同的返回值,是区分不同的执行流从而执行不同的代码
因为父进程可以有很多子进程,之所以要返回子进程的PID,是为了区分父进程控制的是那个子进程
共享代码后会不会产生父子进程使用相同数据
子进程会共享父进程的数据空间。当子进程尝试修改数据时,操作系统会通过写时拷贝机制为其创建独立的数据副本。这种机制确保子进程的修改不会影响父进程的原始数据。实际上,写时拷贝的准备工作在fork函数返回时就已经完成。
这个有点像继承
知识补充:
- 父子进程创建好后,谁先跑是由调度器决定的
cpp
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t ret = fork();//pid_t在Linux中就是int类型的typedef而已
if (ret < 0)
{
perror("fork");
return 1;
}
else if (ret == 0)
{ // child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}
else
{ // father
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
sleep(1);
return 0;
}
学习fork的时候,你会发现打开新世界,因为fork不是有两个返回值吗,因此就出现else和else if能同时跑的场景,不过你可以这样理解我们之前学习编程是只有一个进程的,因此确实只能跑一个,但是fork后相当于有两个进程,因此跑两个也不足为奇吧!
5.3 进程状态宏观理解
三大进程常见状态:运行、阻塞、挂起
运行:
当准备好的进程首先进入 就绪队列,然后由调度器决定何时在 CPU 上运行。
进程在CPU中的执行并非必须完成才能退出,而是基于时间片机制。所有处于运行队列的进程都会在设定的时间段内获得执行机会。
大量进程放到CPU和退出CPU这个动作叫做------进程切换
阻塞:
- 当进程需要读取数据的时候,它会挂在这个该设备下,等待获取数据,这个就叫阻塞状态
挂起(阻塞挂起):
当系统内存不足时,操作系统会把一些暂时不用的进程(例如正在阻塞的)的代码和数据换出到磁盘,以释放内存。
进程的 PCB 仍保留在内存中,记录其状态。
当内存充足或该进程即将被唤醒时,再将代码和数据从磁盘换入内存,恢复执行。
5.4 进程状态微观理解
理解Linux内核源代码对进程的解释 :
bash
"R (running)", /* 0 */ // 运行状态
"S (sleeping)", /* 1 */ // 休眠状态(可中断,会响应操作)
"D (disk sleep)", /* 2 */ // 深度睡眠(不可中断,不响应任何操作)
"T (stopped)", /* 4 */ // 暂停状态(例如被调试器打断点暂停)
"t (tracing stop)", /* 8 */ // 被跟踪暂停(通常与 T 状态行为类似,不作严格区分)
"X (dead)", /* 16 */ // 已终止(无法在进程列表中看到)
"Z (zombie)", /* 32 */ // 僵尸状态(已退出但未被父进程回收)
-
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里
-
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)
-
**D磁盘休眠状态(Disk sleep):**有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
-
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
-
**X死亡状态(dead):**这个状态只是一个返回状态,你不会在任务列表里看到这个状态
僵尸进程:
- 当进程终止时,若父进程未主动回收子进程资源(且父进程仍在运行),子进程将保持僵尸状态(Z状态)。这种情况下,进程占用的系统资源无法被释放,将导致内存泄漏问题。
孤儿进程:
-
当该进程的父进程退出后,那么该进程就没有了父进程,它就变成孤儿,此时由1号进程领养(操作系统领养)
-
为什么要被领养,就是当这个孤儿将来退出时,1 号进程会自动帮它"收尸"(回收退出信息),防止它变成僵尸进程。
5.5 进程的优先级
优先级基本概念:
是什么:对于资源的访问,谁先谁后,PRI(优先级)NI(优先级的修正数据)
为什么:资源有限,进程是竞争关系,如果没有调节那么有些进程的代码长时间无法得到运行------该进程的饥饿问题
怎么办:进入top后按"r"-->输入进程PID-->输入nice值
查看指令:ps -l
补充知识:
UID:代表执行者的身份(用 id 查看)
nice其取值范围是-20至19,一共40个级别,一般PRI的值为120
**PRI值越小越快被执行,那么加入nice值后,**调整进程优先级,在Linux下,就是调整进程nice值
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
函数返回值,为什么会被外部拿到------通过CPU寄存器
系统是如何知道进程当前执行的那行代码------程序计数器(pc)eip(记录当前进程执行指令的下一行指令的地址)
寄存器有很多,最大作用:提供效率,进程和高频数据放入寄存器中
寄存器存放的是,进程相关的临时数据
进程从CPU离开的时候,要将上下文数据保存并带走(目的是为了后面在使用CPU的时候恢复这些数据)
- 进程切换的两个步骤:保存数据(保存到PCB),恢复数据
好了今天的内容就到这里,可能你在看这部分内容的时候会觉得有些知识不能理解,没事的没事的,因为该部分知识和后面讲的有关,在后面的了解中,可能不理解的知识也理解了,今天重点还是fork上面!