fenxinzi 2011-02-10
最近一周学习了Linux 进程编程的知识,现对其总结如下。
在第一部分中我们先对进程的基本概念以及在Linux 中是如何来现实进程的进行介绍
Tiger-John说明 :
许多人在学习中只注重如何编程,却忘了注重原理,不去深究其基本原理。其实操作系统的原理就好 比金庸武侠小说的内功一样,而所有的具体实现如:Linux操作系统,uc/os操作系统都只是武功招式而已。如果我们内功学的很好的话,再来学习具体的实现过程是很快的。而且也会对其知识有更加本质的了解。
一.进程的基本概念:
1.为什么计算机操作系统要引进进程:
在操作系统中引入进程的目的是为了使多个程序并发执行 ,以改善资源利用率及提高系统吞吐量。
2.进程的概念:
进程是程序的一次执行,进程是拥有资源的最小单位和调度单位(在引入线程的操作系统中,线程是最小的调度单位)
3.进程由什么组成
进程由进程控制块(PCB),数据,程序3部分组成。其中PCB是进程的灵魂。
4.进程的状态:
进程的三种最基本的状态是:运行态(running),就绪态(readying), 阻塞态(block)
5.进程和程序的区别:
进程和程序的主要区别是进程是动态的,程序是静态的。进程时运行中的程序,程序是一些保存在硬盘上的可执行的代码。
6.进程的优点和缺点
(任何事物都是有其两面性。我们在学习的时候要注意其优点和缺点。人们也就再发现事物缺点的过程中,不断的去改善它,从而引入了新的事物。在操作系统的学习过程中,我们会发现很多这样的例子。人们在不断追求完美的过程中,不断的引入新的知识点--进程和线程的出现就足可以说明这一切)
优点:使多个程序并发执行
缺点:程序并发执行时付出了巨大的时空开销,每个进程在进行切换时身上带了过多的“累赘”导致系统效率降低。
于是人们为了解决这个缺点想到让进程在并行时不拥有资源---从而引入了线程的概念:即线程本身不拥有资源或者是很少的资源,进程只是拥有资源的基本单位,线程是调度的基本单位
7.线程的引入:
在操作系统中引入线程则是为了减少程序并发执行时所付出的时空开销,使操作系统具有更好的并发性。
二.Linux中是如何具体实现进程和线程
1.在linux中通过task_struct结构体来描述进程的PCB,我们可以在include/linux/sched.h中看到对进程task_struct的定义和进程状态的描述。
1>linux中的进程状态
a.运行状态:进程正在运行或在运行队列中等待运行 。
b.可中断等待状态:进程正在等待某个事件完成(如等待数据到达)。等待过程中可以被信号或定时器唤醒。
c.不可中断等待状态:进程正在等待某个事件完成并且等待中不可以被信号或定时器唤醒,必须一直等待到事件发生。
d.僵死状态:进程已终止,但进程描述符依然存在,直到父进程调用wait()函数后释放。
e.停止状态:进程因为收到SINSTOP,SIGSTP,SIGTIN,SGIOU信号后停止运行或者该进程正在被跟踪。
Tiger-john说明:
1在include/linux/sched.h 中我们可以看到Linxu中进程状态的具体实现:
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_ZOMBIE 4
#define TASK_STOPPED 8
其中:
TASK_RUNNING是就绪态,进程当前只等待CPU资源。
TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE都是阻塞态,进程当前正在等待除CPU外的其他系统资源;前者可以被信号唤醒,后者不可以。
TASK_ZOMBIE是僵尸态,进程已经结束运行,但是进程控制块尚未注销。
TASK_STOPPED是挂起状态,主要用于调试目的。进程接收到SIGSTOP信号后会进入该状态,在接收到SIGCONT后又会恢复运行。
2.我们可以在终端中通过命令ps或pstree查看当前系统中的进程
用ps命令可以查看进程的当前状态。运行状态为R,可中断等待状态为S,不可中断等待状态为D,僵死状态为Z,停止状态为T。
实例:
think@Ubuntu:~$ ps -eo pid,stat
PID STAT
1 Ss
2 S
3 S
37 SN
364 Ss
371 S<
442 S<s
1060 Sl
1081 Ssl
1085 Ssl
1203 Ss+
3782 Ss
3803 R+
Tiger-John说明:
2>主要的函数有:
pid_t getpid(void) :获得进程ID
pid_t getppid(void):获得进程父进程的ID
pid_t getuid(void) :获得进程的实际用户ID
pid_t geteuid(void) :获得进程的有效用户ID
pid_t getgid(void) : 获得进程的实际组ID
pid_t getegid(void) 获得进程的有效组ID
Tiger-Johen说明:
这些函数的声明在 sys/types.h和unistd.h 头文件中。
2>用户ID和组ID的相关概念
a.实际用户ID(uid) :标识运行该进程的用户
b.有效用户ID( euid): 标识以什么用户身份来运行进程。
例如:一个普通用户A,运行了一个程序,而这个程序是以root 身份来运行的,着程序运行时就具有root 权限。此时,实际用户ID时A用户的ID,而有效用户ID是root用户ID
3>函数实例:
表头文件:#include <unistd.h>
#include<sys/types.h>
函数定义:pid_t getpid(void)
函数说明:getpid()用来取得目前进程的进程识别码,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题
返回值:目前进程的进程识别码
函数实例:
#include<stdio.h>
#include<sys/types.h>
#include <unistd.h>
main()
{
printf("pid = %d\n", getpid());
}
三.进程的内存映像
1.Linux下程序转化成进程
a.Linux下C程序的生成分为4个阶段:
预编译
编译
汇编
链接
Tiger-Johen说明:
编译器gcc进过预编译,编译,汇编3个步骤将源程序文件转换为目标文件。
b.当程序执行时,操作系统将可执行程序复制到内存中。程序转化为进程通常需要经过以下步骤:
内核将程序读入内存,为程序分配内存空间
内核为该进程分配进程标识符(PID)和其他资源
内核为该进程保存PID及相应的状态信息,把进程放到运行队列中等待执行。程序转化为进程后就可以被操作系统的调度程序执行了。
2.进程的内存映像
a.进程的内存映像是指内核在内存中如何存放可执行程序文件 。在将程序转化为进程的过程中,操作系统将可执行程序由硬盘复制到内存中。
b.linux下程序映像的一般布局如下:(从低地址到高地址)
1>代码段:代码段是只读的,可被��个进程共享。
2>数据段: 存储已被初始化的变量,包括全局变量和已被初始化的静态变量。
3>未初始化数据段:存储未被初始化的静态变量,它也被称为bss段
4>堆:用于存放程序运行中动态分配的变量
5>栈:用户函数调用,保存函数的返回地址,函数的参数,函数内部定义的局部变量。
Tiger-Johen说明:
可执行程序和内存映像的区别 :
a.可执行程序位于磁盘中而内存映像位于内存中;
b.可执行程序没有堆栈,因为程序 被加载到内存中才会分配堆栈;
c.可执行程序虽然也有未初始化数据段但它并不被储存在位于硬盘中的可执行文件中;
d.可执行程序时静态的,不变的,而内存映像随着程序的执行时在动态变化的