最近一周学习了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说明:
在运行结果中有一些后缀字符,其意义分别为< (高优先级进程),N(低优先级进程),L(内存锁页,即页不可以被换出内存),s(该进程为会话首进程),l(多线程进程),+(进程位于前台进程组)。
例如:Ssl说明该进程处于可中断等待状态,且该进程为会话首进程,而且是一个多线程的进程。
2.linux系统的进程间通信有哪几种方式
1>管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用 。进程的亲缘关系通常是指父子进程关系。
2>有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信 。
3>信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4>消息队列( message queue ) : 消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5> 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6> 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
7>套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
3.进程控制
1>linux进程控制包括创建进程,执行进程,退出进程以及改变进程优先级等。
在linux系统中,用于对进程进行控制的系统调用有:
a.fork:用于创建一个新进程。
b.exit :用于终止进程
c.exec :用于执行一个应用程序
d.wait :将父进程挂起,等待子进程终止
e.getpid :获取当前进程的进程ID
f.nice : 该变进程的优先级
4.进程标识
1>Linux操作系统中,每个进程都是通过唯一的进程ID标识的。进程ID 是一个非负数。每个进程除了进程ID外还有一些其它信息,都可以通过相应的函数获得。
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.可执行程序时静态的,不变的,而内存映像随着程序的执行时在动态变化的