重温Linux-5.Linux系统所提供的服务

重温Linux-5.Linux系统所提供的服务

所有的Linux系统,都提供了一个共同的抽象和接口的集合。Unix的核心内容包括文件和进程的抽象、管道和套接字管理的接口等等。

文件和文件系统

Linux遵循一切皆是文件的理念(虽然并不是像某些系统严格)。文件是Linux中最基本和重要的抽象。

  • 在系统编程中,文件必须被打开才能被访问。
  • 文件可以以只读方式或者只写方式打开,或者两者皆有。
  • 一个打开的文件通过唯一的文件描述符进行引用。
  • 此描述符是打开文件的元数据至其本身的映射。
  • 在Linux内核中,这个描述符叫文件描述符,用一个整数表示(c语言中的int类型)。
  • 文件描述符在用户空间中共享,允许用户程序用文件描述符直接访问文件。

普通文件

常说的文件实际上是普通文件。一个普通文件包含以线性字节数组组织的数据,通常称为字节流。
  • 在Linux中,文件没有更进一步的组织方式或者格式。
  • 字节可以是任意值,亦可以以任何方式被组织在一个文件中,在系统级别,除了字节流,Linux并没有要求文件有特定的结构。
  • 文件中任何字节都可以被读和写,这些操作开始于特定的字节,这个特定的字节位置被称为文件偏移量。
  • 这个文件是内核与每一个打开的文件关联的元数据非常重要的一部分。
  • 当首次打开文件时,位置是0。
  • 文件的位置也可以手工指定一个值,就算超出了文件的结尾也行。
  • 在超出文件结尾之后的将会导致中间的字节填充为0。
  • 尽管可以通过这样的方式在文件的末尾写入字节,但是却不允许在文件头部之前写入字节。
  • 文件位置起始于0,她不可能还是负值。
  • 在文件中间写入字节将覆盖文件偏移量上的值。
  • 文件的大小通过字节来计算,称为文件长度。
  • 文件长度是组成文件的线性数组里字节的数目。文件的长度可以通过截断来改变。
  • 一个文件可以通过删除文件结尾部分而截断成比原来小的文件。
  • 截断操作也可以将文件变得更大。在这种 情况下,文件将以“0”进行填充。
  • 文件最大值,如同文件位置的最大值,受限于Linux内可用于管理文件的C语言类型的大小。
  • 内核并没有对并发文件访问强加任何限制,不同的 进程能够对同一个文件进行读写操作。
  • 并发访问的结果取决于独立操作的顺序,但是通常不可预料。
  • 用户控件程序必须调整他们自己的顺序并保证并发文件访问可以同步。
  • 文件通过文件名进行访问,,事实上文件并非通过文件名直接关联文件。
  • 文件通过inode(信息节点)来访问,inode使用唯一的数值进行标识。此值称为inode编号,通常简写为i-number或者ino。
  • 一个inode存储文件关联的元数据,如文件的时间戳、所有者、类型、长度以及文件数据的地址-唯独没有文件名。inode既是Unix文件系统在磁盘上实际物理对象,也是Linux内核中的数据结构的概念实体。

目录和链接

  • 通过inode编号访问文件显然不太明智。所以人们通常使用文件名访问文件,目录就是提供访问文件时所需的名字,目录将易读的名字和inode编号进行映射。名字与inode的配对,称为链接(link)。
  • 映射在物理磁盘上的形式,可以是一个简单的表格、一个哈希表或者其他任何形式。
    一个目录可以被视为任何普通的文件,唯一的不同点是它仅仅存储名字和inode的映射。
    当用户空间应用请求打开一个指定文件时,内核打开包含指定文件名的目录,然后搜索文件。
  • 内核根据文件名获取inode编号,然后根据inode编号找到对应的inode。inode包含文件相关的元数据,其中包括文件数据在磁盘中的存储位置。
    目录内的链接能指向别的目录的inode。这意味着目录可以嵌套到别的目录中,形成目录层次。
  • 当用户输入类似/home/cslqm/test.txt的文件路径时,内核打开类似路径名时,它通过遍历路径上的每一个目录项,来查找下一项的inode。内核从/开始,获取home的inode,跳转到那,然后获取cslqm的inode,再进入该项,最终找到test.txt的inode。这个操作过程为目录或者路径解析。
  • 内核使用缓存来存储目录解析的结构,提供时间局部性支持,加快查询速度。
  • 一个从根目录开始的路径是指定的,也叫做绝对路径。当遇到相对路径时,内核在当前的工作目录下进行路径解析。
  • 虽然目录可以看成是普通的文件,但是内核不允许像操作普通文件一样打开和操作它们。它们必须通过几个特殊的系统调用来操作它们,不管什么情况,也只能调用两种操作:添加链接和删除链接(link)。如果允许用户空间绕过内核的管理而进行目录操作,那么一个非常简单的错误都可能使文件系统崩溃。

