Linux从程序到进程
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!
计算机如何执行进程呢?这可以说是计算机运行的核心问题。即使我们已经编写好程序,但程序是死的文本,只有成为活的进程才能带来产出。我们已经从Linux进程基础中了解了进程的一些概况。现在我们看一下从程序到进程的漫漫征程。
1. 一段程序
我们下面展示一个简单的C语言程序,我们假设该程序已经经过编译,成为可执行的程序文件vamei.exe。
#include <stdio.h>int glob=0; /*global variable*/void main(void) {int main1=5; /*local variable of main()*/int main2; /*local variable of main()*/main2 = inner(main1); /* call inner() function */printf("From Main: glob: %d \n", glob);printf("From Main: main2: %d \n", main2);
}int inner(int inner1) { /*inner1 is an argument, also local to inner()*/int inner2=10; /*local variable of inner()*/printf("From inner: glob: %d \n", glob);return(inner1+inner2);
}
(选取哪一个语言或者具体的语法并不是关键,大部分语言都可以写出类似上面的程序。在看python教程的读者也可以利用python的函数结构和print写一个类似的python程序。当然,还可以是C++,Java,Objective-C等等。选用C语言的原因是:它是为UNIX而生的语言。)
在main()函数中,我们调用了inner()函数,并在inner范围内进行了一次printf()以便输出。在从该函数返回之后,我们又在main()的范围之内进行了两次printf()。
我们还要注意各个变量的作用范围。简单地说,变量可以分为全局变量和局部变量。在所有函数之外声明的变量为全局变量,比如glob,在任何时候都可以使用。在函数内定义的变量为局部变量,只能在该函数的作用域(range)内使用,比如说我们在inner()工作的时候不能使用main()函数中声明的main1变量,而在main()中我们无法使用inner()函数中声明的inner2变量。
我们并不在意这个程序的具体功能和结果。我们关心的是这个程序的运行过程。下图为该程序的运行过程,以及各个变量的作用范围:
运行流程
2. 进程空间
为了进一步了解上面的程序的运行,我们还需要知道进程是如何使用内存的。当程序文件运行为进程的时候,进程在内存中得到空间(进程自己的小房间)。每个进程空间按照如下方式分为不同区域:
内存空间
Text区域用来储存指令(instruction),来告诉程序每一步的操作。Global Data用于存放全局变量,stack用于存放局部变量,heap用于存放动态变量 (dynamic variable. 程序利用malloc系统调用,直接从内存中为dynamic variable开辟空间)。Text和Global data在进程一开始的时候就确定了,并在整个进程中保持固定大小。
Stack(栈)以stack frame为单位。当程序调用函数的时候,比如main()函数中调用inner()函数,stack会向下增长一个stack frame。Stack frame中存储该函数的参数和局部变量,以及该函数的返回地址(return address)。此时,计算机将控制权从main()转移到inner(),inner()函数处于激活(active)状态。位于Stack最下方的frame和Global Data就构成了当前的环境(context)。激活函数可以从中调用需要的变量。典型的编程语言都只允许你使用位于stack最下方的frame ,而不允许你调用其它的frame (这也符合stack结构“先进后出”的特征。但也有一些语言允许你调用stack的其它部分,相当于允许你在运行inner()函数的时候调用main()中声明的局部变量,比如Pascal)。当函数又进一步调用另一个函数的时候,一个新的frame会继续增加到stack下方,控制权转移到新的函数中。当激活函数返回的时候,会从stack中弹出(pop,就是读取并删除)该frame,并根据frame中记录的返回地址,将控制权交给返回地址所指向的指令(比如从inner()函数中返回,继续执行main()中赋值给main2的操作)。
下图是stack在运行过程中的变化,箭头表示stack增长的方向,每个方块代表一个stack frame。开始的时候我们有一个为main()服务的frame,随着调用inner(),我们为inner()增加一个frame。在inner()返回时,我们再次只有main()的frame,直到最后main()返回,其返回地址为空,所以进程结束。
stack变化
在进程运行的过程中,通过调用和返回函数,控制权不断在函数间转移。进程可以在调用函数的时候,原函数的stack frame中保存有在我们离开时的状态,并为新的函数开辟所需的stack frame空间。在调用函数返回时,该函数的stack frame所占据的空间随着stack frame的弹出而清空。进程再次回到原函数的stack frame中保存的状态,并根据返回地址所指向的指令继续执行。上面过程不断继续,stack不断增长或减小,直到main()返回的时候,stack完全清空,进程结束。
当程序中使用malloc的时候,heap(堆)会向上增长,其增长的部分就成为malloc从内存中分配的空间。malloc开辟的空间会一直存在,直到我们用free系统调用来释放,或者进程结束。一个经典的错误是内存泄漏(memory leakage), 就是指我们没有释放不再使用的heap空间,导致heap不断增长,而内存可用空间不断减少。
由于stack和heap的大小则会随着进程的运行增大或者变小,当stack和heap增长到两者相遇时候,也就是内存空间图中的蓝色区域(unused area)完全消失的时候,进程会出现栈溢出(stack overflow)的错误,导致进程终止。在现代计算机中,内核一般都会为进程分配足够多的蓝色区域,如果我们即时清理的话,stack overflow是可以避免的。但是,在进行一些矩阵运算的时候,由于所需的内存很大,依然可能出现stack overflow的情况。一种解决方式是增大内核分配给每个进程的内存空间。如果依然不能解决问题的话,我们就需要增加物理内存。
Stack overflow可以说是最出名的计算机错误了,所以才有IT网站(stackoverflow.com)以此为名。
在高级语言中,这些内存管理的细节对于用户来说不透明。在编程的时候,我们只需要记住上一节中的变量作用域就可以了。但在想要写出复杂的程序或者debug的时候,我们就需要相关的知识了。
3. 进程附加信息
除了上面的信息之外,每个进程还要包括一些进程附加信息,包括PID,PPID,PGID(参考Linux进程基础以及Linux进程关系)等,用来说明进程的身份、进程关系以及其它统计信息。这些信息并不保存在进程的内存空间中。内核会为每个进程在内核自己的空间中分配一个变量(task_struct结构体)以保存上述信息。内核可以通过查看自己空间中的各个进程的附加信息就能知道进程的概况,而不用进入到进程自身的空间 (就好像我们可以通过门牌就可以知道房间的主人是谁一样,而不用打开房门)。每个进程的附加信息中有位置专门用于保存接收到的信号(正如我们在Linux信号基础中所说的“信箱”)。
4. fork & exec
现在,我们可以更加深入地了解fork和exec(参考Linux进程基础)的机制了。当一个程序调用fork的时候,实际上就是将上面的内存空间,包括text, global data, heap和stack,又复制出来一个,构成一个新的进程,并在内核中为改进程创建新的附加信息 (比如新的PID,而PPID为原进程的PID)。此后,两个进程分别地继续运行下去。新的进程和原有进程有相同的运行状态(相同的变量值,相同的instructions...)。我们只能通过进程的附加信息来区分两者。
程序调用exec的时候,进程清空自身内存空间的text, global data, heap和stack,并根据新的程序文件重建text, global data, heap和stack (此时heap和stack大小都为0),并开始运行。
(现代操作系统为了更有效率,改进了管理fork和exec的具体机制,但从逻辑上来说并没有差别。具体机制请参看Linux内核相关书籍)
这一篇写了整合了许多东西,所以有些长。这篇文章主要是概念性的,许多细节会根据语言和平台乃至于编译器的不同而有所变化,但大体上,以上的概念适用于所有的计算机进程(无论是Windows还是UNIX)。更加深入的内容,包括线程(thread)、进程间通信(IPC)等,都依赖于这里介绍的内容。
总结:
函数,变量的作用范围,global/local/dynamic variables
global data, text,
stack, stack frame, return address, stack overflow
heap, malloc, free, memory leakage
进程附加信息, task_struct
fork & exec
相关文章:

struct stat结构体的详解和用法
[cpp] view plaincopy//! 需要包含de头文件 #include <sys/types.h> #include <sys/stat.h> S_ISLNK(st_mode):是否是一个连接.S_ISREG(st_mode):是否是一个常规文件.S_ISDIR(st_mode):是否是一个目录S_ISCHR(st_mode)&a…

反射setaccessible_反射技术
反射机制作用动态的加载类、动态的获取类的信息(属性,方法,构造器)动态构造对象动态调用类和对象的任意方法、构造器动态调用和处理属性获取泛型信息处理注解获取Class对象的方式有哪些?1. Class clazz Class.forName(全限定类名);2. 类名.c…

pthread_cond_wait()函数的详解
http://hi.baidu.com/tjuer/item/253cc6d66b921317d90e4483 了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。 首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象&#x…

CentOS7下python虚拟环境
搭建python虚拟环境 1.我们先创建一个隐藏目录 .virtualenvs,所有的虚拟环境都放在此目录下 :mkdir /root/.virtualenvs 2.安装虚拟环境 确认pip:whereis pip3 pip3 install virtualenv 安装virtualenvwrapper,为避免超时错误&…

WCDMA中的URA和LA/RA
1、关于URA的概念:URA(UTRAN Registration Area)是UTRAN内部区域的划分适用于UE处于RRC连接状态的情形,而且只能在UTRAN端使用(比如由UTRAN发起的寻呼)。一 个URA包含了一个或多个Cell,具体由决…

