系统监控一

 

  • 介绍

这节课,我将会为大家介绍进程的有关概念—进程是如何产生的、进程和程序的区别、进程运行过程中会发生什么、子进程和父进程、进程状态等知识点。内容多且枯燥,请各位仔细听讲、认真思考。

 

1)进程的产生及pid

       我们知道软件是安装到磁盘上的,比如之前的课上我们安装过源码包的Apache,当时是将它安装到了/usr/local/apache目录下的,它一个应用程序,提供web服务,那么如果你不去触碰它,它就会静静的躺在磁盘上,做一个安静的美男子。不过,这通常是不可行的,因为我们安装了它,就是因为要用它,而要用它就需要运行它,那么好,在你执行了它的可执行文件后,内核中相关的管理程序就会进行相关的调度,比如为它分配内存空间用来存放程序的代码和数据、在程序运行期间还会为其分配CPU、IO、网络等资源。我们知道程序的运行是在内存中的,而到了内存中程序就不叫程序了,它改叫进程了!比如Apache运行后会产生httpd进程,nfs-utils运行后会产生rpc.nfsd进程等等,正是这些进程的运行才可以提供相应的服务,所以我们管这些进程叫做服务的daemon—也就是服务的守护进程。另外,我们同时也知道,内存中的进程可能会有多个,那么系统如何标示每一个进程呢?这个问题很简单,比如中国人民千千万,怎么来标示每一个公民呢à用公民身份证号,也就是ID;同样的,系统给每一个进程分配一个进程身份证号—叫做process id,简称pid。

       再有,我们知道对于国家来说,因为资源有限,人口数量达到一定规模后会触碰到人口红线,也就是说人口数量需要控制。同样的内存资源也是有限的,内存中的进程数量也不能无限制增长,也需要对进程数量进行控制。所以系统中pid数量也是是有限的。

 

 

2)进程和程序的区别

在之前我们说过程序,现在又说进程,那有的同学可能就要问了,程序和进程有什么区别,这里简单介绍一下:

       程序:是存放在磁盘上的一系列代码和数据的映像,是一个静止的实体。

       进程:可以理解为是一个执行中的程序,它是动态的实体。

 

       比如你是一个计算机教授,今天是你女儿的生日,你要为她准备一个惊喜---亲自制作一个蛋糕。可是你平时忙于工作,没有时间学习做蛋糕,只好赶鸭子上架,从网上搜了一下制作蛋糕的步骤:第一步分离蛋清和蛋黄;第二部,把白糖加到蛋清里,再加入一点盐,朝一个方向划圈圈,第三步怎么怎么样等等。

       那么好,有了制作步骤之后,你就开始行动了,你应该是来到厨房中,打鸡蛋;分离蛋清、蛋黄;加白糖、加盐,划圈圈…等等直到制作完毕。

 

       好的,在这个过程中,大家能分得清什么是程序,什么是进程了吗?这里说一下,在这里,菜谱相当于程序,那是有人提前编好的,程序也是啊,程序是程序员们提前写好的;而你从开始制作蛋糕,到制作中、再到制作完毕的整个活动的过程就相当于执行中的程序à进程。

 

       另外,进程是资源分配的基本单位,又是调度运行的基本单位。比如,用户运行一个程序,系统就创建相应的进程,并为它分配资源,包括内存空间、磁盘空间、I/O设备等。然后,把该进程放人进程的就绪队列。到了它的时钟周期,进程调度程序选中它,为它分配CPU以及其它有关资源,这时进程才真正运行起来。

 

3)进程的运行过程:上下文切换、缺页中断

       我们知道操作系统中处于运行队列的进程可能会有多个,比如你在听歌的同时还在欣赏一些美图,但我们说操作系统在同一个时间点只能运行一个进程,也就是说同一个时间点只有一个进程占有CPU资源,而进程一次可以占用多长时间的CPU呢?进程占用CPU的时间以时间片为计,单位通常是us/ns级的。

       那么,一个时钟周期那么短,如果遇到需要执行时间比较长的进程怎么办呢?我们采用的策略是让所用处在运行状态的进程排队—轮到谁谁去使用CPU资源。这类似我们上学的时候提着暖瓶排队打水一样,只不过队伍最前面的同学只能占用水龙头很短暂比如1s钟的时间,这一次肯定不能灌满暖瓶,怎么办呢?继续到队伍后面排队去…直到灌满暖瓶,任务完成。

       在其中,当你用完水龙头之后,下一个同学就要占用水龙头打水了,这我们可以称之为切换打水人;类似的,进程占用CPU一个时间片后,需要切换下一个进程,这个过程是怎样一个过程呢?

       值得注意的是,在操作系统中,CPU切换到另一个进程需要保存当前进程的状态并恢复另一个进程的状态:当前运行的任务转为就绪(或者挂起、删除)状态,另一个被选定的就绪任务成为当前任务。而这就叫做Content switch,上下文切换。上下文切换包括保存当前任务的运行环境,恢复将要运行任务的运行环境。

       另外,在进程运行期间如果用到数据,数据从哪里来呢?实际上,当程序启动的时候,Linux内核首先检查CPU 的缓存和物理内存,如果数据已经在内存里就忽略,如果数据不在内存里就引起一个缺页中断(Page Fault),这时也会触发进程阻塞,时间片让出,也就是产生上下文切换。之后,从硬盘中读取缺页,并把缺页缓存到物理内存里。这里缺页中断可分为主缺页中断(Major Page Fault)和次缺页中断(Minor Page Fault),要从磁盘读取数据而产生的中断是主缺页中断;而数据已经在内存中有了缓存,从内存缓存区中读取数据而产生的中断叫次缺页中断。time 命令可以用来查看某程序第一次启动的时候产生了多少主缺页中断和次缺页中断:

       

 

       从上面的内存缓存区(也叫文件缓存区File Buffer Cache)读取页比从硬盘读取页要快得多,所以Linux 内核希望能尽可能产生次缺页中断,并且能尽可能避免主缺页中断(从硬盘读),这样随着次缺页中断的增多,文件缓存区也逐步增大,直到系统只有少量可用物理内存的时候Linux 才开始释放一些不用的页。我们运行Linux 一段时间后会发现虽然系统上运行的程序不多,但是可用内存总是很少,这样给大家造成了Linux 对内存管理很低效的假象,事实上是Linux 把那些暂时不用的物理内存高效的利用起来做了缓存(内存缓存区)了。

 

 

 

 

