Linux网络编程-UNIX/Linux 模型

UNIX/Linux 模型

目录

UNIX/Linux 模型

1.1 UNIX/Linux 基本结构

1.2 输入和输出

1.3 进程


1.1 UNIX/Linux 基本结构

图 2-1 绘出了 UNIX 系统的高层次的体系结构。图中心的硬件部分向操作系统提供基本服务。操作系统直接与硬件交互,向程序提供公共服务,并使他们同硬件特性隔离。当我们把整个系统看成层的集合时,通常将操作系统成为系统内核,或简称内核,此时强调的是它同用户程序的隔离。因为程序是不依赖于其下面的硬件的,所以,如果程序对硬件没做什么假定的话,就容易把它们在不同硬件上运行的 UNIX 系统之间迁移。比如,那些假定了机器字长的程序比起没假定机器字长的程序来就较难于搬到其它机器上。

外层的程序,诸如 shell 及编辑程序(vi),是通过引用一组明确定义的系统调用而与内核交互的。这些系统调用通知内核为调用程序做各种操作,并在内核与调用程序之间交换数据。图中出现的一些程序属于标准的系统配置,就是大家所知道的命令。但是由名为a.out 的程序所指示的用户私用程序也可以存在于这一层。此处的 a.out 是被 C 编译程序产生的可执行文件的标准名字。其它应用程序能在较低的程序层次之上构筑而成,因此它们存在于本图的最外层。比如,标准的 C 编译程序 cc 就处在本图的最外层;它引用 C 预处理程序、两次编译程序、汇编程序及装入程序(称为连接—编译程序),这些都是彼此分开的低层程序。虽然该图对应用程序只描绘了两个级别的层次,但用户能够对层次进行扩从, 直到级别的数目适合于自己的需要。确实,为 UNIX 系统所偏爱的程序设计风格鼓励把现存程序组合起来去完成一个任务。

图 2- 1 UNIX 系统的高层次的体系结构

一大批提供了对系统的高层次看法的应用子程序及应用程序,诸如 shell、编辑程序、SCCS(Source Code Control System)及文档准备程序包等,都逐渐变成了“UNIX 系统” 这一名称的同义语。然而,它们最终都使用由内核提供的底层服务,并通过系统调用(System Call)的集合利用这些服务。系统调用的集合及其实现系统调用的内部算法形成了内核的主体。简言之,内核提供了 UNIX/Linux 系统全部应用程序所依赖的服务,并且内核的定义了这些服务。下面我们将进一步介绍内核,对内核的体系结构提出一个总的看法,勾画出它的基本概念和结构,这将帮助读者更好的学习以后的内容。

图 2-2 Unix 系统内核结构



图 2-2 给出了内核的框图,示出了各种模块及他们之间的相互关系,特别的,它示出了内核的两个主要成分:左边的文件子系统和右边的进程控制子系统。虽然,在实际上, 由于某些模块同其它模块的内部操作进行交互而使内核偏离该模型,但该图仍可以作为观察内核的一个有用的逻辑观点。

在图 2-2 中我们看到了三个层次:用户、内核及硬件。系统调用与库接口体现了图 2-1 中描绘的用户程序与内核间的边界。系统调用看起来象 C 程序中普通的函数调用,而库把这些函数调用映射成进入操作系统所需要的源语。然而,汇编语言程序可以不经过系统调用库而直接引用系统调用。程序常常使用像标准 I/O 库这样一些其它的库程序以提供对系统调用的更高级的使用。由于在编译期间把这些库连接到程序上,因此,以这里的观点来说,这些库是用户程序的一部分。

图 2-2 把系统调用的集合分成与文件子系统交互作用的部分及与进程控制子系统交互作用的部分。文件子系统管理文件,其中包括分配文件空间,管理空闲空间,控制对文件的存取,以及为用户检索数据。进程通过一个特定的系统调用集合,比如通过系统调用open,close,read,write,stat,chown 以及 chmod 等与文件子系统交互。

文件子系统使用一个缓冲机制存取文件数据,缓冲机制调节在核心与二级存储设备之间的数据流。缓冲机制同块 I/O 设备驱动程序交互作用,以便启动往核心去的数据传送及从核心的来的数据传送。设备驱动程序是用来控制外围设备操作的核心模块。块 I/O 设备是随机存取存储设备,或者说,它们的设备驱动程序似的它们的设备驱动程序使得它们对于系统的其它部分来说好像是随机存取存储设备。例如,一个磁带驱动程序可以允许核心把一个磁带装置作为一个随机存取存储设备看待。文件子系统和可以在没有缓冲机制干预的情况下直接与“原始”I/O 设备驱动程序交互作用。原始设备,有时也被成为字符设备, 包括所有非块设备的设备。

