一、冯诺依曼体系结构与操作系统
计算机应包含五大单元:
输入设备:采集数据的,比如典型的键盘,网卡接收网络中的数据
输出设备:进行数据输出,比如典型显示器,网卡向网络中发送数据
存储器:进行中间数据缓冲,比如内存
运算器:进行数据运算 运算器+控制器=CPU中央处理器
控制器:进行设备控制
注意:所有设备都是围绕存储器工作的
1、cpu不会直接从输入设备获取数据进行处理,而是先把数据放到存储器种,cpu从存储器种获取数据处理。
2、cpu不会直接将数据交给输出设备进行输出,而是先把数据放到存储器中,控制输出设备从存储器种获取数据输出。
问:存储器实际上就是内存,为什么不是硬盘呢?
答:硬盘的数据吞吐量太低了,而内存的数据吞吐量特别大,是硬盘的数十倍。如ddr4内存数据读写速度约3GB/s。
问: 内存速度这么快,为什么不用内存存储数据,而要使用硬盘?
答:因为硬盘与内存的存储介质不同,内存是易失性介质,数据掉电就会丢失,而硬盘掉电后数据不会丢失。
系统调用接口:操作系统向用户提供的访问内核的接口
库函数与系统调用函数接口的关系:库函数封装了系统调用接口
二、进程
进程:一段程序运行的过程。
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
一个程序运行起来,有数据以及指令需要被cpu执行处理,根据冯诺依曼体系结构,cpu不会直接去硬盘上找到程序文件进行执行处理,需要先将程序数据信息,加载到内存中,然后cpu从内存中获取数据以及指令进行执行处理,程序运行会被加载到内存中。
操作系统中的进程都是同时运行的。
问:cpu只有一个,如何做到多个程序同时运行?
答:cpu的分时机制:实现系统同时运行多个程序的技术。cpu只负责执行指令,处理数据,因此操作系统的进程管理就体现出来,对程序的运行调度进行管理。
cpu进行程序运行处理的时候,每个程序都只运行一段很短的时间(给一个程序分配一个时间片),时间片运行完毕则由操作进行调度,让另一个程序的代码数据在cpu上进行处理。
cpu分时机制实现cpu轮询处理每一个运行中的程序,而程序运行调度由操作系统进行管理。
因此,这种切换轮询处理以极快的频率进行切换运行每一个程序,给我们造成一个假象:程序在同时运行。
管理思路:操作系统将每一个程序的运行信息保存下来,进行调度管理的时候才能知道这个程序上一次运行到了哪里。
PCB
对操作系统来说,进程就是PCB,是一个程序运行的动态描述,通过PCB,才能实现程序的运行调度管理。
PCB:为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块(PCB),它是进程实体的一部分,是操作系统中最重要的记录性数据结构。它是进程管理和控制的最重要的数据结构,每一个进程均有一个PCB,在创建进程时,建立PCB,伴随进程运行的全过程,直到进程撤消而撤消。
PCB描述信息:linux下的PCB就是一个task_struct结构体
内存指针——能够让操作系统调度程序运行的时候,知道进程对应的指令以及数据在内存中的位置。
上下文数据——程序运行过、运行中、即将执行的指令和数据,操作调度切换进程运行的时候能够让cpu这个进程接着切换之前继续运行,继续处理之前没有处理完的数据,切换时保存正在执行的指令以及数据信息。
程序计数器——也属于上下文数据,保存的就是指令位置,切换回来知道从哪里运行。
标识符——能够让操作系统识别唯一的运行中的程序。
IO信息、状态信息、优先级、记账信息
对操作系统来说,管理程序的运行,就是将程序的运行描述起来,然后组织起来进行管理,描述的运行信息对于操作系统来说就是运行中的程序,就是进程。
进程状态:每个进程PCB中都会描述一个运行的状态信息,通过状态信息,告诉操作系统这个进程现在应该干什么。
僵尸进程(重要)
一个进程退出了,但是进程资源没有完全被释放,等待处理的一种状态。
危害:资源泄露(可能会导致正常进程起不来)
产生:一个进程先于父进程退出,父进程没有关注子进程退出状态,导致子进程资源无法完全被释放,进入僵尸状态。
解决:进程等待——一直关注子进程,退出了就能直接发现。
孤儿进程
父进程先于子进程退出,子进程就会成为孤儿进程。
特性:让出中断,进入系统后台运行,并且父进程成为1号进程。
1号进程:在centos7之前,叫init进程,在centos7之后,叫systemd进程。系统中父进程是1号进程的进程,通常以d结尾,表示自己是个在后台默默运行的服务。
进程创建
进程创建:pid_t fork(viod) – 通过复制调用进程,创建了一个新的进程
1、进程就是pcb,创建一个进程就是创建了一个pcb,复制了调用fork的这个进程pcb的信息(内存指针、程序计数器、上下文数据)
2、这个新的进程,运行的代码与调用fork的进程一样,并且运行位置也相同。
3、两个进程运行的程序相同:父进程是调用进程,子进程是新建进程。
4、在父进程中返回子进程的pid,是大于0的;在子进程中返回0;返回-1表示创建子进程失败。
fork创建子进程之后,根据操作系统的调度来决定父子进程运行先后顺序。
为什么要创建子进程:为了保护父进程,分摊压力。
子进程干的事情与父进程一样,然而使用返回值分流后可以有所不同。有任务了,父进程创建一个子进程去完成任务,出问题了崩溃的是子进程,父进程没有影响。
环境变量
环境变量:终端shell中进行系统运行环境配置的变量
作用:
1、可以是系统环境配置更加灵活
2、可以通过环境变量向子进程传递数据
操作指令:env-查看所有环境变量
echo——直接打印某个变量的内容
set——查看所有变量,不只是环境变量
unset——删除变量,包括环境变量
典型唤醒变量:PATH ——存储程序运行默认的搜索路径
代码操作:
char *getenv(char *key) :通过环境变量名称获取环境变量内容
main函数的三个参数
extern char **environ;库中的全局变量,每个节点指向一个环境变量,但是使用时需要生命,告诉编译器有这个变量。
设置环境变量:setenv()/putenv()
程序地址空间
地址:内存地址——对内存以字节为存储单元的一个编号;通过地址就能找到具体对应的内存单元
程序:即为代码,保存在程序文件中
编译器在编译程序生成可执行程序文件时,就会对每一条指令,每一个数据,进行一个地址排号,当程序运行时,就会将指令以及数据放到指定的内存地址位置,cpu就会根据地址偏移去执行指令,以及找到对应的数据进行处理。
程序运行后才会占据内存——因此程序地址空间通常被称为进程地址空间。
进程中所访问的地址都是虚拟地址,并非物理内存地址,我们所说的程序地址空间, 实际上也是一个虚拟的地址空间,是操作系统为进程通过一个mm_struct结构体所描述的一个假的地址空间:mm_struct(task_size,start_code,end_code)——通过大小以及区域的编号来描述。
虚拟地址不具备存储能力,数据的存储依然要放到物理内存中。
问:为什么操作系统不让进程直接访问物理内存,而是弄了一个虚拟地址空间,让进程访问虚拟地址呢?
答:1、程序在编译时,编译器就会给指令和数据进行地址编号;但是如果某个地址内存已经被占用,则这个程序就运行不起来了(编译器地址的管理麻烦——无法动态获知什么时候哪块内存是否会使用,无法进行代码及数据的地址赋值)
2、仅存直接访问物理内存,如果有一个野指针,你在操作的时候有可能把其他进程中的数据改变(无法进行内存访问控制)
3、程序运行加载通常需要使用一块连续的内存空间,对内存的利用率比较低。
注:通过虚拟地址空间映射到物理内存上进行数据存储,可以实现数据在物理内存上的离散式存储,提高内存的利用率。
并且每个进程都有自己的虚拟地址空间,因此对于每个进程来说,都会拥有自己的一块连续的空间使用。
虚拟地址是不具备存储能力,数据的存储依然还是要放到物理内存中。