news 2026/9/4 6:04:11

Linux编程-复习

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux编程-复习

Linux基础命令:

查看进程信息:

ps -aux | grep a.out a:所有用户进程 u:显示所有者、CPU、内存 x:无终端进程也显示

查看线程信息:

ps -elf | grep a.out -e 列出所有进程 -l 长格式,显示更多详细字段 -f 完整格式,显示命令行、PPID父进程号

什么是进程:

程序的一次运行实例称为进程,程序被加载进内存、操作系统为它分配资源后,正在运行的实体
进程是操作系统资源分配的最小单位。资源包括:内存空间、文件描述符、信号处理表、工作目录、用户权限等。

在系统中使用什么结构体表示一个进程:

Linux 内核中,每一个进程在内核里都对应一个struct task_struct结构体,这个结构体就是大名鼎鼎的PCB :Process Control Block 进程控制块。所有进程的task_struct被内核放进一个双向循环链表,叫进程链表,内核遍历这条链表就能查到系统所有进程,就是ps命令的数据源。

系统通过什么来区分两个正在运行的进程:

Process ID,进程号,唯一区分每一个运行中的 task_struct进程。
PPID,父进程 ID,只是记录它的父进程是谁,多个子进程可以有同一个 PPID,不唯一,不能区分进程

当一个进程运行后,32bit的系统给用户分配的内存空间多大,有哪些区域,每个区域存储什么

32 位地址总线,最大寻址空间:2的32次方=4GB

  • 用户空间:0 ~ 3GB(0x00000000 ~ 0xBFFFFFFF) 大小 = 3GB
  • 内核空间:3GB ~ 4GB(0xC0000000 ~ 0xFFFFFFFF) 大小 = 1GB

用户空间 3GB 内部分区,从低地址→高地址

地址从小到大顺序:代码段 → 数据段 → BSS 段 → 堆 → 栈 → 共享库区

1. 代码段 .text

存放编译好的机器指令、程序二进制代码,属性为只读,可执行,防止运行时修改指令,声明周期为,整个进程运行期间

2. 初始化数据段 .data数据段

存放已经初始化的全局变量、static 静态变量,属性可读、可写入

3. 未初始化数据段 .bss

存放未初始化的全局变量、未初始化静态变量,程序加载时内核会自动把这片内存清零
.text + .data + .bss 这三块的大小在编译时就已经确定,运行中不会扩张收缩。

4. 堆 heap

属性:读写,运行时可以动态扩大、缩小。手动释放 free ();忘记释放 → 内存泄漏。堆区位于 BSS 上方,地址向上增长,从低地址往高地址扩张。
申请堆区空间的函数

malloc() calloc() realloc()

5. 共享库 / 内存映射区 mmap

堆和栈中间的一片区域。存放:动态库 .so 文件(shturl.)、mmap 映射的文件、匿名共享内存、线程栈也常分配在这里

6. 用户栈 stack(栈)

位置靠近 3GB 边界的高地址,向下增长从高地址→低地址,默认大小一般 8MB。存放局部变量、函数参数、函数返回地址、ebp栈帧、寄存器现场、函数调用的时候栈自动伸缩,函数返回自动释放。递归太深,栈不断向下扩张,超出 8MB →栈溢出 Segmentation fault

0x00000000 ┌─────────────────────┐ │ .text 代码段 │ ├─────────────────────┤ │ .data 初始化数据 │ ├─────────────────────┤ │ .bss 未初始化数据 │ ├─────────────────────┤ │ heap 堆 ↑向上扩 │ │ │ │ 空闲间隙 │ │ mmap共享库区 │ │ │ │ stack 栈 ↓向下扩 │ ├─────────────────────┤ │ 命令行参数、环境变量 │ 0xBFFFFFFF (3G边界) | ─────────────────────── 0xC0000000 ┌─────────────────────┐ │ 内核空间 1GB │ │ (所有进程共享) │ └─────────────────────┘ 0xFFFFFFFF (4G)

