操作系统与进程(1)

简介: 操作系统与进程(1)

文章目录

冯诺依曼体系结构

冯诺依曼体系结构

1.输入设备:键盘,磁盘(外设,读取文件(没读的时候就在磁盘)),网卡,显卡,话筒,摄像头(效率是s,ms级别)

2.输出设备:显示器,打印机,磁盘(写文件,就是把数据写到磁盘上),网卡,显卡,音箱等

3.存储器(内存):离cpu越近的设备存储效率越高也越贵,有了内存,cpu就不用直接和外设打交道,存储器的快慢决定快慢(内存是体系结构的核心设备)

4.运算器&&控制器(CPU):运算器(算术运算与逻辑运算)

控制器(读取指令,分析指令,执行指令)(效率ns级别)


a和b通过qq发消息


任何的外设,在数据层面,基本都是有限和内存打交道!,

cpu,在数据层面上,也直接和内存打交道

操作系统

启动的(将软件数据与代码,加载到内存中)操作系统,才有意义


是什么

OS 是一款软件,专门针对软硬件资源进行管理工作的软件

(核心工作就是做管理的)


为什么

对下:管理好软硬件资源。对上:给用户提供稳定高效安全的运行环境

方式 目的


怎么办

核心在管理,管理是对人的属性数据进行管理,描述属性结合的就是结构体,里面就有被管理者的全部数据,再加个联系指针连接起来(链表,哈希,树等),对学生的管理就变成了对链表的管理,对学生的褒奖工作就变成了对链表的增删查改


本质(先描述再组织)

管理理念:先描述,再组织,

可以转化成对目标的管理,

转化成对数据的管理

进程(软件)

加载到内存的程序就叫做进程

系统中可能存在大量的进程,操作系统要管理进程

如何管理进程?

先描述,再组织

任何进程在形成的时候,操作系统要为该进程创建PCB,进程控制块( 先描述,struct PCB 里面就是进程的所有属性,结构体描述),

PCB

OS 上面,PCB 进程控制块,就是一个结构体类型

