此篇文章主要会带你介绍 Linux 操作系统,包括 Linux 本身、Linux 如何使用、以及系统调用和 Linux 是如何工作的。

Linux 简介

UNIX 是一个交互式系统,用于同时处理多进程和多用户同时在线。为什么要说 UNIX,那是因为 Linux 是由 UNIX 发展而来的,UNIX 是由程序员设计,它的主要服务对象也是程序员。Linux 继承了 UNIX 的设计目标。从智能手机到汽车,超级计算机和家用电器,从家用台式机到企业服务器,Linux 操作系统无处不在。

大多数程序员都喜欢让系统尽量简单,优雅并具有一致性。举个例子,从最底层的角度来讲,一个文件应该只是一个字节集合。为了实现顺序存取、随机存取、按键存取、远程存取只能是妨碍你的工作。相同的,如果命令

ls A*

意味着只列出以 A 为开头的所有文件,那么命令

rm A*

应该会移除所有以 A 为开头的文件而不是只删除文件名是 A* 的文件。这个特性也是最小吃惊原则(principle of least surprise)

最小吃惊原则一半常用于用户界面和软件设计。它的原型是:该功能或者特征应该符合用户的预期,不应该使用户感到惊讶和震惊。

一些有经验的程序员通常希望系统具有较强的功能性和灵活性。设计 Linux 的一个基本目标是每个应用程序只做一件事情并把他做好。所以编译器只负责编译的工作,编译器不会产生列表,因为有其他应用比编译器做的更好。

很多人都不喜欢冗余,为什么在 cp 就能描述清楚你想干什么时候还使用 copy?这完全是在浪费宝贵的 hacking time。为了从文件中提取所有包含字符串 ard 的行,Linux 程序员应该输入

grep ard f

Linux 接口

Linux 系统是一种金字塔模型的系统,如下所示

应用程序发起系统调用把参数放在寄存器中(有时候放在栈中),并发出 trap 系统陷入指令切换用户态至内核态。因为不能直接在 C 中编写 trap 指令,因此 C 提供了一个库,库中的函数对应着系统调用。有些函数是使用汇编编写的,但是能够从 C 中调用。每个函数首先把参数放在合适的位置然后执行系统调用指令。因此如果你想要执行 read 系统调用的话,C 程序会调用 read 函数库来执行。这里顺便提一下,是由 POSIX 指定的库接口而不是系统调用接口。也就是说,POSIX 会告诉一个标准系统应该提供哪些库过程,它们的参数是什么,它们必须做什么以及它们必须返回什么结果。

除了操作系统和系统调用库外,Linux 操作系统还要提供一些标准程序,比如文本编辑器、编译器、文件操作工具等。直接和用户打交道的是上面这些应用程序。因此我们可以说 Linux 具有三种不同的接口:系统调用接口、库函数接口和应用程序接口

Linux 中的 GUI(Graphical User Interface) 和 UNIX 中的非常相似,这种 GUI 创建一个桌面环境,包括窗口、目标和文件夹、工具栏和文件拖拽功能。一个完整的 GUI 还包括窗口管理器以及各种应用程序。

Linux 上的 GUI 由 X 窗口支持,主要组成部分是 X 服务器、控制键盘、鼠标、显示器等。当在 Linux 上使用图形界面时,用户可以通过鼠标点击运行程序或者打开文件,通过拖拽将文件进行复制等。

Linux 组成部分

事实上,Linux 操作系统可以由下面这几部分构成

  • 引导程序(Bootloader):引导程序是管理计算机启动过程的软件,对于大多数用户而言,只是弹出一个屏幕,但其实内部操作系统做了很多事情
  • 内核(Kernel):内核是操作系统的核心,负责管理 CPU、内存和外围设备等。
  • 初始化系统(Init System):这是一个引导用户空间并负责控制守护程序的子系统。一旦从引导加载程序移交了初始引导,它就是用于管理引导过程的初始化系统。
  • 后台进程(Daemon):后台进程顾名思义就是在后台运行的程序,比如打印、声音、调度等,它们可以在引导过程中启动,也可以在登录桌面后启动
  • 图形服务器(Graphical server):这是在监视器上显示图形的子系统。通常将其称为 X 服务器或 X。
  • 桌面环境(Desktop environment):这是用户与之实际交互的部分,有很多桌面环境可供选择,每个桌面环境都包含内置应用程序,比如文件管理器、Web 浏览器、游戏等
  • 应用程序(Applications):桌面环境不提供完整的应用程序,就像 Windows 和 macOS 一样,Linux 提供了成千上万个可以轻松找到并安装的高质量软件。