每个进程 3GB 用户空间,开 10 个进程是不是就要 30GB 物理内存?
不是!虚拟内存≠物理内存,内核通过页表,把虚拟地址映射到物理内存;多个进程虚拟地址可以映射到不同物理页,也可以共享物理页(共享库)。

什么是虚拟地址

虚拟地址是操作系统为每个应用进程独立分配的虚构内存地址,并非内存条上真实的物理地址。所有程序代码、指针操作、变量地址,全程使用的都是虚拟地址,CPU 不会直接通过虚拟地址读写真实内存。

硬件 MMU + 系统页表会自动完成虚拟地址→物理地址的映射转换: 进程只认虚拟地址,无需感知真实内存位置;无映射的虚拟地址访问会触发缺页异常,由系统动态分配内存。

核心作用

  1. 内存隔离安全:每个进程虚拟地址空间独立,相同虚拟地址互不干扰,杜绝进程互相篡改内存、程序崩溃连锁影响。
  2. 内存高效利用:支持虚拟内存、内存置换,闲置数据可暂存硬盘,突破物理内存大小限制运行程序。
  3. 程序通用可加载:程序基于虚拟地址编译,无需绑定固定物理内存位置,可灵活加载到内存任意位置。
  4. 权限管控:可对虚拟地址设置读、写、执行权限,保护内核数据和程序代码安全。

多进程可以干什么

1. 实现并发任务,几件事一起跑;实现多任务,并发任务。
2. 故障隔离,一个任务崩了不连累主程序;每个进程内存独立、互相隔离。
3. 充分利用多核 CPU 算力;启多个进程,操作系统可以把不同进程调度到不同 CPU 核心,实现真正的并行计算
4. 解耦业务,程序模块化拆分;模块之间使用IPC通信,单个模块可以单独重启、升级;

进程的状态有哪些

1. 新建(创建态)

进程刚被创建,PCB 进程控制块正在初始化,资源还没分配完成,还没进入就绪队列。

2. 就绪态 Ready

  • 已经分配好了除CPU以外的所有资源
  • 万事俱备,只等 CPU 调度,一旦获得 CPU 立刻运行

3. 运行态 Running

  • 进程占有 CPU,正在处理器上执行代码
  • 单核 CPU 同一时刻只能有1 个进程处于运行态

4. 阻塞态(等待态 / 睡眠态)Blocked

进程等待某事件,主动放弃 CPU。 例:等待键盘输入、读磁盘数据、等待信号、等待锁。⚠️阻塞不能直接回到运行态!事件完成后,只能先回到就绪态。

5. 终止态(结束态)Terminated

进程执行完毕,或被杀死。PCB 资源等待操作系统回收。

子进程创建后拥有哪些资源

子进程几乎复制父进程的地址空间,采用写时复制 COW,并不是立刻全部拷贝。

  • 进程地址空间(虚拟内存)

    • 代码段 (.text)、只读数据段 (.rodata)
    • 全局数据段 (.data/.bss)、堆
    • 用户栈
    • 初始父子指向同一块物理内存;一旦任意一方修改数据,操作系统才复制一份(写时复制)。
  • 文件描述符表父进程所有打开过的 fd (0 标准输入、1 标准输出、2 标准错误、文件、socket) 都会复制一份。

    • 父子共享同一个文件表项(文件偏移量、文件状态标志)
    • 一个进程修改文件读写偏移,另一个也会受影响。
  • 用户 ID、组 ID,会话 ID、进程组 ID

    • uid、gid、有效用户 ID
    • 会话 sid、进程组 pgid
    • 控制终端
  • 信号处理方式

    • 每个信号的处理函数 (SIG_DFL / SIG_IGN / 自定义函数) 被复制。
    • 未处理的信号不会继承
  • 当前工作目录、根目录、文件权限掩码 umask

  • 环境变量表、资源限制 rlimit

