程序、进程、线程

本文从高级语言角度介绍程序空间分配,包括代码段、数据段、BSS段等。阐述进程内存分配,如堆域和栈域。介绍进程控制块(PCB)及其包含内容、进程状态,区分程序和进程。还讲解了线程概念,对比进程和线程的区别,如进程是资源分配单位,线程是CPU调度单位。

程序空间分配

站在高级语言的角度,根据APUE(Advanced Programming in the UNIX Environment UNIX环境高级编程),一个程序分为如下段:

  1. text:代码段,可执行指令的集合,在内存中被映射为只读,存放代码和常量
  2. data:初始化后数据的集合,存放在memory的可写空间内,存放已初始化的全局变量、静态变量、局部静态变量。
  3. 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包括

  1. 进程标识符 name :PID
  2. 进程当前的状态 status:为了管理方便,系统设计时将 状态相同 的进程组成一个队列,如 就绪进程队列、
  3. 进程对应的程序和数据地址,以便把PCB与其程序和数据联系起来。
  4. 进程资源清单:列出进程拥有的除CPU外的资源记录。如 I/O设备
  5. 进程优先级 priorty:反应进程的急迫程度,由用户和系统设置
  6. CPU现场保护区 cpustatus:
  7. 进程同步与通信机制:用于实现进程间 互斥、同步和通信所需的信号量等
  8. 进程所在队列PCB的链接字
  9. 与进程相关的其他信息

进程状态

  1. 运行状态R
  2. 可中断睡眠状态S
  3. 不可中断睡眠状态D
  4. 暂停状态T
  5. 僵尸状态Z
  6. 退出状态X

程序和进程的区别

  1. 程序时静态的,以文件的形式存在,是存放在磁盘中的可执行文件。
  2. 进程是动态的,有生命周期,在内存中运行。
  3. 操作系统由很多个进程组成
  4. 每个进程要想运行起来,都要被操作系统管理,每个进程都有自己的进程编号PID

程序运行成为进程的过程

双击运行一个可执行文件时

  1. 创建一个进程。
    向操作系统申请一个进程,对32位系统来说 一个进程内存最大占 4G 2^32
  2. 把数据从硬盘 “拷贝/加载” 到进程的空间。
    数据从一个存储器/硬盘(慢) 到 另外一个存储器/内存 DMA控制器模块做拷贝。DMA拷贝完向CPU发出DMA中断,通知CPU已经拷贝完成
  3. 由操作系统接管这个进程。
    操作系统会进行 进程调度

线程

  1. 一个进程的多个执行线路叫线程。线程是“一个进程内部的控制序列”
  2. 一切进程至少都有一个执行线程
  3. 进程分配线程的时间。CPU的一个核心执行一个线程。
  4. 进程抢夺到时间片后,进程把时间分配给线程
  5. 一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程拥有它自己的变量和PID
  6. 在进程中创建一个新线程的时候,新的执行线程会拥有自己的堆栈、局部变量,但与它的创建者共享全局变量、文件描述符、信号处理器和当前的子目录状态

进程和线程的区别

  1. 进程是资源分配的基本单位,线程是CPU调度的基本单位
  2. CPU就像一座工厂,时刻在运行。
  3. 假设工厂电力有限,一次只能供给给一个车间使用。也就是说,一个车间开工时,其他车间都必须停工。(单个CPU一次只能运行一个任务)
  4. 一个车间里,可以有很多工人,他们协同完成一个任务(一个进程可以包括多个线程)
  5. 车间的空间工人是共享的(一个进程的内存空间是共享的,每个线程都可以使用这些共享内存)
  6. 每个车间的大小不同,有些车间只能容纳一个人(一个线程使用某些共享内存时,其他线程必须等待它结束才能使用)
  7. 防止他人进入的简单方法,就是在门口上锁(互斥锁、防止多个线程同时读写某个内存区域)
  8. 还有些房间,可以同时容纳N个人,人数大于N时,多出来的人只能在外面等待(某些内存区域,只能供给固定数目的线程使用)
  9. 解决方法是,在门口挂N把钥匙。进去的人取一把钥匙,出来再把钥匙挂回原处,后面的人发现钥匙空了,就必须在门口等待。(信号量,保证多个线程不会互相冲突)

参考材料

C程序内存分配

