此篇文章主要会带你介绍 Linux 操做系统,包括 Linux 自己、Linux 如何使用、以及系统调用和 Linux 是如何工做的。程序员
UNIX 是一个交互式系统,用于同时处理多进程和多用户同时在线。为何要说 UNIX,那是由于 Linux 是由 UNIX 发展而来的,UNIX 是由程序员设计,它的主要服务对象也是程序员。Linux 继承了 UNIX 的设计目标。从智能手机到汽车,超级计算机和家用电器,从家用台式机到企业服务器,Linux 操做系统无处不在。shell
大多数程序员都喜欢让系统尽可能简单,优雅并具备一致性。举个例子,从最底层的角度来说,一个文件应该只是一个字节集合。为了实现顺序存取、随机存取、按键存取、远程存取只能是妨碍你的工做。相同的,若是命令浏览器
ls A*
复制代码
意味着只列出以 A 为开头的全部文件,那么命令缓存
rm A*
复制代码
应该会移除全部以 A 为开头的文件而不是只删除文件名是 A*
的文件。这个特性也是最小吃惊原则(principle of least surprise)
bash
最小吃惊原则一半经常使用于用户界面和软件设计。它的原型是:该功能或者特征应该符合用户的预期,不该该使用户感到惊讶和震惊。服务器
一些有经验的程序员一般但愿系统具备较强的功能性和灵活性。设计 Linux 的一个基本目标是每一个应用程序只作一件事情并把他作好。因此编译器只负责编译的工做,编译器不会产生列表,由于有其余应用比编译器作的更好。网络
不少人都不喜欢冗余,为何在 cp 就能描述清楚你想干何时还使用 copy?这彻底是在浪费宝贵的 hacking time
。为了从文件中提取全部包含字符串 ard
的行,Linux 程序员应该输入并发
grep ard f
复制代码
Linux 系统是一种金字塔模型的系统,以下所示socket
应用程序发起系统调用把参数放在寄存器中(有时候放在栈中),并发出 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 操做系统能够由下面这几部分构成
引导程序(Bootloader)
:引导程序是管理计算机启动过程的软件,对于大多数用户而言,只是弹出一个屏幕,但其实内部操做系统作了不少事情内核(Kernel)
:内核是操做系统的核心,负责管理 CPU、内存和外围设备等。初始化系统(Init System)
:这是一个引导用户空间并负责控制守护程序的子系统。一旦从引导加载程序移交了初始引导,它就是用于管理引导过程的初始化系统。后台进程(Daemon)
:后台进程顾名思义就是在后台运行的程序,好比打印、声音、调度等,它们能够在引导过程当中启动,也能够在登陆桌面后启动图形服务器(Graphical server)
:这是在监视器上显示图形的子系统。一般将其称为 X 服务器或 X。桌面环境(Desktop environment)
:这是用户与之实际交互的部分,有不少桌面环境可供选择,每一个桌面环境都包含内置应用程序,好比文件管理器、Web 浏览器、游戏等应用程序(Applications)
:桌面环境不提供完整的应用程序,就像 Windows 和 macOS 同样,Linux 提供了成千上万个能够轻松找到并安装的高质量软件。尽管 Linux 应用程序提供了 GUI ,可是大部分程序员仍偏好于使用命令行(command-line interface)
,称为shell
。用户一般在 GUI 中启动一个 shell 窗口而后就在 shell 窗口下进行工做。
shell 命令行使用速度快、功能更强大、并且易于扩展、而且不会带来肢体重复性劳损(RSI)
。
下面会介绍一些最简单的 bash shell。当 shell 启动时,它首先进行初始化,在屏幕上输出一个 提示符(prompt)
,一般是一个百分号或者美圆符号,等待用户输入
等用户输入一个命令后,shell 提取其中的第一个词,这里的词指的是被空格或制表符分隔开的一连串字符。假定这个词是将要运行程序的程序名,那么就会搜索这个程序,若是找到了这个程序就会运行它。而后 shell 会将本身挂起直到程序运行完毕,以后再尝试读入下一条指令。shell 也是一个普通的用户程序。它的主要功能就是读取用户的输入和显示计算的输出。shell 命令中能够包含参数,它们做为字符串传递给所调用的程序。好比
cp src dest
复制代码
会调用 cp 应用程序并包含两个参数 src
和 dest
。这个程序会解释第一个参数是一个已经存在的文件名,而后建立一个该文件的副本,名称为 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]*
复制代码
会列举出全部以 a
、b
或者 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 命令的学习网站:www.shellscript.sh/
shell 脚本其实也是一段程序,shell 脚本中能够对变量进行赋值,也包含循环控制语句好比 if、for、while 等,shell 的设计目标是让其看起来和 C 类似(There is no doubt that C is father)。因为 shell 也是一个用户程序,因此用户能够选择不一样的 shell。
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
能够容许全部者改变访问权限;文件目录的的建立和删除可使用 mkdir
和 rmdir
命令;使用 ls
能够查看目录文件,ls 能够显示不少属性,好比大小、用户、建立日期等;sort 决定文件的显示顺序
Linux 应用程序还包括过滤器 grep,grep
从标准输入或者一个或多个输入文件中提取特定模式的行;sort
将输入进行排序并输出到标准输出;head
提取输入的前几行;tail 提取输入的后面几行;除此以外的过滤器还有 cut
和 paste
,容许对文本行的剪切和复制;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 的内核结构
内核直接坐落在硬件上,内核的主要做用就是 I/O 交互、内存管理和控制 CPU 访问。上图中还包括了 中断
和 调度器
,中断是与设备交互的主要方式。中断出现时调度器就会发挥做用。这里的低级代码中止正在运行的进程,将其状态保存在内核进程结构中,并启动驱动程序。进程调度也会发生在内核完成一些操做而且启动用户进程的时候。图中的调度器是 dispatcher。
注意这里的调度器是
dispatcher
而不是scheduler
,这二者是有区别的scheduler 和 dispatcher 都是和进程调度相关的概念,不一样的是 scheduler 会从几个进程中随意选取一个进程;而 dispatcher 会给 scheduler 选择的进程分配 CPU。
而后,咱们把内核系统分为三部分。
从图中能够看出 I/O 层次的关系,最高层是一个虚拟文件系统
,也就是说无论文件是来自内存仍是磁盘中,都是通过虚拟文件系统中的。从底层看,全部的驱动都是字符驱动或者块设备驱动。两者的主要区别就是是否容许随机访问。网络驱动设备并非一种独立的驱动设备,它其实是一种字符设备,不过网络设备的处理方式和字符设备不一样。
上面的设备驱动程序中,每一个设备类型的内核代码都不一样。字符设备有两种使用方式,有一键式
的好比 vi 或者 emacs ,须要每个键盘输入。其余的好比 shell ,是须要输入一行按回车键将字符串发送给程序进行编辑。
网络软件一般是模块化的,由不一样的设备和协议来支持。大多数 Linux 系统在内核中包含一个完整的硬件路由器的功能,可是这个不能和外部路由器相比,路由器上面是协议栈
,包括 TCP/IP 协议,协议栈上面是 socket 接口,socket 负责与外部进行通讯,充当了门的做用。
磁盘驱动上面是 I/O 调度器,它负责排序和分配磁盘读写操做,以尽量减小磁头的无用移动。
I/O 右边的是内存部件,程序被装载进内存,由 CPU 执行,这里会涉及到虚拟内存的部件,页面的换入和换出是如何进行的,坏页面的替换和常用的页面会进行缓存。
进程模块负责进程的建立和终止、进程的调度、Linux 把进程和线程看做是可运行的实体,并使用统一的调度策略来进行调度。
在内核最顶层的是系统调用接口,全部的系统调用都是通过这里,系统调用会触发一个 trap,将系统从用户态转换为内核态,而后将控制权移交给上面的内核部件。