Shell

尽管 Linux 应用程序提供了 GUI ,但是大部分程序员仍偏好于使用命令行(command-line interface),称为shell。用户通常在 GUI 中启动一个 shell 窗口然后就在 shell 窗口下进行工作。

shell 命令行使用速度快、功能更强大、而且易于扩展、并且不会带来肢体重复性劳损(RSI)

下面会介绍一些最简单的 bash shell。当 shell 启动时,它首先进行初始化,在屏幕上输出一个 提示符(prompt),通常是一个百分号或者美元符号,等待用户输入

等用户输入一个命令后,shell 提取其中的第一个词,这里的词指的是被空格或制表符分隔开的一连串字符。假定这个词是将要运行程序的程序名,那么就会搜索这个程序,如果找到了这个程序就会运行它。然后 shell 会将自己挂起直到程序运行完毕,之后再尝试读入下一条指令。shell 也是一个普通的用户程序。它的主要功能就是读取用户的输入和显示计算的输出。shell 命令中可以包含参数,它们作为字符串传递给所调用的程序。比如

cp src dest

会调用 cp 应用程序并包含两个参数 srcdest。这个程序会解释第一个参数是一个已经存在的文件名,然后创建一个该文件的副本,名称为 dest。

并不是所有的参数都是文件名,比如下面

head -20 file

第一个参数 -20,会告诉 head 应用程序打印文件的前 20 行,而不是默认的 10 行。控制命令操作或者指定可选值的参数称为标志(flag),按照惯例标志应该使用 - 来表示。这个符号是必要的,比如

head 20 file

是一个完全合法的命令,它会告诉 head 程序输出文件名为 20 的文件的前 10 行,然后输出文件名为 file 文件的前 10 行。Linux 操作系统可以接受一个或多个参数。

为了更容易的指定多个文件名,shell 支持 魔法字符(magic character),也被称为通配符(wild cards)。比如,* 可以匹配一个或者多个可能的字符串

ls *.c

告诉 ls 列举出所有文件名以 .c 结束的文件。如果同时存在多个文件,则会在后面进行并列。

另一个通配符是问号,负责匹配任意一个字符。一组在中括号中的字符可以表示其中任意一个,因此

ls [abc]*

会列举出所有以 ab 或者 c 开头的文件。

shell 应用程序不一定通过终端进行输入和输出。shell 启动时,就会获取 标准输入、标准输出、标准错误文件进行访问的能力。

标准输出是从键盘输入的,标准输出或者标准错误是输出到显示器的。许多 Linux 程序默认是从标准输入进行输入并从标准输出进行输出。比如

sort

会调用 sort 程序,会从终端读取数据(直到用户输入 ctrl-d 结束),根据字母顺序进行排序,然后将结果输出到屏幕上。

通常还可以重定向标准输入和标准输出,重定向标准输入使用 < 后面跟文件名。标准输出可以通过一个大于号 > 进行重定向。允许一个命令中重定向标准输入和输出。例如命令

sort <in >out

会使 sort 从文件 in 中得到输入,并把结果输出到 out 文件中。由于标准错误没有重定向,所以错误信息会直接打印到屏幕上。从标准输入读入,对其进行处理并将其写入到标准输出的程序称为 过滤器

考虑下面由三个分开的命令组成的指令

sort <in >temp;head -30 <temp;rm temp

首先会调用 sort 应用程序,从标准输入 in 中进行读取,并通过标准输出到 temp。当程序运行完毕后,shell 会运行 head ,告诉它打印前 30 行,并在标准输出(默认为终端)上打印。最后,temp 临时文件被删除。轻轻的,你走了,你挥一挥衣袖,不带走一片云彩