子进程不会继承的资源

  1. 进程 ID (PID):自己全新的 pid,父进程 pid (PPID) 是父进程 id
  2. 父进程的锁(互斥锁、文件锁)不会继承
  3. 闹钟定时器 alarm,未到期的闹钟被清除
  4. 父进程挂起的信号队列,不会传给子进程
  5. 每个进程独立的内核栈、PCB 进程控制块

进程退出后发生了什么,如何避免僵尸进程

  • 进程调用exit()/return/ 被信号杀死 → 进入终止状态
  • 用户空间资源(堆、栈、文件描述符)立刻释放
  • 但是 PCB(进程控制块)不会马上释放!
    • 内核保留 PCB,存放进程退出状态码
    • 等待父进程调用 wait () /waitpid ()读取退出状态
  • 如果父进程一直不调用 wait 回收
  • 子进程就变成僵尸进程 (Zombie,状态 Z)

避免僵尸进程:

方案 1:父进程主动调用 wait () /waitpid ()(阻塞回收)
方案 2:父进程非阻塞轮询 waitpid
方案 3:信号处理,等待SIGCHLD信号到来,在信号回调函数回收子进程资源
方案 4:使用孙进程,或退出父进程,使得子进程可以被init收养,会自动回收资源

在子进程中需要执行一个外部可执行程序,需要怎么做,与原来子进程相比发生了什么变化

子进程调用 exec 系列函数,加载外部可执行程序
常用函数:execl、execlp、execv、execvp、execve

exec 执行之后,子进程发生了哪些变化

✅ 会被替换、销毁掉的资源
  1. 用户空间地址空间完全清空
    • 原来 fork 复制过来的 .text 代码段、.data、.bss、堆、用户栈,全部丢弃
    • 载入新程序的代码、数据,开辟全新堆栈
    • 原来子进程里面定义的变量、函数全部消失,再也不能访问
    • exec 成功,旧程序被完全覆盖
保留不变,继承下来的内核资源——不会被替换
  1. PID 进程 ID 不变!!(最重要!PID 永远不会变)
  2. 文件描述符 fd(默认继承,打开的文件、socket 还在)
  3. 可以用fcntl(fd, F_SETFD, FD_CLOEXEC)设置执行 exec 时自动关闭该文件描述符
  4. 进程组 ID、会话 ID、控制终端
  5. 用户 ID、组 ID(uid,gid)
  6. 当前工作目录、根目录、umask 掩码
  7. 信号处理方式:
    • 原先自定义的信号处理函数,全部重置为默认 SIG_DFL
    • 被忽略的信号 (SIG_IGN),保持忽略状态不变
✅ 新生成、全新的资源
  1. 新程序的代码段、数据段、堆、栈
  2. 全新的信号栈
  3. 新程序 main 函数的 argv 参数、环境变量(execve 可以自定义环境)

什么是线程

线程Thread:操作系统能够进行 CPU 调度和分派的最小单位。

  • 进程:资源分配的最小单位,拥有独立地址空间、文件、堆内存
  • 线程:CPU 调度执行的最小单位,不拥有独立资源,共用所属进程的资源
对比项进程线程
资源所有权拥有独立内存、地址空间共享所属进程资源
切换开销大(进程上下文切换)小(线程上下文切换)
隔离性进程之间完全独立,互不影响一条线程崩溃,整个进程直接崩溃
通信进程间通信 IPC(管道、共享内存、消息队列)直接读写全局变量,通信简单

单线程:任务排队,做完一个再做下一个。
多线程:一个程序里同时开启多条执行流,宏观上并发执行多个任务
注意:单核 CPU 同一时刻只能跑 1 个线程,靠 CPU 快速轮流切换线程,制造 “同时运行” 的假象;多核 CPU 才可以做到真正的并行。

创建线程函数,函数指针的作用

创建线程函数原型如下