在Linux系统中,PCB ->struct task_struct{ //进程的所有属性 }

类比shell和bash的关系

类比媒婆和王婆的关系

我们所有启动的程序的过程都是在系统上面创建进程

把程序运行起来,就是把程序加载到内存,就是把程序由磁盘加载到内存中变成一个进程

进程的属性

程序运行结束进程就结束了

进程vs程序

有了进程控制块,所有进程管理任务与进程对于的程序毫无关系!!!

与进程对应的内核创建的进程的 PCB 强相关

进程=程序+操作系统维护进程的相关数据结构(更多的数据结构)(进程控制块)

PCB的内部构成

  • pid:描述本进程的唯一标识符,原来区别其他进程

结束进程

kill -9 pid

  • ppid:获得其父的pid(在命令行上运行的命令,基本上其父进程都是-bash)

状态:任务状态,退出代码,退出信号等。

0

输出离他最近执行命令的退出码


优先级:相对于其他进程的优先级,CPU 只有1个,进程有非常多个,确认先后的问题,


程序计数器:程序中即将被执行的下一条指令的地址


内存指针:可以通过PCB 找到对应的代码和数据


IO状态信息:进程可能要进行的IO,(从输入设备读入)(输出设备读出),进程在进行IO,


记账信息:进程被os调度器调度的信息(os为了较为均衡的调用每个进程(获得cpu的资源(进程被执行))),有处理器时间总和,使用的时间限制,记帐号


上下文数据:(进程执行时处理器的寄存器中的数据)寄存器(当前正在运行的进程的临时数据)

时间片(单次运行每个进程的最大时间10ms,没跑完,排到最后等待继续运行),在cpu情况下,用户感受到多个进程都在运行(靠cpu快速切换完成的),进程在运行期间是由切换的,进程可能存在大量的临时数据----》暂时在cpu的寄存器中保存,

保护上下文

恢复上下文

虽然寄存器硬件只有一份,但是寄存器里面的数据是你这个进程的,走的时候,把寄存器里面的数据返回pcb里面,进入下一个进程,等待下一次使用,再进入时,再把之前的临时数据放回cpu

通过上下文的保护和恢复,我们可以感受到进程被切换的

查看进程的方案

ls /proc

exe就是当前执行的文件路径

cwd就是当前工作路径

fork

fork就是用来创建子进程的


演示

1 #include <iostream>
  2 using namespace  std;
  3 #include<unistd.h>
  4  
  5 int main()
  6 {                                                                 
  7     fork();//创建子进程
  8     cout<<"hello proc "<<getpid()<<"hello parent "<<getppid()<<endl;
  9     sleep(1);                                                                                   
 10     return 0;                                                                                    
 11 }    

上面的代码我们fork后,只写了一行的打印,但是运行结果是有两条的内容

如何理解fork创建子进程

有我们在命令行里面./cmd or run command(执行指令) ,与fork相比:在操作系统角度上,创建进程的方式都是没有差别的,只不过fork创建的进程间有父子关系


fork本质是创建进程 ----> 系统里面多了一个进程 —>在系统里面多了一份与进程相关的内核数据结构 (task_struct) + 进程的代码和数据(我们只是fork了,创建了子进程,但是子进程对应的代码和数据)---------》


默认情况下,会继承父进程的代码和数据

内核数据结构task_struct 也会以父进程为模板,初始化子进程的task_struct

例如父亲是做鞋的工厂老板,那么你就会继承父亲的基因(数据结构),同时你也会子承父业继续做鞋(继承代码和数据)


进程具有独立性


代码

fork之后子进程和父进程代码是共享的

代码是不可以被修改的

父子代码只有一份

数据

默认情况下“数据也是共享的”,不过因为进程具有独立性,所以也要考虑修改的情况

可以通过“写时拷贝”来完成进程数据的独立性

fork的返回值

我们创建的子进程,就是为了和父进程干一样的事情???

一般是没有意义的,我们一般还是希望要让父进程和子进程做不一样的事

我们通常是用fork的返回值来完成

  • 失败: <0
  • 成功:
  • 给父进程返回子进程的pid
  • 给子进程返回0
 1 #include <iostream>                                                       
  2 using namespace  std;             
  3 #include<unistd.h>  
  4 #include <sys/types.h>  
  5 int main()  
  6 {  
  7     pid_t id=fork();//获得其返回值 
  8     cout<<"hello proc "<<getpid()<< " hello parent "<<getppid()<<" ret: "<< id <<endl;  
  9     sleep(1);                                                                                            
 10     return 0;                                                                      
 11 }    

如何理解有两个返回值?

如果一行函数已经执行return,那么函数的核心功能执行完了

pid_t fork()
{
//创建子进程的逻辑
 return XXX;//也是语句,父子共享return
}


到return的时候,他创建子进程的逻辑也完了,所以子进程已经有了,父进程要return,子进程也要return


返回的是数据吗?return的时候也会写入吗?


返回的也是数据,会,发生了写时拷贝


如何理解两个返回值的设置


父:子=1:n,


子进程的父进程只有1个,而父进程可以有很多的子进程,所以要得到其子进程的pid来控制子进程,而子进程通过ppid就可以找到父进程

对多进程的控制

#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
    pid_t id=fork();
    if(id==0)//利用if else,进行分流,实现父子进程完成不同的事
    {
//child
        while(true)
        {
      cout<<"I am 子进程 pid="<<getpid()<<" ppid="<<getppid()<<endl;
            sleep(1);
        }
    }
    else if(id>0)
    {
    while(true)
        {
      cout<<"I am 父进程 pid="<<getpid()<<" ppid="<<getppid()<<endl;
            sleep(2);
        }
    }
    else
    {
    //todo
    }
    sleep(1);
    return 0;
}

fork之后谁先跑呢??

不确定

进程状态

进程的状态信息在哪里呢??

在 task_struct(PCB)

进程状态的意义:

方便OS 快速判断进程,完成特定的功能,比如调度,本质是一种分类具体的状态

R:运行态,不一定正在CPU上面正在运行,但是如果处于运行的队列(等待CPU)中,那么这些状态都可以称为R状态,随时都可以被CPU进行调度


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p20zfQoB-1647504125418)(picture/image-20220310140329017.png)]


S/D:睡眠状态,当我们想要完成某种任务的时候,任务条件不具备,需要进程进行某种等待时,就S,D,等待资源就绪


如:今天想打游戏,但是电脑没开,那么我们就要开机等待电脑开机,那么此时我们就是S,D


所谓的进程,在运行的时候可能会因为运行的需要,可以会在不同的队列里,


在不同的队列里面,所处的状态是不一样的


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3K13Q8eR-1647504125419)(picture/image-20220310141732027.png)]


如果资源就绪了那么S状态就变成了R状态,放到运行队列里,


如果这个进程突然卡死了,那OS就会把他从运行队列放到等待队列里面去


我们把,从运行状态的task_struct(run_queue),放到等待队列中,就叫做挂起等待(阻塞)


从等待队列,放到运行队列,被CPU调度,就叫做唤醒进程

#include<iostream>
#include<unist.h>
int main()
{
    sleep(10);
    cout<<"hello "<<endl;
return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0vxSbIJj-1647504125420)(picture/image-20220310144349978.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uAi5lsGD-1647504125420)(picture/image-20220310143310732.png)]


