本文已拆分为上下两篇,此为上篇。 上篇聚焦操作系统内核与 C/C++ 底层原理,下篇聚焦 Java/Python/Go/Rust 运行时与并发模型。

【编程语言】从内核到应用:操作系统与六大编程语言底层原理完全指南(下)-CSDN博客

📖 目录

  • 上篇:操作系统内核与 C/C++ 底层原理

    • 一、操作系统内核有哪些功能模块?运行完整流程是怎样的?

    • 二、操作系统内核是 C 语言实现的,为什么 C 标准库还依赖操作系统?底层原理是什么?

    • 三、C++ 和 C 语言的区别是什么?C++ 有哪些场景应用?

    • 四、C 语言从编译到运行的完整流程是什么?动态链接的作用是什么?

  • 下篇:Java/Python/Go/Rust 运行时与并发模型

    • 五、Java 和 Python 底层是 C/C++ 实现的,底层原理是什么?

    • 六、Go 和 Rust 都编译成机器码执行,和 C/C++ 有何区别?

    • 七、Rust 如何实现安全?Go 为什么内存占用少、适合高并发?

    • 八、操作系统内核各模块属于进程吗?第一个进程如何启动?进程如何切换?

👨‍💻 作者介绍

大家好,我是 CodeStats

一个在底层技术上“考古”了四年的硬核爱好者,也是 WWAIC(全周项目 AI 编程) 范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源),也喜欢用通俗的语言拆解 CPU、JVM、操作系统的运行本质。

本文适合谁? 如果你是刚入门的开发者,本文能帮你建立从硬件到软件的全景认知;如果你是有经验的工程师,本文的底层视角或许能帮你解开一些长期困惑。无论你主攻哪门语言,理解底层原理都会让你走得更远。

一、操作系统内核有哪些功能模块?运行完整流程是怎样的?

1.1 内核的五大核心功能模块

Linux 内核根据核心功能划分为 5 大子系统

子系统 核心职责 关键机制
进程调度(Process Scheduler) 管理 CPU 资源,决定哪个进程何时使用 CPU 调度策略、上下文切换、系统调用接口
内存管理(Memory Manager) 管理物理内存,提供虚拟内存机制 页表映射、按需分页、Swap 交换
虚拟文件系统(VFS) 将不同设备抽象为统一文件接口 open/close/read/write,一切皆文件
网络子系统(Network) 管理网络设备,实现网络协议栈 TCP/IP、套接字、网卡驱动
进程间通信(IPC) 实现进程之间的数据交换 管道、信号、共享内存、消息队列

1.2 内核的完整运行流程

从按下电源键到用户程序运行,内核经历了以下阶段:

阶段一:引导加载(Boot) —— BIOS/UEFI 从硬盘加载 Bootloader(如 GRUB),Bootloader 再将内核镜像(vmlinuz)加载到物理内存。

阶段二:内核初始化(Startup) —— 内核入口(startup_64)开启分页(设置 CR3 寄存器)、建立中断描述符表(IDT)、初始化内核栈,然后跳入 C 语言主函数 start_kernel()

阶段三:核心子系统初始化 —— 依次初始化中断(trap_init)、调度器(sched_init)、内存管理(mm_init)、文件系统和网络。

阶段四:启动第一个进程 —— start_kernel() 最后调用 rest_init(),创建 PID=0 的 Idle 进程和 PID=1 的 init 进程(systemd)。

阶段五:进入用户态 —— init 进程启动 Shell 和系统服务,等待用户输入命令。

阶段六:运行用户程序 —— 用户执行 ./hello,Shell 调用 fork() + execve(),内核加载程序,进程开始运行。

关键认知:在 PID 1 启动之前,整个系统处于 “无进程” 状态——CPU 执行的是内核本身的机器码,这些代码不属于任何进程。

二、操作系统内核是 C 语言实现的,为什么 C 标准库还依赖操作系统?底层原理是什么?

2.1 核心答案:完全不矛盾

这个问题的关键在于区分 “C 语言语法” 和 “C 标准库(libc)”

C 语言本身(语法)不依赖操作系统 —— int a = b + c;iffor、指针运算,这些直接被编译器翻译成 MOVADDJMP 等 CPU 指令,不需要任何操作系统帮忙。

C 标准库(libc)依赖操作系统 —— printfmallocfopen 这些函数要操作显示器、分配内存、读写文件,而这些操作必须通过操作系统内核(因为用户态程序不能直接执行 IN/OUT 等特权指令)。

2.2 内核如何用 C 语言但不依赖 libc?