int pthread_create( pthread_t *thread, //线程的tid号 const pthread_attr_t *attr, //线程属性,默认NULL为不分离,需要主动回收 void *(*start_routine) (void *), //重点:线程函数指针 void *arg //传给线程执行函数的参数 );
  • start_routine保存的是线程入口函数的地址
  • 操作系统新建出来的子线程,启动之后,就跳转到这个地址开始执行代码
  • 主线程执行main(); 新建的子线程没有自带入口,必须你告诉它:去跑哪一个函数。这个 “告诉” 就是靠函数指针传过去。
  • 函数指针就是指向执行函数首地址的一个指针,在这里直接填函数名。是因为函数名出现在这个创建线程的函数中的时候,编译器会自动将函数名转化为函数指针
  • 实际上函数名只是一个代码字段的入口地址标签,类似于汇编语言里loop标签,用于跳转作用,并不等于是一个指针,指针是一种变量类型,因此一个函数指针是可以操作指向任意同类型的函数的,而这里的函数名,只能代表自己这个函数。

线程分离属性 detachstate

1. 基础概念

每个pthread线程都有一个属性:分离属性 (detachstate),只有二种状态:

① 可接合态(默认)PTHREAD_CREATE_JOINABLE;线程创建出来默认就是可接合

  • 线程退出后,它的退出状态、返回值会被保留在内存中(TCB 资源不会释放)
  • 必须由别的线程调用:pthread_join(tid, &retval)
    • join会:等待线程结束 + 回收线程资源 + 获取返回值
  • 后果:忘记 join → 线程变成僵尸线程,资源泄漏

② 分离态PTHREAD_CREATE_DETACHED

  • 线程退出时,自动释放所有线程资源,没人可以再 pthread_join 它
  • 不能调用pthread_join(),调用会直接失败
  • 线程的返回值会直接丢弃,无法获取
  • 一句话总结:可接合:资源等别人来回收;分离:线程干完活自己回收自己。

什么是同步和互斥,Linux提供的对应机制名称是什么,什么是内核对象

互斥 (Mutex‑Mutual Exclusion)

目的:解决资源竞争问题,保护临界资源多个线程不能同时访问同一个共享资源,同一时刻只允许一个线程进入临界区。

目的:排他、独占、抢资源、防乱改

例子:两个线程同时修改同一个全局变量,加互斥锁保证一次只有一个线程修改。

2. 同步 (Synchronization)

目的:控制线程执行先后顺序,线程之间按约定次序运行线程 A 必须等线程 B 做完某件事之后,A 才能继续执行。

目的:先后顺序、等待、通知、条件依赖

例子:生产者先放数据,消费者才能取数据;消费者没数据就要阻塞等待生产者。

内核对象是由 Linux 操作系统内核管理、创建、维护的数据结构,存放在内核空间,用户程序不能直接访问它。用户进程只能拿到一个句柄 / 文件描述符 ID,通过系统调用,让内核替你操作这个对象。

用户对象 VS 内核对象
  • 用户对象:在你的程序 (用户空间) 内存里,由应用代码管理。 例如:pthread_mutex_t、普通局部变量、全局变量。
  • 注意:pthread_mutex 互斥锁本质是用户态对象,部分锁操作可以不陷入内核。
  • 内核对象:在内核空间,归操作系统管理。进程消亡内核对象不一定立刻销毁。
Linux 常见内核对象举例
  • 进程 PCB (task_struct)
  • 文件、文件描述符 fd
  • 管道
  • System V 信号量、消息队列、共享内存段
  • 信号、定时器
  • 套接字 socket

什么是死锁,死锁的四个必要条件,如何避免死锁,发生死锁如何解决

死锁:两个或多个线程 / 进程,各自拿着对方需要的资源,又互相等待对方释放资源,所有人都被永久阻塞,谁也无法继续往下执行。

  1. 互斥条件资源同一时刻只能被一个线程占用;别人想要必须等待。锁就是典型互斥资源。
  2. 请求与保持条件(占有且等待)线程已经占有了至少一个资源,又去请求另一个被别人占有的资源,并且不释放自己手上已经拿到的锁。

  3. 不可剥夺条件别人已经拿到的资源,不能被其他线程强行抢走;只能由持有者主动释放。 互斥锁不能被别的线程强制解锁。

  4. 环路等待条件线程之间形成一条等待闭环:A 等 B、B 等 C、C 等 A,构成一个资源等待的环形链。

⚠️注意:四个条件只是必要条件,不是充分条件。四个条件同时具备不一定死锁,但是死锁发生的时候,四个条件一定全部成立。需要解决死锁,破坏死锁的四个必要条件即可

1. 破坏「互斥条件」

尽量不用互斥;使用无锁方案。大部分临界资源必须互斥访问,这条很难破坏,很少用。

2. 破坏「请求与保持」

方案:线程一次性申请所有需要的锁,要么全部拿到,一把都不拿; 不允许拿一把锁之后,再去申请第二把。

3. 破坏「不可剥夺」

如果申请不到新锁,主动放弃自己手里已经持有的所有锁,过一会再重新申请。

4. 破坏「环路等待」⭐最常用、最简单

给所有锁规定统一的申请顺序,所有线程必须按相同顺序拿锁。

进程间通信方式

1. 管道 Pipe(匿名管道)

  • 特点:半双工(单向通信);只能用于有血缘关系进程(父子、兄弟进程);数据一次性读取,读完就没;存在内核缓冲区。
  • 使用:pipe()系统调用
  • 限制:没有名字,只能通过继承文件描述符传递。

2. 命名管道 FIFO(有名管道)

  • 特点:半双工;无血缘进程之间也可以通信;在磁盘上生成一个管道文件,文件仅起标识,数据仍然存放内核缓冲区。
  • 使用:mkfifo()创建
  • 管道 (Pipe)、FIFO 本质都是字节流,没有消息边界。

3. 消息队列 Message Queue(System‑V IPC)

  • 特点:内核创建的队列;进程间传递带类型的消息块;数据有边界;消息读取可以按消息类型读取,不一定要先进先出;消息读完不会自动消失。
  • 内核对象,生命周期随内核;进程退出队列不会销毁。
  • API:msggetmsgsndmsgrcvmsgctl

4. 共享内存 Shared Memory

  • 速度最快的 IPC 方式!多个进程同一块物理内存映射到自己的虚拟地址空间
  • 缺点:本身不带同步互斥机制,必须额外搭配信号量、互斥锁来保护数据。
  • API:shmgetshmatshmdtshmctl

5. 信号量 Semaphore

信号量不是用来传递数据!!它是同步互斥工具,用来解决进程 / 线程资源竞争。

  • System‑V 信号量:一组信号量集合;常用于配合共享内存做进程同步。
  • POSIX 有名 / 无名信号量:线程、进程都能用。
  • API:semgetsemopsemctl

6. 信号 Signal

  • Linux 异步通信方式。进程收到信号后中断当前工作,执行信号处理函数。
  • 属于简单通知机制,只能传递一个信号编号,不能携带大量数据
  • 例子:SIGKILL、SIGINT(ctrl+c)
  • API:kill()、raise()、signal()、sigaction()

7. 套接字 Socket(网络 IPC)

  • 支持本机进程通信 + 跨主机网络通信
  • 本地域套接字 (Unix‑domain socket):仅本机进程通信,不走网卡,效率高。
  • TCP/UDP socket:跨电脑通信。
通信方式能否传大量数据有无同步能力适用进程特点
匿名管道 pipe中等自带读写阻塞父子进程单向,血缘进程
有名管道 fifo中等自带读写阻塞任意进程磁盘管道文件
消息队列较大自带阻塞任意进程带消息类型,面向消息
共享内存最大无同步,需额外加锁任意进程速度最快
信号量不能传数据专门做同步互斥-锁工具
信号 signal极少 (仅编号)异步通知任意进程简单事件通知
socket协议控制本机 / 跨主机网络通信
  1. 匿名管道最大限制:没有名字,无法被外部非亲缘进程打开,只能 fork 继承 fd。
  2. FIFO 就是为了解决匿名管道不能跨非亲缘进程而生。
  3. System‑V 的三种 IPC(消息队列、共享内存、信号量)依靠key值标识,完全不需要血缘
  4. 信号只要拿到 PID,不管有没有血缘,都可以发送。

有名管道的读阻塞、写阻塞和管道破裂的条件

操作条件行为
open (O_RDONLY, 阻塞)无写端打开读端阻塞
open (O_WRONLY, 阻塞)无读端打开写端阻塞
read()缓冲区空,写端存活读阻塞
read()缓冲区空,所有写端关闭read 返回 0
write()管道缓冲区满,读端存活写阻塞
write()所有读端已经关闭,执行 write发送 SIGPIPE,管道破裂

无名管道 (pipe) 和 有名管道 (FIFO)

1. 无名管道 pipe

int pipe(int fd[2]);
  1. 没有文件名、没有磁盘节点,只存在于内存内核缓冲区
  2. 只能用于具有血缘关系的进程通信:父子进程、兄弟进程(fork 出来)
  3. 半双工通信:一端读、一端写;两个文件描述符:fd[0]读端fd[1]写端
  4. 生命周期随进程:所有相关进程关闭管道,管道自动销毁
  5. 只能单向数据流;若要双向通信,需要创建 2 条管道
  6. 不能通过 open 打开,只能由 pipe 创建,再 fork 共享文件描述符

2. 有名管道 FIFO

int mkfifo(const char *pathname, mode_t mode);
  1. 有文件名,存在于文件系统,是一个特殊管道文件(磁盘上仅占 inode,数据还是放内核内存缓冲区)
  2. 无血缘限制:任意两个进程,只要知道管道文件名就可以通信
  3. 半双工,同样单向;双向通信也要两条 FIFO
  4. 生命周期持久:创建之后文件一直存在,调用unlink()才删除;进程退出管道文件不会自动消失
  5. 使用方式:mkfifo创建 →open打开 → read/write 读写 → close 关闭 → unlink 删除
对比项无名管道 pipe有名管道 FIFO
存储位置内核缓冲区 (内存),无文件文件系统有文件名,数据仍在内核缓冲区
通信进程必须有血缘关系 (父子 / 兄弟)任意进程,无血缘要求
创建函数pipe()mkfifo()
打开方式fork 继承文件描述符,不能 open使用open()打开管道文件
生命周期所有进程关闭后,管道消失文件永久存在,需要 unlink 删除
访问路径无路径名有路径名
阻塞特性读写都会阻塞 (默认)读写都会阻塞 (默认)
适用场景父子进程快速 IPC互不相关进程之间通信

Linux中什么是信号,最常用的信号有哪些

信号Signal是 Linux/Unix 下一种异步通信方式,属于进程间通信 (IPC)。

  • 信号本质就是一个数字编号,用来通知进程发生了某个事件。
  • 信号是异步:进程不知道信号什么时候到来,信号可以在进程运行的任意时刻打断它。
  • 谁可以发信号:内核、另一个进程、键盘快捷键(Ctrl+C)。
  • 进程收到信号后,有 3 种处理方式:
    1. 默认动作:系统预设行为(终止、暂停、忽略、核心转储)
    2. 忽略信号:收到信号什么也不干signal() / sigaction()
    3. 捕获信号(自定义处理):注册一个信号处理函数,收到信号就执行自己写的回调函数
  • ⚠️ 有两个信号不能被捕获、不能被忽略SIGKILL(9)SIGSTOP(19)
信号名编号含义触发来源默认动作
SIGINT2中断信号键盘Ctrl + C终止进程
SIGQUIT3退出信号键盘Ctrl + \终止 + 生成 core 核心转储文件
SIGKILL9强制杀死进程kill -9 进程号立刻终止,不可捕获 / 忽略
SIGSEGV11段错误(非法内存访问)代码越界、空指针解引用终止 + core 转储
SIGALRM14闹钟信号alarm()定时函数终止进程
SIGTERM15优雅终止信号kill 进程号(不带‑9)终止进程,可以被捕获忽略
SIGSTOP19暂停进程Ctrl+Z、kill‑19暂停,不可捕获 / 忽略
SIGCONT18继续运行暂停进程kill -18恢复被暂停的进程运行
SIGCHLD17子进程状态改变(子进程退出)子进程结束,发给父进程默认忽略

收到信号之后可以有哪些处理方式,可以修改处理动作吗

默认处理(SIG_DFL)

Linux 内核预先规定好该信号的默认行为,一共就 5 类:

  1. 终止进程:退出程序,如 SIGINT (2)、SIGTERM (15)
  2. 终止 + 生成 core 转储文件:方便调试崩溃,如 SIGQUIT (3)、SIGSEGV (11)
  3. 暂停进程:进程挂起停止运行,如 SIGSTOP (19)
  4. 继续运行:唤醒暂停进程,如 SIGCONT (18)
  5. 忽略信号:收到信号什么都不做,如 SIGCHLD (17) 默认忽略

2. 忽略信号(SIG_IGN)

进程收到信号直接丢掉,完全不响应。

例外:SIGKILL (9)、SIGSTOP (19) 绝对不能忽略

3. 捕获信号(自定义信号处理函数)

程序员写一个回调函数,当信号到来时,内核打断当前代码,跳转去执行你写的信号处理函数,处理完再回到原来代码继续运行。

例外:SIGKILL、SIGSTOP 不能捕获

修改处理动作:注册对应信号的处理函数

typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);