进程控制子系统负责进程同步、进程间通讯,存储管理及进程调度。当要执行一个文件而把该文件装入存储器中时,文件子系统与进程控制子系统交互:进程子系统在执行可执行文件之前,把它们读到内存中。输入输出存储管理模块控制存储分配。在任何时刻, 只要系统没有足够的屋里存储供所有进程使用,核心就在内存与二级存储之间对进程进行交换,以便所有的进程都得到公平的执行机会。

调度程序模块把 CPU 分配给进程。该模块调度各进程依次运行,直到它们因等待资源而自愿放弃 CPU,或者知道它们最近一次的运行时间超出一个时间量,从而核心抢占它们。于是调度程序选择最高优先权的合格进程投入运行;当原来的进程成为最高优先权的合格进程时,还会再次投入运行。进程间通信有几种形式,从时间的异步软中断信号到进程间消息的同步传输,等等。本书中主要的讲的网络通信,也是进程间通信的一种。

最后,硬件控制负责处理中断及与及其与机器通信。象磁盘或终端这样的设备可以在一个进程正在执行时中断 CPU。如果出现这种情况,在对中断服务完毕之后核心可以恢复被中断了的进程的执行。中断不是由特殊的进程服务的,而是由核心中的特殊函数服务的。这些特殊函数是在当前运行的进程上下文中被调用的。


1.2 输入和输出


输入和输出是交互式的操作系统的一个重要的组成部分。在 UNIX/Linux 中,采用了以抽象文件为基础的输入/输出系统,减少了系统对硬件的依赖性,简化了输入/输出的操作, 同时又增加了代码的灵活性。但是,由于使用了抽象的概念,所以在理解和掌握上有一定的难度,需要认真的体会。下面,我们就简要介绍一下 UNIX 的文件系统。

1.2.1 UNIX/Linux 文件系统简介

UNIX 的文件系统有如下的特点:
*层次结构
*对文件数据的一致对待
*建立与删除文件的能力
*文件的动态增长
*文件数据的权限保护
* 把外围设备作为文件看待
文件系统被组织成树状,称为目录树。目录树有一个成为根(root)的节点(记做“/”)。文件系统结构中的每个非树节点都是文件的一个目录(directory),树的叶节点上的文件既可以是目录,也可以是正规文件(regular files),还可以是特殊设备文件(special device files)。文件名由路径名(path name)给出,路径名描述了怎样在一个文件系统树中确定一个文件的位置。路径名是一个分量名序列,各分量名之间用“/”隔开。分量是一个字符序列,它致命一个北唯一的包含在前级(目录)分量中的文件名。一个完整的路径名由一个斜杠字符开始,并且指明一个文件,这个文件可以从文件系统的根开始,沿着该路径名的后继分量名所在的那个分支游历文件树而找到。

在 UNIX/Linux 系统中,程序不了解内核按怎样的内部格式存贮文件,而把数据作为无格式的字节流看待。程序可以按他们自己的意愿去解释字节流,但这种解释与操作系统如何存储数据无关。因此,对文件中数据进行存取的语法是由系统定义的,并且对所有的程序都是同样的。但是,数据的语义是由程序自己定义的。

比如,正文格式化程序 troff 希望在正文的每一行尾部着到换行符,而系统记帐程序则希望找到定长记录。两个程序都使用相同的系统服务,以存取文件中作为字节流存在的数据,而在程序内部,它们通过分析把字节流解释成适当的格式。如果哪一个程序发现格式是错误的,则由它自己负责采取适当的行动。

从这方面说,目录也像正规文件。系统把目录中的数据作为字节流看待。但是由于该数据中包含许多以预定格式记录的目录中的文件名,所以操作系统以及诸如 ls 这样的程序就能够在目录中发现文件。

i 节点(inode):

Linux 缺省使用一种叫 EXT 2 的文件系统,在这种文件系统中,每个文件在它所在的目录中都有一个对应的 inode,其中保存了文件的文件名,长度,存取权限等信息。可以这样认为: 目录就是由 inode 所组成的特殊文件。

对一个文件的存取权限由与文件相联系的 access permissions 所控制。存取权限能够分别对文件所有者,同组用户及其它人这三类用户独立的建立许可权,以控制读写及执行的许可权。如果目录存取权限允许的话,则用户可以创建文件。新创建的文件是文件系统目录结构的树叶节点。

对于用户来说,UNIX 系统把设备看成文件。以特殊设备文件标名的设备,占据着文件系统目录结构中的节点位置。程序存取正规文件时使用什么语法,他们在存取设备时也使用什么语法。读写设备的语义在很大程度上与读写正规文件时相同。设备保护方式与正规文件的保护方式相同:都是通过适当建立它们的(文件)存取许可权实现的。由于设备名看起来象正规文件名,并且对于设备和正规文件能执行相同的操作,所以大多数程序在其内部不必知道它们所操纵的文件的类型。


