当前位置: 首页 > news >正文

Linux --OS和PCB

目录

认识冯诺依曼系统

操作系统概念与定位

1.概念

2.设计OS的目的

3.OS的核心功能

4.系统调⽤和库函数概念

深⼊理解进程概念,了解PCB

1.基本概念与基本操作

2.描述进程-PCB

基本概念

task_ struct 的内容分类


认识冯诺依曼系统

  在计算机中小到个人的笔记本,大到企业的服务器,大多都遵从冯诺依曼体系结构。

  截⾄⽬前,我们所认识的计算机,都是由⼀个个的硬件组件组成 , 输⼊单元:包括键盘, ⿏标,扫描仪, 写板等 。中央处理器(CPU):含有运算器和控制器等 。输出单元:显⽰器,打印机等

  为什么大多数的计算机都要遵从这套体系呢,这是因为cpu在数据层面,不会和外设直接打交道,而是通过这里的储存器也就是内存,进行交换。当程序运行时首先会加载到内存中,由内存和cpu进行交互。那么为什么要有内存,内存其实是cpu和外设间的一个巨大缓存,外设如果和cpu直接进行交互,cpu的传输速率很快,但是外设的传输速率很慢,那么这个计算机的传输速率就会很慢,就像木桶定律一样,一只水桶能装多少水取决于它最短的那块木板,所以内存就诞生了,能够使程序提前加载到内存中,在需要运行程序时会直接与cpu交互,且速率并不会太慢,这是一个伟大的发明,使一般计算机能够拥有不错的效率,促进了计算机的发展。

  下面利用qq收发数据时来理解冯诺依曼

  关于冯诺依曼,必须强调⼏点:不考虑缓存情况,这⾥的CPU能且只能对内存进⾏读写,不能访问外设(输⼊或输出设备) 。外设(输⼊或输出设备)要输⼊或者输出数据,也只能写⼊内存或者从内存中读取。 ⼀句话,所有设备都只能直接和内存打交道。

操作系统概念与定位

1.概念

  任何计算机系统都包含⼀个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
内核(进程管理,内存管理,⽂件管理,驱动管理)
其他程序(例如函数库,shell程序等等

2.设计OS的目的

在计算机中OS对上为用户和程序提供了一个安全稳定高效的运行环境,对下管理所有的软硬件资源,不直接与硬件进行交互,而是通过驱动程序,每一种硬件都有自己对于的驱动程序。

3.OS的核心功能

OS不直接与硬件进行交互,而是作为一个管理者的身份,描述被管理对象(硬件)并组织被管理对象,使用struct结构体描述对象,用链表的其他的高效数据结构组织起来。

4.系统调⽤和库函数概念

  在计算机中用户并不能直接去和硬件交互,而是需要通过系统调用来交互,因为不是每个用户都深入了解底层硬件的,使用系统调用的方法能够极大保证安全的运行环境。

  在开发⻆度,操作系统对外会表现为⼀个整体,但是会暴露⾃⼰的部分接⼝,供上层开发使⽤,
这部分由操作系统提供的接⼝,叫做系统调⽤。
    系统调⽤在使⽤上,功能⽐较基础,对⽤⼾的要求相对也⽐较⾼,所以,有⼼的开发者可以对部
分系统调⽤进⾏适度封装,从⽽形成库,有了库(c++等一些语言库),就很有利于更上层⽤⼾或者开发者进⾏⼆次开发。图形化操作界面也是系统调用封装的一种形式,如安卓系统,其实内核用的也是Linux。
  

深⼊理解进程概念,了解PCB

1.基本概念与基本操作

课本概念:程序的⼀个执⾏实例,正在执⾏的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。
进程的进一步理解其实是:内核数据结构体和程序的代码和数据,因为软件在加载到内存中的时候还包含着自己的进程信息,而这个进程信息被放在一个结构体中。

2.描述进程-PCB

基本概念

进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
  在Linux中描述进程的结构体叫做task_struct,task_struct是Linux内核的⼀种数据结构,它会被装载到RAM(内存)⾥并且包含着进程的信息。
为什么要有 PCB,因为OS要管理进程,通过进程信息也就是PCB来调度程序运行。

task_ struct 的内容分类

标⽰符: 描述本进程的唯⼀标⽰符,⽤来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执⾏的下⼀条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下⽂数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]。
I/O状态信息: 包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表。
记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。
组织进程
可以在内核源代码⾥找到它。所有运⾏在系统⾥的进程都以task_struct链表的形式存在内核⾥。

进程属性

1.查看进程

  在进程创建以后会 /proc系统文件下创建对应的属性文件夹, 在进程的信息可以通过 /proc 系统⽂件夹查看,因为Pid的唯一的,所以每一个带Pid序号的文件夹里面都是一个进程的详细属性。
如:要获取PID为1的进程信息,你需要查看 /proc/1 这个⽂件夹。
  ⼤多数进程信息同样可以使⽤top和ps这些⽤⼾级⼯具来获取,我启动了一个自己写的循环进程,然后用ps ajx | head -1 && pa ajx |grep code 来查看进程。这个命令的意思是查看进程信息属性第一行和带有code关键字的进程。
  这里我们可以看到code这个进程的Pid是27328,那么它对应会在/proc下创建一个27328文件来储存code的进程信息
  /proc 是是一个内存级的文件,即这个文件并不会写入文件中,电脑关机这些文件就没有了,进程也就都关闭了。一个进程关闭对应的/proc下的属性文件夹也会被删除。所以此时我将code终止,27328文件夹就自动会删除了。