共享内存是什么,共享内存和管道的区别

共享内存(Shared Memory)是Linux 进程间通信 IPC 的一种方式。通过在内核开辟一块物理内存缓冲区,映射到多个不同进程的虚拟地址空间。 多个进程都可以直接读写这同一块内存,进程之间不需要内核转发拷贝数据。

对比项管道 (pipe/FIFO)共享内存 (System‑V)
数据传递方式两次拷贝 (用户→内核→用户)无拷贝,直接访问物理内存
速度最快 IPC
同步互斥自带同步阻塞机制,内核管理无同步!需要程序员手动加锁 / 信号量
数据特性字节流;读走之后数据就消失数据保留,可反复读写
通信方向半双工 (单向)全双工,双向读写
进程关系pipe:要有血缘;FIFO 无血缘任意进程,无血缘限制
生命周期管道:所有进程关闭就销毁FIFO:文件需 unlink 删除共享内存段手动 shmctl 删除,进程退出不会自动删
适用场景少量数据、简单顺序传输大批量数据高速交换
  • 管道通信时数据需要从发送进程拷贝到内核缓冲区,再从内核拷贝到接收进程;共享内存将同一块物理内存映射到多个进程虚拟地址,进程直接读写内存,省去两次拷贝,效率更高
  • 管道自带内核提供的同步阻塞机制;共享内存没有同步机制,必须额外使用信号量等方式实现进程同步,防止竞争访问
  • 管道的数据一旦被读取就消失;共享内存的数据可以被多个进程反复读取。

