云计算百科
云计算领域专业知识百科平台

【无标题】Linux 应用软件编程笔记:多任务、线程、IPC进程通信

前言

本篇笔记整理 Linux 应用编程核心三大模块:多进程、多线程、IPC进程间通信,是嵌入式 Linux、后端 C 开发的高频考点与工程常用技术,包含原理、函数接口、区别对比,同时补充易踩坑细节,适合期末复习、面试复盘。

一、多任务

多任务:让程序具备并发处理多个任务的能力,Linux 实现方式分为:多进程、多线程

  • 进程
  • 1.1 什么是进程

    程序:存放在硬盘上的静态指令集合( .c 编译后的可执行文件)
    进程:正在运行的程序,加载进内存,占用内存、CPU 资源,拥有完整独立 0~4G 虚拟地址空间。
    在这里插入图片描述

    ✅ 进程和程序核心区别

  • 程序静态存硬盘;进程动态运行在内存
  • 一个程序运行可以产生多个进程
  • 进程具备完整生命周期:创建 → 运行 → 消亡
  • 进程是 操作系统资源分配的最小单位
  • 1.2 进程内存布局(0~4G虚拟空间)

    分区 作用
    内核空间(3G~4G) 操作系统内核代码、驱动、进程管理,所有进程共享
    栈区(8M 默认) 局部变量、函数形参、函数返回地址,自动开辟释放
    堆区 malloc/calloc/realloc 手动申请,必须手动 free 释放
    .data段 初始化的全局变量、静态局部变量
    .bss段 未初始化全局/静态变量,系统默认清零
    文本区 代码指令、字符串常量,只读

    💡 补充: fork() 创建子进程时,早期 Linux 使用完全拷贝,现代采用 写时拷贝(COW):父子进程先共享内存,任意一方修改数据时才单独复制内存副本,节省内存开销。

    1.3 进程调度

    CPU 宏观并行、微观串行(并发)

    • 并行:多核 CPU,多个任务同时执行
    • 并发:单核 CPU,时间片轮转,快速切换任务,宏观看起来同时运行

    常见调度算法:

  • 时间片轮转(Linux 默认)
  • 先来先服务
  • 高优先级优先
  • 短作业优先
  • 1.4 进程状态

    经典三态模型

    就绪态 →(分配CPU)运行态 →(时间片用完 / 请求IO)阻塞态

    • 就绪:万事俱备,只等待 CPU 时间片
    • 运行:正在被 CPU 执行
    • 阻塞:等待IO资源( fgets 、 scanf ),主动放弃CPU

    Linux 完整七状态

  • R 运行态:正在执行 / 等待CPU
  • S 可中断睡眠:等待资源,可被信号唤醒
  • D 不可中断睡眠:等待硬件,不能被信号打断
  • T 暂停态: Ctrl+Z 暂停进程
  • Z 僵尸态:进程结束,但父进程未回收PCB资源
  • X 消亡态:资源完全回收
  • W 换出态(旧版内核)
  • 1.5 进程相关常用命令

    bash

    ps -aux # 查看所有进程,PID、状态、CPU/内存占用
    ps -ef # 查看父子进程 PPID、PID
    top # 实时监控进程资源占用
    pstree -p # 树形查看父子进程关系
    kill -9 PID # 强制杀死进程
    kill -l # 查看所有信号
    fg / bg # 前后台进程切换

    1.6 进程核心函数

  • pid_t fork(void);
    • 返回值:父进程返回子进程PID;子进程返回 0 ;失败返回 -1
    • 作用:创建子进程,复制父进程用户空间,共享文件描述符
  • getpid() 获取自身PID; getppid() 获取父进程PID
  • 进程退出: exit() / _exit() / return
    • exit() :刷新缓冲区,调用退出处理函数
    • _exit() :直接退出,不刷新缓冲区
  • 资源回收: wait() / waitpid()
    • wait() :阻塞等待任意子进程结束,回收僵尸进程
    • waitpid() :灵活指定回收哪个子进程,支持非阻塞回收

    1.7 僵尸进程 & 孤儿进程

  • 僵尸进程 Z:子进程先结束,父进程没调用 wait 回收PCB,残留资源。
    ✅ 危害:占用PID资源,大量僵尸会耗尽系统PID
    ✅ 解决:父进程调用 wait/waitpid ;或者父进程提前退出,让 init(1号进程) 收养回收
  • 孤儿进程:父进程先结束,子进程被 1号init进程收养,自动回收,无危害
  • 1.8 exec 函数族

    execl / execlp / execv / execvp

    作用:在当前进程内部,替换文本段为新程序,PID不变,不创建新进程
    常用场景:fork 创建子进程后,子进程调用 exec 执行外部程序

    二、线程

    2.1 线程基础概念

    线程:轻量级进程,是 CPU任务调度的最小单位
    线程隶属于进程,一个进程至少有1个主线程。
    同一个进程内所有线程共享:堆区、全局变量、文件描述符、.data/.bss/文本段
    每个线程私有独立栈区(默认8M)

    2.2 进程 VS 线程 对比表
    在这里插入图片描述

    对比项 进程 线程
    资源属性 资源分配最小单位 CPU调度最小单位
    创建开销 大,完整拷贝虚拟内存 极小,仅分配独立栈
    内存隔离 完全独立,互不干扰 共享大部分内存,栈私有
    通信方式 必须依靠IPC机制 直接读写全局变量(需要互斥锁保护)
    安全性 高,进程崩溃互不影响 低,一个线程崩溃,整个进程全部退出

    2.3 线程核心函数

    ⚠️ 编译线程代码必须加链接库: gcc test.c -o test -pthread

  • 创建线程
  • c

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
    void *(*start_routine) (void *), void *arg);

    参数:线程ID地址、属性(NULL默认)、任务函数指针、传给任务函数的参数
    返回:成功0,失败错误码

  • 资源回收
    • pthread_join(thread, &retval) :阻塞等待线程结束,回收资源,默认非分离线程必须回收
    • pthread_detach(tid) :设置分离属性,线程结束后由操作系统自动回收,不需要join

    2.4 线程同步与互斥

    互斥(竞争临界资源,保证同一时间只有一个线程访问)

    临界资源:多个线程都能访问的资源(全局变量、文件)
    临界区:访问临界资源的代码片段

    工具:互斥锁 pthread_mutex_t
    接口: pthread_mutex_init / lock / unlock / destroy

    同步(控制线程执行先后顺序)

    工具:信号量 sem_t
    sem_init 初始化、 sem_wait(P-1申请) 、 sem_post(V+1释放)

    2.5 死锁

    死锁:多个线程互相持有对方需要的锁,互相等待,永久阻塞
    ✅ 死锁四大必要条件:互斥、持有并等待、不可剥夺、循环等待
    ✅ 解决思路:

  • 统一锁申请顺序
  • 设置锁超时 pthread_mutex_trylock
  • 破坏循环等待条件
  • 三、IPC 进程间通信机制

    IPC:Inter Process Communication,进程之间交换数据的机制
    进程内存相互独立,不能直接访问对方内存,必须借助内核提供的IPC通道

    IPC 分类

  • 传统通信:无名管道 pipe、有名管道 fifo、信号 signal
  • System V IPC:消息队列、共享内存、信号量集
  • 网络通信:socket(支持跨主机通信)
  • 3.1 无名管道 pipe

    • 适用范围:有亲缘关系进程(父子、兄弟)
    • 原理:内核缓冲区,FIFO先进先出,半双工(单向通信)
    • 函数: int pipe(int pipefd[2]);
    • pipefd[0]:读端 read
    • pipefd[1]:写端 write
      ✅ 管道4个特性:
  • 写阻塞:管道写满,write阻塞
  • 读阻塞:管道为空,read阻塞
  • 读返回0:所有写端关闭,read读到0,代表通道关闭
  • 管道破裂:所有读端关闭,继续write → 进程收到SIGPIPE信号,程序终止
  • 3.2 有名管道 FIFO mkfifo

    • 适用范围:同一主机任意进程(无亲缘限制)
    • 特点:存在文件系统的管道文件,数据依然存内核缓冲区,文件仅作为访问入口
    • 使用流程: mkfifo() 创建管道文件 → open打开 → read/write读写 → close关闭

    3.3 信号 signal

    信号:异步软中断,不携带大数据,仅传递通知事件
    常见信号:

  • SIGINT 2 :Ctrl+C 终止程序
  • SIGQUIT 3 :Ctrl+\\
  • SIGKILL 9 :强制杀死进程,不能捕获忽略
  • SIGSEGV 11 :段错误
  • SIGALRM 14 :alarm定时产生的闹钟信号
  • 核心接口:

  • signal(int signum, sighandler_t handler) :注册信号处理函数
  • kill(pid_t pid, int sig) :向指定进程发送信号
  • alarm(unsigned int sec) :定时,时间到发送SIGALRM
  • pause() :阻塞等待任意信号
  • 补充:信号处理规则
    信号默认动作:终止进程、忽略、暂停;信号可以自定义捕获处理函数

    3.4 System V IPC

    (1)消息队列

    内核维护链表,可以按消息类型收发数据,支持优先级读取
    流程: ftok() 获取key → msgget() 创建队列 → msgsnd() 发送 → msgrcv() 接收 → msgctl() 删除队列

    (2)共享内存(速度最快IPC)

    原理:多个进程映射同一块内核内存到各自用户空间,不需要数据拷贝

    注意:共享内存本身不带同步互斥,必须手动搭配信号量/互斥锁防止竞争访问
    流程: shmget 创建 → shmat 映射挂载 → 直接读写内存 → shmdt 卸载 → shmctl 删除

    (3)信号量集

    多用于进程之间同步互斥,和线程sem信号量作用类似,支持多组信号量管理

    3.5 Socket 套接字

    既可以本机进程通信,也支持跨主机网络通信(TCP/UDP),是使用最广泛的IPC,后续网络编程重点学习。

    四、学习总结 & 工程使用建议

  • 多进程:隔离性好,稳定性高;资源开销大,适合任务隔离场景
  • 多线程:轻量高效,数据共享方便;需要谨慎处理锁竞争、死锁问题
  • IPC选型参考
    • 父子简单单向通信:无名管道 pipe
    • 本机任意进程简单通信:有名管道 fifo
    • 异步事件通知:信号 signal
    • 大量数据高速传输:共享内存(搭配信号量)
    • 按类型分发消息:消息队列
    • 跨主机通信:socket TCP/UDP

    后续拓展:结合本次 TCP 客户端/服务端代码,完成多线程服务端、管道聊天程序、共享内存传输图片等综合实战项目。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【无标题】Linux 应用软件编程笔记:多任务、线程、IPC进程通信
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!