2.通过系统调用来获取进程标识符

  在进程信息中我们可以看到每个进程都有自己的Pid和ppid,ps是通过一些系统调用遍历/proc文件夹来获取的,那么这两个标识符我们自己也可以通过系统调用来获取。

  进程pid我们可以使用getpid()来获取,父进程id可以用getppid()来获取,这两个函数存在于sys/types.h头文件中。

#include<iostream>
#include<unistd.h>
#include<sys/types.h>using namespace std;int main()
{while(1){cout<<"pid :"<<getpid()<<endl;cout<<"ppid :"<<getppid()<<endl;sleep(1);}return 0;
}

  这是一个进程id获取的代码,运行起来可以发现获取的的id和pid确实一致

  我们在/proc文件夹找到对应进程信息能够看到exe和cwd

  那么这两个属性是什么呢,cwd为current work  dir即当前工作目录,exe为当前可执行程序所在的路径。创建一个可执行程序或者是在当前路径下创建的吗?其实是在当前的工作目录下创建的,我们可以用chdir来改变这个路径,这里用创建一个txt文件来展示

int main()
{chdir("./mydir");FILE* fp=fopen("log.txt","w");if(fp == NULL){}while(1){cout<<"pid :"<<getpid()<<endl;cout<<"ppid :"<<getppid()<<endl;sleep(1);}return 0;
}

可以看到目前的cwd更改了,而且log.txt也创建在更改的cwd目录下了

3.通过系统调用创建进程--fork

  如果我们想要在代码中创建一个进程,那么就需要使用fork系统调用,fork的有分流的意思,即本来是一个进程,fork后就变成了一个父进程和子进程,通过man可以得知fork的返回值有两个,父进程返回子进程的Pid,子进程返回0,这是因为父进程和子进程是一比n的关系,因为一个父进程可以用于多个子进程,但是子进程的父进程是唯一的。

int main()
{pid_t id = fork();if(id > 0){while(1){cout<<"我是父进程 pid :"<<getpid()<<" ppid :"<<getppid()<<" id :"<<id<<endl;sleep(1);}}else if(id == 0){while(1){cout<<"我是子进程 pid :"<<getpid()<<" ppid :"<<getppid()<<" id :"<<id<<endl;sleep(1);}}return 0;
}

  

  可以看出父进程对应的id确实就是子进程的Pid,而子进程的id为0,他的父进程正是16918,这两个循环其实是在同时运行的,fork以后进程一分为二,这两个进程共享代码(采⽤写时拷⻉),但是数据是独立的,所以各自拥有各自的id值,也就能够同时进入这两个循环,而不是一会id大于0一会id等于0。所以进程之间有很强的独立性,多个进程直接运行时互不影响,这里可以用一个例子来说明他们的数据独立且互不影响。

int globl=0;int main()
{pid_t id = fork();if(id > 0){while(1){cout<<"我是父进程 pid :"<<getpid()<<" ppid :"<<getppid()<<" globl :"<<globl<<endl;sleep(1);}}else if(id == 0){globl++;while(1){cout<<"我是子进程 pid :"<<getpid()<<" ppid :"<<getppid()<<" globl :"<<globl<<endl;sleep(1);}}return 0;
}

  这里增加了一个全局变量来说明,如果父子进程确实数据独立,那么他们的globl的值应该就是不一样的,运行代码可以看出确实如此。

  这里结合系统调用再说明一下为什么fork以后会出现两个id返回值,这是为了数据的独立性,在fork的过程中,先会将父进程的PCB拷贝给子进程,然后子进程再调整新的属性,将新的PCB连接到进程列表中,此时子进程就已经创建了而不是返回id以后才会创建,所以此时父子进程数据已经独立了,返回的时候根据PCB的属性返回不同的id。

相关文章:

  • NLP学习路线图(十三):正则表达式
  • Java研学-MongoDB(一)
  • 什么是 TOML?
  • [Windows] 本地无损放大软件-realesrgan-gui
  • 3D-激光SLAM笔记
  • 使用VSCode在WSL和Docker中开发
  • 小程序使用npm包的方法
  • 【HTML】基础学习【数据分析全栈攻略:爬虫+处理+可视化+报告】
  • LeetCode - 21. 合并两个有序链表
  • Correlations氛围测试:文本或图像的相似度热图
  • [面试精选] 0206. 反转链表
  • 二叉搜索树——AVL
  • Redisson学习专栏(四):实战应用(分布式会话管理,延迟队列)
  • 机器视觉2D定位引导一般步骤
  • C++基础算法————深度优先搜索(DFS)
  • 高考加油!UI界面生成器!
  • B3623 枚举排列(递归实现排列型枚举)
  • python魔法函数
  • 【基础算法】模拟算法
  • @PathVariable注解-补充
  • 商业网站建设开发/厨师培训
  • 网站打开慢的解决方法/武汉网站seo德升
  • 武汉城乡建设网站/济宁seo公司
  • 网页模板网站模板/seo数据分析哪些方面
  • 旅游网站设计页面/天天外链官网
  • 国企网站建设的意义/成都关键词排名系统