欢迎光临
我们一直在努力

C语言核心概念复习——进程与线程通信

进程与线程通信机制

一、进程间通信(IPC)方式

1.1 管道(Pipe)

1.1.1 无名管道(匿名管道)
  • 创建方式:通过pipe()系统调用创建

  • 特点:

    • 只能在有亲缘关系的进程间使用(父子进程、兄弟进程)

    • 单向通信,一个管道只能单向传输数据

    • 数据存放在内核缓冲区中

    • 生命周期随进程结束而结束

// 创建无名管道
int pipe_fd[2];
pipe(pipe_fd);
// pipe_fd[0]:读端
// pipe_fd[1]:写端

1.1.2 有名管道(FIFO)
  • 创建方式:

    bash

    # 命令行创建
    mkfifo pipe_name

    # 或使用系统调用
    mkfifo("pipe_name", 0666);

  • 特点:

    • 可以在任意进程间使用

    • 在文件系统中有一个路径名(如 /tmp/myfifo)

    • 必须两个端口都打开才能正常通信

    • 容量限制:64KB

1.1.3 管道阻塞情况
阻塞类型触发条件结果
写阻塞 管道为空时写入 等待有数据可读
读阻塞 读端已打开,但管道为空 等待数据写入
写满阻塞 管道已满(64KB) 等待有空间可写
管道破裂 读端关闭,写端继续写入 收到SIGPIPE信号

1.2 共享内存(Shared Memory)

  • 速度:比管道快,因为是直接内存访问

  • 特点:

    • 多个进程共享同一块物理内存区域

    • 只能读取最新一次的数据(覆盖写入)

    • 需要加入同步机制防止数据竞争

    • 内存较大,适合大数据传输

  • 问题:需要手动同步,否则会出现数据不一致

1.3 信号(Signal)

  • 定义:软件中断,用于通知进程发生了某个事件

  • 常见信号:

    • SIGINT (2):Ctrl+C 中断信号

    • SIGKILL (9):强制终止信号(不可捕获)

    • SIGTERM (15):正常终止信号

    • SIGSEGV (11):段错误信号

  • 特点:异步通信,不能传递复杂数据

1.4 信号量(Semaphore) – 进程间

  • 作用:进程间的同步机制,控制对共享资源的访问

  • 类型:

    • 二值信号量(0或1)

    • 计数信号量(0-N)

  • 操作:

    • P操作(wait):申请资源,信号量减1

    • V操作(signal):释放资源,信号量加1

1.5 消息队列(Message Queue)

  • 函数:msgsnd() 发送消息,msgrcv() 接收消息

  • 特点:

    • 消息链表,存储在内核中

    • 每个消息有类型标识

    • 支持不同优先级的消息

    • 消息按类型或先进先出方式读取

1.6 套接字(Socket)

  • 特点:

    • 最通用的IPC机制

    • 支持不同主机间的进程通信

    • 支持多种协议(TCP/UDP)

    • 可用于网络编程和本地进程通信

二、线程间通信

2.1 全局变量

  • 最简单的线程通信方式

  • 所有线程共享进程的全局数据区

  • 需要同步机制保护,避免数据竞争

int global_var = 0; // 所有线程可见

void* thread_func(void* arg) {
global_var++; // 需要同步保护
return NULL;
}

2.2 互斥(Mutex)

  • 作用:保证同一时间只有一个线程访问共享资源

  • 相关函数:

    pthread_mutex_lock(&mutex); // 加锁
    pthread_mutex_unlock(&mutex); // 解锁
    pthread_mutex_trylock(&mutex); // 尝试加锁(非阻塞)

  • 特点:

    • 保护临界区代码

    • 防止多个线程同时修改共享数据

    • 避免数据不一致

2.3 同步(信号量)

  • 相关函数:

    sem_wait(&sem); // P操作,等待信号量
    sem_post(&sem); // V操作,释放信号量

  • 用途:

    • 控制线程执行顺序

    • 限制同时访问资源的线程数量

    • 实现生产者-消费者模型

