当前位置: 首页 > news >正文

Linux 触发用户态到内核态切换的是:系统调用、中断与异常

此文是 ChatGPT 生成的文章,帮助理解 : 用户态什么时候会切换到内核态

在 Linux 中,程序平时运行在用户态,而内核代码运行在内核态。很多人会把“切换到内核态”理解成“用户程序一调用函数就进内核了”,其实不是。真正会触发用户态到内核态切换的,是系统调用、中断和异常

一、什么是切换到内核态

CPU 运行程序时,会处在不同的权限级别。
用户程序运行在低权限的用户态,不能直接操作硬件,也不能直接执行内核函数。
当程序需要操作文件、申请内存、访问设备、等待输入输出时,就必须通过受保护的入口进入内核,由内核代为完成。

这个过程就是:

用户态 → 内核态 → 返回用户态

二、不是“调用函数就切换”,而是“发生特定事件才切换”

很多常见函数名看起来像是在做系统级事情,比如:

  • printf()
  • malloc()
  • memcpy()

但它们本身不一定会切换到内核态

它们通常先在用户态完成自己的工作,只有在内部确实需要内核帮助时,才会发起系统调用,进入内核态。

例如:

  • malloc()先尝试在用户堆里分配
  • 不够时才通过brk()mmap()向内核申请更多空间
  • 这时才发生用户态到内核态切换

三、最常见的切换方式:系统调用

用户程序不能直接调用内核内部函数,必须通过系统调用进入内核。

例如:

  • read()
  • write()
  • open()
  • ioctl()
  • fork()
  • mmap()

这些函数的最终路径,都会经过系统调用入口。