等待我们设定的10s就绪,那么他就处于S状态,


可以直接立即终止(可中断睡眠)


深度睡眠 ;不可中断睡眠,


假设进程发出了一个命令,要将1t数据(数据量非常大)都放到磁盘里面,让磁盘去读写,之后磁盘就开始工作了,这个时候进程就开始等待


等待磁盘把任务完成,这个时候,OS来了发现进程在休息,就把他给杀掉了,


磁盘读写完了,要把失败与否的结论告诉进程,但是发现进程不见了,


进程:是OS把我杀掉的


磁盘:是进程不见的


没有办法解决问题


本质上是因为操作系统把正在等待的进程给杀掉的


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JH3pVRNg-1647504125421)(picture/image-20220310153558797.png)]


所以为了解决这个问题,就有了深度睡眠的D状态,

进程进入了D状态,不可被杀掉


T :暂停状态,完全暂停不会因为某种条件达成变成R,做某些任务的时候,进程需要暂停


t(trace stop):经常调试程序的时候,在调试的时候,进程所处的状态,打个断点,就停下来了,临时查看很多资源数据


x(dead):死亡状态,回收进程资源=进程相关的数据结构+你的代码和数据(和创建的操作是一样的),死亡状态无法查到


z(zombie):僵尸状态,先进入僵尸状态再进入死亡状态


为什么要有僵尸状态


为了辨别退出死亡原因,进程退出时要暂时保存退出的信息


