用户空间和内核空间
我们这里简单解释下这两个概念,我们平时的编程就是在用户空间去做的,然后内核空间一般是操作系统的地盘,主要就是和硬件打交道,控制资源分配等,它受到一些特定的保护,我们不能随意闯入
可以注意下,就是 用户空间的级别是比内核空间的级别低 的,换句话说,内核空间里的是老大,我们想要做一些与 硬件 打交道的就必须得经过内核的同意,比如说输出字符串到屏幕上,屏幕就算是硬件,还有读取文件,这是磁盘IO,磁盘也是硬件,这些都是要经过内核同意的
清楚了这两个概念后,你可能会疑惑那要怎么得到内核的同意呢,答案就是系统调用,系统调用就相当于是内核空间和用户空间的桥梁,系统调用可以看成是函数吧,用户空间把用到的参数传递给系统调用,然后在通过系统调用传递给内核,最后执行结果
当然平时我们使用的是库函数,系统调用已经被封装好了,我们不用知道,所以有些库函数也可以叫做 外壳函数,意思就是对系统调用进行了包裹
比如说 printf 这个C语言库函数,它的内部实际上调用了系统调用 write 函数,不过库函数会提供更丰富的功能就是了
系统调用的流程
上面是一个例子,用户态其实就是用户空间,内核态就是内核空间
让我们解析下这个系统调用 execve 的执行步骤,我们不必关心它的作用是什么
首先,我们在 用户空间 的 应用程序 调用了 execve 函数,我们这里传入了三个参数 path, argv, envp, 然后进入 execve 函数的内部,也就是外壳函数的内部
然后就一直往下执行,遇到 int 0x80 这条指令,这条指令的意思就是 引发中断,你可以把它看成是 我要进入内核空间 的意思,然后我们可以看到这里进入内核空间前,传入的参数是4个,比我们在 应用程序 中传入的多了一个 __NR__execve ,这多出来的一个就是标识execve函数的 中断号,你可以理解为函数名( 这很好理解,系统调用其实也相当于是函数,他们在内核空间中也有属于自己的代码,所以中断号其实就和函数名差不多,是为了区分和方便调用 )
再然后,经过一些检查,我们成功进入了内核空间,进入到的是 中断处理程序,其实就是系统调用的具体代码的入口啦,然后根据我们的 中断号 跳转到具体的代码中执行,最后执行完后,在一路返回到用户空间中
以上就是系统调用的大致流程了
补充
这里先给出书上的一段话
系统调用允许进程向内核请求服务。与用户空间的函数调用相比,哪怕是最简单的系统调用都会产生显著的开销,其原因是为了执行系统调用,系统需要临时性地切换到核心态,此外,内核还需验证系统调用的参数、用户内存和内核内存之间也有数据需要传递
我在这里给出我的理解,系统调用会产生显著的开销,主要是因为系统临时性的切换到内核态,这个切换涉及到CPU中的一些寄存器,比如CS、SS、DS等等,具体的功能大家就自行查阅了,我想说的是,当你进行内核态和用户态的切换时,这些寄存器的值也要进行改变(先保存旧值,然后赋新值),因为CPU就只认得这些寄存器,所以要让CPU切换到内核去执行,就必须让这些寄存器指向内核空间,然后切换的过程中还需要检查传入的参数,数据的传递等等,最后还要从内核态回到用户态
对比下用户空间的函数调用,他们是在同一层级,所以也就不需要进行这种复杂的切换
总之,不同层级的切换一般是比同层级的切换复杂的,所以开销也大一点
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理