Linux进程调度算法

  • 实时进程(Real‑time):优先级高,对响应时间要求苛刻。
  • 普通进程(CFS,完全公平调度):交互式程序、后台任务。
1、实时进程调度算法(2 种)

实时进程优先级:1‑99,数字越大优先级越高。

(1)SCHED_FIFO 先来先服务(FIFO)
  • 非时间片轮转
  • 一旦进程获得 CPU,就一直运行,直到:主动放弃 CPU、阻塞、被更高优先级进程抢占
  • 同优先级:先就绪先运行
  • 低优先级进程永远抢不过高优先级
(2)SCHED_RR 时间片轮转
  • 带时间片的 FIFO
  • 同优先级进程轮流执行,分配固定时间片
  • 时间片用完,放到同优先级队列末尾;高优先级依然可以随时抢占
  • 实时进程可以抢占普通进程
2、普通进程调度:CFS 完全公平调度器(Linux 2.6.23 之后默认)

调度策略:SCHED_OTHER

CFS 核心思想

不按固定时间片,给每个进程分配公平的 CPU 运行时间。引入关键概念:

  1. 虚拟运行时间 vruntime
    • 进程每跑一会,虚拟时间就累加
    • 优先级高(nice 值小)→ vruntime 增长慢,能获得更多 CPU 时间
  2. 红黑树(平衡二叉搜索树)
    • 就绪进程全部放在红黑树里
    • 每次选 vruntime 最小的进程上 CPU 运行
  3. nice 值:‑20 ~ 19,默认 0
    • nice 越小 → 优先级越高,分得 CPU 越多