在task_struct存了进程退出的所有信息


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWpdwoVG-1647504125421)(https://raw.githubusercontent.com/zevin02/image/master/%20202203121022560.png)]

状态代码验证

R

:没有IO ,所以就不用等待

  1 #include<iostream>
  2 using namespace std;
  3 int main()
  4 {
  5     while(true);
  6     return 0;                                                                                            
  7 }

S

  1 #include<iostream>
  2 using namespace std;
  3 int main()
  4 {
  5     while(true)
  6     {
  7        cout<<"hello"<<endl;
  8     }                                                                                              
  9     return 0;         
 10 }                     
~         

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYSD1QuG-1647504125422)(https://raw.githubusercontent.com/zevin02/image/master/%20202203111201188.png)]



[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6blxo0h-1647504125423)(https://raw.githubusercontent.com/zevin02/image/master/%20202203121024675.png)]


有可能还能检测到r状态


打印,是往显示器打印,外设慢,IO,实际上等待外设就绪是很慢的,所以大部分状态都是S状态,CPU太快了


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWVrfyha-1647504125423)(https://raw.githubusercontent.com/zevin02/image/master/%20202203121028603.png)]


暂停进程

继续进程

后面没有+号,在后台运行

但是按ctrl c无法结束进程

结束进程

+

:带了+,说明是在前台运行,在命令行输入任何内容都是没有用的,但是ctrl c来干掉进程


后台进程./myproc &,就变成后台进程,可以在命令行中输入命令,无法用ctrl c来结束进程,只能用kill -9 pid来结束进程


z

如果没有人检测回收进程(父进程),该进程退出或进入z

1 #include <iostream>                                                                                
  2 #include<unistd.h>
  3 using namespace std;
  4 int main()                                       
  5 {                 
  6     pid_t id=fork();
  7     if(id==0)
  8     {                 
  9        //child       
 10        while(true)
 11       {                
 12          cout<<"I am child,running"<<endl;
 13           sleep(2);
 14        }                                   
 15     }                                       
 16     else           
 17     {  
 18        //father
 19        cout<<"I am father ,doing nothing"<<endl;
 20        sleep(50); 
 21     }  
       return 0;
 22 }

子进程给终止掉,但是父进程没有回收,就变成了僵尸进程

(子进程先死了,父进程还在运行)

孤儿进程

被1号进程领养,1号进程也叫做操作系统OS

父进程终止了,子进程还在运行

  1 #include <iostream>                                                                                
  2 #include<unistd.h>
  3 using namespace std;
  4 int main()                                       
  5 {                 
  6     pid_t id=fork();
  7     if(id==0)
  8     {                 
  9        //child       
 10        while(true)
 11       {                
 12          cout<<"I am child,running"<<endl;
 13           sleep(2);
 14        }                                   
 15     }                                       
 16     else           
 17     {  
 18        //father
 19        cout<<"I am father ,doing nothing"<<endl;
 20        sleep(10);
     exit(-1);
 21     }  
       return 0;
 22 }

进程优先级

为什么会有优先级?

本质上是资源太少的问题,优先级本质是获得资源,分配资源的一种方式

查看进程的方案

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5   while(1)
  6   {
  7   ¦ printf("I am a process ,pid %d,ppid %d\n",getpid(),getppid());                           
  8   ¦ sleep(1);
  9   }
 10   return 0;
 11 }
~

UID 相当于我的名字,不过他是以数字来标识(类似身份证号码)

PRI就是优先级数据

NI优先级的修正数据,

linux中的优先级数据,值越小,优先级越高,


就尽快的享受到某种资源


PRI vs NI


加入nice值,PRI(new)=PRI(old)+nice


所以nice为正,优先级变满,


反之亦然


特别不建议自己修改优先级


优先级设置

nice值(-20~19)

old不会因为之前的改变而改变,还是一开始的值

nice值为什么是一个相对小的范围

优先级再怎么设置,也只能是一种相对的优先级,不能出现绝对的优先级,避免有的进程出现饥饿问题,而我们的调度器

就是为了较为均衡的让每个进程享受到CPU资源,

相关文章
|
13天前
|
消息中间件 人工智能 分布式计算
探索操作系统的核心:进程管理的艺术
在现代计算的广阔领域中,操作系统扮演着至关重要的角色,它不仅是用户与计算机硬件之间的桥梁,更是确保系统稳定、高效运行的指挥官。本文旨在深入探讨操作系统中一个核心组件——进程管理的奥秘,揭示其背后的原理、机制以及对现代计算环境的重要性。
|
20天前
|
算法 调度
深入理解操作系统:进程调度与优先级反转问题
【9月更文挑战第36天】操作系统是计算机科学中的核心概念,它管理着计算机的硬件资源和软件进程。在多任务处理环境中,进程调度是保证系统高效运行的关键机制之一。本文将探讨进程调度的基本概念、调度算法以及它们如何影响系统性能。同时,我们还将讨论优先级反转问题,这是一个在实时系统中常见的问题,它可能导致系统响应时间不可预测。通过分析优先级反转的原因和解决方案,我们可以更好地理解操作系统的设计和优化策略。
|
10天前
|
算法 调度 UED
探索操作系统的心脏:深入理解进程调度
【10月更文挑战第7天】在数字世界的海洋中,操作系统是那艘承载着软件与硬件和谐共处的巨轮。本文将带你潜入这艘巨轮的核心区域——进程调度系统,揭示它如何精准控制任务的执行顺序,保障系统的高效运行。通过深入浅出的语言,我们将一起解码进程调度的奥秘,并借助代码示例,直观感受这一机制的魅力所在。准备好,让我们启航吧!
|
8天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
12 1
|
8天前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
9 1
|
10天前
|
算法 调度 UED
深入理解操作系统的进程调度算法
【10月更文挑战第7天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。它不仅影响系统的性能和用户体验,还直接关系到资源的合理分配。本文将通过浅显易懂的语言和生动的比喻,带你一探进程调度的秘密花园,从最简单的先来先服务到复杂的多级反馈队列,我们将一起见证算法如何在微观世界里编织宏观世界的和谐乐章。
|
15天前
|
消息中间件 算法 Linux
深入理解操作系统:进程管理与调度
【10月更文挑战第2天】本文将带你进入操作系统的核心领域之一——进程管理与调度。我们将从进程的基本概念出发,探讨进程的生命周期、状态转换以及进程间通信机制。文章还将介绍现代操作系统中常见的进程调度算法,并通过实际代码示例,展示如何在Linux系统中实现简单的进程创建和管理。无论你是操作系统的初学者还是有一定基础的开发者,这篇文章都将为你提供新的视角和深入的理解。
|
19天前
|
算法 调度 UED
探索操作系统的心脏:进程调度策略解析
在数字世界的每一次跳动背后,是操作系统中进程调度策略默默支撑着整个计算生态的有序运行。本文将深入剖析进程调度的奥秘,从理论到实践,揭示其对计算性能和系统稳定性的决定性影响。通过深入浅出的讲解和实例分析,我们不仅能理解不同调度策略的工作原理,还能学会如何根据实际应用场景选择或设计合适的调度算法。让我们跟随这篇文章的脚步,一起走进操作系统的核心,解锁进程调度的秘密。
|
16天前
|
缓存 算法 调度
深入浅出操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅。我们将从进程管理的基本概念出发,逐步深入到内存管理的复杂世界,最终探索如何通过实践技巧来优化系统性能。文章将结合理论与实践,通过代码示例,帮助读者更好地理解操作系统的核心机制及其在日常技术工作中的重要性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往操作系统深层次理解的大门。
|
18天前
|
iOS开发 MacOS
MacOS环境-手写操作系统-40-进程消息通讯 和 回车键处理
MacOS环境-手写操作系统-40-进程消息通讯 和 回车键处理
19 2