命令行中的第一个程序通常会产生输出,在上面的例子中,产生的输出都不 temp 文件接收。然而,Linux 还提供了一个简单的命令来做这件事,例如下面

sort <in | head -30

上面 | 称为竖线符号,它的意思是从 sort 应用程序产生的排序输出会直接作为输入显示,无需创建、使用和移除临时文件。由管道符号连接的命令集合称为管道(pipeline)。例如如下

grep cxuan *.c | sort | head -30 | tail -5 >f00

对任意以 .t 结尾的文件中包含 cxuan 的行被写到标准输出中,然后进行排序。这些内容中的前 30 行被 head 出来并传给 tail ,它又将最后 5 行传递给 foo。这个例子提供了一个管道将多个命令连接起来。

可以把一系列 shell 命令放在一个文件中,然后将此文件作为输入来运行。shell 会按照顺序对他们进行处理,就像在键盘上键入命令一样。包含 shell 命令的文件被称为 shell 脚本(shell scripts)

推荐一个 shell 命令的学习网站:https://www.shellscript.sh/

shell 脚本其实也是一段程序,shell 脚本中可以对变量进行赋值,也包含循环控制语句比如 if、for、while 等,shell 的设计目标是让其看起来和 C 相似(There is no doubt that C is father)。由于 shell 也是一个用户程序,所以用户可以选择不同的 shell。

Linux 应用程序

Linux 的命令行也就是 shell,它由大量标准应用程序组成。这些应用程序主要有下面六种

  • 文件和目录操作命令
  • 过滤器
  • 文本程序
  • 系统管理
  • 程序开发工具,例如编辑器和编译器
  • 其他

除了这些标准应用程序外,还有其他应用程序比如 Web 浏览器、多媒体播放器、图片浏览器、办公软件和游戏程序等

我们在上面的例子中已经见过了几个 Linux 的应用程序,比如 sort、cp、ls、head,下面我们再来认识一下其他 Linux 的应用程序。

我们先从几个例子开始讲起,比如

cp a b

是将 a 复制一个副本为 b ,而

mv a b

是将 a 移动到 b ,但是删除原文件。

上面这两个命令有一些区别,cp 是将文件进行复制,复制完成后会有两个文件 a 和 b;而 mv 相当于是文件的移动,移动完成后就不再有 a 文件。cat 命令可以把多个文件内容进行连接。使用 rm 可以删除文件;使用 chmod 可以允许所有者改变访问权限;文件目录的的创建和删除可以使用 mkdirrmdir 命令;使用 ls 可以查看目录文件,ls 可以显示很多属性,比如大小、用户、创建日期等;sort 决定文件的显示顺序

Linux 应用程序还包括过滤器 grep,grep 从标准输入或者一个或多个输入文件中提取特定模式的行;sort 将输入进行排序并输出到标准输出;head 提取输入的前几行;tail 提取输入的后面几行;除此之外的过滤器还有 cutpaste,允许对文本行的剪切和复制;od 将输入转换为 ASCII ;tr 实现字符大小写转换;pr 为格式化打印输出等。

程序编译工具使用 gcc

make 命令用于自动编译,这是一个很强大的命令,它用于维护一个大的程序,往往这类程序的源码由许多文件构成。典型的,有一些是 header files 头文件,源文件通常使用 include 指令包含这些文件,make 的作用就是跟踪哪些文件属于头文件,然后安排自动编译的过程。

下面列出了 POSIX 的标准应用程序

程序 应用
ls 列出目录
cp 复制文件
head 显示文件的前几行
make 编译文件生成二进制文件
cd 切换目录
mkdir 创建目录
chmod 修改文件访问权限
ps 列出文件进程
pr 格式化打印
rm 删除一个文件
rmdir 删除文件目录
tail 提取文件最后几行
tr 字符集转换
grep 分组
cat 将多个文件连续标准输出
od 以八进制显示文件
cut 从文件中剪切
paste 从文件中粘贴

Linux 内核结构