内容概要:本文系统阐述了企业新闻发稿在生成式引擎优化(GEO)时代下的全渠道策略与效果评估体系,涵盖当前企业传播面临的预算、资源、内容与效果评估四大挑战,并深入分析2025年新闻发稿行业五大趋势,包括AI驱动的智能化转型、精准化传播、首发内容价值提升、内容资产化及数据可视化。文章重点解析央媒、地方官媒、综合门户和自媒体四类媒体资源的特性、传播优势与发稿策略,提出基于内容适配性、时间节奏、话题设计的策略制定方法,并构建涵盖品牌价值、销售转化与GEO优化的多维评估框架。此外,结合“传声港”工具实操指南,提供AI智能投放、效果监测、自媒体管理与舆情应对的全流程解决方案,并针对科技、消费、B2B、区域品牌四大行业推出定制化发稿方案。; 适合人群:企业市场/公关负责人、品牌传播管理者、数字营销从业者及中小企业决策者,具备一定媒体传播经验并希望提升发稿效率与ROI的专业人士。; 使用场景及目标:①制定科学的新闻发稿策略,实现从“流量思维”向“价值思维”转型;②构建央媒定调、门户扩散、自媒体互动的立体化传播矩阵;③利用AI工具实现精准投放与GEO优化,提升品牌在AI搜索中的权威性与可见性;④通过数据驱动评估体系量化品牌影响力与销售转化效果。; 阅读建议:建议结合文中提供的实操清单、案例分析与工具指南进行系统学习,重点关注媒体适配性策略与GEO评估指标,在实际发稿中分阶段试点“AI+全渠道”组合策略,并定期复盘优化,以实现品牌传播的长期复利效应。
程序进程线程是计算机系统中三个密切相关但又有所区别的概念,它们在操作系统和应用程序的运行中扮演着不同的角色。 **程序(Program)** 是指存储在磁盘上的可执行文件,它是一组静态的指令集合。程序本身并不占用系统资源,只有当它被加载到内存中并开始执行时,才会形成进程程序可以被多个进程同时使用,例如多个用户运行同一个程序,每个用户都会有一个独立的进程实例[^1]。 **进程(Process)** 是程序的一次动态执行过程,它是操作系统进行资源分配的基本单位。每个进程都有其独立的地址空间、内存、数据栈以及其他的辅助数据结构。这意味着不同进程之间是相互隔离的,一个进程中的错误通常不会直接影响到另一个进程。然而,这种隔离也导致了进程间通信(IPC)较为复杂,需要通过特定机制如管道、套接字或共享内存等来实现。此外,创建和切换进程相对于线程来说开销更大,因为每个进程都有自己的资源副本[^1]。 **线程(Thread)** 则是进程中执行运算的最小单位,也是处理器调度和分派的基本单位。线程必须依附于某个进程而存在,且同一进程内的所有线程共享该进程的全局变量和资源,比如堆内存、文件描述符等。线程之间的通信非常直接且高效,因为它们可以直接读写同一进程的数据。线程还具有轻量级的特点,相对于进程线程的创建、销毁和上下文切换的开销要小得多。线程可以分为用户级线程和内核级线程,其中用户级线程由运行时库管理,而内核级线程则由操作系统直接管理和调度[^2]。 综上所述,程序是静态的指令集合,进程程序运行时的一个实例,而线程则是进程内部用于执行任务的部分。三者之间的关系可以理解为:一个程序可以对应多个进程,而每一个进程又可以包含多个线程。这种层次结构允许程序设计者利用多线程提高程序的并发性和效率,同时也保持了进程间的隔离性以增强系统的稳定性。 ### 程序进程线程的关系 - **程序进程**:程序是静态的代码,而进程程序在内存中的动态执行。一个程序可以启动多个进程,每个进程都是独立的。 - **进程线程**:进程是资源分配的单位,而线程是CPU调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源。 - **线程程序**:线程本身不能独立存在,它必须依附于一个进程,而这个进程又来源于某个程序。 ### 程序进程线程的区别 - **程序** 是静态的文件,不占用运行时资源,仅当被加载时才成为进程。 - **进程** 是独立的实体,拥有自己完整的资源集,包括虚拟地址空间、文件描述符等。 - **线程** 是进程的一部分,不拥有独立的资源,但可以独立调度和执行。 在实际应用开发中,开发者可以根据需求选择是否使用多进程或多线程模型。如果需要更高的安全性和隔离性,则倾向于使用多进程;若追求性能和资源共享,则更倾向于采用多线程。 ```c // 示例:创建两个线程来并行处理任务 #include <pthread.h> #include <stdio.h> void* thread_function(void* arg) { int thread_id = *(int*)arg; printf("Thread %d is running\n", thread_id); return NULL; } int main() { pthread_t threads[2]; int ids[2] = {1, 2}; for (int i = 0; i < 2; ++i) { if (pthread_create(&threads[i], NULL, thread_function, &ids[i])) { fprintf(stderr, "Error creating thread %d\n", i); return 1; } } for (int i = 0; i < 2; ++i) { if (pthread_join(threads[i], NULL)) { fprintf(stderr, "Error joining thread %d\n", i); return 2; } } return 0; } ``` 上述C语言示例展示了如何使用POSIX线程API创建两个线程,并让它们分别执行相同的函数。这体现了线程作为进程内独立执行路径的能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值