Linux 内核编译时使用 GCC 的 -ffreestanding(独立环境编译) 选项:

  • 禁用标准库(没有 stdio.hstdlib.h

  • 没有 malloc/free(内核自己实现 kmalloc/kfree

  • 没有 printf(内核自己实现 printk

  • 部分底层操作必须用内联汇编(如切换 CR3 寄存器、执行 IN/OUT 指令)

c

// 内核源码示例:直接操作显存地址(不依赖任何库函数)
char *video_memory = (char *)0xB8000; 
*video_memory = 'A';  // 纯 C 指针操作,直接写显存

2.3 系统调用的底层指令

当用户程序调用 printf 时,glibc 最终会执行 SYSCALL 指令(x86-64 架构),CPU 从用户态切换到内核态,由内核代为执行。

结论:内核用 C 语言的核心语法写,但不依赖 C 标准库。系统调用是内核提供给用户程序的接口,内核自己不需要“调用自己”。

三、C++ 和 C 语言的区别是什么?C++ 有哪些场景应用?

3.1 核心区别:不仅仅是“面向对象”

维度 C 语言 C++
编程范式 过程式 多范式(过程式 + OOP + 泛型)
内存管理 手动 malloc/free RAII + 智能指针,也可手动 new/delete
抽象代价 无抽象,直接映射汇编 零开销抽象——不用的功能不付出代价
函数调用 静态绑定 支持虚函数(动态绑定,有 vtable 开销)
内存布局 struct 只有数据 无虚函数的 class 内存布局与 C 的 struct 完全一致

3.2 C++ 的四大核心特性

① RAII(资源获取即初始化) —— 利用栈对象的析构函数自动管理资源生命周期。无论函数如何 return,资源都会被自动释放。

② 零开销抽象(Zero-overhead Abstraction) —— 模板在编译期展开,生成的机器码与手写 C 代码一样高效。

③ constexpr(编译期计算) —— 在编译阶段执行数学运算,生成的二进制只有结果,没有计算过程。

④ 编译期多态(CRTP) —— 通过模板在编译期确定调用关系,无运行时开销。

3.3 C++ 的典型应用场景

场景 为什么用 C++
游戏引擎(Unreal) 帧率敏感,需要极致性能和底层硬件控制
浏览器内核(Chrome/Blink) 海量 DOM 操作 + JIT 编译,需要高性能内存管理
高频交易系统 纳秒级延迟,需绕过内核直接操作网卡
数据库引擎(MySQL/LevelDB) 需要精细控制内存和 I/O 缓存
嵌入式/自动驾驶 实时性要求高,需禁用异常和 RTTI 以精简二进制

四、C 语言从编译到运行的完整流程是什么?动态链接的作用是什么?

4.1 编译的四个阶段

C 语言的编译过程分为 4 个阶段

阶段 命令 输入 → 输出 做了什么
① 预处理 gcc -E .c → .i 处理 #include#define,宏展开,纯文本替换
② 编译 gcc -S .i → .s 词法/语法/语义分析,生成汇编代码
③ 汇编 gcc -c .s → .o 汇编代码转成二进制机器码(目标文件)
④ 链接 gcc(调用 ld .o + 库 → 可执行文件 合并目标文件,解析符号引用

4.2 动态链接 vs 静态链接

静态链接:库的机器码被直接复制进最终的可执行文件。优点:独立运行;缺点:文件大、内存占用高、更新库需重新编译。

动态链接:可执行文件只记录“需要哪个库的哪个函数”,运行时由动态链接器/lib64/ld-linux-x86-64.so.2)加载库并绑定地址。优点:节省磁盘/内存、库可独立更新;缺点:运行时需能找到依赖库。

动态链接的底层机制:可执行文件的 .interp 段记录了动态链接器的路径。内核加载可执行文件时,先启动动态链接器,由它加载 libc.so.6 等共享库,完成符号重定位后,才跳转到程序的 _start 入口。

4.3 运行时:从 ./hello 到进程

执行 ./hello 时,Shell 调用 execve 系统调用陷入内核,内核加载 ELF 文件,发现 .interp 段后启动动态链接器,动态链接器加载 libc.so.6 并重定位符号,最后跳转到 _start → __libc_start_main → main()

📌 下篇预告

上篇我们聊完了操作系统内核的底层机制和 C/C++ 的编译运行原理。下篇将深入:

  • Java:JVM 如何解释执行 + JIT 编译?本地方法(JNI)如何调用 C/C++ 代码?

  • Python:CPython 解释器如何执行字节码?GIL 为什么是性能瓶颈?

  • Go:Goroutine 为什么极其轻量?GMP 调度模型如何实现高并发?

  • Rust:所有权和借用检查如何在编译期保证内存安全?

  • 终极对比:六大语言从源码到 CPU 指令的完整路径对比

如果觉得本文有帮助,欢迎点赞、收藏、关注! 你的支持是我持续输出硬核技术内容的动力。有任何疑问或想深入了解的方向,欢迎在评论区留言讨论。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