在上面我们看到了 Linux 的整体结构,下面我们从整体的角度来看一下 Linux 的内核结构

内核直接坐落在硬件上,内核的主要作用就是 I/O 交互、内存管理和控制 CPU 访问。上图中还包括了 中断调度器,中断是与设备交互的主要方式。中断出现时调度器就会发挥作用。这里的低级代码停止正在运行的进程,将其状态保存在内核进程结构中,并启动驱动程序。进程调度也会发生在内核完成一些操作并且启动用户进程的时候。图中的调度器是 dispatcher。

注意这里的调度器是 dispatcher 而不是 scheduler,这两者是有区别的

scheduler 和 dispatcher 都是和进程调度相关的概念,不同的是 scheduler 会从几个进程中随意选取一个进程;而 dispatcher 会给 scheduler 选择的进程分配 CPU。

然后,我们把内核系统分为三部分。

  • I/O 部分负责与设备进行交互以及执行网络和存储 I/O 操作的所有内核部分。

从图中可以看出 I/O 层次的关系,最高层是一个虚拟文件系统,也就是说不管文件是来自内存还是磁盘中,都是经过虚拟文件系统中的。从底层看,所有的驱动都是字符驱动或者块设备驱动。二者的主要区别就是是否允许随机访问。网络驱动设备并不是一种独立的驱动设备,它实际上是一种字符设备,不过网络设备的处理方式和字符设备不同。

上面的设备驱动程序中,每个设备类型的内核代码都不同。字符设备有两种使用方式,有一键式的比如 vi 或者 emacs ,需要每一个键盘输入。其他的比如 shell ,是需要输入一行按回车键将字符串发送给程序进行编辑。

网络软件通常是模块化的,由不同的设备和协议来支持。大多数 Linux 系统在内核中包含一个完整的硬件路由器的功能,但是这个不能和外部路由器相比,路由器上面是协议栈,包括 TCP/IP 协议,协议栈上面是 socket 接口,socket 负责与外部进行通信,充当了门的作用。

磁盘驱动上面是 I/O 调度器,它负责排序和分配磁盘读写操作,以尽可能减少磁头的无用移动。

  • I/O 右边的是内存部件,程序被装载进内存,由 CPU 执行,这里会涉及到虚拟内存的部件,页面的换入和换出是如何进行的,坏页面的替换和经常使用的页面会进行缓存。

  • 进程模块负责进程的创建和终止、进程的调度、Linux 把进程和线程看作是可运行的实体,并使用统一的调度策略来进行调度。

在内核最顶层的是系统调用接口,所有的系统调用都是经过这里,系统调用会触发一个 trap,将系统从用户态转换为内核态,然后将控制权移交给上面的内核部件。

Linux 操作系统!开篇!!!的更多相关文章

  1. Linux操作系统备份之三:通过二进制拷贝(dd)方式实现Linux操作系统数据的备份

    前面有两篇文章,<Linux操作系统备份之一:使用LVM快照实现Linux操作系统数据的在线备份>和<Linux操作系统备份之二:通过tar拷贝分区实现Linux操作数据的在线备份& ...

  2. Linux操作系统备份之二:通过tar拷贝分区实现Linux操作数据的在线备份

    http://www.tektea.com/archives/2163.html. 在<Linux操作系统备份之一:使用LVM快照实现Linux操作系统数据的在线备份>文章中,我们介绍了使 ...

  3. 安装linux操作系统

    安装双操作系统; 1 0. 介绍: 1 1 实验环境: 2 2. 实验准备: 2 3.开始安装: 2 1 制作U盘启动工具: 2 2.安装LinuxOS. 3 2.1在windowOS中划分60G空间 ...

  4. 在Linux操作系统下备份恢复技术的应用 转自https://yq.aliyun.com/articles/50205?spm=5176.100239.blogcont24250.9.CfBYE9

    摘要: 安全防护:在Linux操作系统下备份恢复技术的应用  原文参见:http://linux.chinaunix.net/techdoc/system/2005/12/19/925898.shtm ...

  5. 献给初学者:谈谈如何学习Linux操作系统

    本文出自 “技术成就梦想” 博客,请务必保留此出处http://ixdba.blog.51cto.com/2895551/569329. 为了能把这篇不错的文章分享给大家.所以请允许我暂时用原创的形式 ...

  6. Linux操作系统备份之一:使用LVM快照实现Linux操作系统数据的在线备份

    这里我们讨论Linux操作系统的备份. 在生产环境,客户都会要求做全系统的数据备份,用于系统崩溃后的一种恢复手段.这其中就包含操作系统数据的备份恢复. 由于是生产环境,客户都会要求备份不中断业务,也就 ...

  7. 安装Kali Linux操作系统Kali Linux无线网络渗透

    安装Kali Linux操作系统Kali Linux无线网络渗透 Kali Linux是一个基于Debian的Linux发行版,它的前身是BackTrack Linux发行版.在该操作系统中,自带了大 ...

  8. 使用 /proc 文件系统来访问 linux操作系统 内核的内容 && 虚拟文件系统vfs及proc详解

    http://blog.163.com/he_junwei/blog/static/19793764620152743325659/ http://www.01yun.com/other/201304 ...

  9. linux 操作系统下c语言编程入门

    2)Linux程序设计入门--进程介绍 3)Linux程序设计入门--文件操作 4)Linux程序设计入门--时间概念 5)Linux程序设计入门--信号处理 6)Linux程序设计入门--消息管理  ...