背景音乐的实现
通过利用Service来实现该功能将要播放的歌曲放入raw文件夹中[html] view plaincopy <strong>新建一个AudioService 类,<span style"font-family: Arial, Helvetica, sans-serif;">AudioService 类</span><span style"font-fami…

打牌软件可以控制吗_使用crm软件真的可以帮助企业省钱吗
使用crm软件真的可以帮助企业省钱吗大多数企业管理者认为:“客户关系系统有什么用?真的可以帮助企业发展吗?自己做一套excel版本不就行了”其实,不以为然,当我们去寻找用户时或者管理用户需要工作人员做一些繁琐的事情会极大的降低员工的工…

MS_SQL_获取字符串最后出现的字符串及位置
一.如:6.7.8.2.3.4.x得到最后一个.后面的字符串: declare str1 varchar(50) set str16.7.8.2.3.4.x select REVERSE(SUBSTRING(REVERSE(str1),1,CHARINDEX(.,REVERSE(str1))-1)) -------- string:x-- --------------------------------------…

redis(3)-redis基本类型
在redis安装目录下存在redis自带的客户端,启动即可使用。如果设置了密码,需要输入auth 123456进行验证。123456为密码。 redis的基本数据类型: 1.字符串类型(String) redis 127.0.0.1:6379> SET mykey "redis" OK redis 1…

虚函数与虚继承寻踪
封装、继承、多态是面向对象语言的三大特性,熟悉C的人对此应该不会有太多异议。C语言提供的struct,顶多算得上对数据的简单封装,而C的引入把struct“升级”为class,使得面向对象的概念更加强大。继承机制解决了对象复用的问题&…

信息记录拉取失败_天猫入驻为什么失败?猫店侠做详细解读
天猫入驻为什么失败?这是很多商家都想要知道的一件事情,猫店侠想说其实这也很正常,只要不是一味盲目的入驻,就还有机会。首先失败商家要看看失败的反馈内容,看看是哪方面不达标,再着重进行补充,…

Linux查看目录挂载点
用命令 df 即可 # df /var/lib/ Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda3 135979984 66905292 62055896 52% /加上-kh更容易看些: # df /var/lib/ -kh Filesystem Size Used Avail Use% Mounted o…

Android:你好,androidX!再见,android.support
190325 补充:莫名问题的解决 181106 补充:修改未迁移成功的三方库 1、AndroidX简介 点击查看Android文档中对androidx的简介 按照官方文档说明 androidx 是对 android.support.xxx 包的整理后产物。由于之前的support包过于混乱,所以…

设计模式:简单工厂、工厂方法、抽象工厂之小结与区别
简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。 本文是本人对这三种模式学习后的一个小结以及对他们之间的区别的理解…

云计算和大数据时代网络技术揭秘(八)数据中心存储FCoE
数据中心存储演化——FCoE数据中心三大基础:主机 网络 存储在云计算推动下,存储基础架构在发生演变传统存储结构DAS、SAN在发展中遇到了布线复杂、能耗增多的缺点(原生性),需要对架构做根本的改变。FCoE是业界无可争议…

在plsql里面怎么去掉空行_盐渍樱花怎么做?详细做法告诉您,一年都不会坏,学会再也不用买...
盐渍樱花怎么做?详细做法告诉您,一年都不会坏,赶紧收藏学会它!樱花季说的就是现在,虽然到了飘落的季节,但是还是到处可见的樱花朵朵。俗话说:花无百日红。真的是啊,每年的三四月是最…

