程序、进程、线程
目录
- 程序空间分配
- 进程
- 进程内存分配
- PCB和进程
- PCB包括
- 进程状态
- 程序和进程的区别
- 程序运行成为进程的过程
- 线程
- 进程和线程的区别
- 参考材料
程序空间分配
站在高级语言的角度,根据APUE(Advanced Programming in the UNIX Environment UNIX环境高级编程),一个程序分为如下段:
- text:代码段,可执行指令的集合,在内存中被映射为只读,存放代码和常量
- data:初始化后数据的集合,存放在memory的可写空间内,存放已初始化的全局变量、静态变量、局部静态变量。
- bss:全局未初始化变量、静态未初始化变量的集合,不占用可执行文件的大小,只记载 需要多少空间 来 存储 未初始化数据,不分配实际的空间。
进程
进程内存分配
当程序被加载到内存单元时,需要另外两个域:堆域和栈域。一个正在运行的程序占用的内存区域被分为以下五部分:
- 代码段
- 初始化数据段:data段属于静态内存分配。
- 未初始化数据段:BSS段属于静态内存分配。
- 堆(heap):程序员通过 malloc或new 开辟 。保存函数内部动态分配内存,是一种用来保存程序信息的函数结构,更准确的说是保存程序的动态变量。堆是“先进先出”数据结构(管型)。只允许在堆的一端插入数据,在另一端移走数据。堆的地址空间“向上增加”,地址向上生长。
- 栈(stack):存放函数的局部变量(但不包括静态变量,静态变量存放在 数据段中)、参数以及返回值。是一种后进先出的数据结构(桶型)。栈的地址空间“向下减少”,地址向下生长。
名称 | 内容 |
---|---|
代码段 | 可执行代码、字符串常量 |
数据段 | 已初始化全局变量、已初始化全局静态变量、已初始化局部静态变量(以上若初始化为0 则在BSS段)、常量数据 |
BSS/ZI段 | 未初始化全局变量、未初始化全局静态变量、未初始化局部静态变量 |
栈 | 局部变量、函数参数(烫烫烫) |
堆 | 动态内存分配(屯屯屯) |
操作系统负责代码段、数据段和BSS段的加载,并将在内存中为这些段分配空间,栈也由操作系统分配和管理,不需要程序员管理,堆有程序员自己管理,申请和释放空间。
动态
#include <stdio h="">
const int g_A = 10; //代码段
int g_B = 20; //数据段
static int g_C = 30; //数据段
static int g_D; //BSS段
int g_E; //BSS段
char *p1; //BSS段
void main( )
{
int local_A; //栈
int local_B; //栈
static int local_C = 0; //静态局部变量 BSS段
static int local_D; //静态局部变量 BSS段
char *p3 = "123456"; //123456在代码段,p3在栈上
p1 = (char *)malloc( 10 ); //堆,分配得来得10字节的区域在堆区
strcpy( p1, "123456" ); //123456{post.content}放在常量区,编译器可能会将它与p3所指向 的"123456"优化成一块
printf("hight address\n");
printf("-------------栈--------------\n");
printf( "栈, 局部变量, local_A, addr:0x%08x\n", &local_A );
printf( "栈, 局部变量,(后进栈地址相对local_A低) local_B, addr:0x%08x\n", &local_B );
printf("-------------堆--------------\n");
printf( "堆, malloc分配内存, p1, addr:0x%08x\n", p1 );
printf("------------BSS段------------\n");
printf( "BSS段, 全局变量, 未初始化 g_E, addr:0x%08x\n", &g_E, g_E );
printf( "BSS段, 静态全局变量, 未初始化, g_D, addr:0x%08x\n", &g_D );
printf( "BSS段, 静态局部变量, 初始化, local_C, addr:0x%08x\n", &local_C);
printf( "BSS段, 静态局部变量, 未初始化, local_D, addr:0x%08x\n", &local_D);
printf("-----------数据段------------\n");
printf( "数据段,全局变量, 初始化 g_B, addr:0x%08x\n", &g_B);
printf( "数据段,静态全局变量, 初始化, g_C, addr:0x%08x\n", &g_C);
printf("-----------代码段------------\n");
printf( "代码段,全局初始化变量, 只读const, g_A, addr:0x%08x\n\n", &g_A);
printf("low address\n");
return;
}
PCB和进程
进程与进程控制块(PCB process control block)是一一对应的。系统为了管理进程设置的一个专门的数据结构,用它来记录进程的外部特征,描述进程的运动变化过程。系统利用PCB来控制和管理进程,PCB是系统感知进程存在的唯一标志。
Linux系统启动的时候,最先启动的进程 init进程,进程编号是1
init进程是所有进程的祖先。基于init进程才能运行其他进程
PCB包括
- 进程标识符 name :PID
- 进程当前的状态 status:为了管理方便,系统设计时将 状态相同 的进程组成一个队列,如 就绪进程队列、
- 进程对应的程序和数据地址,以便把PCB与其程序和数据联系起来。
- 进程资源清单:列出进程拥有的除CPU外的资源记录。如 I/O设备
- 进程优先级 priorty:反应进程的急迫程度,由用户和系统设置
- CPU现场保护区 cpustatus:
- 进程同步与通信机制:用于实现进程间 互斥、同步和通信所需的信号量等
- 进程所在队列PCB的链接字
- 与进程相关的其他信息
进程状态
- 运行状态R
- 可中断睡眠状态S
- 不可中断睡眠状态D
- 暂停状态T
- 僵尸状态Z
- 退出状态X
程序和进程的区别
- 程序时静态的,以文件的形式存在,是存放在磁盘中的可执行文件。
- 进程是动态的,有生命周期,在内存中运行。
- 操作系统由很多个进程组成
- 每个进程要想运行起来,都要被操作系统管理,每个进程都有自己的进程编号PID
程序运行成为进程的过程
双击运行一个可执行文件时
- 创建一个进程。
向操作系统申请一个进程,对32位系统来说 一个进程内存最大占 4G 2^32 - 把数据从硬盘 “拷贝/加载” 到进程的空间。
数据从一个存储器/硬盘(慢) 到 另外一个存储器/内存 DMA控制器模块做拷贝。DMA拷贝完向CPU发出DMA中断,通知CPU已经拷贝完成 - 由操作系统接管这个进程。
操作系统会进行 进程调度
线程
- 一个进程的多个执行线路叫线程。线程是“一个进程内部的控制序列”
- 一切进程至少都有一个执行线程
- 进程分配线程的时间。CPU的一个核心执行一个线程。
- 进程抢夺到时间片后,进程把时间分配给线程
- 一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程拥有它自己的变量和PID
- 在进程中创建一个新线程的时候,新的执行线程会拥有自己的堆栈、局部变量,但与它的创建者共享全局变量、文件描述符、信号处理器和当前的子目录状态
进程和线程的区别
- 进程是资源分配的基本单位,线程是CPU调度的基本单位
- CPU就像一座工厂,时刻在运行。
- 假设工厂电力有限,一次只能供给给一个车间使用。也就是说,一个车间开工时,其他车间都必须停工。(单个CPU一次只能运行一个任务)
- 一个车间里,可以有很多工人,他们协同完成一个任务(一个进程可以包括多个线程)
- 车间的空间工人是共享的(一个进程的内存空间是共享的,每个线程都可以使用这些共享内存)
- 每个车间的大小不同,有些车间只能容纳一个人(一个线程使用某些共享内存时,其他线程必须等待它结束才能使用)
- 防止他人进入的简单方法,就是在门口上锁(互斥锁、防止多个线程同时读写某个内存区域)
- 还有些房间,可以同时容纳N个人,人数大于N时,多出来的人只能在外面等待(某些内存区域,只能供给固定数目的线程使用)
- 解决方法是,在门口挂N把钥匙。进去的人取一把钥匙,出来再把钥匙挂回原处,后面的人发现钥匙空了,就必须在门口等待。(信号量,保证多个线程不会互相冲突)
参考材料
C程序内存分配
还没有评论,来说两句吧...