diff --git "a/docs/\345\210\235\347\272\247\347\257\207/j-10.\350\277\233\347\250\213\347\233\270\345\205\263\347\232\204shell\345\221\275\344\273\244.md" "b/docs/\345\210\235\347\272\247\347\257\207/j-10.\350\277\233\347\250\213\347\233\270\345\205\263\347\232\204shell\345\221\275\344\273\244.md" index af4ac6e..294e957 100644 --- "a/docs/\345\210\235\347\272\247\347\257\207/j-10.\350\277\233\347\250\213\347\233\270\345\205\263\347\232\204shell\345\221\275\344\273\244.md" +++ "b/docs/\345\210\235\347\272\247\347\257\207/j-10.\350\277\233\347\250\213\347\233\270\345\205\263\347\232\204shell\345\221\275\344\273\244.md" @@ -4,19 +4,19 @@ ## 什么是进程 -实际上,在前面两章,我们已经简单提及了进程这个概念。当时我们说,进城就是在运行中的程序。这是一个十分简化的理解。 +实际上,在前面两章,我们已经简单提及了进程这个概念。当时我们说,进程就是在运行中的程序。这是一个十分简化的理解。 在计算机发展的初期,计算机相比于现在价格高昂,计算能力比较弱,功能也很简单。那时还很难说有操作系统的概念,人们将写好的程序和相应的输入数据(放在某种存储设备中,比如打孔纸带或者磁带)依次放入计算机。这里我说的依次是指,前一个程序运行完成之后,再放入下一个程序和相应数据。因为计算能力比较弱,计算机需要话很多时间(几个小时或者更对)来完成任务。也就是说,计算机一次只能运行一个程序。 当任务比较少的的时候,这种方式看上去没什么太多问题。但是当任务比较多开始排队的时候,问题就出现了,由于不知道下一个任务何时结束,所以程序员或者管理员有时提交下一个任务会不及时,这就会导致这中间的计算资源被浪费。 -因此人们为了解决这个问题提供了一种方式,大家将一批程序一次性提交给计算机,计算机的输出端有某种装置,它可以在前一个任务完成之后自动取出下一个程序(和它的数据)并读入。这样程序员只需要定期去检查是否输出了结果就行了。也就是说,这时计算机可以按顺序一次性执行一批程序。这样的系统也就被成为批处理系统。 +因此人们为了解决这个问题提供了一种方式,大家将一批程序一次性提交给计算机,计算机的输出端有某种装置,它可以在前一个任务完成之后自动取出下一个程序(和它的数据)并读入。这样程序员只需要定期去检查是否输出了结果就行了。也就是说,这时计算机可以按顺序一次性执行一批程序。这样的系统也就被称为批处理系统。 -后来,随着计算机内存大小的增加,人们终于有可能将多个程序同时加载到内存里面了。也就是任务不必完成一项再开始下一项了。于是人们发现,可以利用这一点解决另外两个问题了。第一,程序运行过程中不总是在使用全部计算资源,有的时候程序会将时间花在和存储设备或者其他外部设备交互上,这些时间里,cpu资源实际上是被浪费的。第二,一些程序可能因为错误,或者有人写出恶意程序,导致这些程序不能停止。如果发生了这种情况,后面的任务将永远无法开始运行。 +后来,随着计算机内存大小的增加,人们终于有可能将多个程序同时加载到内存里面了。也就是任务不必完成一项再开始下一项了。于是人们发现,还可以利用这一点解决另外两个问题。第一,程序运行过程中不总是在使用全部计算资源,有的时候程序会将时间花在与存储设备或者其他外部设备交互上,这些时间里,cpu资源实际上是被浪费的。第二,一些程序可能因为错误,或者有人写出恶意程序,导致这些程序不能停止。如果发生了这种情况,后面的任务将永远无法开始运行。 解决这种问题的方式就是让每个程序共享cpu。每个程序可以在cpu上运行一段时间,然后它就必须主动或被动地让出cpu给其他程序运行。在一个程序和外部设备通信的时候,这个程序就可以把cpu让给其他程序使用,这样就解决了第一个问题;每个程序占用cpu足够长的时间之后就必须把cpu让给其他程序,这样就解决了第二个问题。但是从每个程序的视角来看,它们看上去像是独占了cpu,因为在放弃cpu给其他程序的前后,这个程序的运行是连贯的。这样,我们就有了进程的概念,每个正在运行的程序就是一个进程,在这些进程看来,它们似乎独占地使用cpu和内存,但是实际上进程之间是共享这些资源的。另外,进程还有一个重要特点,为了安全考虑,虽然进程共享物理内存,但是每个进程的内存是隔离的,也就是进程只能使用自己的内存数据。 -总结一下这一节:进程是计算机上正在运行(准确来说是活跃,因为进程可能处于会放弃cpu而处于暂停状态)的程序,它们共享cpu和内存,但是操作系统让它们认为自己在独占这些资源。 +总结一下这一节:进程是计算机上正在运行(准确来说是活跃,因为进程可能会放弃cpu而处于暂停状态)的程序,它们共享cpu和内存,但是操作系统让它们认为自己在独占这些资源。 ## shell对于进程的抽象 @@ -26,11 +26,11 @@ linux操作系统中,进程可以分成组,并且可以对同一个组的进 ### 任务的运行状态 -任务总共有三种运行状态(进程也有类似的概念):正在运行的任务,停止(stopped)的任务,和终止(terminated)的任务。其中停止的任务和终止的任务的主要区别是,停止的任务实际上是暂停的,也就是之后还可以继续运行,终止的任务则是进程被终止或者执行完成不可能再继续运行的任务。 +任务总共有五种运行状态(进程也有类似的概念):正在运行(Running)的任务,停止(stopped)的任务,终止(terminated)的任务,结束(Done)的任务和被杀死(Killed)的任务。其中停止的任务和终止的任务的主要区别是,停止的任务实际上是暂停的,也就是之后还可以继续运行,终止的任务则是进程被终止或者执行完成不可能再继续运行的任务。 ### 前台任务和后台任务 -和任务相关的另一个重要的概念是前台(foreground)任务和后台(background)任务。这两个概念是对于正在运行的任务的,其中前台任务是指在启动之后会让`shell`暂停,等它运行结束之后`shell`才会继续运行的任务;而后台任务就是在启动之后`shell`不会暂停,可以继续运行其他任务。 +和任务相关的另一个重要的概念是前台(foreground)任务和后台(background)任务。这两个概念是对于正在运行的任务的,其中前台任务是指在启动之后会让`shell`暂停,等它运行结束之后`shell`才会继续运行的任务;而后台任务就是在启动之后`shell`不会暂停,可以继续使用`shell`启动其他任务。 ## 进程间通信 @@ -40,7 +40,7 @@ linux操作系统中,进程可以分成组,并且可以对同一个组的进 ### 什么是信号 -信号是操作系统为我们提供的一种软件方式实现的中断。简单来说就是可以在一个程序的正常运行过程中,打断这个这个程序的运行并给它传递一个信息(也就是一个定义好的信号)。被打断正常执行之后,程序会立即进入一个特殊的中断处理程序,完成这个中断处理程序的执行之后会回到之前被打断的地方继续执行。Linux系统定义了30种标准信号,每种信号都可以表示一种信息。每一种信号都有一个默认的处理程序,大部分信号的默认处理程序都是什么也不做或者终止程序。如果我们想要让我们的程序在收到信号的时候做不同的行为,也可以在程序中自己定义某个或者某些信号的处理程序。下面这个表中是一些常用的信号的默认行为和含义(来自signal的manpage),其中默认行为这一栏中Term表示终止进程(Terminate),Core表示终止程序并转储内核(就当做是终止程序就行),Ign表示忽略信号(Ignore),Stop表示暂停进程(之后还可以恢复运行)。后面的Comment是对这个信号的简单解释。这个表格中的内容并不很重要,仅供参考。 +信号是操作系统为我们提供的一种软件方式实现的中断。简单来说就是可以在一个程序的正常运行过程中,打断这个这个程序的运行并给它传递一个信息(也就是一个定义好的信号)。被打断正常执行之后,程序会立即进入一个特殊的中断处理程序,完成这个中断处理程序的执行之后会回到之前被打断的地方继续执行。Linux系统定义了30种标准信号,每种信号都可以表示一种信息。每一种信号都有一个默认的处理程序,大部分信号的默认处理程序都是什么也不做或者终止程序。如果我们想要让我们的程序在收到信号的时候做不同的行为,也可以在程序中自己定义某个或者某些信号的处理程序。下面这个表中是一些常用的信号的默认行为和含义(来自signal的manpage),其中默认行为这一栏中Term表示终止进程(Terminate),Core表示终止程序并转储内核(就当做是终止程序就行),Ign表示忽略信号(Ignore),Stop表示暂停进程(之后还可以恢复运行), Cont表示从暂停的状态中恢复(如果处于运行状态就忽略)。后面的Comment是对这个信号的简单解释。这个表格中的内容并不很重要,仅供参考。 Signal | Standard | Action | Comment | -------|--------------|---------|----------| @@ -87,17 +87,17 @@ sleep 10 ## 查看进程信息的命令——ps -在介绍使用命令来发送命令之前,我们首先还需要介绍一个重要的概念,就是pid(Process Idification)。它是一个数字,标识了一个进程,在每个时刻,同一个系统中,每个进程的pid是唯一的。也就是,在系统中,我们使用一个进程的pid来表示这进程。那么我们当然要问,我们能获取一个进程的pid吗?当然可以,并且也是通过一个命令,也就是`ps`。 +在介绍使用命令来发送信号之前,我们首先还需要介绍一个重要的概念,就是pid(Process Idification)。它是一个数字,标识了一个进程,在每个时刻,同一个系统中,每个进程的pid是唯一的。也就是,在系统中,我们使用一个进程的pid来表示这进程。那么我们当然要问,我们能获取一个进程的pid吗?当然可以,并且也是通过一个命令,也就是`ps`。 这个命令的功能是将系统某一时刻,全部进程的状态打印在屏幕上。如果你只是使用`ps`你会发现,打印出来的内容很少,这是因为`ps`默认只打印在当前终端前台运行的进程。 -`ps`命令的选项有两套系统,比较复杂,感兴趣的同学可以自己去阅读`ps`的手册,这里我们只介绍最常用的一个命令 +`ps`命令的选项格式有两套系统,比较复杂,感兴趣的同学可以自己去阅读`ps`的手册,这里我们只介绍最常用的一个命令 ```bash ps aux ``` 其中选项`a`标识打印所有进程的信息,`u`表示结果中显示对于用户来说重要的信息,包括进程所属的用户名,cpu和内存占用率,`x`表示显示所有进程(而不是只有前台进程组的进程)。 -我们会看到,`ps`打印出来的信息当中,包含很多信息。其中对于我们来说重要的信息包括,在第二列的pid和最后一类的command。最后一列的command就表示当初启动这个进程的时候使用的命令,因为这里包括软件名称和命令行参数,所以我们实际上是使用这一列的信息来查找我们想找的进程的。如果利用我们后面会讲到的`grep`命令,就可以组合出查找特定进程的命令,就像下面这样 +我们会看到,`ps`打印出来的信息当中,包含很多信息。其中对于我们来说重要的信息包括,在第二列的pid和最后一列的command。最后一列的command就表示当初启动这个进程的时候使用的命令,因为这里包括软件名称和命令行参数,所以我们实际上是使用这一列的信息来查找我们想找的进程的。如果利用我们后面会讲到的`grep`命令,就可以组合出查找特定进程的命令,就像下面这样 ```bash ps aux | grep bash ``` @@ -114,7 +114,7 @@ ps aux | grep bash ### 发送信号 使用`kill`来发送信号的方式也比较简单。首先正如他名字说的那样,你可以在后面直接接一个位置参数表示你要杀死的进程号。它会给这个进程发送一个SIGTERM信号,让这个进程直接终止(不过需要注意,虽然收到SIGTERM的默认行为是终止进程,但是程序可以更改这个行为,所以进程不一定会终止)。 -> 而且明明有SIGKILL信号,这个信号的默认行为是终止进程,并且这个默认行为不能更改,为什么`kill`命令不会发送SIGKILL呢?当然如果你想要强制终止一个进程,可以使用下面介绍的方式 +> 而且明明有SIGKILL信号,这个信号的默认行为是终止进程,并且这个默认行为不能更改,为什么`kill`命令不会发送SIGKILL呢?这很奇怪。当然如果你想要强制终止一个进程,可以使用下面介绍的方式 另外,你可以在进程号前面加一个`-`号,这表示向这个进程所在的进程组的所有进程发送同一个信号。 @@ -156,9 +156,9 @@ sleep 10& ``` [1] 3021 ``` -这里前面的`[1]`是bash个任务的一个任务号,我们可以使用这个号码来表示一个任务(请注意,任务和进程的含义不完全相同),后面的`3021`则是在操作系统中这个任务所在的进程组号。 +这里前面的`[1]`是bash任务的任务号,我们可以使用这个号码来表示一个任务(请注意,任务和进程的含义不完全相同),后面的`3021`则是在操作系统中这个任务所在的**进程组号**。 -这里我们也就能发现,实际上bash中的任务基本就是操作系统的一个进程组。因此,即使我们不能通过键盘操作向后台任任务发送信号,也可以通过这个进程组号给后台任务发送信号。(还记得吗,我们上面在`kill`命令那里提到过,在号码前面加上`-`就可以表示进程组号)。 +这里我们也就能发现,实际上bash中的任务基本就是操作系统的一个进程组。因此,即使我们不能通过键盘操作向后台任务发送信号,也可以通过这个进程组号给后台任务发送信号。(还记得吗,我们上面在`kill`命令那里提到过,在号码前面加上`-`就可以表示进程组号)。 ### 查看shell中的任务——jobs @@ -171,7 +171,7 @@ jobs [1]+ Stopped sleep 100 [2]- Running sleep 100 & ``` -最前面`[]`中的是shell中的任务编号,后面跟着的`-`/`+`没有什么具体含义,然后的`Stopped`/`Running`/`Done`/`Killed`表示这个任务的状态是暂停/正在运行/已经结束/被SIGKILL终止,最后是执行这个任务的时候使用的命令。和一些其他命令一样,我们也可以使用`-l`选项来打印更详细的信息。打印出来的内容会在状态信息前面多一个这个任务的进程组的编号。 +最前面`[]`中的是shell中的任务编号,然后的`Stopped`/`Running`/`Done`/`Killed`表示这个任务的状态是暂停/正在运行/已经结束/被SIGKILL终止,最后是执行这个任务的时候使用的命令。和一些其他命令一样,我们也可以使用`-l`选项来打印更详细的信息。打印出来的内容会在状态信息前面多一个这个任务的进程组的编号。 ### 在后台继续一个暂停的任务——bg @@ -197,7 +197,7 @@ bg 1 ### 使用信号在后台继续一个暂停的任务 -最后我们还可以使用信号在继续一个暂停的任务,这个继续的任务会自动在后台运行。前面我们在介绍SIGTSTP那里介绍任务的暂停状态的时候提到,如果我们向一个暂停的任务发送一个信号,它就会停止自己的状态,但是大部分信号都会有它原本的作用,会终止一个进程等等。不过如果仔细看了我列出的那几个信号,你就会从中发现一个SIGCONT信号。这个信号就是专门用来给我们继续一个暂停的任务的。现在你可以重复一下上一节中前面的步骤,然后使用 +最后我们还可以使用信号继续一个暂停的任务,这个继续的任务会自动在后台运行。前面我们在介绍SIGTSTP那里介绍任务的暂停状态的时候提到,如果我们向一个暂停的任务发送一个信号,它就会停止自己的状态,但是大部分信号都会有它原本的作用,会终止一个进程等等。不过如果仔细看我列出的那几个信号,你就会从中发现一个SIGCONT信号。这个信号就是专门用来给我们继续一个暂停的任务的。现在你可以重复一下上一节中前面的步骤,然后使用 ```bash jobs -l ``` @@ -205,19 +205,19 @@ jobs -l ```bash kill -CONT -4280 ``` -来这个任务(注意要加`-`因为任务是进程组)发送一个SIGCONT信号。这个命令也有其他形式,例如使用SIGCONT的编号而不是`-CONT`,这里你可以根据你的习惯。然后再使用`jobs`命令检查一下任务的状态,你会发现这个任务在后台继续执行了。 +来向这个任务(注意要加`-`因为任务是进程组)发送一个SIGCONT信号。这个命令也有其他形式,例如使用SIGCONT的编号而不是`-CONT`,这里你可以根据你的习惯。然后再使用`jobs`命令检查一下任务的状态,你会发现这个任务在后台继续执行了。 ### 将后台任务放到前台——fg -前面介绍了几种如何将一个任务在后台执行的方式,这一节的最后,我们再来介绍一下如何将一个后台任务放到前台执行。这个命令也很简单,就是`fg`(foreground),它和`bg`类似,但是它不止可以用于暂停的进程,也可以用于正在运行的进程。大家可以自己仿照上面的例子来尝试一下这命令。 +前面介绍了几种如何将一个任务在后台执行的方式,这一节的最后,我们再来介绍一下如何将一个后台任务放到前台执行。这个命令也很简单,就是`fg`(foreground),它和`bg`类似,但是它不止可以用于暂停的进程,也可以用于正在运行的进程。大家可以自己仿照上面的例子来尝试一下这个命令。 ## 持续运行的后台任务 很多时候,我们需要后台任务的一个重要原因是有些程序我们需要持续运行,但是我们不希望它们占用终端资源影响我们运行其他任务。比如,如果你在你的机器搭建一个网站,那么你需要运行一个http server。我们不需要交互地使用者server,只需要它一直在后台自己运行就可以了。这种时候,我们就需要后台任务。但是,或许你们已经发现了,当我们关闭了终端的时候,比如你关闭了图形化界面的终端窗口,或者断开了和远程服务器的ssh连接。这时,前台任务当然不用说,但是即使是后台任务也会终止。 -这是因为当我们关闭终端的时候,终端上运行的所有任务都会收到一个SIGHUP信号,这信号的默认行为就是终止。当然,如果是我们自己写的一个程序,我们可以通过重新注册一个信号处理程序来修改这种行为,但是如果是使用别人的程序,这么做就不可行。不过,shell也给我们提供了一种方式,就是nohup命令。在这个命令后面接我们要执行的命令,这任务就会在收到SIGHUP什么也不做,继续执行。再配合上面提到的将任务在后台运行的方式,我们就可以实现,让一个命令在后台运行并且即使终端关闭了也终止。当然,因为终端被关闭的时候shell会终止,所以你之后就没法在`jobs`的任务列表中找到它了,但是你仍然可以通过`ps`来找到它。 +这是因为当我们关闭终端的时候,终端上运行的所有任务都会收到一个SIGHUP信号,这信号的默认行为就是终止。当然,如果是我们自己写的一个程序,我们可以通过重新注册一个信号处理程序来修改这种行为,但是如果是使用别人的程序,这么做就不可行。不过,shell也给我们提供了一种方式,就是`nohup`命令。在这个命令后面接我们要执行的命令,这任务就会在收到SIGHUP时什么也不做,继续执行。再配合上面提到的将任务在后台运行的方式,我们就可以实现,让一个命令在后台运行并且即使终端关闭了也不终止。当然,因为终端被关闭的时候shell会终止,所以你之后就没法在`jobs`的任务列表中找到它了,但是你仍然可以通过`ps`来找到它。 -最后,`bash`中(其他的shell行为可能不同),当你使用`nohup`执行一个命令的时候,这命令的输出信息会被重定向到当前文件夹下的`nohup.out`文件中。当然,如果你不想让输出信息被放到这文件中的话,你可以在命令自己使用重定向。 +最后,`bash`中(其他的shell行为可能不同),当你使用`nohup`执行一个命令的时候,这命令的输出信息会被重定向到当前文件夹下的`nohup.out`文件中。当然,如果你不想让输出信息被放到这文件中的话,你可以在命令中自己使用重定向。 ## 小结