文件链接-硬连接

  • 我们把不同名字映射到同一个inode信号节点的多个链接称为硬连接。
    在一个复杂的文件系统结构中,硬连接允许多个路径指向相同的数据。
    硬连接可以在同一个目录下,也可以在不同的目录下。
  • 删除目录结构中的一个文件将会引发一个unlink操作,该操作将文件名和inode的映射信息从目录中移除。然而,因为Linux支持硬连接,文件系统不能在每一个unlink操作都移除inode以及和它相关联的数据。
  • 如果文件系统别的地方存在一个硬连接呢?为了保证每一个文件在所有的链接都移除后才彻底删除文件,每一个inode还包含一个链接计数来跟踪文件系统中指向该文件的硬连接数目。当路径名解除链接,计数-1。当它为0时,inode和它关联的数据才真正从文件系统中删除。

符合链接

  • 因为inode编号在自己文件系统之外没有任何意义,所以不能跨文件系统建立硬连接。
  • 为了允许跨文件系统建立链接,Unix系统还实现了符号链接。
  • 每一个符号链接(symlink)都有自己的inode和包含被链接文件完整路径的数据块。这意味者符号链接可以指向任何地方,包括不同文件系统上的文件和目录,甚至不存在的文件和目录。指向不存在文件的符号链接称为坏链接。
  • 相比硬连接,符号链接的开销大,因为有效的解析符号链接需要解析两个文件:符号链接和被链接的文件。硬链接不需要此开销。

特殊文件

特殊文件是以文件方式表示的内核对象。Unix支持了一些不同的特殊文件。
- Linux支持四种类型的特殊文件:块设备文件、字符设备文件、命名管道和Unix域套接字。
特殊文件是将某些抽象融入文件系统中的一种方法,是一切皆是文件理论的实际。

  • Linux提供了创建特殊文件的系统调用。
  • 对Unix系统的设备进行访问通过设备文件来实现,设备文件的表现和特征和文件系统中的普通文件一样。
  • 设备文件可以被打开、读取和写入,以此允许用户空间程序访问和操作系统上的 (物理和虚拟的)设备。Unix的设备通常分为两类:字符设备和块设备。每种类型都有自己的专用设备文件。

字符设备

  • 访问字符设备如同访问字节线性队列。设备驱动程序将字节按序写入队列,用户空间程序则按照字节被写入队列的顺序进行读取。键盘就是典型的字符设备。如果你敲入“big”,应用程序将从键盘设备中读取b,然后是i,最后是g。
  • 当没有更多的字符需要读取时,设备返回end-of-file(EOF)。字符设备通过字符设备文件(character device file)进行访问。

块设备

块设备是以字节数组的方式进行访问的。设备驱动将字节映射到可寻址的设备上。块设备通常是存储设备、硬盘、软盘等,它们通过块设备文件进行访问。

命名管道

命名管道通常称为FIFOs,是“先进先出”的简称,是一种以文件描述符为信道的进程间通信(IPC)机制,通过一种特殊文件进行访问。普通管道是将一个程序的输出以“管道”的方式传送给另一个程序,并作为该程序的输入。它们通过系统调用在内存中创建而不在任何文件 系统中存在。命名管道和普通管道一样,但是通过文件进行访问。称为FIFO特殊文件,不相关的进程也可以访问这个文件进行交互。

套接字

套接字时最后一种特殊文件。套接字是进程间通信的高级形式,它允许不同进程通信。事实上,套接字是网路和因特网编程的基础。它们演化出多个变种,包括Unix域套接字,这是本地机器进行交互的套接字格式。相比网络上的套接字交互需要通过主机名和端口对来确定交互的对象,Unix域套接字使用文件系统上的特殊文件进行交互,该文件为套接字文件。

文件系统和名字空间

  • 如同所有的Unix系统一样,Linux提供了一个全局统一的 文件和目录的名字空间。某些操作系统将磁盘和驱动器分割成独立的名字空间。
  • 文件系统是以合法层次结构组织的文件和目录的集合。文件系统能从全局的文件和目录的名字空间独立的添加和移除。这些操作称为挂载和卸载。每个文件系统都要挂载在名字空间中特定的位置,这个位置称为挂载点。
  • 文件系统的根目录可以通过挂载点访问。例如,挂载CD到/media/cdrom,然后就可以通过访问挂载点来访问CD上的文件系统的根目录。第一个被挂载的文件系统位于名字空间的根部/,称为根文件系统。
  • Linux系统总有一个根文件系统。
  • 文件系统一般来讲存在于物理介质上的(如存在磁盘上),同时Linux也支持存储在内存上的虚拟文件系统和跨网络的网络文件系统。
  • 块设备最小访问地址单元为扇区,扇区是设备的物理单位。扇区一般是2的指数倍,通常是512字节。块设备无法移动或者访问比扇区更小的数据单元。所有的I/O操作都发生在一个或多个扇区上。
  • 同样,文件系统中最小的逻辑地址单元是块,块是文件系统中的抽象而不是对物理介质的抽象,通常是2的指数倍与扇区大小的乘积。
  • 块一般大于扇区,但是必须小于页的大小。页> 块 > 扇区

