本文主要详细介绍了Linux的基本过程,具有一定的参考价值。感兴趣的朋友可以参考一下。
计算机实际能做的事情其实很简单,比如计算两个数的和,在内存中找地址等等。这些基本的计算机动作被称为指令。所谓程序就是这样一系列指令的集合。通过程序,我们可以让计算机完成复杂的运算。大多数时候,程序是作为可执行文件存储的。这样的可执行文件就像一个菜谱,电脑可以根据菜谱做出美味的饭菜。
所以,程序和进程(process)的区别又是什么呢?
流程是程序的具体实现。只有当菜谱没有用的时候,我们总是要按照菜谱的指示一步一步的去做菜。Process就是执行程序的过程,类似于按照菜谱实际烹饪的过程。同一个程序可以执行多次,每次都可以加载到内存中一个独立的空间,产生多个进程。不同的进程也可以有自己独立的IO接口。
操作系统的一个重要作用就是为进程提供便利,比如为进程分配内存空间,管理进程的相关信息等。这就像为我们准备了一个漂亮的厨房。
看一眼进程
首先,我们可以使用$ps命令查询正在运行的进程,比如$ps -eo pid,comm,cmd。下图显示了执行结果:
(-e表示列出所有进程,-o pid,comm,cmd表示我们需要pid,COMMAND,CMD信息)
每一行代表一个过程。每行分为三列。第一列PID(进程标识)是一个整数,每个进程都有一个唯一的PID来表示自己的标识。进程也可以根据PID识别其他进程。第二列命令是这个过程的缩写。第三列CMD是对应于运行时的过程和参数的程序。
(第三列有一些用括号[]括起来。它们是内核功能的一部分,被装扮成便于操作系统管理的进程。我们不必去想它们。)
让我们看看第一行。PID为1,名称为init。这个过程是通过执行文件/bin/init生成的。Linux启动时,init是系统创建的第一个进程,这个进程会一直存在,直到我们关闭电脑。这个过程特别重要,我们会一直提到。
如何创建一个进程
实际上,当计算机启动时,内核只建立一个init进程。Linux内核不提供直接建立新进程的系统调用。所有剩余的进程都是由init进程通过fork机制建立的。新的进程是通过从旧的进程中复制自身而获得的,这被称为fork。Fork是一个系统调用。这个过程存在于内存中。每个进程在内存中分配自己的地址空间。当进程分叉时,Linux在内存中为新进程开辟一个新的内存空间,并将旧进程空间的内容复制到新空间。之后,两个进程同时运行。
旧进程成为新进程的父进程,相应地,新进程是旧进程的子进程。除了PID之外,一个进程还将有一个PPID(父PID)来存储父进程的PID。如果我们继续追溯PPID,我们总会发现它的源头是init进程。因此,所有进程也形成了以init为根的树形结构。
如下,我们查询当前shell下的流程:
root@vamei:~# ps -o pid,ppid,cmd
PID PPID CMD
16935 3101须道一号
16939 16935-巴什
23774 16939 ps -o pid、ppid、cmd
我们可以看到第二个进程bash是第一个进程sudo的子进程,而第三个进程ps是第二个进程的子进程。
您还可以使用pstree命令来显示整个进程树:
initnetworkmanagerdhclient
2*[{NetworkManager}]
accounts-daemon{accounts-daemon}
acpid
apache2apache2
2*[apache226*[{apache2}]]
at-spi-bus-laun2*[{at-spi-bus-laun}]
atd
avahi-daemonavahi-daemon
bluetoothd
colord2*[{colord}]
console-kit-dae64*[{console-kit-dae}]
cron
cupsd2*[dbus]
2*[dbus-daemon]
dbus-launch
dconf-service2*[{dconf-service}]
dropbox15*[{dropbox}]
firefox27*[{firefox}]
gconfd-2
geoclue-master
6*[getty]
gnome-keyring-d7*[{gnome-keyring-d}]
gnome-terminalbash
bashpstree
gnome-pty-helpe
shR{R}
3*[{gnome-terminal}]
Fork通常作为函数调用。这个函数将返回两次,将子进程的PID返回给父进程,将0返回给子进程。实际上,子进程可以随时查询它的PPID来知道它的父进程是谁,这样一对父进程和子进程就可以随时相互查询。
通常调用fork函数后,程序会设计一个if选择结构。当PID等于0时,说明进程是子进程,那么让它执行一些指令,比如用exec库函数读取另一个程序文件,在当前进程空间执行(这其实是使用fork的一个很大的目的:为某个程序创建一个进程);PID为正整数时,表示是父进程,其他指令会被执行。因此,在建立子进程后,它可以执行与父进程不同的功能。
子进程的终结(termination)
子进程终止时,会通知父进程,清空其占用的内存,并在内核中留下自己的退出信息(退出代码,如果运行顺利,为0;如果有错误或异常情况,则为0的整数)。在此消息中,它将解释进程退出的原因。当父进程知道子进程结束时,它负责在子进程上使用等待系统调用。这个wait函数可以从内核中取出子进程的退出信息,并清除这些信息在内核中占用的空间。但是,如果父进程在子进程之前结束,子进程将成为孤儿进程。孤儿进程将被init进程采用,init进程将成为该进程的父进程。当子进程终止时,init进程负责调用wait函数。
当然,一个坏的程序也可能导致子进程的退出信息滞留在内核中(父进程不调用子进程的wait函数)。在这种情况下,子进程就变成了僵尸进程。当僵尸进程大量积累时,内存空间就会被挤压。
进程与线程(thread)
虽然在UNIX中,进程和线程是两个相关但不同的东西,但在Linux中,线程只是一个特殊的进程。多个线程可以共享内存空间和IO接口。因此,进程是实现Linux程序的唯一途径。
总结
程序、进程、PID、存储空间
子进程、父进程、PPID、分叉、等待
这就是本文的全部内容。希望对大家的学习有帮助,支持我们。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。