Linux基础知识——常用shell命令介绍(三)
一、改变文件权限 chmod:change mode 语法:# chmod [选项-option] 权限 FILE 选项:-R 递归修改权限 --reference 参照文件或目录给予权限 权限定义方式: 1.同时修改三类用户的权限: 8进制数字方式 # chmod 666 /abc /*将/abc的权限改为…

免费版CloudFlare CDN基本设置参考
CDN有很多,网上都有介绍,用户比较多的CloudFlare CDN大家都知道,配置起来也比较简单,合理的配置,才能提升网站的速度和网站安全。不同的网站需求配置不一样,以下是我的配置情况,仅供参考。 我网…

从Java类库看设计模式
//From http://www.uml.org.cn/j2ee/201010214.asp 很多时候,对于一个设计来说(软件上的,建筑上的,或者它他工业上的),经验是至关重要的。好的经验给我们以指导,并节约我们的时间;坏的经验则给我们以借鉴,可…

method=post 怎么让查看源代码看不到_网站文档不能复制怎么办?教你3个小妙招,1分钟轻松化解...
不知道大家平常在查找资料时,碰到网页资料不能下载时,是怎么样进行处理的。那么笔者今天就来分享我查找不能复制文档时,所用的3个小妙招,帮助轻松化解,一起来看看吧。1、保存网页当我们遇到一个不能直接复制的文档&…

Missing number
题目链接:http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id114468 题目大意: 多组案例T,每个案例含n2个数据,这n2个数据构成一组有序列,现在已知这组数据中的n个,请找出缺失的两个数据。 …

[leetcode]Surrounded Regions @ Python
原题地址:https://oj.leetcode.com/problems/surrounded-regions/ 题意: Given a 2D board containing X and O, capture all regions surrounded by X. A region is captured by flipping all Os into Xs in that surrounded region. For example, X X …
Android Drawable 详解(教你画画!)
参考 1、Android中的Drawable基础与自定义Drawable2、android中的drawable资源3、Android开发之Shape详细解读 Drawable分类 Noxml标签Class类含义1shapeShapeDrawable特定形状,模型的图样2selectorStateListDrawable不同状态选择不同的图样3layer-listLayerDrawabl…

Carrier frequency 和 EARFCN的关系
Carrier frequency 和 EARFCN的关系 我们处理UE log时,看到LTE cell 都是用EARFCN/PCI来标示的,那么EARFCN和frequency 之间是什么关系呢? 1. EARFCN: 缩写: E-UTRA Absolute Radio Frequency Channel Number, 取值范围: 0…

十五天精通WCF——第六天 你必须要了解的3种通信模式
十五天精通WCF——第六天 你必须要了解的3种通信模式 原文:十五天精通WCF——第六天 你必须要了解的3种通信模式wcf已经说到第六天了,居然还没有说到这玩意有几种通信模式,惭愧惭愧,不过很简单啦,单向,请求-响应&#…

从未在一起更让人遗憾_明明是真爱,却又不能在一起
深爱一个人,若是无缘成为夫妻相偎相依在一起,在分开的很长一段时间里,一定会在深夜难眠,在梦中哭醒,因为你太爱他,太想他。人生那么长,在我们的一生中,总会有一个人,在你…

使用 IntraWeb (8) - 系统模板
我们可以自定义系统错误模板, 编辑 IWError.html 放到模板文件夹后, 它将替换默认的模板.{在主页面, 这是要模拟一个系统错误} procedure TIWForm1.IWButton1Click(Sender: TObject); beginRelease; end;修改前后的 IWError.html 对比:我想办法抠出了 IWError.html 源文件, 从里…

Java中常量定义的几种方式
编程中使用常量的优点: 常量提取出来有利于代码阅读,而且下次再做这种判断不用手写或复制并且提高代码的复用率,方便修改,直接通过常量类就能得到。不过我觉得提取出来并不会有利于代码性能提升,因为常量分配在内存的常…

PCT-36.523
LTE v 36 523-1:Part 1: Protocol conformance specification 定义了每个Case 运行的流程 v 36.523-2:Part 2: Implementation Conformance Statement(ICS) proforma specification. Table 4-1 定义了每个Case的详细背景: 1. 最早那个release 引入的这一c…

测试笔试题之相关概念
1、对手机软件的压力测试通常包括: (1)存储压力 (2)响应能力压力 (3)网络流量压力 (4)边界压力 2、针对手机应用软件的系统测试,我们通常从如下几个角度开展&…