4)子进程和父进程

       好的,了解了进程的运行过程,上下文切换以及缺页中断这几个重要的概念之后,我们再来看一下进程如何产生的。进程的产生有多种方式,比如调用fork()/vfork()/exec()/__clone()函数都可以产生进程。说一下fork函数,fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程。在一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的内存空间。然后把原来进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

       fork是派生的意思,也就是说进程是“生”出来的,是谁生出来的呢?是由调用fork函数的哪个进程生出来的。那么,这样就会存在父进程和子进程的关系了。Linux系统中的第一个进程也是唯一一个由kernel产生的进程是init进程,可以说它是所有进程的爹—>所有进程的父进程,或者是爷爷辈的进程(因为进程可以再次派生子进程)。另外,我们知道pid表示进程号,那么ppid就用来表示父进程号了。Ppid中第一个p就是parent的简写。

       这里需要注意的是:我们知道通常情况下进程不会永远运行下去,也就是说进程总有任务完成,退出的时候,那么当一个进程完成了它的工作,内核会释放进程所占用的大部分资源,包括打开的文件,占用的内存等。但是仍然为它保留一定的信息(包括它的进程号,退出状态,进程占用的CPU时间等)。这些信息保存到什么时候呢?保存到父进程通过wait() / waitpid()系统调用来取走之后才会被释放。

 

5)进程的状态

       好,我们前面提到过进程是执行中的程序,那么进程就会有运行、睡眠、挂起、退出、僵尸等几种不同的状态---这听起来跟我们人的生老病死是挺像的,其实是何止像啊,简直是一模一样。那么好,现在让我们看一看系统是怎么标示进程的状态的吧,我们以ps工具标示的进程状态来看:

       R运行runnable (on run queue),指的是进程正处在一个时钟周期内占用CPU资源。

       S sleeping,浅度睡眠状态:此时进程正在睡眠(被阻塞),等待资源的到来时唤醒,也可以通过其他进程信号或时钟中断唤醒,从而进入运行队列。(Linux 中使用TASK_INTERRUPTIBLE 宏表示此状态)。

       D uninterruptible sleep (usually IO),深度睡眠状态:其和浅度睡眠基本类似,但不可以被其他进程信号或时钟中断唤醒。通常是由进程等待IO引起。

       

       T  stopped or traced,暂停状态。进程暂停执行接受某种处理,通常在调试程序时需要将进程暂停。比如:debugger执行ptrace()系统调用来监控一个测试程序。

       Z僵死a defunct ("zombie") process。一个进程使用fork()创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的遗留信息,那么子进程的进程描述符仍然保存在系统中,也就是说进程的pid,退出状态,进程所占的cpu时间这些信息仍然保存在系统中。这种进程称之为僵死进程。

 

       需要注意的是:任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。如果父进程在子进程结束之前退出,那么这样的子进程成为孤儿进程,孤儿进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

 

       我们知道当进程退出后(init除外),系统仍然为其保留进程的描述符信息,也就是存在僵尸的状态。但这样可能会导致问题:如果父进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将会因为没有可用的进程号而导致系统不能产生新的进程. 这也是僵尸进程的危害,应当避免。

       严格地来说,僵死进程并不是问题的根源,他的罪魁祸首是产生出大量僵死进程的那个父进程,因为他没有调用wait或waitpid函数。因此,当我们寻求如何消灭系统中大量的僵死进程的时后,答案就是把产生大量僵死进程的那个元凶枪毙掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)--杀死僵尸进程们的父进程。在枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进程,而孤儿进程会被init进程接管,init进程会调用wait()来获取孤儿进程的进程描述符信息,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程就能瞑目而去,一路好走了。

 

好的,这些基本的概念有了之后,我们来实操一下,包括寻找子进程的父进程、杀死进程、调整进程的优先级、脱机管理问题等。

 

  • 知识点介绍

       

       ps、pstree –Aup查看父进程子进程关系

       ps –ef 、ps aux  、top à介绍进程的状态

              top :

                     -d:刷新间隔

                     -p pid: 查看某进程的信息

       杀死进程

              kill

              killall

              pgrep

              pkill

       脱机管理问题 nohup ping localhost  注销再登录查看即可验证

 

你可能感兴趣的:(系统监控)