随机推荐

  1. Java实现 LeetCode 395 至少有K个重复字符的最长子串

    395. 至少有K个重复字符的最长子串 找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k .输出 T 的长度. 示例 1: 输入: s = " ...

  2. Java实现 蓝桥杯VIP 算法提高 扫雷

    算法提高 扫雷 时间限制:1.0s 内存限制:256.0MB 问题描述 扫雷游戏你一定玩过吧!现在给你若干个n×m的地雷阵,请你计算出每个矩阵中每个单元格相邻单元格内地雷的个数,每个单元格最多有8个相 ...

  3. java实现复制网站内容

    复制网站内容 复制代码 本程序将网站"www.baidu.com"首页的内容复制保存在文件test.html中.写了如下代码,请完善之: import java.net.*; im ...

  4. css背景图片加载失败,页面部分图标无法显示

    1.问题表现:首屏缺失部分图标.点击按钮切换为激活状态时,部分按钮的激活态图标无法显示. 2.问题原因:网络极差,断断续续,点击时添加class:active变为激活态, active.png这张图片 ...

  5. 用斗地主的实例学会使用java Collections工具类

    目录 一.背景 二.概念 1.定义 2.方法 2.1.排序方法 2.2.查找/替换方法 三.斗地主实例 3.1.代码结构 3.2.常量定义 3.3.单只牌类 3.4.玩家类 3.5.主程序 四.深入理 ...

  6. 树莓派使用 OLED 屏显示图片及文字

    树莓派默认是不带显示屏的,如果想要查看系统的一些信息,需要使用电脑登录到树莓派,或者通过 HDMI 连接外接显示器查看.这样做总是有点麻烦,我们可以通过外接一个 OLED 屏来显示一些关键参数或者图片 ...

  7. npm run dev启动项目,electron提示throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')

    npm run dev 项目,提示 throw new Error('Electron failed to install correctly, please delete node_modules/ ...

  8. iOS-字典转双模型的实现过程中需要关注的细节

    如果有以上结构的plist文件,那么应该怎么将其中的字典转换成模型? 显然一个模型已经无法搞定了,此时需要用到双数据模型(字典转模型). 我写了两种方式来实现模型的转换: 方法一 第一个模型:CarM ...

  9. 05-IntentFilter的匹配规则

    IntentFilter的匹配规则 原则上一个Intent不应该既是显示调用又是隐式调用,如果二者共存的话以显式调用为主 隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过 ...

  10. 利用BeanMap进行对象与Map的相互转换

    javabean与map的转换有很多种方式,比如: 1.通过ObjectMapper先将bean转换为json,再将json转换为map,但是这种方法比较绕,且效率很低,经测试,循环转换10000个b ...