个人笔记)
冯诺依曼体系结构计算机大多遵守冯诺依曼体系结构各部件说明输入单元键盘摄像头麦克风磁盘等中央处理器(CPU)含有运算器和控制器等运算器进行数据计算任务运算又分为算术运算和逻辑运算控制器对计算过程进行一定的控制输出单元显示器打印机播放器硬件磁盘等存储器又称为内存输入设备和输出设备都是外设CPU是内设注上述部件都是硬件用系统总线和IO总线连起来体系的运作原理外设要输入或者输出数据只能写入内存或者从内存中读取输入到内存的数据经过cpu处理后返回存储器传回内存再输出数据。因此所有设备的数据流动都直接和内存发生不会跳过内存和其他设备进行数据传输。一般来说存储容量越大速度就越慢下面是运算速度金字塔为什么要用存储器作为中介外设存储量大因此速度慢假如数据在输入设备中处理后以极慢的速度传到速度极快的cpu中处理大部分时间cpu都在空等运行效率将非常慢。因此引入运算速度和存储容量居于外设和内设的之间的内存作为中介提高运算效率操作系统概念任何计算机系统都包含一个基本的程序集合称为操作系统(OS)。操作系统包括内核进程管理内存管理文件管理驱动管理和其他程序例如函数库shell程序等等。操作系统是一款专门做管理的软件其设计目的是为了管理好所有的软硬件资源从而为用户程序应用程序提供一个良好的执行环境。操作系统在计算机体系中的位置如下图所示。系统调用和库函数概念补充在开发角度操作系统对外表现为一个整体但适度暴露自己的部分接口供上层开发者使用这部分由操作系统提供的接口叫做系统调用system call。 系统调功能比较基础对用户的要求相对也比较高所以有心的开发者可以对部分系统调用进行适度封装进而形成库有了库就有利于更上层用户或者开发者进行二次开发。例如touch命令只停留在第一层的”指令操作“上”创建文件“这一操作本质是通过系统调用接口实现的。“管理”是什么管理的核心就是先描被管理对象再组织被管理对象在一个大型的系统里面如大学由于底下学生众多校长不可能对每个学生的实际情况都了如指掌只能权衡学生的哪些属性比较重要如绩点、违纪次数、志愿时长等。这些指标确立下来以后由于学生数量众多需要有合理的方式对这些指标统一管理如使用登记簿学生档案等。再以代码为例相当于针对需要管理的数据创建了一个类,再选择合适的数据结构进行管理。这就是“先描述再组织的过程。这个逻辑将会贯穿我们学习操作系统始终。操作系统管理软硬件资源的方式知道了管理的本质后就可以理解操作系统是如何进行管理的了先对进程进行描述再对进程组织进行组织。什么是进程程序是一个静态的指令集合比如你电脑上的一个.exe文件或一段源代码。它只是在硬盘上的一堆数据。进程的官方解释当程序被操作系统加载到内存中并开始执行时它就变成了一个“活”的东西——进程。它拥有自己的生命周期创建、运行、暂停、终止。计算机运行时会有多个进程为了对进程更好地管理按照前文讲到地管理逻辑需要对这些进程先描述再组织。先描述操作系统会为每个进程创建结构体对象进程控制块PCB这些对象里面包含进程的各种重要属性如进程编号pid、进程状态、优先级、程序计数器即将被执行的下一条指令的地址以及指针指向这个进程对应的代码和数据的。创建完毕后像校长一样操作系统不再关心代码和数据只需管理好PCB即可。此时可以得到进程的通俗解释进程PCB数据结构对象代码和数据而操作系统只关心PCB代码则靠程序员维护。再组织操作系统会选择合适的数据结构将这些进程组织起来进程的最基本组织方式是双链表但需要注意的是操作系统不是靠某种数据结构对象就能把所有进程管理起来一个进程可能存在于多个数据结构对象之中。后续的Linux学习主要围绕着操作系统级别的数据结构展开。实操操作获取进程的PID在代码中通过调用getpid()函数来获取并打印进程的pid之后就可以在操作系统层面上通过pid查看进程的状态查看系统为进程创建的文件夹得到pid之后可以使用“ ps aux ”指令查看当前系统运行的所有进程在其中可以看找到正在运行的进程的Pid当然也可以通过“ ps aux | grep 进程pid ”精确查看指定的进程此外当进程被创建的时候系统自动为进程创建了文件夹在/proc路径下可以看到所有的正在运行的进程的文件夹每个文件夹包含了很多进程的信息进入文件夹以后可以看到有很多东西此外还可以通过getppid()获取当前进程的父进程pid发现多个进程运行时父进程都是一个pid这就是bash进程这里展开讲进程的父子关系根系统启动后内核启动 PID1。它负责初始化系统并启动其他必要的服务 。树干PID1 会启动一个图形登录界面或终端登录程序等待输入用户名和密码。树枝登录成功后登录管理器会创建一个Shell进程比如bash。这个bash的父进程PPID就是登录管理器。树叶:在bash里输入./process运行代码。这时bash会调用fork()系统调用见下文复制自己然后通过exec()把你的程序加载进去。所以我们的程序的父进程PPID就是bash。关于fork()要在操作系统中创建进程除了通过运行程序让系统为我们自动创建以外还可以使用fork函数主动创建。fork()是一个库函数调用fork之后系统会为当前进程创建子进程这两个进程都会运行后续的代码。子进程的fork会返回0,而父进程的fork()返回一个大于0的数。下面代码示例通过fork()手动创建子进程并运行后续代码可以看到明明是一个if else分支每次运行却输出了两句话由此引出下面有关问题为什么fork要给子进程返回0给父进程返回子进程fork之后代码父子共享fork返回不同的返回值是为了区分不同的执行流执行不同的代码块。fork函数怎么做到返回两次的首先fork本质是一个函数这个函数的任务是创建一个子进程所以肯定在返回之前已经创建了子进程而父子进程都会执行接下来的代码所以返回这句话总共会被两个fork各执行一次。为什么变量会有不同的内容书接上回如果在fork函数内部子进程已经被创建出来了那么返回到调用fork语句的时候已经存在了两个ret。父进程和子进程不共享数据但是共享代码。fork干了什么事情fork创建一个新的进程子进程它几乎是父进程的完整副本。子进程获得独立的PCB进程控制块但其中的大部分属性如环境变量、打开的文件描述符、信号处理方式等从父进程复制而来。子进程的代码段、数据段、堆、栈等内存区域在逻辑上是父进程的副本。但如果fork后立即复制父进程的全部内存导致耗时且浪费内存因此内核采用写时拷贝技术共享物理内存fork之后父子进程的虚拟地址空间映射到同一块物理内存页并将这些页标记为“只读”延迟复制只要父子进程都不修改这些页它们就始终共享节省内存。触发复制当其中一方试图写入某个页时CPU 触发缺页异常内核分配新的物理页将原页内容复制过去然后更新该进程的页表使其指向新的物理页。此后父子进程对该页的操作互不影响。为什么要创建子进程为了让父子执行不同的事情所以也需要让两个进程执行不同的代码块fork创建出子进程以后父子进程是谁先运行这是由调度器决定的、不确定的调度器决定了从内存中挑选哪一个进程运行它有自己的一套查找算法调度原则一般保证公平也就是所有进程的调用次数是接近的。进程状态task_struct有一个成员变量用于表示进程的状态如何查看进程状态之前提到的查看进程的命令中有一列就是代表着进程的状态R运行状态(running):通常一个cpu维护一个运行队列位于该队列的进程都是运行态虽然是按顺序运行但因为cpu的速度很快所以这些进程可在极短的时间内都被运行)为了防止运行时间过久每个进程都有一个时间片假如时间片是10毫秒10毫秒内进程没有运行完就会回到运行队列尾部重新排队。从而避免了计算机对某个进程进行死磕的情况。S睡眠状态(sleeping):意味着这个进程在等待事件完成。D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态D状态是内核给正在和硬件打交道的进程穿的“防弹衣”防止它在关键时刻被误杀保护数据完整性。如果硬件永远不回应这个进程就永远卡死谁也奈何不了它。比如磁盘正在写入T停止状态(stopped)可以通过发送 SIGSTOP 信号给进程来停止T进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行X死亡状态(dead)这个状态只是一个返回状态你不会在任务列表里看到这个状态。僵死状态(Zombies)僵死状态Zombies是一个比较特殊的状态。当进程退出并且父进程使用wait(系统调用后面会学没有读取到子进程退出的返回代码时就会产生僵死尸进程。僵死进程会以终止状态保持在进程表中并且会一直在等待父进程读取退出状态代码。所以只要子进程退出而父进程还在运行但父进程没有读取子进程状态子进程进入Z状态僵尸进程危害维护退出状态本身要用数据维护这也属于进程基本信息所以保存在PCB中所以Z状态一直不退出PCB一直都要维护如果父进程创建了很多子进程而不回收就会造成内存资源的浪费。因为数据结构对象本身就要占用内存想想看定义一个结构体变量就是要在内存的某个位置开辟空间这就会造成内存泄漏至于如何避免后面会进行学习。孤儿进程如果父进程提前退出子进程后退出进入Z之后那该如何处理呢父进程先退出子进程就称之为孤儿进程因为进程的回收一定要指定一个父亲进程所以1号进程即前面提到的进程的根成为这个孤儿进程的父亲负责回收该进程。进程优先级基本概念进程的运行需要占用资源。cpu资源分配的先后顺序就是进程的优先权priority)如果某进程一直得不到资源会造成“饥饿问题”在用户层面的表现上就是用户卡死长时间无响应比如抢课无响应。优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用可以改善系统性能。还可以把进程运行到指定的CPU上这样一来把不重要的进程安排到某个CPU可以大大改善系统整体性能。查看系统进程的各个属性在linux或者unix系统中用ps-l命令则会类似输出以下几个内容UID: 代表执行者的身份PID: 代表这个进程的代号PPID代表这个进程是由哪个进程发展衍生而来的亦即父进程的代号PRIpriori代表这个进程可被执行的优先级其值越小越早被执行NI代表这个进程的nice值PRI and NIPRI即进程的优先级通俗点说就是程序被CPU执行的先后顺序此值越小进程的优先级别越高NI表示进程可被执行的优先级的修正数值通过修改nice值使得PRI(new)PRI(old)nice当nice值为负值的时候该程序优先级值将变小即其优先级会变高则其越快被执行所以调整进程优先级在Linux下就是调整进程nice值。nice的取值范围是-20至19一共40个级别。但值得注意的是系统调度器已经非常智能所以不需要我们过多干涉优先级因此PRI(old)指的是最开始调度器为进程分配的PRI值而不是上一次更改后的PRI值也就是说无论我们怎么改nice值改几次都不会使得该进程的PRI脱离初始PRI-20如何查看进程的优先级用top命令更改已存在进程的nice进入top后按“r”–输入进程PID–输入nice值从而对进程的nice值进行修改操作系统如何根据优先级开展调度首先在运行队列里面用进程指针做哈希桶数组下标代表着优先级。同时维护两个哈希桶一个负责按照优先级运行进程运行数组另一个用于存储运行时新增的运行请求等待数组假如运行数组执行完毕直接交换这两数组指针继续按顺序运行新运行数组中指针指向的进程等待数组里的进程如何判断是否运行完毕位图40个下标是否存有进程指针直接用40个0或者1表示假如这40个都是0说明这个数组里面已经没有进程指针了运行完了直接交换指针。每次运行完一个进程--即可这时只需O1时间复杂度即可判断运行数组是否为空。优先级其他有关概念竞争性: 系统进程数目众多而CPU资源只有少量所以进程之间是具有竞争性的。为了高效完成任务更合理竞争相关资源便具有了优先级。独立性: 多进程运行需要独享各种资源多进程运行期间互不干扰并行: 多个进程在多个CPU下分别同时进行运行这称之为并行并发: 多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发