2.4 读写锁(Read-Write Lock)

  • 特点:

    • 允许多个线程同时读

    • 只允许一个线程写

    • 写操作具有排他性

  • 适用场景:读多写少的共享数据

2.5 条件变量(Condition Variable)

  • 作用:线程间条件等待和通知

  • 相关函数:

    pthread_cond_wait(&cond, &mutex); // 等待条件
    pthread_cond_signal(&cond); // 通知一个等待线程
    pthread_cond_broadcast(&cond); // 通知所有等待线程

  • 通常与互斥锁配合使用

三、进程创建与内存管理

3.1 进程创建过程

3.1.1 内存分配

当创建新进程时,内核会分配:

  • task_struct 结构体

    • 进程控制块(PCB)

    • 包含进程的所有信息

    • 如:PID、状态、优先级、打开文件等

  • mm_struct 结构体

    • 内存描述符

    • 管理进程的虚拟地址空间

    • 包含页表、内存区域等信息

  • 3.1.2 进程创建函数
    • fork():

      • 创建子进程

      • 子进程复制父进程的完整地址空间

      • 开销较大

    • vfork():

      • 创建子进程,但共享父进程的地址空间

      • 子进程先运行,父进程阻塞

      • 子进程退出或exec后父进程继续

    • clone():

      • 共享 mm_struct(内存描述符)

      • 可以控制共享哪些资源

      • 用于创建线程

    3.2 CPU与内存管理

    3.2.1 CPU中的MMU
    • MMU:内存管理单元(Memory Management Unit)

    • 功能:

      • 虚拟地址 → 物理地址 转换

      • 内存保护

      • 缓存控制

    • 核心组件:页表

      • 存储虚拟页到物理页的映射关系

      • 每个进程有自己的页表

      • 由操作系统维护

    3.2.2 页表工作原理

    虚拟地址 → [MMU] → 物理地址

    页表查询

    3.3 进程初始化

    3.3.1 内核init
    • 第一个进程:init进程(PID=1)

    • 由内核创建,所有其他进程的祖先

    • 职责:

      • 初始化系统

      • 启动系统服务

      • 管理孤儿进程

    3.3.2 exec() 和用户init

    // 典型启动流程
    fork() → exec("/sbin/init") → 用户空间初始化

    • exec():替换当前进程的映像

    • 用户init:

      • /sbin/init 或 /etc/init

      • 读取配置文件(如 /etc/inittab)

      • 启动登录进程或图形界面

    四、通信机制对比总结

    4.1 进程间通信对比

    机制速度数据量复杂度适用场景
    管道 小(64KB) 简单 有亲缘关系进程
    共享内存 最快 复杂(需同步) 大数据传输
    信号 极小 简单 简单事件通知
    消息队列 中等 中等 中等 结构化消息
    套接字 复杂 网络/跨主机

    4.2 线程同步机制对比

    机制作用适用场景
    互斥锁 保护临界区 防止数据竞争
    信号量 控制访问数量 资源池管理
    条件变量 条件等待通知 生产者-消费者
    读写锁 读多写少 共享数据访问

    五、关键概念详解

    5.1 进程创建的内存开销

    创建进程:
    1. 分配task_struct(约1KB)
    2. 分配mm_struct(约100字节)
    3. 复制页表(4KB/页)
    4. 复制数据段、代码段等

    • fork():复制整个地址空间 → 开销大

    • clone():共享地址空间 → 开销小(用于创建线程)

    5.2 线程与进程的资源共享

    资源进程间线程间
    代码段 不共享 共享
    数据段 不共享 共享
    不共享 共享
    独立 独立(每个线程有自己的栈)
    文件描述符 不共享 共享
    信号处理 独立 共享

    5.3 管道阻塞的深入理解

    写端 → [管道缓冲区] → 读端

    阻塞情况:
    1. 读端关闭,写端写入 → SIGPIPE
    2. 写端关闭,读端读取 → 返回0(EOF)
    3. 缓冲区空,读端读取 → 阻塞
    4. 缓冲区满,写端写入 → 阻塞

    赞(0)
    未经允许不得转载:171主机测评 » C语言核心概念复习——进程与线程通信
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址