锁的机制

锁类型核心特点典型场景
互斥锁 Mutex同一时间仅 1 个线程访问;谁上锁谁解锁普通共享资源、队列保护
递归锁同一个线程可重复上锁递归函数
读写锁 rwlock读共享,写独占读多写少数据
自旋锁抢不到锁就空转,不休眠锁占用时间极短,内核
信号量 Semaphore计数,可跨线程释放锁资源限流、进程间同步
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 6:03:50

DeepSeek Harness部署手册:安装、配置与插件开发实战

之前有不少读者在视频平台看到 DeepSeek Harness 的安装演示,觉得“万物可插件”的思路很新颖,但自己动手时却容易卡在不同环节。尤其是评论区里高频出现的“卡在 pnpm dsh web”“插件安装后不生效”“下载太慢”等问题,图文教程却很少。这篇…

作者头像 李华
网站建设 2026/9/4 6:00:11

开源 AI 智能体 OpenClaw,Windows 整合包安装避坑汇总

🦞玩转 OpenClaw|Windows 一键整合包,本地 AI 自动化轻松搞定💻 最近爆火的 OpenClaw(小龙虾 Agent)🔥,和普通对话 AI 完全不一样! 不再只是输出文字回答,它…

作者头像 李华
网站建设 2026/9/4 5:59:54

