mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-17 06:33:48 +08:00
del
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
<audio id="audio" title="03 | 你可以把Linux内核当成一家软件外包公司的老板" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/50/9a/50def8552f29579af4fe4dd67af1a39a.mp3"></audio>
|
||||
|
||||
在平时的生活中,我们几乎时时刻刻都在使用操作系统,只是大部分时间你都意识不到它的存在。比如你买了一部手机或者一台平板电脑,立马就能上手使用,这是因为它们里面都预先安装了操作系统。
|
||||
|
||||
所以啊,哪有什么岁月静好,只不过有人替你负重前行。而操作系统就扮演了这样一个负重前行的角色。那操作系统到底在背后默默地做了哪些事情,才能让我们轻松地使用这些电子设备呢?要想回答这个问题,我们需要把眼光放回到自己攒电脑的那个时代。
|
||||
|
||||
## 电脑组装好就能直接用吗?
|
||||
|
||||
那时候买电脑,经常是这样一个情景:三五个哥们儿一起来到电脑城,呼啦呼啦采购了一大堆硬件,有密密麻麻都是针脚的**CPU**;有铺满各种复杂电路的一块板子,也就是**主板**;还需要买块**显卡**,用来连接显示器;还需要买个**网卡**,里面可以插网线;还要买块**硬盘**,将来用来存放文件;然后还需要一大堆**线**,将这些设备和主板连接起来;最终再来一个**鼠标**,一个**键盘**,还有一个**显示器**。设备差不多啦,准备开整!
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ed/45/ed03667738a92d66626914fe5dc78d45.png" alt="">
|
||||
|
||||
好不容易组装完这一大堆硬件,还是不能直接用,你还需要安装一个操作系统。安装操作系统也是一件非常复杂的事,一点儿也不亚于把刚才那堆东西组装起来。这个安装过程可能会涉及十几个步骤、几十项配置。每一步骤配置完了,点击下一步,会出现个进度条。伴随着一堆难以理解的描述,最终安装步骤到达百分之百,才出现你熟悉的那个界面。
|
||||
|
||||
我这么说起来好像很容易,但是要把这事儿讲清楚估计得用一个专栏。这个复杂程度,咱们父母估计是上不了手了。所以,那个时候,能把这套东西都组装起来,是一件很拉风的事情。很多IT男甚至因为这项绝技“泡”到了妹子。
|
||||
|
||||
当操作系统安装完毕的时候,我妈通常会要求我一定要装一个QQ。看到妈妈在你装好的操作系统前愉快地和她的朋友聊天,这时候,经历过以上过程的你,多少应该能感受到操作系统的厉害了。
|
||||
|
||||
**操作系统究竟是如何把这么多套复杂的东西管理起来,从而弄出来一个简单到父母都会用的东西呢?**
|
||||
|
||||
很多事情就怕细想。不知道你有没有产生过这些疑问:
|
||||
|
||||
<li>
|
||||
桌面上的图标到底是啥?凭啥我在鼠标上一双击,就会出来一个美丽的画面?这都是从哪里跑出来的?
|
||||
</li>
|
||||
<li>
|
||||
凭什么我在键盘上噼里啪啦地敲,某个位置就会显示我想要的那些字符?
|
||||
</li>
|
||||
<li>
|
||||
电脑怎么知道我鼠标点击的是这个地方,又是怎么知道我要输入的是这个地方?
|
||||
</li>
|
||||
<li>
|
||||
我在键盘上点“a”,是谁在显示器上画出“a”这个图像呢?
|
||||
</li>
|
||||
<li>
|
||||
为什么我一回车,这些字符就发到遥远的另外一台机器上去了?
|
||||
</li>
|
||||
|
||||
对于普通用户来讲,其实只要会用就行了,但是咱们作为专业人士,要深入探究一下背后的答案。你别小看“双击鼠标打开聊天软件”这样一个简单的操作,它几乎涵盖了操作系统的所有功能。我们就从这个熟悉的操作,来认识陌生的操作系统。
|
||||
|
||||
操作系统其实就像一个软件外包公司,其内核就相当于这家外包公司的老板。所以接下来的整个课程中,请你将自己的角色切换成这家软件外包公司的老板,设身处地地去理解操作系统是如何协调各种资源,帮客户做成事情的。
|
||||
|
||||
想要学好咱们这门课,你要牢牢记住这段话,把这个概念牢牢扎根在心里,我之后的讲解都会基于此,帮你理解、记忆那些难搞的概念和原理。
|
||||
|
||||
同时,为了防止你混淆,我这里先强调一下。今后我所说的“用户”,都是指操作系统的用户,“客户”则是指外包公司的客户,这两者是对应的。
|
||||
|
||||
## “双击QQ”这个过程,都需要用到哪些硬件?
|
||||
|
||||
好,现在用户开始对着屏幕上的QQ图标双击鼠标了。
|
||||
|
||||
**鼠标和键盘**是计算机的**输入设备**。大部分的普通用户想要告诉计算机应该做什么,都是通过这两个设备。例如,用户移动了一下鼠标,鼠标就会通过鼠标线给电脑发消息,告知电脑,鼠标向某个方向移动了多少距离。
|
||||
|
||||
如果是一家外包公司,怎么才能知道客户的需求呢?你需要配备销售、售前等角色,专门负责和客户对接,把客户需求拿回来,我们把这些人统称为**客户对接员**。你可以跟客户说,有什么事儿都找对接员。
|
||||
|
||||
屏幕,也就是**显示器**,是计算机的**输出设备**,将计算机处理用户请求后的结果展现给客户,要不然用户无法知道自己的请求是不是到达并且执行了。
|
||||
|
||||
显示器上面显示的东西是由**显卡**控制的。无论是显示器还是显卡,这里都有个“坐标”的概念,也就是说,什么图像在哪个坐标,都是定义好了才画上去的。本来在某个坐标画了一个鼠标箭头,当接到鼠标移动的事件之后,你应该按相同的方向,按照一定的比例(鼠标灵敏度),在屏幕的某个坐标再画一个鼠标箭头。
|
||||
|
||||
作为外包公司,当客户给你提了需求,不管你做还是不做,最终做成什么样,你都需要给客户反馈,所以你要配备交付人员,将做好的需求展示给他们看。
|
||||
|
||||
在操作系统中,**输入设备驱动**其实就是**客户对接员**。有时候新插上一个鼠标的时候,会弹出一个通知你安装驱动,这就是操作系统这家外包公司给你配备对接人员呢。当客户告诉对接员需求的时候,对于操作系统来讲,输入设备会发送一个中断。这个概念很好理解。客户肯定希望外包公司把正在做的事情都停下来服务它。所以,这个时候客户发送的需求就被称为**中断事件**(Interrupt Event)。
|
||||
|
||||
显卡会有**显卡驱动**,在操作系统中称为**输出设备驱动**,也就是上面说的**交付人员**。
|
||||
|
||||
## 从点击QQ图标,看操作系统全貌
|
||||
|
||||
有了**客户对接员**和**交付人员**,外包公司就可以处理用户“在桌面上点击QQ图标”的事件了。
|
||||
|
||||
首先,鼠标双击会触发一个中断,这相当于客户告知客户对接员“有了新需求,需要处理一下”。你会事先把处理这种问题的方法教给客户对接员。在操作系统里面就是调用中断处理函数。操作系统发现双击的是一个图标,就明白了用户的原始诉求,准备运行QQ和别人聊天。
|
||||
|
||||
你会发现,运行QQ是一件大事,因为将来的一段时间,用户要一直和QQ进行交互。这就相当于你们公司接了一个大单,而不是处理零星的客户需求,这个时候应该单独立项。一旦立了项,以后与这个项目有关的事情,都由这个项目组来处理。
|
||||
|
||||
立项可不能随便立,一定要有一个**项目执行计划书**,说明这个项目打算怎么做,一步一步如何执行,遇到什么情况应该怎么办等等。换句话说,对QQ这个程序来说,它能做哪些事情,每件事情怎么做,先做啥后做啥,都已经作为程序逻辑写在程序里面,并且编译成为二进制了。这个程序就相当于项目执行计划书。
|
||||
|
||||
电脑上的程序有很多,什么有道云笔记的程序、Word程序等等,它们都以二进制文件的形式保存在硬盘上。硬盘是个物理设备,要按照规定格式化成为文件系统,才能存放这些程序。文件系统需要一个系统进行统一管理,称为**文件管理子系统**(File Management Subsystem)。
|
||||
|
||||
对于你们公司,项目立得多了,项目执行计划书也会很多,同样需要有个统一保存文件的档案库,而且需要有序地管理起来。
|
||||
|
||||
当你从资料库里面拿到这个项目执行计划书,接下来就需要开始执行这个项目了。项目执行计划书是静态的,项目的执行是动态的。
|
||||
|
||||
同理,当操作系统拿到QQ的二进制执行文件的时候,就可以运行这个文件了。QQ的二进制文件是静态的,称为**程序**(Program),而运行起来的QQ,是不断进行的,称为**进程**(Process)。
|
||||
|
||||
说了这么多,怎样才能立项呢?你会发现,一个项目要想顺畅进行,需要用到公司的各种资源,比如说盖个公章、开个证明、申请个会议室、打印个材料等等。这里有个两难的权衡,一方面,资源毕竟是有限的,甚至是涉及机密的,不能由项目组滥取滥用;另一方面,就是效率,咱是一个私营企业,保证项目申请资源的时候只跑一次,这样才能比较高效。
|
||||
|
||||
为了平衡这一点,一方面涉及核心权限的资源,还是应该被公司严格把控,审批了才能用;另外一方面,为了提高效率,最好有个统一的办事大厅,明文列出提供哪些服务,谁需要可以来申请,然后就会有回应。
|
||||
|
||||
在操作系统中,也有同样的问题,例如多个进程都要往打印机上打印文件,如果随便乱打印进程,就会出现同样一张纸,第一行是A进程输出的文字,第二行是B进程输出的文字,全乱套了。所以,打印机的直接操作是放在操作系统内核里面的,进程不能随便操作。但是操作系统也提供一个办事大厅,也就是**系统调用**(System Call)。
|
||||
|
||||
系统调用也能列出来提供哪些接口可以调用,进程有需要的时候就可以去调用。这其中,立项是办事大厅提供的关键服务之一。同样,任何一个程序要想运行起来,就需要调用系统调用,创建进程。
|
||||
|
||||
一旦项目正式立项,就要开始执行,就要成立项目组,将开发人员分配到这个项目组,按照项目执行计划书一步一步执行。为了管理这个项目,我们还需要一个项目经理、一套项目管理流程、一个项目管理系统,例如程序员比较熟悉的Jira。如果项目多,可能一个开发人员需要同时执行多个项目,这就要考验项目经理的调度能力了。
|
||||
|
||||
在操作系统中,进程的执行也需要分配CPU进行执行,也就是按照程序里面的二进制代码一行一行地执行。于是,为了管理进程,我们还需要一个**进程管理子系统**(Process Management Subsystem)。如果运行的进程很多,则一个CPU会并发运行多个进程,也就需要CPU的调度能力了。
|
||||
|
||||
每个项目都有自己的私密资料,这些资料不能被其他项目组看到。这些资料主要是项目在执行的过程中,产生的很多中间成果,例如架构图、流程图。
|
||||
|
||||
执行过程中,难免要在白板上或者本子上写写画画,如果不同项目的办公空间不隔离,一方面,项目的私密性不能得到保证,A项目的细节,B项目也能看到;另一方面,项目之间会相互干扰,A项目组的人刚在白板上画了一个架构图,出去上个厕所,结果B项目组的人就给擦了。
|
||||
|
||||
如果把不同的项目组分配到不同的会议室,就解决了这个问题。当然会议室是有限的,需要有人管理和分配,并且需要一个**会议室管理系统**。
|
||||
|
||||
在操作系统中,不同的进程有不同的内存空间,但是整个电脑内存就这么点儿,所以需要统一的管理和分配,这就需要**内存管理子系统**(Memory Management Subsystem)。
|
||||
|
||||
如果想直观地了解QQ如何使用CPU和内存,可以打开任务管理器,你就能看到QQ这个进程耗费的CPU和内存。
|
||||
|
||||
项目执行的时候,有了一定的成果,就要给客户演示。例如客户说要做个应用,我们做出来了要给客户看看,如果客户说哪里需要改,可以根据客户的需求再改,这就需要交付人员了。
|
||||
|
||||
QQ启动之后,有一部分代码会在显示器上画一个对话框,并且将键盘的焦点放在了输入框里面。CPU根据这些指令,就会告知显卡驱动程序,将这个对话框画出来。
|
||||
|
||||
于是使用QQ的用户就会很开心地发现,他能和别人开始聊天了。
|
||||
|
||||
当用户通过键盘噼里啪啦打字的时候,键盘也是输入设备,也会触发中断,通知相应的输入设备驱动程序。
|
||||
|
||||
我们假设用户输入了一个“a”。这就像客户提出了新的需求给客户对接员。客户对接员收到需求后,因为是对接这个项目的,所以就回来报告,客户提新需求了,项目组需要处理一下。项目执行计划书里面一般都会有当遇到何种需求应该怎么做的规定,项目组就按这个规定做了,然后让交付人员再去客户那里演示就行了。
|
||||
|
||||
对于QQ来讲,由于键盘闪啊闪的焦点在QQ这个对话框上,因而操作系统知道,这个事件是给这个进程的。QQ的代码里面肯定有遇到这种事件如何处理的代码,就会执行。一般是记录下客户的输入,并且告知显卡驱动程序,在那个地方画一个“a”。显卡画完了,客户看到了,就觉得自己的输入成功了。
|
||||
|
||||
当用户输入完毕之后,回车一下,还是会通过键盘驱动程序告诉操作系统,操作系统还是会找到QQ,QQ会将用户的输入发送到网络上。QQ进程是不能直接发送网络包的,需要调用系统调用,内核使用网卡驱动程序进行发送。
|
||||
|
||||
这就像客户对接员接到一个需求,但是这个需求需要和其他公司沟通,这就需要依靠公司的对外合作部,对外合作部在办事大厅有专门的窗口,非常方便。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e1/4a/e15954f1371a4c782f028202dce1f84a.jpeg" alt="">
|
||||
|
||||
## 总结时刻
|
||||
|
||||
到这里,一个外包公司大部分的职能部门都凑齐了。你可以对应着下图的操作系统内核体系结构,回顾一下它们是如何组成一家公司的。
|
||||
|
||||
QQ的运行过程,只是一个简单的比喻。在后面的章节中,我会展开讲述每个部分是怎么工作的,最后我会再将这个过程串起来,这样你就能了解操作系统的全貌了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/21/f5/21a9afd64b05cf1ffc87b74515d1d4f5.jpeg" alt="">
|
||||
|
||||
## 课堂练习
|
||||
|
||||
学习Linux,看代码是必须的。你可以找到最新版本的Linux代码,在里面找找,这几个子系统的代码都在哪里。
|
||||
|
||||
欢迎留言和我分享你的思考和疑问,也欢迎你把今天的内容分享给你的朋友,和他一起学习、进步。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8c/37/8c0a95fa07a8b9a1abfd394479bdd637.jpg" alt="">
|
||||
@@ -0,0 +1,292 @@
|
||||
<audio id="audio" title="04 | 快速上手几个Linux命令:每家公司都有自己的黑话" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/de/18/de5b946452e8ab098fe6292e90372018.mp3"></audio>
|
||||
|
||||
如果你还没有上手用过Linux,那么接下来的课程,你可能会感受到困惑。因为没有一手的体验,你可能很难将Linux的机制和你的使用行为关联起来。所以这一节,咱们先介绍几个上手Linux的命令,通过这些命令,我们试试先把Linux用起来。
|
||||
|
||||
为什么我把Linux命令称为“黑话”呢?就像上一节我们介绍的,Linux操作系统有很多功能,我们有很多种方式可以使用这些功能,其中最简单和直接的方式就是**命令行**(Command Line)。命令行就相当于你请求服务使用的专业术语。干任何事情,第一步就是学会使用正确的术语。这样,Linux作为服务方,才能听懂。这些术语可不就是“黑话”吗?
|
||||
|
||||
Window系统你肯定很熟悉吧?现在,我就沿着你使用Windows的习惯,来给你介绍相应的Linux命令。
|
||||
|
||||
## 用户与密码
|
||||
|
||||
当我们打开一个新系统的时候,第一件要做的事就是登录。系统默认有一个Administrator用户,也就是系统管理员,它的权限很大,可以在这个系统上干任何事。Linux上面也有一个类似的用户,我们叫Root。同样,它也具有最高的操作权限。
|
||||
|
||||
接下来,你需要输入密码了。密码从哪里来呢?对于Windows来讲,在你安装操作系统的过程中,会让你设置一下Administrator的密码;对于Linux,Root的密码同样也是在安装过程中设置的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ee/76/ee95d03b1390ae08ca9c752621b03476.png" alt="">
|
||||
|
||||
对于Windows,你设好之后,可以多次修改这个密码。比如说,我们在控制面板的账户管理里面就可以完成这个操作。但是对于Linux呢?不好意思,没有这么一个统一的配置中心了。你需要使用命令来完成这件事情。这个命令很好记,passwd,其实就是password的简称。
|
||||
|
||||
```
|
||||
# passwd
|
||||
Changing password for user root.
|
||||
New password:
|
||||
|
||||
```
|
||||
|
||||
按照这个命令,我们就可以输入新密码啦。
|
||||
|
||||
在Windows里,除了Administrator之外,我们还可以创建一个以自己名字命名的用户。那在Linux里可不可以创建其他用户呢?当然可以了,我们同样需要一个命令useradd。
|
||||
|
||||
```
|
||||
useradd cliu8
|
||||
|
||||
```
|
||||
|
||||
执行这个命令,一个用户就被创建了。它不会弹出什么让你输入密码之类的页面,就会直接返回了。因为接下来你需要自己调用passwd cliu8来设置密码,再进行登录。
|
||||
|
||||
在Windows里设置用户的时候,用户有一个“组”的概念。你可能没注意过,不过我一说名字你估计就能想起来了,比如“Adminsitrator组”“Guests组”“Power User组”等等。同样,Linux里也是分组的。前面我们创建用户的时候,没有说加入哪个组,于是默认就会创建一个同名的组。
|
||||
|
||||
能不能在创建用户的时候就指定属于哪个组呢?我们来试试。我们可以使用-h参数看一下,使用useradd这个命令,有没有相应的选项。
|
||||
|
||||
```
|
||||
[root@deployer ~]# useradd -h
|
||||
Usage: useradd [options] LOGIN
|
||||
useradd -D
|
||||
useradd -D [options]
|
||||
|
||||
|
||||
Options:
|
||||
-g, --gid GROUP name or ID of the primary group of the new account
|
||||
|
||||
```
|
||||
|
||||
一看还真有这个选项。以后命令不会用的时候,就可以通过-h参数看一下,它的意思是help。
|
||||
|
||||
如果想看更加详细的文档,你可以通过man useradd获得,细细阅读。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/17/2d/179b8fdca3d8d57e8f1d32f3aab60a2d.png" alt="">
|
||||
|
||||
上一节我们说过,Linux里是“命令行+文件”模式。对于用户管理来说,也是一样的。咱们通过命令创建的用户,其实是放在/etc/passwd文件里的。这是一个文本文件。我们可以通过cat命令,将里面的内容输出在命令行上。组的信息我们放在/etc/group文件中。
|
||||
|
||||
```
|
||||
# cat /etc/passwd
|
||||
root:x:0:0:root:/root:/bin/bash
|
||||
......
|
||||
cliu8:x:1000:1000::/home/cliu8:/bin/bash
|
||||
|
||||
|
||||
# cat /etc/group
|
||||
root:x:0:
|
||||
......
|
||||
cliu8:x:1000:
|
||||
|
||||
```
|
||||
|
||||
在/etc/passwd文件里,我们可以看到root用户和咱们刚创建的cliu8用户。x的地方应该是密码,密码当然不能放在这里,不然谁都知道了。接下来是用户ID和组ID,这和/etc/group里面就对应上了。
|
||||
|
||||
/root和/home/cliu8是什么呢?它们分别是root用户和cliu8用户的主目录。主目录是用户登录进去后默认的路径。其实Windows里面也是这样的。当我们打开文件夹浏览器的时候,左面会有“文档”“图片”“下载”等文件夹,路径在C:\Users\cliu8下面。要注意,同一台电脑,不同的用户情况会不一样。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d2/a7/d21ce3cd2ade7b71300df6a805b45aa7.png" alt="">
|
||||
|
||||
/bin/bash的位置是用于配置登录后的默认交互命令行的,不像Windows,登录进去是界面,其实就是explorer.exe。而Linux登录后的交互命令行是一个解析脚本的程序,这里配置的是/bin/bash。
|
||||
|
||||
## 浏览文件
|
||||
|
||||
终于登录进来啦,接下来你可以在文件系统里面随便逛一逛、看一看了。
|
||||
|
||||
可以看到,Linux的文件系统和Windows是一样的,都是用文件夹把文件组织起来,形成一个树形的结构。这一点没有什么差别。只不过在Linux下面,大多数情况,我们需要通过命令行来查看Linux的文件。
|
||||
|
||||
其实在Windows下也有命令行,例如cd就是change directory,就是切换目录;cd .表示切换到当前目录;cd ..表示切换到上一级目录;使用dir,可以列出当前目录下的文件。Linux基本也是这样,只不过列出当前目录下的文件我们用的是ls,意思是list。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/27/2e/27cc0efe8d33b730eba8aee7d51cda2e.png" alt="">
|
||||
|
||||
我们常用的是ls -l,也就是用列表的方式列出文件。
|
||||
|
||||
```
|
||||
# ls -l
|
||||
drwxr-xr-x 6 root root 4096 Oct 20 2017 apt
|
||||
-rw-r--r-- 1 root root 211 Oct 20 2017 hosts
|
||||
|
||||
```
|
||||
|
||||
其中第一个字段的第一个字符是**文件类型**。如果是“-”,表示普通文件;如果是d,就表示目录。当然还有很多种文件类型,咱们后面遇到的时候再说,你现在先记住我说的这两个就行了。
|
||||
|
||||
第一个字段剩下的9个字符是**模式**,其实就是**权限位**(access permission bits)。3个一组,每一组rwx表示“读(read)”“写(write)”“执行(execute)”。如果是字母,就说明有这个权限;如果是横线,就是没有这个权限。
|
||||
|
||||
这三组分别表示文件所属的用户权限、文件所属的组权限以及其他用户的权限。例如,上面的例子中,-rw-r–r--就可以翻译为,这是一个普通文件,对于所属用户,可读可写不能执行;对于所属的组,仅仅可读;对于其他用户,也是仅仅可读。如果想改变权限,可以使用命令chmod 711 hosts。
|
||||
|
||||
第二个字段是**硬链接**(hard link)**数目**,这个比较复杂,讲文件的时候我会详细说。
|
||||
|
||||
第三个字段是**所属用户**,第四个字段是**所属组**。第五个字段是文件的大小,第六个字段是**文件被修改的日期**,最后是**文件名**。你可以通过命令chown改变所属用户,chgrp改变所属组。
|
||||
|
||||
## 安装软件
|
||||
|
||||
好了,你现在应该会浏览文件夹了,接下来应该做什么呢?当然是开始安装那些“装机必备”的软件啦!
|
||||
|
||||
在Windows下面,在没有类似软件管家的软件之前,我们其实都是在网上下载installer,然后再进行安装的。
|
||||
|
||||
就以我们经常要安装的JDK为例子。应该去哪里下载呢?为了安全起见,一般去官网比较好。如果你去JDK的官网,它会给你一个这样的列表。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/5e/02/5e54fe2dba0e86e14a7a92d9ea46c202.jpg" alt="">
|
||||
|
||||
对于Windows系统,最方便的方式就是下载exe,也就是安装文件。下载后我们直接双击安装即可。
|
||||
|
||||
对于Linux来讲,也是类似的方法,你可以下载rpm或者deb。这个就是Linux下面的安装包。为什么有两种呢?因为Linux现在常用的有两大体系,一个是CentOS体系,一个是Ubuntu体系,前者使用rpm,后者使用deb。
|
||||
|
||||
在Linux上面,没有双击安装这一说,因此想要安装,我们还得需要命令。CentOS下面使用rpm -i jdk-XXX_linux-x64_bin.rpm进行安装,Ubuntu下面使用dpkg -i jdk-XXX_linux-x64_bin.deb。其中-i就是install的意思。
|
||||
|
||||
在Windows下面,控制面板里面有程序管理,我们可以查看目前安装了哪些软件,可以删除这些软件。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4c/9b/4c0cddd6f5ea77bc4aeabc135e6e8a9b.png" alt="">
|
||||
|
||||
在Linux下面,凭借rpm -qa和dpkg -l就可以查看安装的软件列表,-q就是query,a就是all,-l的意思就是list。
|
||||
|
||||
如果真的去运行的话,你会发现这个列表很长很长,很难找到你安装的软件。如果你知道要安装的软件包含某个关键词,可以用一个很好用的搜索工具grep。
|
||||
|
||||
rpm -qa | grep jdk,这个命令是将列出来的所有软件形成一个输出。| 是管道,用于连接两个程序,前面rpm -qa的输出就放进管道里面,然后作为grep的输入,grep将在里面进行搜索带关键词jdk的行,并且输出出来。grep支持正则表达式,因此搜索的时候很灵活,再加上管道,这是一个很常用的模式。同理dpkg -l | grep jdk也是能够找到的。
|
||||
|
||||
如果你不知道关键词,可以使用rpm -qa | more和rpm -qa | less这两个命令,它们可以将很长的结果分页展示出来。这样你就可以一个个来找了。
|
||||
|
||||
我们还是利用管道的机制。more是分页后只能往后翻页,翻到最后一页自动结束返回命令行,less是往前往后都能翻页,需要输入q返回命令行,q就是quit。
|
||||
|
||||
如果要删除,可以用rpm -e和dpkg -r。-e就是erase,-r就是remove。
|
||||
|
||||
我们刚才说的都是没有软件管家的情况,后来Windows上有了软件管家,就方便多了。我们直接搜索一下,然后点击安装就行了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4c/9b/4c0cddd6f5ea77bc4aeabc135e6e8a9b.png" alt="">
|
||||
|
||||
Linux也有自己的软件管家,CentOS下面是yum,Ubuntu下面是apt-get。
|
||||
|
||||
你可以根据关键词搜索,例如搜索jdk、yum search jdk和apt-cache search jdk,可以搜索出很多很多可以安装的jdk版本。如果数目太多,你可以通过管道grep、more、less来进行过滤。
|
||||
|
||||
选中一个之后,我们就可以进行安装了。你可以用yum install java-11-openjdk.x86_64和apt-get install openjdk-9-jdk来进行安装。
|
||||
|
||||
安装以后,如何卸载呢?我们可以使用yum erase java-11-openjdk.x86_64和apt-get purge openjdk-9-jdk。
|
||||
|
||||
Windows上的软件管家会有一个统一的服务端,来保存这些软件,但是我们不知道服务端在哪里。而Linux允许我们配置从哪里下载这些软件的,地点就在配置文件里面。
|
||||
|
||||
对于CentOS来讲,配置文件在/etc/yum.repos.d/CentOS-Base.repo里。
|
||||
|
||||
```
|
||||
[base]
|
||||
name=CentOS-$releasever - Base - 163.com
|
||||
baseurl=http://mirrors.163.com/centos/$releasever/os/$basearch/
|
||||
gpgcheck=1
|
||||
gpgkey=http://mirrors.163.com/centos/RPM-GPG-KEY-CentOS-7
|
||||
|
||||
|
||||
```
|
||||
|
||||
对于Ubuntu来讲,配置文件在/etc/apt/sources.list里。
|
||||
|
||||
```
|
||||
deb http://mirrors.163.com/ubuntu/ xenial main restricted universe multiverse
|
||||
deb http://mirrors.163.com/ubuntu/ xenial-security main restricted universe multiverse
|
||||
deb http://mirrors.163.com/ubuntu/ xenial-updates main restricted universe multiverse
|
||||
deb http://mirrors.163.com/ubuntu/ xenial-proposed main restricted universe multiverse
|
||||
deb http://mirrors.163.com/ubuntu/ xenial-backports main restricted universe multiverse
|
||||
|
||||
```
|
||||
|
||||
这里为什么都是163.com呢?因为Linux服务器遍布全球,不能都从一个地方下载,最好选一个就近的地方下载,例如在中国,选择163.com,就不用跨越重洋了。
|
||||
|
||||
**其实无论是先下载再安装,还是通过软件管家进行安装,都是下载一些文件,然后将这些文件放在某个路径下,然后在相应的配置文件中配置一下。**例如,在Windows里面,最终会变成C:\Program Files下面的一个文件夹以及注册表里面的一些配置。对应Linux里面会放的更散一点。例如,主执行文件会放在/usr/bin或者/usr/sbin下面,其他的库文件会放在/var下面,配置文件会放在/etc下面。
|
||||
|
||||
所以其实还有一种简单粗暴的方法,就是将安装好的路径直接下载下来,然后解压缩成为一个整的路径。在JDK的安装目录中,Windows有jdk-XXX_Windows-x64_bin.zip,这是Windows下常用的压缩模式。Linux有jdk-XXX_linux-x64_bin.tar.gz,这是Linux下常用的压缩模式。
|
||||
|
||||
如何下载呢?Linux上面有一个工具wget,后面加上链接,就能从网上下载了。
|
||||
|
||||
下载下来后,我们就可以进行解压缩了。Windows下可以有winzip之类的解压缩程序,Linux下面默认会有tar程序。如果是解压缩zip包,就需要另行安装。
|
||||
|
||||
```
|
||||
yum install zip.x86_64 unzip.x86_64
|
||||
apt-get install zip unzip
|
||||
|
||||
```
|
||||
|
||||
如果是tar.gz这种格式的,通过tar xvzf jdk-XXX_linux-x64_bin.tar.gz就可以解压缩了。
|
||||
|
||||
对于Windows上jdk的安装,如果采取这种下载压缩包的格式,需要在系统设置的环境变量配置里面设置JAVA_HOME和PATH。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ab/be/ab4e83ac1300658649989a2e016ac0be.png" alt="">
|
||||
|
||||
在Linux也是一样的,通过tar解压缩之后,也需要配置环境变量,可以通过export命令来配置。
|
||||
|
||||
```
|
||||
export JAVA_HOME=/root/jdk-XXX_linux-x64
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
|
||||
```
|
||||
|
||||
export命令仅在当前命令行的会话中管用,一旦退出重新登录进来,就不管用了,有没有一个地方可以像Windows里面可以配置永远管用呢?
|
||||
|
||||
在当前用户的默认工作目录,例如/root或者/home/cliu8下面,有一个.bashrc文件,这个文件是以点开头的,这个文件默认看不到,需要ls -la才能看到,a就是all。每次登录的时候,这个文件都会运行,因而把它放在这里。这样登录进来就会自动执行。当然也可以通过source .bashrc手动执行。
|
||||
|
||||
要编辑.bashrc文件,可以使用文本编辑器vi,也可以使用更加友好的vim。如果默认没有安装,可以通过yum install vim及apt-get install vim进行安装。
|
||||
|
||||
**vim就像Windows里面的notepad一样,是我们第一个要学会的工具**。要不然编辑、查看配置文件,这些操作你都没办法完成。vim是一个很复杂的工具,刚上手的时候,你只需要记住几个命令就行了。
|
||||
|
||||
vim hello,就是打开一个文件,名字叫hello。如果没有这个文件,就先创建一个。
|
||||
|
||||
我们其实就相当于打开了一个notepad。如果文件有内容,就会显示出来。移动光标的位置,通过上下左右键就行。如果想要编辑,就把光标移动到相应的位置,输入i,意思是insert。进入编辑模式,可以插入、删除字符,这些都和notepad很像。要想保存编辑的文本,我们使用esc键退出编辑模式,然后输入“:”,然后在“:”后面输入命令w,意思是write,这样就可以保存文本,冒号后面输入q,意思是quit,这样就会退出vim。如果编辑了,还没保存,不想要了,可以输入q!。
|
||||
|
||||
好了,掌握这些基本够用了,想了解更复杂的,你可以自己去看文档。
|
||||
|
||||
通过vim .bashrc,将export的两行加入后,输入:wq,写入并且退出,这样就编辑好了。
|
||||
|
||||
## 运行程序
|
||||
|
||||
好了,装好了程序,可以运行程序了。
|
||||
|
||||
我们都知道Windows下的程序,如果后缀名是exe,双击就可以运行了。
|
||||
|
||||
Linux不是根据后缀名来执行的。它的执行条件是这样的:只要文件有x执行权限,都能到文件所在的目录下,通过./filename运行这个程序。当然,如果放在PATH里设置的路径下面,就不用./了,直接输入文件名就可以运行了,Linux会帮你找。
|
||||
|
||||
这是**Linux执行程序最常用的一种方式,通过shell在交互命令行里面运行**。
|
||||
|
||||
这样执行的程序可能需要和用户进行交互,例如允许让用户输入,然后输出结果也打印到交互命令行上。这种方式比较适合运行一些简单的命令,例如通过date获取当前时间。这种模式的缺点是,一旦当前的交互命令行退出,程序就停止运行了。
|
||||
|
||||
这样显然不能用来运行那些需要“永远“在线的程序。比如说,运行一个博客程序,我总不能老是开着交互命令行,博客才可以提供服务。一旦我要去睡觉了,关了命令行,我的博客别人就不能访问了,这样肯定是不行的。
|
||||
|
||||
于是,我们就有了**Linux运行程序的第二种方式,后台运行**。
|
||||
|
||||
这个时候,我们往往使用nohup命令。这个命令的意思是no hang up(不挂起),也就是说,当前交互命令行退出的时候,程序还要在。
|
||||
|
||||
当然这个时候,程序不能霸占交互命令行,而是应该在后台运行。最后加一个&,就表示后台运行。
|
||||
|
||||
另外一个要处理的就是输出,原来什么都打印在交互命令行里,现在在后台运行了,输出到哪里呢?输出到文件是最好的。
|
||||
|
||||
最终命令的一般形式为nohup command >out.file 2>&1 &。这里面,“1”表示文件描述符1,表示标准输出,“2”表示文件描述符2,意思是标准错误输出,“2>&1”表示标准输出和错误输出合并了。合并到哪里去呢?到out.file里。
|
||||
|
||||
那这个进程如何关闭呢?我们假设启动的程序包含某个关键字,那就可以使用下面的命令。
|
||||
|
||||
```
|
||||
ps -ef |grep 关键字 |awk '{print $2}'|xargs kill -9
|
||||
|
||||
```
|
||||
|
||||
从这个命令中,我们多少能看出shell的灵活性和精巧组合。
|
||||
|
||||
其中ps -ef可以单独执行,列出所有正在运行的程序,grep上面我们介绍过了,通过关键字找到咱们刚才启动的程序。
|
||||
|
||||
awk工具可以很灵活地对文本进行处理,这里的awk '{print $2}'是指第二列的内容,是运行的程序ID。我们可以通过xargs传递给kill -9,也就是发给这个运行的程序一个信号,让它关闭。如果你已经知道运行的程序ID,可以直接使用kill关闭运行的程序。
|
||||
|
||||
在Windows里面还有一种程序,称为服务。这是系统启动的时候就在的,我们可以通过控制面板的服务管理启动和关闭它。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f2/a6/f24f0f11bcb9a177861a4782ba1d82a6.png" alt="">
|
||||
|
||||
Linux也有相应的服务,这就是**程序运行的第三种方式,以服务的方式运行**。例如常用的数据库MySQL,就可以使用这种方式运行。
|
||||
|
||||
例如在Ubuntu中,我们可以通过apt-get install mysql-server的方式安装MySQL,然后通过命令systemctl start mysql启动MySQL,通过systemctl enable mysql设置开机启动。之所以成为服务并且能够开机启动,是因为在/lib/systemd/system目录下会创建一个XXX.service的配置文件,里面定义了如何启动、如何关闭。
|
||||
|
||||
在CentOS里有些特殊,MySQL被Oracle收购后,因为担心授权问题,改为使用MariaDB,它是MySQL的一个分支。通过命令yum install mariadb-server mariadb进行安装,命令systemctl start mariadb启动,命令systemctl enable mariadb设置开机启动。同理,会在/usr/lib/systemd/system目录下,创建一个XXX.service的配置文件,从而成为一个服务。
|
||||
|
||||
systemd的机制十分复杂,这里咱们不讨论。如果有兴趣,你可以自己查看相关文档。
|
||||
|
||||
最后咱们要学习的是如何关机和重启。这个就很简单啦。shutdown -h now是现在就关机,reboot就是重启。
|
||||
|
||||
## 总结时刻
|
||||
|
||||
好了,掌握这些基本命令足够你熟练操作Linux了。如果你是个初学者,这些命令估计看起来还是很多。我把今天这些基本的命令以及对应的操作总结了一下,方便你操作和查阅。
|
||||
|
||||
你不用可以去死记硬背,按照我讲的这个步骤,从设置用户和密码、浏览文件、安装软件,最后到运行程序,**自己去操作几遍,再自己整理一遍**,手脑并用,加深理解,巩固记忆,效果可能会更好。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/88/e5/8855bb645d8ecc35c80aa89cde5d16e5.jpg" alt="">
|
||||
|
||||
## 课堂练习
|
||||
|
||||
现在你应该已经学会了安装JDK和MySQL,你可以尝试搭建一个基于Java+MySQL的服务端应用,上手使用一下。
|
||||
|
||||
欢迎留言和我分享你的疑惑和见解,也欢迎你收藏本节内容,反复研读。你也可以把今天的内容分享给你的朋友,和他一起学习、进步。
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
<audio id="audio" title="05 | 学会几个系统调用:咱们公司能接哪些类型的项目?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/e7/b1/e7ffca8ca5b09224969b3237723a0bb1.mp3"></audio>
|
||||
|
||||
上一节我们讲了几个重要的Linux命令行,只有通过这些命令,用户才能把Linux系统用起来,不知道你掌握得如何了?其实Linux命令也是一个程序,只不过代码是别人写好的,你直接用就可以了。你可以自己试着写写代码,通过代码把Linux系统用起来,这样印象会更深刻。
|
||||
|
||||
不过,无论是别人写的程序,还是你写的程序,运行起来都是进程。如果你是一家外包公司,一个项目的运行要使用公司的服务,那就应该去办事大厅,也就是说,你写的程序应该使用系统调用。
|
||||
|
||||
你看,系统调用决定了这个操作系统好用不好用、功能全不全。对应到咱们这个公司中,作为一个老板,你应该好好规划一下,你的办事大厅能够提供哪些服务,这决定了你这个公司会被打五星还是打差评。
|
||||
|
||||
## 立项服务与进程管理
|
||||
|
||||
首先,我们得有个项目,那就要有立项服务。对应到Linux操作系统中就是**创建进程**。
|
||||
|
||||
创建进程的系统调用叫fork。这个名字很奇怪,中文叫“分支”。为啥启动一个新进程叫“分支”呢?
|
||||
|
||||
在Linux里,要创建一个新的进程,需要一个老的进程调用fork来实现,其中老的进程叫作**父进程**(Parent Process),新的进程叫作**子进程**(Child Process)。
|
||||
|
||||
前面我们说过,一个进程的运行是要有一个程序的,就像一个项目的执行,要有一个项目执行计划书。本来老的项目,按照项目计划书按部就班地来,项目执行到一半,突然接到命令,说是要新启动一个项目,这个时候应该怎么办呢?
|
||||
|
||||
一个项目的执行是很复杂的,需要涉及公司各个部门的工作,比如说,项目管理部门需要给这个项目组开好Jira和Wiki,会议室管理部要为这个项目分配会议室等等。
|
||||
|
||||
所以,我们现在有两种方式,一种是列一个清单,清单里面写明每个新项目组都要开哪些账号。但是,这样每次有项目,都要重新配置一遍新的Jira、Wiki,复杂得很。另一种方式就是咱们程序员常用的方式,CTRL/C + CTRL/V。也就是说,如果想为新项目建立一套Jira,但又觉得一个个填Jira里面的选项太麻烦,那就可以拷贝一个别人的,然后根据新项目的实际情况,将相应的配置改改。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f4/78/f433f5d14e79612032ea625b44ac6178.jpeg" alt="">
|
||||
|
||||
Linux就是这样想的。当父进程调用fork创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。按理说,如果不进行特殊的处理,父进程和子进程都按相同的程序代码进行下去,这样就没有意义了。
|
||||
|
||||
所以,我们往往会这样处理:对于fork系统调用的返回值,如果当前进程是子进程,就返回0;如果当前进程是父进程,就返回子进程的进程号。这样首先在返回值这里就有了一个区分,然后通过if-else语句判断,如果是父进程,还接着做原来应该做的事情;如果是子进程,需要请求另一个系统调用execve来执行另一个程序,这个时候,子进程和父进程就彻底分道扬镳了,也就产生了一个分支(fork)了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e8/7f/e8ee83d78538bd43d3835662ded92e7f.jpeg" alt="">
|
||||
|
||||
同样是“先拷贝,再修改”的策略,你可能会问,新进程都是父进程fork出来的,那到底谁是第一个呢?
|
||||
|
||||
作为一个外包公司老板,有了新项目当然会分给手下做,但是当公司刚起步的时候呢?没有下属,只好自己上了。先建立**项目运行体系**,等后面再做项目的时候,就都按这个来。
|
||||
|
||||
对于操作系统也一样,启动的时候先创建一个所有用户进程的“祖宗进程”。这个在讲系统启动的时候还会详细讲,我这里先不多说。
|
||||
|
||||
有时候,父进程要关心子进程的运行情况,这毕竟是自己身上掉下来的肉。有个系统调用waitpid,父进程可以调用它,将子进程的进程号作为参数传给它,这样父进程就知道子进程运行完了没有,成功与否。
|
||||
|
||||
所以说,所有子项目最终都是老板,也就是祖宗进程fork过来的,因而它要对整个公司的项目执行负最终的责任。
|
||||
|
||||
## 会议室管理与内存管理
|
||||
|
||||
项目启动之后,每个项目组有独立的会议室,存放自己项目相关的数据。每个项目组都感觉自己有独立的办公空间。
|
||||
|
||||
在操作系统中,每个进程都有自己的内存,互相之间不干扰,有独立的**进程内存空间**。
|
||||
|
||||
那独立的办公空间里面,都放些什么呢?
|
||||
|
||||
项目执行计划书肯定是要放进去的,因为执行过程中肯定要不断地看。对于进程的内存空间来讲,放程序代码的这部分,我们称为**代码段**(Code Segment)。
|
||||
|
||||
项目执行的过程中,会产生一些架构图、流程图,这些也放在会议室里面。有的画在白板上,讨论完了,进入下个主题就会擦了;有的画在纸和本子上,讨论的时候翻出来,不讨论的时候堆在那里,会保留比较长的一段时间,除非指明的确不需要了才会去销毁。
|
||||
|
||||
对于进程的内存空间来讲,放进程运行中产生数据的这部分,我们称为**数据段**(Data Segment)。其中局部变量的部分,在当前函数执行的时候起作用,当进入另一个函数时,这个变量就释放了;也有动态分配的,会较长时间保存,指明才销毁的,这部分称为**堆**(Heap)。
|
||||
|
||||
一个进程的内存空间是很大的,32位的是4G,64位的就更大了,我们不可能有这么多物理内存。就像一个公司的会议室是有限的,作为老板,你不可能事先都给项目组分配好。哪有这么多会议室啊,一定是需要的时候再分配。
|
||||
|
||||
所以,进程自己不用的部分就不用管,只有进程要去使用部分内存的时候,才会使用内存管理的系统调用来登记,说自己马上就要用了,希望分配一部分内存给它,但是这还不代表真的就对应到了物理内存。只有真的写入数据的时候,发现没有对应物理内存,才会触发一个中断,现分配物理内存。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e9/0b/e9bcfb17a7ac8c21bcc6b0828641850b.jpeg" alt="">
|
||||
|
||||
这里我们介绍两个在堆里面分配内存的系统调用,brk和mmap。
|
||||
|
||||
当分配的内存数量比较小的时候,使用brk,会和原来的堆的数据连在一起,这就像多分配两三个工位,在原来的区域旁边搬两把椅子就行了。当分配的内存数量比较大的时候,使用mmap,会重新划分一块区域,也就是说,当办公空间需要太多的时候,索性来个一整块。
|
||||
|
||||
## 档案库管理与文件管理
|
||||
|
||||
项目执行计划书要保存在档案库里,有一些需要长时间保存,这样哪怕公司暂时停业,再次经营的时候还可以继续使用。同样,程序、文档、照片等,哪怕关机再开机也能不丢的,就需要放在文件系统里面。
|
||||
|
||||
文件之所以能做到这一点,一方面是因为**介质**,另一方面是因为**格式**。公司之所以强调资料库,也是希望将一些知识固化为标准格式,放在一起进行管理,无论多少人来人走,都不影响公司业务。
|
||||
|
||||
文件管理其实花样不多,拍着脑袋都能想出来,无非是创建、打开、读、写等。
|
||||
|
||||
对于文件的操作,下面这六个系统调用是最重要的:
|
||||
|
||||
<li>
|
||||
对于已经有的文件,可以使用open打开这个文件,close关闭这个文件;
|
||||
</li>
|
||||
<li>
|
||||
对于没有的文件,可以使用creat创建文件;
|
||||
</li>
|
||||
<li>
|
||||
打开文件以后,可以使用lseek跳到文件的某个位置;
|
||||
</li>
|
||||
<li>
|
||||
可以对文件的内容进行读写,读的系统调用是read,写是write。
|
||||
</li>
|
||||
|
||||
但是别忘了,Linux里有一个特点,那就是**一切皆文件**。
|
||||
|
||||
<li>
|
||||
启动一个进程,需要一个程序文件,这是一个**二进制文件**。
|
||||
</li>
|
||||
<li>
|
||||
启动的时候,要加载一些配置文件,例如yml、properties等,这是文本文件;启动之后会打印一些日志,如果写到硬盘上,也是**文本文件**。
|
||||
</li>
|
||||
<li>
|
||||
但是如果我想把日志打印到交互控制台上,在命令行上唰唰地打印出来,这其实也是一个文件,是标准输出**stdout文件**。
|
||||
</li>
|
||||
<li>
|
||||
这个进程的输出可以作为另一个进程的输入,这种方式称为**管道**,管道也是一个文件。
|
||||
</li>
|
||||
<li>
|
||||
进程可以通过网络和其他进程进行通信,建立的**Socket**,也是一个文件。
|
||||
</li>
|
||||
<li>
|
||||
进程需要访问外部设备,**设备**也是一个文件。
|
||||
</li>
|
||||
<li>
|
||||
文件都被存储在文件夹里面,其实**文件夹**也是一个文件。
|
||||
</li>
|
||||
<li>
|
||||
进程运行起来,要想看到进程运行的情况,会在/proc下面有对应的**进程号**,还是一系列文件。
|
||||
</li>
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e4/df/e49b5c2a78ac09903d697126bfe6c5df.jpeg" alt="">
|
||||
|
||||
每个文件,Linux都会分配一个**文件描述符**(File Descriptor),这是一个整数。有了这个文件描述符,我们就可以使用系统调用,查看或者干预进程运行的方方面面。
|
||||
|
||||
所以说,文件操作是贯穿始终的,这也是“一切皆文件”的优势,就是统一了操作的入口,提供了极大的便利。
|
||||
|
||||
## 项目异常处理与信号处理
|
||||
|
||||
在项目运行过程中,不一定都是一帆风顺的,很可能遇到各种异常情况。作为老板,处理异常情况的能力是非常重要的,所以办事大厅也一定要包含这部分服务。
|
||||
|
||||
当项目遇到异常情况,例如项目中断,做到一半不做了。这时候就需要发送一个**信号**(Signal)给项目组。经常遇到的信号有以下几种:
|
||||
|
||||
<li>
|
||||
在执行一个程序的时候,在键盘输入“CTRL+C”,这就是中断的信号,正在执行的命令就会中止退出;
|
||||
</li>
|
||||
<li>
|
||||
如果非法访问内存,例如你跑到别人的会议室,可能会看到不该看的东西;
|
||||
</li>
|
||||
<li>
|
||||
硬件故障,设备出了问题,当然要通知项目组;
|
||||
</li>
|
||||
<li>
|
||||
用户进程通过kill函数,将一个用户信号发送给另一个进程。
|
||||
</li>
|
||||
|
||||
当项目组收到信号的时候,项目组需要决定如何处理这些异常情况。
|
||||
|
||||
对于一些不严重的信号,可以忽略,该干啥干啥,但是像SIGKILL(用于终止一个进程的信号)和SIGSTOP(用于中止一个进程的信号)是不能忽略的,可以执行对于该信号的默认动作。每种信号都定义了默认的动作,例如硬件故障,默认终止;也可以提供信号处理函数,可以通过sigaction系统调用,注册一个信号处理函数。
|
||||
|
||||
提供了信号处理服务,项目执行过程中一旦有变动,就可以及时处理了。
|
||||
|
||||
## 项目组间沟通与进程间通信
|
||||
|
||||
当某个项目比较大的时候,可能分成多个项目组,不同的项目组需要相互交流、相互配合才能完成,这就需要一个项目组之间的沟通机制。项目组之间的沟通方式有很多种,我们来一一规划。
|
||||
|
||||
首先就是发个消息,不需要一段很长的数据,这种方式称为**消息队列**(Message Queue)。由于一个公司内的多个项目组沟通时,这个消息队列是在内核里的,我们可以通过msgget创建一个新的队列,msgsnd将消息发送到消息队列,而消息接收方可以使用msgrcv从队列中取消息。
|
||||
|
||||
当两个项目组需要交互的信息比较大的时候,可以使用**共享内存**的方式,也即两个项目组共享一个会议室(这样数据就不需要拷贝来拷贝去)。大家都到这个会议室来,就可以完成沟通了。这时候,我们可以通过shmget创建一个共享内存块,通过shmat将共享内存映射到自己的内存空间,然后就可以读写了。
|
||||
|
||||
但是,两个项目组共同访问一个会议室里的数据,就会存在“竞争”的问题。如果大家同时修改同一块数据咋办?这就需要有一种方式,让不同的人能够排他地访问,这就是信号量的机制**Semaphore**。
|
||||
|
||||
这个机制比较复杂,我这里说一种简单的场景。
|
||||
|
||||
对于只允许一个人访问的需求,我们可以将信号量设为1。当一个人要访问的时候,先调用sem_wait。如果这时候没有人访问,则占用这个信号量,他就可以开始访问了。如果这个时候另一个人要访问,也会调用sem_wait。由于前一个人已经在访问了,所以后面这个人就必须等待上一个人访问完之后才能访问。当上一个人访问完毕后,会调用sem_post将信号量释放,于是下一个人等待结束,可以访问这个资源了。
|
||||
|
||||
## 公司间沟通与网络通信
|
||||
|
||||
同一个公司不同项目组之间的合作搞定了,如果是不同公司之间呢?也就是说,这台Linux要和另一台Linux交流,这时候,我们就需要用到网络服务。
|
||||
|
||||
不同机器的通过网络相互通信,要遵循相同的网络协议,也即**TCP/IP网络协议栈**。Linux内核里有对于网络协议栈的实现。如何暴露出服务给项目组使用呢?
|
||||
|
||||
网络服务是通过套接字Socket来提供服务的。Socket这个名字很有意思,可以作“插口”或者“插槽”讲。虽然我们是写软件程序,但是你可以想象成弄一根网线,一头插在客户端,一头插在服务端,然后进行通信。因此,在通信之前,双方都要建立一个Socket。
|
||||
|
||||
我们可以通过Socket系统调用建立一个Socket。Socket也是一个文件,也有一个文件描述符,也可以通过读写函数进行通信。
|
||||
|
||||
好了,我们分门别类地规划了这么多办事大厅的服务,如果这些都有了,足够我们成长为一个大型跨国公司了。
|
||||
|
||||
## 查看源代码中的系统调用
|
||||
|
||||
你如果问,这里的系统调用列举全了吗?其实没有,系统调用非常多。我建议你访问[https://www.kernel.org](https://www.kernel.org/)下载一份Linux内核源代码。因为在接下来的整个课程里,我讲述的逻辑都是这些内核代码的逻辑。
|
||||
|
||||
对于64位操作系统,找到unistd_64.h文件,里面对于系统调用的定义,就是下面这样。
|
||||
|
||||
```
|
||||
#define __NR_restart_syscall 0
|
||||
#define __NR_exit 1
|
||||
#define __NR_fork 2
|
||||
#define __NR_read 3
|
||||
#define __NR_write 4
|
||||
#define __NR_open 5
|
||||
#define __NR_close 6
|
||||
#define __NR_waitpid 7
|
||||
#define __NR_creat 8
|
||||
......
|
||||
|
||||
```
|
||||
|
||||
## 中介与Glibc
|
||||
|
||||
如果你做过开发,你会觉得刚才讲的和平时咱们调用的函数不太一样。这是因为,平时你并没有直接使用系统调用。虽然咱们的办事大厅已经很方便了,但是为了对用户更友好,我们还可以使用中介**Glibc**,有事情找它就行,它会转换成为系统调用,帮你调用。
|
||||
|
||||
Glibc是Linux下使用的开源的标准C库,它是GNU发布的libc库。**Glibc为程序员提供丰富的 API,除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装**。
|
||||
|
||||
每个特定的系统调用对应了至少一个Glibc封装的库函数,比如说,系统提供的打开文件系统调用sys_open对应的是Glibc中的open函数。
|
||||
|
||||
有时候,Glibc一个单独的API可能调用多个系统调用,比如说,Glibc提供的printf函数就会调用如sys_open、sys_mmap、sys_write、sys_close等等系统调用。
|
||||
|
||||
也有时候,多个API也可能只对应同一个系统调用,如Glibc下实现的malloc、calloc、free等函数用来分配和释放内存,都利用了内核的sys_brk的系统调用。
|
||||
|
||||
## 总结时刻
|
||||
|
||||
学了这么多系统调用,我们还是用一个图来总结一下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ff/f0/ffb6847b94cb0fd086095ac263ac4ff0.jpg" alt="">
|
||||
|
||||
## 课堂练习
|
||||
|
||||
有个命令strace,常用来跟踪进程执行时系统调用和所接收的信号。你可以试一下咱们学过的命令行,看看都执行了哪些系统调用。
|
||||
|
||||
欢迎留言和我分享你的疑惑和见解,也欢迎你收藏本节内容,反复研读。你也可以把今天的内容分享给你的朋友,和他一起学习、进步。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8c/37/8c0a95fa07a8b9a1abfd394479bdd637.jpg" alt="">
|
||||
Reference in New Issue
Block a user