1.2.2 流和标准 I/O 
UNIX/Linux 内核为我们提供了一系列用于访问文件系统(包括其它 I/O 设备)的系统调用,如 open,close 等,通过这些系统调用我们可以实现全部的 I/O 功能。但由这些系统调用组成的 I/O 系统也存在使用不便,缺乏灵活性等的缺点。
为了提高 I/O 系统的模块性和灵活性,Ritchie 提出了流的概念。“流”是在内核空间中的流驱动程序与用户空间中的进程之间的一种全双工处理和数据传输通路。在内核中,流通过流首、驱动程序以及它们之间的零个或多个模块组成。流首是流最靠近用户进程的那一端。由流上用户进程发出的所有系统调用都由流首处理。

流驱动程序可以是提供外部 I/O 设备服务的一种设备驱动程序;也可以是一种软件驱动程序,通常这种驱动程序称为伪设备驱动程序。流驱动程序主要处理内核与设备间的数据传输。除了进行流机制使用的数据结构与该设备理解的数据结构间的转换之外,它很少或根本不处理别的数据。

在流首和驱动程序之间可以插入一个或多个模块,以便在流首和驱动程序间传递消息时对其进行中间处理。流模块由用户进程在流中动态的互联。创建这种连接不需要内核编程、汇编或连接编辑。


流使用队列结构,以保持与压入的模块或打开的流设备有关的信息。队列总是成对分配,一个用于读另一个用于写。每一个驱动程序、模块和流首都各有一个队列对。只要打开流或者把模块压入到流中,就分配队列对数据以消息的形式在驱动程序和流首之间以及在模块间传递。消息是一组数据结构,它们用于在用户进程、模块和驱动程序间传递数据、状态和控制信息。从流首向驱动程序, 或者从继承向设备传递的消息称之为“顺流”传播(也称之为“写侧”)。类似的,消息以另一方向传递,即从设备向进程或从驱动程序向流首方向传递,称之为“逆流”传播(也 称之为“读侧”)。

一个流消息由一个或多个消息块构成。每一个“块”是由首部、数据块和数据缓冲区组成的三元组,流首在用户进程的数据空间和流内核数据空间之间传输数据。用户进程发送给驱动程序的数据被打包成流消息,然后顺流传递。当包含数据的消息经由逆流到达流首时,此消息由流首处理,它把数据复制到用户缓冲区中。

在流内部,消息由类型指示符区分。逆流发送的某些消息类型可能导致流首执行特定的动作,如,送一个信号给用户进程。其它消息类型主要在流内部传递信息,用户进程不会直接见到这些消息。

流的概念已经被 UNIX/Linux 系统所广泛使用。如进程通信中的管道就是用流来实现的。

Ritchie 还为 C 开发了一个基于流的 I/O 库,称为标准 I/O 库。这个库具有有效的、功能强大的和可移植的文件访问性能。组成库的例行程序提供了一个用户不可见的自动缓冲机构,从而使得访问文件的次数和调用系统调用的次数最小化,取得了较高的效率。这个库的使用范围较广,因为它提供了许多比系统调用 read 和 write 更强的性能,如格式输出和数据转换等。标准 I/O 例行库还是可移植的,它们不受任何 UNIX 的特殊性的限制,并且已经成为与 UNIX 无关的 C 语言 ANSI 标准部分。任何 C 编译程序都提供对标准 I/O 库全部例行程序的访问,而不管其操作系统是什么。

输入输出(文件系统及其操作)是 UNIX/Linux 程序设计中的基础和重要组成,但是由于在大部分 C 语言教材中对此都有比较详细的介绍,故请对这个问题有兴趣的读者自行参阅其它资料,这里不再赘述。


1.3 进程


在多道程序工作的环境下,操作系统必须能够实现资源的共享和程序的并发执行,从而使程序的执行出现了并行、动态和相互制约的新特征。为了能反映程序活动的这些新特点,UNIX 引入了进程(process)这个概念。UNIX 的进程是一个正在执行的程序的映象。这里需要注意的是程序和进程的区别。一个程序是一个可执行的文件,而一个进程则是一个执行中的程序实例。在 UNIX/Linux 系统中可以同时执行多个进程(这一特征有时称为多任务设计),对进程数目无逻辑上的限制,并且系统中可以同时存在一个程序的多个实例。各种系统调用允许进程创建新进程、终止进程、对进程执行的阶段进行同步及控制对各种事件的反映。在进程使用系统调用的条件下,进程便相互独立的执行了。


进程是 UNIX/Linux 程序设计中最重要的部分,在后面的更新中我们将对进程作详细的介绍。

请大家关注更新,我会定期更新更多有关Linux网络编程的学习内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值