网站做网络营销,电子商务网站前台建设,申请免费网站公司,江西网站建设公司目录 一、冯诺依曼体系结构
二、进程
1、关于进程
关于PCB结构体
2、查看进程
①ps ②/proc
3、getpid 4、getppid
5、fork()
fork基本用法
6、进程状态
7、孤儿进程
8、进程优先级
修改nice值#xff1a;top
9、进程的几个特性 一、冯诺依曼体系结构
冯诺依曼体…
目录 一、冯诺依曼体系结构
二、进程
1、关于进程
关于PCB结构体
2、查看进程
①ps ②/proc
3、getpid 4、getppid
5、fork()
fork基本用法
6、进程状态
7、孤儿进程
8、进程优先级
修改nice值top
9、进程的几个特性 一、冯诺依曼体系结构
冯诺依曼体系结构如下图所示 而上面的输入设备输出设备存储器运算器控制器又是什么呢
存储器内存
输入设备键盘摄像头话筒磁盘网卡...
输出设备显示器音响磁盘网卡...
CPU(中央处理器)包括运算器、控制器
运算器算术运算逻辑运算
控制器CPU是可以响应外部事件的协调外部就绪事件 首先说说运行速度
CPU寄存器 内存 磁盘/SSD固态硬盘 光盘 磁带
那既然CPU运行速度是最快的为什么冯诺依曼体系结构中还要在输入输出设备中间用存储器呢直接用CPU处理输入输出岂不是更好用速度更快。
当然可以这样用但是如果真的这样使用就会造成昂贵的成本原本几千快的电脑如果全部使用CPU处理各种数据可能会花费十几万甚至几十万的成本这是十分不划算的并且我们大众需要的是性价比高的即价钱便宜性能好所以这里引用了内存即存储器速度比磁盘快价钱也比CPU便宜可以很好解决这方面问题。
存储器可以将数据缓存在存储器中进而CPU在存储器中读取数据时不用再访问外设可以在一定程度上提高效率 CPU读取数据数据代码都是从内存中读取的。站在数据角度上CPU不和外设直接交互
CPU要处理数据需要先将外设中的数据加载到内存。站在数据角度上外设只和内存直接交互 有句话是程序要运行必须先被加载到内存中
现在我们可以用冯诺依曼体系结构知识解释因为CPU读数据都是从内存中读取的所以程序要运行必须先被加载到内存中 下面举个例子能更深入的理解冯诺依曼体系结构的知识
小王和小张在两个不同的城市他们用QQ联系那么QQ发送一句话的过程大概是怎样的呢
小王通过输入设备(键盘)将数据输入到存储器(内存)中CPU通过内存分析数据后再将数据写回给内存之后内存再将数据给到输出设备(网卡)接着就发送到网络(后面会说到这里只涉及冯诺依曼相关知识)然后到了小张那边用输入设备(网卡)接收消息把数据从网卡读到内存里继续刚刚的过程将数据交给CPU计算分析然后CPU再将数据写回内存最后再将数据刷新到输出设备(显示器)上
具体步骤如下图所示紫色箭头就是上述全部过程 二、进程
操作系统先描述再组织
1、关于进程
Windows下我们自己电脑上启动一个软件本质就是启动了一个进程
Linux下运行一条命令在运行的时候其实就是在系统层面创建了一个进程
Linux是可以同时加载多个程序的是可以同时存在大量的进程在系统中的(OS里就是内存中) 下面对进程下定义
进程 对应的代码和数据 进程对应的PCB结构体
当把数据加载到内存中就变为进程了这时会生成一个struct结构体PCB包含了该进程的所有属性这时对进程的管理就变为了对进程结构体PCB链表的增删查改 关于PCB结构体
PCB全称是process control block在不同的操作系统中PCB的名字不同
在Linux中PCB结构体是task_struct它会被装载到内存中并且包含进程的属性信息 task_struct的内容
①标识符描述进程的唯一标识符用来区分其他进程
②状态任务状态退出代码退出信号等
③优先级相对于其他进程的优先级
④程序计数器程序中即将被执行的下一条指令的地址
⑤内存指针包括程序代码和进程相关的数据的指针
⑥上下文数据进程执行时处理器的寄存器中的数据
⑦IO状态信息⑧记账信息等等 2、查看进程
①ps
ps是查看进程的命令
首先打开两个页面都是我自己路径下 在test.c文件里写了死循环打印hello world的程序然后编译运行 在左边的页面内输入psps只能查看当前终端的进程 ps axj是查看所有的进程 而我们要查看的是自己刚刚的进程所以输入ps axj | grep test 再把头部带上即输入ps axj | head -1 ps axj | grep test 这样查看我们可以发现./test是在运行的所以有./test进程又因为grep在查看进程所以还有一个grep进程
头部有一个PID代表进程ID它代表当前进程的ID值
这时我们将右边的死循环Ctrl c终止了这时再执行ps axj | head -1 ps axj | grep test命令会发现只有一个grep进程了 ②/proc
有一个系统文件夹是/proc存放的是进程信息 这时我们运行./test然后查看进程 发现test的进程PID是21599然后我们在proc这个系统文件夹中也可以看到当前的进程PID21599(-l是显示属性-d是只显示路径)
而当我们Ctrl c终止死循环进程时再查看就没有这个进程信息了 所以我们可以发现proc这个文件夹是动态的 3、getpid
getpid是获得我们的进程PID的下面是用man查看getpid的介绍 需要包两个头文件其中返回值pid_t其实就是无符号整型
接下来将test.c改造一下使用getpid这个函数 然后右边窗口运行test后左边窗口查看进程 可以发现getpid获得的PID和我们ps查看的PID相同
而我们如果想终止这个死循环可以Ctrl c终止也可以kill -9 PID终止
Ctrl c终止 kill -9 PID终止 4、getppid
getppid是获得父进程的PID
下面是通过man查看getppid 和getpid一样同样是包含两个头文件同样返回值是pid_t
接下来将test.c里改变一下将ppid也打印出来 通过观察得知
运行出来的PID和PPID与我们grep查看的一样
我们终止后再重新运行如下图 却发现PID变了但是PPID却一直是21147那我们用ps查看一下21147如下图 我们发现PID是21147的进程是bash而前面我们说过bash的shell外壳程序
所以在执行命令时所有的命令最终都是以bash的子进程的方式去运行的 5、fork()
fork()是创建一个子进程
下面是man查看fork fork在头文件unistd.h中
需要注意的是fork函数的返回值
失败的时候返回-1
成功的时候①给父进程返回子进程的pid ②给子进程返回0
下面使用fork给大家示范一下返回值的场景 在fork函数前面打印一下进程的pid然后执行fork函数后分别打印fork函数的返回值ret与对应的pid和ppid 大家观察可知fork函数执行前是父进程在执行pid是22942执行fork函数后成功的话会有两个返回值①给父进程返回子进程的pid ②给子进程返回0
所以ret是0的就是子进程而子进程的pid是22943
ret是子进程pid(22943)的就是父进程而父进程的pid和fork执行前的pid一样都是22942
通过上面的示例可以更清楚的理解fork的两个返回值 fork基本用法
fork之后代码是父子进程共享的
下面例子可以清楚显示 在fork函数后有if和else if语句分别判断fork的两个返回值且都是死循环下面看结果 一个父进程一个子进程交叉进行死循环让父子进程在fork函数后面执行不同的代码所以fork之后有两个不同的执行流可以有两个while(1)同时执行而ret这个返回值在父进程里面是子进程的pid在子进程里面是0
并且可以清楚观察到子进程的ppid就是父进程的pid 一个父进程可能有多个子进程而一个子进程只能有一个父进程
所以父进程子进程 1 n
所以给父进程返回子进程的pid用于区分子进程而子进程只有一个父进程并不需要区分所以返回0
而在创建一个子进程的时候操作系统需要新建一个task_struct(PCB结构体)而这个新建的task_struct内部属性大部分是以父进程为模板的 fork函数实现时运行到return前面就说明核心代码已经执行结束了
这时子进程已经被创建出来了可能会已经被放到运行队列尾部了
所以这时父子进程一起执行父进程被调度时会return然后当子进程被调度时return也会执行所以fork函数会有两个返回值 6、进程状态
操作系统进程的状态
新建PCB资源刚分配好还没有放到运行队列中就是新建状态
运行task_struct 结构体在运行队列中排队就叫做运行态
阻塞等待非CPU资源就绪这个状态就叫做阻塞状态
挂起当内存不足的时候OS通过适当的置换进程的代码和数据到磁盘此时进程的状态就叫做挂起状态
退出退出状态 Linux操作系统进程的状态
R运行状态表明进程要么在运行中要么在运行队列里对应上面的运行态
S睡眠状态也可以称为可中断睡眠表明进程在等待事件完成对应上面的阻塞状态
D磁盘睡眠状态深度睡眠不可以被中断不可以被被动唤醒
t调试状态调试时的状态
T暂停状态单纯的暂停状态
X终止状态瞬时性非常强
Z僵尸状态一个进程已经退出还不允许被OS释放处于一个被检测状态维持该状态是为了让父进程或OS来进行回收
只要子进程退出父进程还在运行但父进程没有读取子进程状态子进程就进入僵尸状态
僵尸进程会造成资源泄露必须使用wait/waitpid接口进行等待处理 7、孤儿进程
子进程退出父进程还在运行但父进程没有读取子进程状态子进程称为“僵尸进程”
而如果父进程先退出子进程还在运行子进程就被称为“孤儿进程”
孤儿进程会被“领养”被1号进程领养init即系统本身
之所以要被领养因为父进程退出后未来子进程如果退出了父进程早已不在没有人来回收它需要领养的进程进行回收
下面是test.c的代码父进程执行三次就结束而子进程一直死循环 继续分为左右两个窗口右边窗口运行左边窗口观察进程 可以观察到右边开始运行以后ps观察进程蓝线划出来的就是子进程这时父进程还没有退出子进程的ppid还是父进程的pid等父进程三次运行完退出后子进程的ppid变为1表示被1号进程领养 8、进程优先级
之所以要有优先级是因为CPU是有限的但是进程太多了所以需要用这种方式竞争资源
而优先级就是确定谁先获得资源谁后获得资源
优先级是选择将谁放在CPU上执行的重要调度指标
Linux中具体的优先级做法
优先级 老的优先级 nice值
下面具体演示
创建两个窗口右边窗口先写一个test.c代码是死循环打印hello world再加上打印pid的值然后运行在左边窗口观察进程信息输入 ps -al | head -1 ps -al | grep test
在还没有运行程序时 运行后 可以看到进程信息中pid和打印的pid相同所以就是正在运行的进程并且蓝色圈圈出来的PRI就是优先级NI就是nice值
PRI表示这个进程被执行的优先级值越小越早被执行
NI是nice值表示进程可被执行的优先级的修正数值
所以加入nice值后新的优先级 老的优先级 nice值当nice值为负数时该程序的优先级值会变小优先级会变高即越快被执行
修改nice值top
首先重复刚刚的操作可以看到当前是PRI是80nice值是0 top相当于Windows中的任务管理器
再打开一个窗口输入top 然后输入r 相当于告诉你renice就是修改nice值而刚刚运行的pid是27103所以再输入27103 现在就可以输入nice值我们输入30再用ps打印观察进程信息 可以发现优先级值变为了99而nice值变为了19那为什么不是30呢
因为Nice值的取值范围是-20 ~ 19超过19自动当做19
所以Nice值为19而优先级值也由刚刚的80变为了99
而我们如果想将优先级调高点那就是nice值调低将nice值输入-100必须以sudo运行top: 这时nice值变为最小值-20同时PRI也由80变为60为什么不是刚刚的99-20变为79呢其实每次设置优先级值都是由80开始设置即从进程最开始的优先级开始设置 9、进程的几个特性
①竞争性进程数量多而CPU资源很少所以进程直接是有竞争属性的所以就有了优先级
②独立性多进程运行要独享各种资源多进程运行期间互不干扰
③并行多个进程在多个CPU下分别、同时进行运行称之为并行
④并发多个进程在一个CPU下采用进程切换的方式在一段时间内让多个进程都得以推进称之为并发里面有时间片、抢占与出让的概念
时间片每一个进程在CPU执行时都会有一段时间比如10ms运行完就该下一个进程进CPU
抢占与出让有可能进程a在CPU中运行的时间是10ms而5ms就进行完了这时进程a就相当于在出让CPU资源而抢占就是指优先级高的可以抢占优先级低的进程的CPU资源 切换
CPU中有很多寄存器如果此时进程a正在运行那么CPU中的寄存器里面保存的一定是进程a的临时数据而寄存器中存储的进程a的临时数据就叫做进程a的上下文数据
当进程a由于并发、时间片的约束暂时被切下来时需要进程a带走自己的上下文数据
带走进程a的上下文数据暂时保存的目的是下次回CPU运行时能够重新恢复上去就能继续按照之前执行的逻辑继续向后执行就如同之前没有中断过一样
CPU中的寄存器只有一份但是上下文数据可以有多份分别用于对应不同的进程