进程

进程也是Unix中一个重要的抽象。
进程是执行中的代码:运动的、生存的、运动的程序。除了目标代码,进程还包含数据。资源、状态以及虚拟化的计算机。我的学校选的教程上是说,

进程是运行着的程序实体。

进程从可执行目标代码开始其生命周期。这些目标代码具有内核能够解析的可执行格式(Linux下最常用的格式是ELF),且可以由机器执行。可执行格式代码包含由元数据,多个代码和数据段。“段”是加载到线性内存块的线性目标代码块。所有片段内的字节将一视同仁,赋予相同的权限,一般也用于同样的目的。
最重要和通用的段莫过于代码段,数据段和bss段。代码段包含已初始化的数据,如定义了值的C变量,通常标记为可读写的。
bss段包含未初始化的全局数据,因为C标准规定C变量的默认值全为0,因此没有必要在磁盘上的目标代码中保存这些0,。
相反,目标代码可以简单的列举bss段中未初始化的变量,内核将映射0页面(全0的内存页)到那个加载进内存的段,为了优化性能人们设计了bss段。
一个进程还和由内核仲裁和管理的系统资源关联,进程典型的资源请求和操作只能通过系统调用。资源包括计时器,挂起信号量、打开文件,网络连接,硬件和进程通信。一个进程的资源,进程相关的数据和统计信息都存储在内核中该进程描述符中。
进程是一种虚拟的抽象,
Linux内核支持抢占式多任务和虚拟内存,它给进程提供了虚拟处理器和内存的虚拟视图。
从进程的视角看,系统完全由该进程控制。也就是说,尽管给定的进程和其他的进程共同调度,但是看起来好像他占有了整个系统。
系统将无缝透明的重新进行进程调度,将系统的处理器和所有进程共享,而进程不会感到区别。类似的,每一个进程获得一个独立的线性地址空间,就像它独立控制整个系统内存。
通过虚拟内存和分页调度,内核允许多个进程共存在系统上,每个进程操作都有自己的地址空间。
内核通过现代处理器的硬件支持管理这种虚拟化,它使得操作系统能够并发管理多个独立的进程。

线程

一个进程包含一个或多个执行线程(通常称为线程),线程时进程中的活动单位。线程是一种抽象,它负责执行代码和维护进程的运行状态。
包含一个线程的进程的进程叫单线程的,包含多个线程的进程的称为多线程的。
线程包括栈(如同在线程系统上的进程栈,主要用于存储局部变量)、处理器状态、目标代码的当前位置(通常是处理器的指令指针)。

进程体系

每个进程都由一个唯一的正整数标识,即为进程ID(PID)。第一个进程的pid是1,接下来每一个进程接受一个新的唯一的pid。
在Linux中,进程有一个严格的层次结构,这就是进程树。进程树以第一个进程,也就是init进程(一般是init(8)程序)为根。新进程通过fork()系统调用创建。forl()复制了调用进程,而原进程称为父进程,新进程称为子进程。
除第一个进程外,每一个进程都有父进程。如果父进程在仙玉子进程终止,内核init进程指定为它的父进程。
如果进程终止,它并不会立即从系统中移除。相反,内核将在内存中保存进程的 部分内容,允许父进程查询终止的状态,这被称为终止进程等待。一旦父进程已经确认它的终止的子进程,子进程就完全的删除了。如一个进程已经终止,但父进程尚未获取它的状态,则称为僵尸进程。init进程等待其他的所有子进程,保证它的子进程不会永远处于僵尸状态。

用户和组