系统调用的过程大致是:

  1. 用户程序调用 libc 接口(这里的libc ,一般指 C 运行时库,也就是常说的 C 标准库 / 标准库实现,在 Linux 上最常见的是 glibc,也可能是 uClibc、musl。它不只是 C 语言标准里的那些函数,还包括很多POSIX 接口的封装

比如:

  • printf()
  • malloc()
  • free()
  • strlen()
  • strcpy()
  • fopen()
  • fread()
  • fwrite()
  • open()
  • read()
  • write()

其中有些是纯库函数,有些是对系统调用的封装。

2. libc 发现需要进入内核

3. 通过syscallint 0x80svc等指令触发切换

4. CPU 进入内核态

5. 内核执行对应服务函数

6. 返回用户态

四、除了系统调用,中断和异常也会切换到内核态

1. 中断

比如:

  • 定时器中断
  • 键盘中断
  • 网卡中断
  • 设备中断

硬件一旦发出中断,CPU 会暂停当前用户程序,转去执行内核中的中断处理代码。

2. 异常

比如:

  • 缺页异常(page fault)
  • 除零异常
  • 非法指令异常

当程序访问了尚未映射的内存页,CPU 也会进入内核,由内核处理缺页,再返回用户态继续执行。

五、以 malloc() 为例理解切换过程

#include <stdio.h> #include <stdlib.h> int main() { int *p = malloc(100); printf("%p\n", p); free(p); return 0; }

这个程序里,malloc(100)表面上只是一个普通函数,但它内部可能分两种情况:

情况一:用户堆里还有空闲内存

malloc()直接从用户态的内存管理器中分配,不进入内核态

情况二:用户堆不够了

malloc()会通过系统调用向内核申请更多空间,例如:

  • brk()
  • mmap()

这时 CPU 才会:

  • 从用户态切换到内核态
  • 内核扩展进程虚拟地址空间
  • 返回用户态
  • malloc()再把结果交给程序

所以,malloc 本身不一定切换内核态,只有在需要向内核要资源时才会切换

六、用户态与内核态各自使用自己的栈

用户栈

  • 每个线程有自己的用户栈
  • 保存函数调用、局部变量、返回地址
  • 程序结束时由内核自动回收

内核栈

  • 每个线程进入内核态时使用自己的内核栈
  • 空间较小,通常只有几 KB 到十几 KB
  • 不能随便放大数组或大对象

也就是说,切换到内核态时,不只是权限变了,连使用的栈也变了

七、 可以这样记忆

用户程序并不是“想进内核就能进内核”,而是必须通过正规入口:

  • 系统调用:最常见
  • 中断:硬件触发
  • 异常:错误或缺页触发

而普通的用户态函数调用,大多数仍然是在用户空间内部完成。

八、一句话总结

用户态切换到内核态,发生在系统调用、中断和异常这些受保护事件中;像malloc()printf()这类函数本身不一定切换,只有它们内部需要内核服务时,才会真正进入内核态。

--------------------------------------------------------------------------------------------------------------------

下面是补充的知识点:

1.libc,一般指C 运行时库,也就是常说的C 标准库 / 标准库实现,在 Linux 上最常见的是glibc,也可能是 uClibc、musl。

它不只是 C 语言标准里的那些函数,还包括很多POSIX 接口的封装

比如:

  • printf()
  • malloc()
  • free()
  • strlen()
  • strcpy()
  • fopen()
  • fread()
  • fwrite()
  • open()
  • read()
  • write()

其中有些是纯库函数,有些是对系统调用的封装。

2. libc 接口和系统调用是什么关系?

可以简单理解成这样:

用户程序 → libc 接口 → 系统调用 → 内核

但要注意:

  • 不是所有 libc 函数都会进入内核
  • 只有需要内核服务时,才会触发系统调用

例子

printf()

大多数时候只是,用户态库函数,先把内容放到缓冲区里。
真正写到终端或文件时,可能才会调用write(),进而进入内核。

malloc()

先由 libc 自己管理堆内存。
如果不够了,才会调用brk()mmap()之类的系统调用。

3. 系统调用指的是什么?

系统调用是用户态 请求 内核服务 的入口
用户程序不能直接执行内核代码,必须通过系统调用。

常见系统调用有很多,按功能大致可以分成几类:

文件和目录相关

  • open
  • close
  • read
  • write
  • lseek
  • stat
  • unlink
  • rename

进程管理相关

  • fork
  • vfork
  • execve
  • exit
  • waitpid
  • getpid
  • kill

内存管理相关

  • brk
  • mmap
  • munmap
  • mprotect

设备和控制相关

  • ioctl

时间相关

  • nanosleep
  • clock_gettime
  • gettimeofday

网络相关

  • socket
  • bind
  • listen
  • accept
  • connect
  • send
  • recv

事件等待相关

  • select
  • poll
  • epoll_create
  • epoll_ctl
  • epoll_wait

4. 一个很重要的区别:库函数不等于系统调用

例如:

  • printf()库函数
  • write()才是更接近内核的接口
  • write()最终会触发系统调用

再比如:

  • malloc()是库函数
  • brk()/mmap()才会进入内核

所以可以记成:

libc 是“用户态工具箱”,系统调用是“进入内核的门”。

5. 最容易混淆的一点

有些函数名字看起来像系统调用,但其实你平时调用的往往是libc 封装后的版本

比如:

int fd = open("a.txt", O_RDONLY);

你写的是open(),但它通常是 libc 提供的包装,内部再调用系统调用进入内核。

open() 是 sys_open 的用户态薄封装,只负责传参和触发系统调用,真正打开文件的是内核里的 sys_open

-------------------------------------------------------------------------------------------------------------

Linux 内核和应用层这一块,可以把它理解成下面这座"三层楼":

对于 应用开发和驱动开发 来说,可以这么理解:

用户程序

库函数(libc)

系统调用

内核函数

驱动函数

硬件

但如果再往下追:

内核函数

驱动函数

寄存器读写

CPU总线

硬件设备

真正最底层的是:

readl();
writel();

或者:

gpio_set_value();

最终变成:

CPU访问寄存器

驱动开发本质上就是在做这件事。

用户程序不能直接调用内核函数,必须通过系统调用进入内核;系统调用只是入口,真正完成工作的仍然是内核中的各种函数和驱动函数。

http://www.rkmt.cn/news/1519761.html

相关文章:

  • DRG Save Editor:三分钟快速上手,告别重复刷矿的存档编辑神器
  • i.MX23视频DAC与SSP接口实战:从寄存器配置到低功耗设计
  • TikTokDownloader终极指南:高效批量下载抖音TikTok视频的完整解决方案
  • FunClip终极指南:基于大语言模型的智能视频剪辑解决方案
  • Python开发中的最佳实践:代码质量与团队协作
  • 苏州学历提升哪家靠谱?7大校区直营机构对比,选对少走3年弯路 - 学历提升信息早知道
  • 光通传奇3 永恒传奇3(GSR版本) (五) 利用CE制作自动打怪挂机简易辅助
  • 2026云南纯玩团推荐纯玩参考TOP3,纯玩无购物,费用和避坑参考 - 旅游发布
  • Spring Security- 退出登录的配置与实现逻辑
  • 广州搬家|搬厂公司盘点 结合资质与项目经验的参考名录 - 互联网科技品牌测评
  • 终极指南:如何用YOLOv8构建工业级视觉检测系统
  • 义乌珠宝银饰批发哪个好 - 资讯速览
  • 毕业答辩PPT模板推荐哪家?高适配平台,新手也能不踩坑 - 品牌测评鉴赏家
  • 2026云南纯玩团推荐TOP5纯玩无购物,费用路线和避坑参考 - 旅游发布
  • 详解AI时代下生产力最佳实践—Iterm2+zsh
  • ANARCI终极指南:5分钟掌握抗体序列编号与分类技术
  • 三步实现微博图片批量下载:无需登录的高效采集方案
  • 2026 国家认可的计算机专业证书
  • 2026主流AI论文写作工具实测测评 - 品牌测评鉴赏家
  • 深入解析NXP SEC描述符命令:FIFO对齐、校验和与密钥加载实战
  • 数学建模与AI学习资源全景整理
  • 基于PLC的直驱风电机组变桨距控制系统设计2(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • WarcraftHelper:魔兽争霸3终极优化工具完整使用指南
  • Udacity AWS机器学习奖学金:云上ML工程实战通关指南
  • 如何快速提取微信聊天记录:打造个人AI助手的完整实战指南
  • MC9328MXL USB FIFO管理:从硬件原理到稳定传输的实战指南
  • 2026 大专可以考哪些金融行业证书
  • CUDA Agent: Large-Scale Agentic RL for High-Performance CUDA Kernel Generation高性能CUDA内核生成的大规模智能体强化学习
  • MC9328MXS微控制器DMA与看门狗定时器实战详解
  • BERTScore技术解析:基于上下文嵌入的文本生成质量评估新范式