当前位置: 首页 > 科技观察

Linux进程基础介绍

时间:2023-03-13 06:50:38 科技观察

计算机实际上能做的事情本质上都是非常简单的,比如计算两个数的和,或者在内存中寻找地址等等。这些最基本的计算机操作称为指令。所谓程序(program)就是这样一系列指令的集合。通过程序,我们可以使计算机完成复杂的操作。大多数情况下,程序都存储为可执行文件。这样的可执行文件就像一个菜谱,计算机可以根据菜谱做出美味的饭菜。那么,程序和进程有什么区别呢?进程是程序的具体实现。只有食谱是没有用的,我们总是要按照食谱的指示一步步做菜。过程就是执行程序的过程,类似于实际按照菜谱做饭的过程。同一个程序可以执行多次,每次都可以在内存中开辟一个独立的空间进行加载,从而产生多个进程。不同的进程也可以有自己独立的IO接口。操作系统的一个重要功能就是为进程提供便利,比如为进程分配内存空间,管理进程的信息等等,就好像在为我们准备一个漂亮的厨房。看一下进程首先,我们可以使用$ps命令来查询正在运行的进程,比如$ps-eopid,comm,cmd,执行结果如下图所示:(-e表示列出所有进程,-opid,comm,cmd表示我们需要PID,COMMAND,CMD信息)每一行代表一个进程。每行分为三列。***列的PID(processIDentity)是一个整数,每个进程都有一个***PID来代表自己的身份,进程也可以根据PID来识别其他进程。第二列COMMAND是这个进程的缩写。第三列CMD是进程对应的程序及其运行时带来的参数。(在第三列用方括号[]括起来的是一些。它们是内核功能的一部分,为了方便操作系统的管理而装扮成进程,我们不用考虑。)查看***行,PID为1,名为init。这个过程是通过执行文件(程序)/bin/init产生的。Linux启动时,init是系统创建的第一个进程,这个进程会一直存在,直到我们关闭电脑。这个过程特别重要,我们会反复提到它。如何创建进程实际上,当计算机开机时,内核(kernel)只是创建了一个init进程。Linux内核不提供直接创建新进程的系统调用。所有剩下的进程都是init进程通过fork机制创建的。新进程是从老进程复制自己得到的,即fork。fork是一个系统调用。进程存在于内存中。每个进程都在内存中分配了自己的一块空间(地址空间)。当进程fork时,Linux在内存中为新进程开辟一块新的内存空间,并将旧进程空间的内容复制到新空间,然后两个进程同时运行。旧进程成为新进程的父进程,相应地,新进程是旧进程的子进程。一个进程除了拥有PID之外,还有一个PPID(parentPID)来存放父进程的PID。如果我们顺着PPID继续往上追,总会发现它的源头是init进程。因此,所有的进程也形成了一个以init为根的树状结构。如下,我们查询当前shell下的进程:root@vamei:~#ps-opid,ppid,cmdPIDPPIDCMD169353101sudo-i1693916935-bash2377416939ps-opid,ppid,cmd可以看到第二个进程bash是第一个进程sudo子进程,第三个进程ps是第二个进程的子进程。您还可以使用$pstree命令来显示整个进程树:init─┬─NetworkManager─┬─dhclient│└─2*[{NetworkManager}]├─accounts-daemon────{accounts-daemon}├─acpid├─apache2─┬─apache2│└─2*[apache2───26*[{apache2}]]├─at-spi-bus-laun────2*[{at-spi-bus-laun}]├─atd├─avahi-daemon───avahi-daemon├─bluetoothd├─colord───2*[{colord}]├─console-kit-dae────64*[{console-kit-dae}]├─cron├─cupsd───2*[dbus]├─2*[dbus-daemon]├─dbus-launch├─dconf-service──2*[{dconf-service}]├─dropbox───15*[{dropbox}]├─firefox────27*[{firefox}]├─gconfd-2├─geoclue-master├─6*[getty]├─gnome-keyring-d──7*[{gnome-keyring-d}]├─gnome-terminal─┬─bash│├─bash──pstree│├─gnome-pty-help│├─sh──R──{R}│└─3*[{gnome-terminal}]fork通常作为函数调用。该函数会返回两次,将子进程的PID返回给父进程,将0返回给子进程。其实一个子进程可以随时查询自己的PPID就知道自己的父进程是谁,这样一对父子进程就可以随时互相查询了。通常在调用fork函数后,程序会设计一个if选择结构。当PID等于0时,表示该进程是子进程,让其执行一些指令,比如使用exec库函数(库函数)读取另一个程序文件,在当前进程空间执行(这实际上是我们使用fork的一个主要目的:为某个程序创建一个进程);而当PID为正整数时,则表示它是父进程,然后执行一些其他的指令。这样,子进程建立后,就可以执行与父进程不同的功能。子进程的终止(termination)当子进程终止时,会通知父进程,清除自己占用的内存,并在内核中留下自己的退出信息(exitcode,如果运行顺利,则为0;如果没有是错误或异常情况,整数>0)。在此消息中,解释了进程退出的原因。父进程有责任在得知子进程已终止时对子进程使用wait系统调用。这个等待函数可以从内核中取出子进程的退出信息,并清除该信息在内核中占用的空间。但是,如果父进程在子进程之前终止,则子进程将成为孤儿进程。孤儿进程会被init进程收养,init进程成为进程的父进程。init进程负责在子进程终止时调用wait函数。当然,一个糟糕的程序也可能导致子进程的退出信息留在内核中(父进程没有为子进程调用wait函数),而在这种情况下,子进程就变成了僵尸(zombie)过程。当大量僵尸进程堆积时,内存空间就会变得拥挤。进程和线程(thread)虽然在UNIX中,进程和线程是两个相关又不同的东西,但是在Linux中,线程只是一种特殊的进程。多个线程可以共享内存空间和IO接口。因此,进程是执行Linux程序的最佳方式。总结程序、进程、PID、内存空间子进程、父进程、PPID、fork、wait