Linux(3)(上)

一.冯诺依曼体系

  • cpu:由运算器和控制器组合的统称

  • 存储器:特指内存,而磁盘(外存)是充当输入和输出设备

  • 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备),总结就是所有设备只和内存打交道(这也是当初进度条为什么会被缓存起来)

  • 冯诺依曼体系都是独立的个体,而个体又是由"线"连接起来的,分成系统总线和IO总线

用冯诺依曼体系结构来表示数据的流动过程

二.操作系统

概念:

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)

  • 其他程序(例如函数库,shell程序等等)

设计OS的目的是什么

  • 与硬件交互,管理所有的软硬件资源

  • 为用户程序(应用程序)提供一个良好的执行环境

定位

在整个计算机软硬件架构中,操作系统的定位是:一款纯正的"搞管理"的软件

为什么需要操作系统

根据这张图知道,操作系统是管理下面的软硬件资源,通过管理这些资源才能为用户提供一个良好的运行环境

但是操作系统是不信任用户的(里面由大量数据怕被损坏)但是操作系统也要供人使用才能发挥作用,**因此操作系统以接口的方式给用户来获取使用操作系统的数据,这个接口就是系统调用接口,因此所有对操作系统的行为,都只能通过它来完成,**但是因为使用复杂因此又嵌套一层也就shell外壳(封装系统调用接口让人方便使用)

操作系统管理软硬件的本质

先描述,后组织:描述要组织对象的性质,然后使用容器(数据结构)来组织,因此操作系统需要先学习数据结构

系统调用和库函数概念

  • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。

  • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

三.进程宏观理解

3.1 进程的概念

  1. 一个已经加载到内存的程序,叫进程(任务)
  2. 进程 = 内核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上面!

相关推荐
我命由我123452 小时前
Photoshop - Photoshop 工具栏(43)标尺工具
学习·ui·职场和发展·求职招聘·职场发展·学习方法·photoshop
AIGC_北苏2 小时前
V3 Admin Vite 5.0 开源项目剖析【一】
学习
zhuzewennamoamtf3 小时前
Linux Regmap API寄存器映射抽象化
linux·运维·服务器
Joker 0073 小时前
Linux nohup命令实战指南
linux·运维·wpf
大聪明-PLUS3 小时前
管理 Linux 内核模块
linux·嵌入式·arm·smarc
lifewange3 小时前
关于进程的 Linux 命令有哪些?
linux·运维·服务器
d111111111d3 小时前
STM32 电源管理模式全解析:低功耗场景该如何选型?
笔记·stm32·单片机·嵌入式硬件·学习
三两肉3 小时前
Linux 网络包的 “快递分拣”:从发送到接收的内核协作全景
linux·网络·计算机网络·tcp
非凡ghost3 小时前
Floorp Browser(基于Firefox火狐浏览器)
前端·windows·学习·firefox·软件需求