Linux 进程的理解

  1. 进程的描述

    通俗的讲,进程就是正在执行的程序或代码。我们知道,程序本身就是一堆代码,开始的时候存储在磁盘上,这时它是静态的、无生命的;只有当程序的代码被加载到内存中,代码才有了生命,才能被CPU动态的执行。

    问题是,现在的操作系统可以并行的执行多个程序,也就是内存中同时存放着多个程序的代码,为了方便管理,必须要合理的组织它们。方式就是由操作系统给每段代码添加一些元数据,这些元数据就是PCB,即任务控制块。

    不难理解的是,每个程序的代码实际上可以分为两部分:指令的数据。指令就是程序代码规定的各种操作;数据就是这些操作的对象。一个程序可以多次被加载到内存成为多个进程,比如同时打开两个vim分别编辑不同的文件。那么问题是:这时候有必要在内存中同时存放多个该程序的指令copy吗?答案是不必的。指令部分被设置为只读且允许系统中正在运行的两个或多个进程之间能够共享这一代码;而数据部分则被各个进程私有,不可以共享,比如每个vim都只能编辑自己的文件。

    那么PCB里面都有些什么呢?

    • 进程id。系统中每个进程有唯一的id,在C语言中用 pid_t 类型表示,其实就是一个非负整数。
    • 进程的状态,有运行、挂起、停止、僵尸等状态。
    • 进程切换时需要保存和恢复的一些CPU寄存器。
    • 描述虚拟地址空间的信息。
    • 描述控制终端的信息。
    • 当前工作目录(Current Working Directory)。
    • umask 掩码。
    • 文件描述符表,包含很多指向 file 结构体的指针。
    • 和信号相关的信息。
    • 用户id和组id。
    • 控制终端、Session和进程组。
    • 进程可以使用的资源上限(Resource Limit)。

      可见,操作系统为了控制进程,PCB的结构还是挺复杂的。

  2. 进程的创建

    经常听说“创建一个进程”,这到底是怎么回事呢?首先能想到的是,进程不是孙猴子,不可能自己蹦出来,肯定是别人“生”的。Linux中,进程是由父进程创建的,准确的说,是父进程中的代码的指令部分主动使用了创建进程的函数fork(),然后一个子进程就被“生”了出来。fork函数如何工作的呢?由于每个进程都有一个PCB,所以它首先要跟操作系统申请一个PCB(PCB是有限的),然后分配新进程内存,接着copy父进程的代码。实际上,fork就是很懒的复制了一下父进程,也就是说,fork函数调用过程中,内存中会会出现两个几乎一模一样的进程,当然除了进程号(它是唯一的)。进程的复制完成后,两个进程都有一个fork函数等待返回(注意,是返回,因为fork函数本身也是一条一条的代码,前面的一部分完成复制功能,子进程出现后,就到了返回的那部分代码了),而它们的返回结果是不同的(操作系统来控制返回结果):父进程中的fork返回子进程的pid;而子进程中的fork返回0;如果fork失败,返回的是-1。

    fork只是创造出了两个几乎一样的进程,它们运行的是同样的代码,这和一开始说的可不一样,因为我们创造一个新进程大多是用来执行新程序代码的。这时我们需要exec类函数,当进程调用一种exec函数时,该进程的程序代码完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。exec 系统调用执行新程序时会把命令行参数和环境变量表传递给 main 函数。环境变量表是进程所处系统环境的一个描述,一段代码要正常执行必然要使用各种系统资源,环境变量表就是对它的一个抽象。但是,exec类函数是需要显式调用的,子进程不会主动加载新的程序代码!所以,一般是在父进程的代码中,根据fork的返回值写一个分支,子进程的分支中显式调用exec。

  3. 进程的终止(参考:Linux C一站式编程)

    一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,操作系统在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用 wait 或 waitpid 获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量 $? 查看,因为Shell是它的父进程,当它终止时Shell调用 wait 或 waitpid 得到它的退出状态同时彻底清除掉这个进程。

    如果一个进程已经终止,但是它的父进程尚未调用 wait 或 waitpid 对它进行清理,这时的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。

    如果一个父进程终止,而它的子进程还存在(这些子进程或者仍在运行,或者已经是僵尸进程了),则这些子进程的父进程改为 init 进程。 init 是系统中的一个特殊进程,通常程序文件是 /sbin/init ,进程id是1,在系统启动时负责启动各种系统服务,之后就负责清理子进程,只要有子进程终止, init 就会调用 wait 函数清理它。

    僵尸进程是不能用 kill 命令清除掉的,因为 kill 命令只是用来终止进程的,而僵尸进程已经终止了。所以一颗可行的办法是,kill其父进程。

    wait 和 waitpid 函数的原型是:

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

若调用成功则返回清理掉的子进程id,若调用出错则返回-1。父进程调用 wait 或 waitpid 时可能会:

+ 阻塞(如果它的所有子进程都还在运行)。

+ 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。

+ 出错立即返回(如果它没有任何子进程)。

时间: 2024-08-26 21:50:02

Linux 进程的理解的相关文章

对Linux进程的理解

在本科期间学习过进程,网上也有很多关于进程的知识,但对于进程到底是什么一直没有让我满意的解答(以下截图来自网络,感觉对进程理解有问题,说得也很虚). 但今天找到这个博文感觉挺符合我的胃口:http://blog.csdn.net/yuqiang_ee_android/article/details/16889257 进程的本质 进程是Linux内核提供的一个环境(资源分配的基本单位),包括: (1)进程控制块(用于操作系统对进程的管理). (2)虚拟内存(分为好几段:正文段.数据段.BBS段.堆

[转载] 理解linux进程

原文: http://tobegit3hub1.gitbooks.io/understanding-linux-processes/content/index.html linux进程在我们上操作系统课程的时候老师就讲过了, 但是当时觉得是那么抽象不可理解. 随着对计算机技术理解的不断深入, 对进程的理解也越来越多了. 不过技术总是飞速的发展着, 特别是cgroup和namespace技术, 对linux进程的理解又多了一分神秘. 本文以非常简短的方式介绍了linux进程的方方面面, 虽然都没有

Linux进程地址空间的理解

对于Linux的虚拟内存的理解,这个例子算是一个很好的引导了,原文链接:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26683523&id=3201345 <Linux内核设计与实现>15章节给出的例子更详细些. ************************************************************************** 先介绍Linux进程地址空间的数据结构更方便理解,

Linux进程环境

1.进程是什么 正在运行的程序,就是进程,进程是动态的,程序是静态的:进程在执行过程中具有状态的变化. 进程具有三个不同的状态 : 运行.就绪.阻塞; 三模型分析 : 查看进程的命令ps -ef 和 top,   kill PID(杀死进程) 运行态:进程获得CPU资源,能够使用CPU计算机程序中的指令,当CPU的使用时间用完时,则进入就绪状态. 就绪态:其它资源已经就绪,只差CPU资源. 阻塞态:处理CPU之外的其它资源还未就绪. 进程的调度:进程启动时处于就绪状态,为了调度进程对CPU的使用

Linux系统的理解及学习Linux内核的心得

作业列表      linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作 linux内核分析作业:操作系统是如何工作的进行:完成一个简单的时间片轮转多道程序内核代码 linux内核分析作业3:跟踪分析Linux内核的启动过程 linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 linux内核分析作业5:分析system_call中断处理过程 linux内核分析作业6:分析Linux内核创建一个新进程的过程 Linux内核分析作业7:L

Linux进程的栈和进程中函数的栈帧

主要理解了Linux进程的栈和函数的栈帧的区别和联系 上图中刻画的是一个Linux进程的结构图,其中本文想要关注的栈如上图所示是栈底在0xc0000000地址递减的一块进程内存区域 Linux系统中为每个进程分配的实际大小未做深究 Linux进程的栈的整体认知就是这么多,即 1)栈底在0xc0000000(并不需要栈底指针,函数的栈帧才需要栈底指针) 2)栈的增长方向,地址递减方向 3)栈顶指针esp 4)大小未知(不是本文关注的重点) 5)里面存放的内容是什么呢? 另一个概念是上文中提到的函数

Linux性能及调优指南(翻译)之Linux进程管理

译文如下:1.1 Linux进程管理 进程管理是操作系统的最重要的功能之一.有效率的进程管理能保证一个程序平稳而高效地运行. Linux的进程管理与UNIX的进程管理相似.它包括进程调度.中断处理.信号.进程优先级.上下文切换.进程状态.进度内存等. 在本节中,我们将描述Linux进程管理的基本原理的实现.它将更好地帮助你理解Linux内核如何处理进程及其对系统性能的影响. 1.1.1 什么是进程?一个进程是一个运行在处理器的程序的一个实例.该进程使用Linux内核能够处理的任何资源来完成它的任

Linux 进程和作业管理

Linux进程和作业管理 ================================================================= 内核的功能:进程管理(进程调用,保存现场,恢复现场).文件系统.网络功能.驱动功能.安全功能 process:运行中的程序的一个副本: 存在生命周期 linux内核存储进程信息的固定格式:task struct(任务结构) 多个任务的task struct组件的链表:task list(任务列表) 进程创建: init进程是由内核创建,其余

Linux 进程通信之 ——信号和信号量总结

如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一样.通信是一个广义上的意义,不仅仅指传递一些massege.他们的用法是基本相同的,所以仅仅要掌握了一种的用法,然后记住其他的用法就能够了. 1. 信号       在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也能够用来做其他的事      情,可是我还不知道做什么.       信号和信号量是