[光学原理与应用-612]:红外辐射发生的根本原因

红外辐射发生的根本原因 根本原因:物体内部带电粒子(分子、原子、电子)的热运动。 只要物体温度>绝对零度(‑273.15 ℃),分子、原子就一直在做无规则热振动、碰撞;带电粒子做加速 …

作者头像 李华
网站建设 2026/9/4 5:59:40

本地部署大模型实战:从硬件选型到Ollama跑通流程

上上个周末,我把那台吃灰两年的主机重新点亮,装了一块 12G 显存的旧卡,然后照着网上的帖子折腾了一整天。说实话,动手之前我以为“本地部署大模型”这件事离普通人很远,毕竟满屏都是“A100”“80G显存”“训练集群”这…

作者头像 李华
网站建设 2026/9/4 5:58:47

阶梯式发压设计:如何准确探测 RAG 服务的最大容量承载点

阶梯式发压设计:如何准确探测 RAG 服务的最大容量承载点 在给企业级 RAG 系统或高并发大模型网关做容量评估时,很多测试人员最常犯的一个错误就是“一步到位”:直接拉起 2000 个并发并发线程,一股脑把流量轰向服务端。 这种粗暴的…

作者头像 李华
网站建设 2026/9/4 5:58:14

你的标书正在被机器“逐条打分”:AI评标的5个影响,每个都直接决定你的得分

AI评标不是用AI来决定谁中标,而是用AI来辅助评委完成“评分”这个环节。但它对标书制作的影响是颠覆性的——因为你的标书从“写给人看”变成了 “同时写给人看和给机器看” 。 这个问题最近问的人越来越多,说明大家已经切实感受到AI评标的普及了。 一、…

作者头像 李华