绪论
Linux(不是商用的)是unix(商用的)操作系统的一个类;
操作系统的基本概念:
任何计算机系统都包含一个名为操作系统的基本程序的集合。在这个集合里最重要的程序称之为内核。当操作系统启动时,内核会被装载到ram里,内核中包含了系统运行不可少的核心过程。系统根本的样子和能力由内核决定,因此我常常将操作系统称之为内核
操作系统完成两个主要目标:
1.与硬件部分交互,为包含在硬件平台上的所有底层可编程的部件提供服务。
2.为运行在计算机系统上的应用程序(即所谓的用户程序)提供执行环境
当程序想要访问硬件资源的时候,必须向操作系统发出一个请求。操作系统(内核)需要对这个资源进行评估,如果允许使用这个资源,那么内核代表应用程序与相关硬件的部分进行交互。
为了实现上述的方式在unix操作系统中:在cpu中 把他们分为两个模式:用户态和内核态;
多用户系统:
多用户系统就是一台能并发和独立的执行分别属于两个或者多个用户的若干应用程序的计算机
并发:意味着几个应用程序能同时处于活动状态并竞争各种资源,如cpu内存硬盘等。
独立:意味着每个应用程序能执行自己的任务,而无需考虑其他用户的应用程序正在干什么。
用户和组
在多系统用户中,每个用户在机器上都有自己的私有空间 别人是不可见的。
为了和其他用户共享资源,每个用户可以是一个或者多个用户组的一个名成员,组由唯一的用户组标识符标识。
还有一种特殊的用户 超级用户 root,系统管理员必须以root身份登陆 方便处理用户账户
进程
一个进程可以定义为程序执行时的一个实例,或者一个运行程序的执行上下文。
区别程序和进程:几个进程能并发地执行同一程序,而同一个进程能够顺序的执行几个程序。
操作系统中叫做调度程序的部分决定那个进程能执行。一些操作系统中只允许非抢占式的进程,也就是说当进程自愿放弃cpu的时候,调度程序才被调用。但是,多用户系统中都的进程必须是抢占式的,操作系统记录下每个进程占有cpu的时间,并周期性的激活调度程序。
类unix系统采用进程/内核模式。每个进程都自以为她是系统中唯一的进程,可以独占操作系统所提供的服务。只有进程发出系统调用(即对内核提出请求),硬件就会把特权模式由用户态变成内核态,然后进程已非常有限的目的开始一个内核的执行过程。一旦这个请求完全得到满足,内核过程将强迫使硬件返回用户态,然后进程从系统调用的下一条指令继续执行。
软硬连接
文件类型
- 普通文件
- 目录
- 符号链接
- 面向块的设备文件
- 面向字符的设备文件
- 管道和命名管道
- 套接字
文件操作的系统调用
当用户访问的一个普通文件或者目录文件的内容时,它实际上访问的是存储在硬件块设备上的一些数据。因为处于用户态的进程不能直接与底层的硬件交互,所以每个实际的文件操作都是在内核态下完成进行。因此unix操作系统定义了几个与文件操作有关的系统调用
打开文件:
进程只能访问打开的文件,为了打开一个文件,进程调用系统调用
fd=open(path,flag,mode)
三个参数的含义如下:
path:表示被打开的文件(相对或绝对)路径
flag:指定打开文件的方式(例如,读,写,读/写,追加)。他也指定了是否创建一个不存在的文件
mode:指定新创建的文件的访问权限
这个系统调用创建一个“打开文件”对象,并返回所谓的文件描述符(file descriptor)的标识符。一个打开文件对象包括:
文件操作的一些数据结构,如指定文件打开方式的一组标致,表示文件当前位置的offset字段,从这个位置开始执行一下个操作(即所谓的指针)等等
进程可以调用一些内核函数指针。这组允许调用的函数集合由参数flag值决定。
为了创建一个新的文件可以调用create()系统调用,它与open()非常相似都是由内核来处理
进程/内核模式
进程是动态的实体,在系统内通常只有有限的生存周期。创建撤销及同步现有的进程任务都委托给内核中的一组例程来完成
内核本身并不是一个进程,而是进程的管理者。进程/内核模式假定:请求内核服务的进程使用所谓系统调用的特殊编程机制。每个系统调用都设置了一组识别进程请求的参数,然后执行与硬件相关的cpu指令完成从用户态到内核态的转换。
unix内核做的不仅仅是处理系统调用,实际上,可以有几种方式激活内核例程:
-
进程调用系统调用
-
正在执行进程的cpu发出异常信号,异常是一些反常情况,例如一个无效的指令。内核代表产生异常的进程处理异常
-
外围设备向cpu发出一个中断信号已通知一个事件的发生,如一个要求注意的请求。每个终端信号都是由内核中的中断处理程序来处理的。因为外围设备与cpu异步操作,因此中断在不可预知的时间段中发生
-
内核线程被执行,因为内核线程运行在内核态,因此必须认为其相应程序是内核的一部分。
进程实现
为了让内核管理进程,每一个进程由一个进程描述符表示,这个描述符包含有关进程当前状态的信息
当内核暂停一个进程的执行时,就把几个相关的处理器寄存器内容保存在进程描述符中,这些寄存器包括:
程序计数器(pc)栈指针寄存器(sp)
通用寄存器
符点寄存器
包含cpu状态信息的处理器控制器寄存器
用户跟踪进程对ram访问的内存管理寄存器
可重入内核
所有的unix内核都是可重入的,这就意味着若干个进程可同时在内核态下执行。
进程地址空间
每个进程运行在他自己的私有地址空间,在用户态下运行的进程涉及到私有栈,数据区和代码区。但在内核态运行时,进程访问内核的数据区和代码区,但是是用另外的私有栈
因为内核是可重入的,因此几个内核控制路径(每个都与不同的进程有关)可以轮流执行。在这种情况下,每个内核控制路径都引用他自己的私有内核栈。
同步和临界区
实现可重入内核需要利用同步机制:
- 非抢占式内核
- 在解决同步问题的方案中,大多数传统的unix内核都是非抢占式的,当进程在内核态时,他不能被任意挂机,也不能被另一个进程代替。因此,在单处理器系统上,中断或异常处理程序不能修改所有的内核数据结构,内核对他们的访问时安全的
- 如果内核支持抢占式,那么在应用同步机制的时候,确保进入临界区前禁止抢占,退出临界区时启动抢占。
禁止中断
单处理器系统上的另一种同步机制是:在进入一个临界区之前禁止所有硬件中断,离开时在重新启动中断;
信号量
每一个信号量可以看作一个对象,其组成如下:
- 一个整数变量
- 一个等待进程的链表
- 两个原子方法:down()对信号量-1和up()对信号量+1
自旋锁
在多处理器系统中,信号量并不是解决同步问题的最佳方法;
在多处理器系统中,使用自旋锁;自旋锁和信号量很相似,但是没进程链表,当一个进程发现锁被另一个进程锁着,他就不停的旋转,执行一个紧凑的循环指令直到锁打开;当然自旋锁在单处理器系统上是无效的;如果一直自旋,结果可能是系统挂起。
- 避免死锁
- 信号和进程间通信
进程管理
unix在进程和他正在执行的程序之间做出了一个很清晰的划分。fork()和exit()系统调用分别用来创建一个新进程和终止一个进程,而调用exec类系统调用则是装入一个新程序
调用fork()的进程是父进程,而新进程而是他的字进程,父子进程能够互相找到对方,因为描述每个进程的数据结构包含两个指针,一个直接指向他的符进程,一个直接指向他的字进程。