Linux中通过用户和组进行认证。每个用户和唯一的正整数关联,称为用户ID(id)。每一个进程与一个用户ID关联,用来识别允许这个进程的用户,一般称为进程的真实uid(real uid)。在Linux内核中,uid是用户的唯一标识。但是用户一般都是通过用户而不是数字id来指代或者其他用户。用户名和他们对应的用户id存储在/etc/password中,而库例程将用户名映射到相应的uid上。
在登陆的过程中,用户向login(1)程序提交用户名和密码。如果提供的用户名和密码正确,login(1)程序将根据/etc/passwprd位用户生成一个登陆shell(login shell),并将用户id作为进程的uid。子进程继承父进程的uid。
uid 0是超级用户root的用户id。root用户拥有任何事情的特权。
login(1)的用户位超级管理员。
除了真实UID(real uid)之外,每一进程有一个有效UID,一个保留uid和文件系统uid。真实UID是启动进程的用户,有效UID可以使进程在其他用户的权限下运行,保留uid保存原来的有效UID,它的值决定了用户将切换到那个有效UID中。未见系统uid和有效uid相等,用来检测文件系统的访问权限。
每一个都归属于一个或多个组,包括列在/etc/password中的基本组(primay group)或登陆组(login group),也可能是/etc/group中其他附加组。每一个进程因此也有一个组ID(gid),因此又有真实gid(real gid),有效gid,保留gid(saved gid)以及文件系统gid(filesystem gid)。进程和用户登入组关联,和其他附加组没有关系。
一些安全机制只允许进程满足特定权限时才能进行某些操作。传统的Unix的原则非常简单:uid为0的进程可以访问的资源,其他的进程都不可访问。
Linux采用了更有效率的机制来代替传统的安全机制,它取消了了简单的二元判断方式,允许内核进行更细粒度的访问控制设置。

权限

Linux上的标准文件权限和安全机制与Unix一致。
每一个文件都有一个所有者,所属组以及权限位集。这些位描述了所有着。所属组以及其他人对文件进行读、写和执行的权限。这三类每一个对应三个位,共9位。文件所有者和权限信息存储在文件inode中。
对于普通文件,权限是显然的 ,它们已经清楚表明读文件,写文件和执行文件的权限。虽然特殊文件实际读和写的内容由特殊文件自己确定,但是特殊文件上的读和写权限与普通文件的一样,窒息感执行在特殊文件上被忽视。目录的读权限时允许目录中的内容被列出,写权限允许在目录中古添加新的链接,执行权限允许目录进入和使用该路径。权限有9个权限位,它们的8进制值(常用的9位表示方式),文本值(如ls显示的结果),还有对应的含义。
除了Unix的权限外,Linux还支持访问控制表(ACLs),ACLs支持更多细节、确切权限及安全控制,只是增加了管理复杂性和磁盘存储的开销。

信号

信号是一种单向异步通知机制,信号可能是从内核发送到进程,可能是从进程到进程,或者进程给自己。信号一般用于通知进程发生某些事件,如段错误或者用户输入Ctrl+C。
Linux内核实现了大概30个信号(实际数字由架构决定),每一信号有一个数字常量和文本名标识。
除了SIGKILL(进程终止)和SIGSTOP(进程停止)外,进程能够根据接收到信号进行控制。他们可以使用默认的信号处理操作,可能是中断进程。中断并做内存信息转储(coredump)、停止进程,或者什么也不做,具体的操作取决于信号值。另外,进程可以选择显式的忽略或者处理信号。忽略信号是将信号丢弃,不做处理。处理信号将执行用户编写的信号处理函数,程序将在接收到信号时调到处理函数,信号处理程序返回后,将把程序控制权回交给原来的程序,在之前中断的指令处继续执行。

进程间通信

允许进程交换信息和通知彼此发生的事件是操作系统最重要的工作之一。Linux内核实现了传统Unix的进程间通信机制。
Linux支持的进程间通信机制包括管道,命名管道,信号量,消息队列,共享内存和快速用户空间互斥体(Futexes)。

  • 管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常指父子进程关系。
  • 命名管道(named pipe):命名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  • 信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止进程正在访问资源时,其他进程也访问资源。因此主要作为进程间以及同一线程之间的同步手段。
  • 消息队列(message queue):消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式效率低而专门设计的。它往往与其他通信机制。
  • 快速用户空间互斥体(Futexes)
    以及其他常用的通信方式:
  • 套接字(socket):套接字也是一种进程间通信机制,与通信机制不同的是,它可用于不同及其间的进程通信。

头文件

Linux系统编程离不开大量的头文件。内核和glibc为系统级编程提供了头文件。

错误处理

在系统编程中,错误通常通过函数的返回值表示,并通过特殊的变量errno来描述。glibc对库函数和系统调用的errno提供透明支持。

函数通过特殊的返回值(通常是-1,具体情况看函数),来通知调用者所发生的错误。
错误值告诉调用函数发生了错误,但是不提供发生错误的原因,errno变量用于定位错误的原因。
该变量在errno.h中定义,定义如下:extern int errno。
errno的值仅仅在errno设置函数显示错误后(通常返回-1)短时间内有效,否则,任何后续成功执行的函数都可以修改其值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值