网站建设课程设计报告php有哪些做平面设计好的网站
2026/4/15 12:51:26 网站建设 项目流程
网站建设课程设计报告php,有哪些做平面设计好的网站,北京未来广告公司,营业推广的形式包括本系列主要旨在帮助初学者学习和巩固Linux系统。也是笔者自己学习Linux的心得体会。 个人主页#xff1a; 爱装代码的小瓶子 文章系列#xff1a; Linux 2. C 文章目录 Linux的魔法世界#xff1a;进程、内存与操作系统的三重奏第一章#xff1a;进程——操作系统里的爱装代码的小瓶子文章系列Linux2.C文章目录Linux的魔法世界进程、内存与操作系统的三重奏第一章进程——操作系统里的执行单元什么是进程进程的背包——内存区域来看看你的进程进程间通信它们如何聊天第二章操作系统内核——幕后的总指挥用户空间 vs 内核空间系统调用跨越边界的桥梁调度器时间的管理者第三章虚拟内存——最伟大的魔法为什么需要虚拟内存虚拟地址空间大小页表地址翻译的秘密武器来看一个真实的进程内存布局缺页中断按需加载Copy-on-Write聪明的内存共享第四章三者关系——完整的图景从命令行到程序运行的旅程操作系统如何协调一切完整的数据流图第五章深入实践——动手探索使用/proc文件系统观察Copy-on-Write性能优化技巧总结核心概念三者的关系为什么理解这些很重要继续探索的路径最后的话参考资源Linux的魔法世界进程、内存与操作系统的三重奏想象一下你打开了一个音乐播放器又启动了浏览器再开了一个文本编辑器。电脑同时运行着这么多程序却不会互相干扰这是为什么如果我说这一切都发生在同一个物理内存条上你可能会更困惑。今天我们就来揭开Linux操作系统的魔法面纱探索进程、操作系统内核和虚拟内存空间是如何协同工作的。第一章进程——操作系统里的执行单元什么是进程你可能在编程时听说过程序和进程这两个词。初学者经常把它们搞混但它们其实有本质区别概念生活比喻核心特点程序冰箱里的食谱静态的躺在磁盘上的文件进程正在做饭的厨师动态的占用CPU和内存的执行实例一个程序可以同时对应多个进程。就像同一份烘焙食谱程序可以有五个人五个进程在各自的厨房里同时制作蛋糕。每个进程都有一个独一无二的身份证号——PIDProcess ID。在Linux中你可以用这个号码来和进程交流。进程的背包——内存区域当一个进程启动时操作系统会为它分配一个背包里面装着它需要的所有东西。这个背包按区域划分进程虚拟地址空间每个进程都有自己独立的一份 ┌─────────────────────────────┐ ↑ │ 命令行参数和环境 │ 高地址 ├─────────────────────────────┤ │ │ 栈 (Stack) │ │ 函数调用、局部变量 │ ↓ │ │ 向下生长 ├─────────────────────────────┤ │ │ ▲ │ │ │ │ │ │ │ 内存映射段 (mmap) │ │ 动态库、共享内存 ├─────────────────────────────┤ │ │ 堆 (Heap) │ │ malloc/new分配的内存 │ ▲ │ │ 向上生长 ├─────────────────────────────┤ │ │ BSS段 │ │ 未初始化的全局变量 ├─────────────────────────────┤ │ │ 数据段 │ │ 已初始化的全局变量 ├─────────────────────────────┤ │ │ 代码段 │ │ 只读程序指令 ├─────────────────────────────┤ │ │ 保留空间 │ │ 防止NULL指针访问 └─────────────────────────────┘ │ 0x0000000000000000栈Stack就像一摞盘子。每次调用函数放一个新盘子函数返回拿走一个盘子。存放局部变量和函数调用信息。堆Heap程序运行时动态申请的内存在这里。malloc、new申请的内存都来自堆。代码段Text程序的机器码只读的——不能修改正在运行的代码。来看看你的进程打开终端试试这些命令# 查看所有进程ps-ef# 查看当前shell进程的信息ps-f$$# 实时监控进程像任务管理器top# 更漂亮的版本如果安装了htophtop# 查看某个进程占用的内存cat/proc/1/status|grep-i mem试着运行一个简单程序然后找到它的PID# 在后台运行一个简单的sleep程序sleep300# 最后一行显示的数字就是PID比如[1] 12345# 现在查看这个进程的详细信息cat/proc/12345/status你会看到一堆有趣的信息进程名字、状态、父进程PID、内存使用情况等。进程间通信它们如何聊天进程是相互隔离的但有时候需要交流。Linux提供了多种方式管道Pipes像一根管子一个进程往里写另一个进程从里面读。# 查看文件内容然后过滤出包含python的行catsomefile.txt|greppython这里cat和grep是两个进程管道|连接了它们的输出和输入。共享内存最快的方式多个进程可以直接读写同一块内存区域。信号Signals像发送一个通知比如CtrlC就是发送SIGINT信号。第二章操作系统内核——幕后的总指挥用户空间 vs 内核空间你写的程序运行在用户空间——一个受限的环境。为什么主要是为了安全用户空间只能访问自己的内存不能直接操作硬件需要通过系统调用请求帮助内核空间可以访问所有内存可以操作硬件拥有最高权限这就像一个国家普通公民用户空间不能随便进入政府办公区内核空间需要办理手续系统调用。系统调用跨越边界的桥梁当你的程序需要做一些特权操作时比如打开文件或创建新进程必须调用系统调用。// 用户代码intfdopen(myfile.txt,O_RDONLY);// 系统调用这行代码背后发生了什么用户程序调用open() ↓ 切换到内核模式 ↓ 内核检查权限打开文件 ↓ 返回文件描述符 ↓ 切换回用户模式 ↓ 用户程序继续执行调度器时间的管理者电脑只有一个CPU或者几个却有几十甚至上百个进程都在想运行。谁来决定谁什么时候能跑调度器Scheduler调度器给每个进程分配时间片Time Slice通常是几毫秒。时间片用完就切换到下一个进程。因为切换很快我们感觉所有程序在同时运行。进程的状态创建 → 就绪 → 运行 → 阻塞 → 就绪 ↑ ↓ └────────────────┘就绪Ready准备好了等着CPU运行Running正在CPU上执行阻塞Blocked等待某个事件比如网络数据你可以查看进程状态ps-eo pid,stat,cmd|head状态字母含义R Running/RunnableS Sleeping可中断的等待D Uninterruptible sleep通常在等待I/OT Stopped被暂停Z Zombie僵尸进程第三章虚拟内存——最伟大的魔法为什么需要虚拟内存如果进程直接访问物理内存会有大问题没有隔离一个进程的错误可能破坏其他进程地址冲突每个进程都想用0x1000这个地址内存不足物理内存有限但程序想要的更多虚拟内存解决了这些问题每个进程都以为自己拥有整个地址空间实际上访问的是映射后的物理内存。虚拟地址空间大小在现代64位Linux系统上x86-64架构┌─────────────────────────────┐ │ 内核空间 (128TB) │ 0xffff800000000000 ├─────────────────────────────┤ │ 非 canonical 区间 │ ├─────────────────────────────┤ │ 用户空间 (128TB) │ 0x0000000000000000 └─────────────────────────────┘实际上Linux默认使用48位虚拟地址256TB而不是完整的64位太大了用不完。用户空间0x0000000000000000 到 0x00007fffffffffff128TB内核空间0xffff800000000000 到 0xffffffffffffffff128TB页表地址翻译的秘密武器虚拟内存不是整块映射而是分成固定大小的页Pages。默认页大小是4KB。页表Page Table存储虚拟页到物理页的映射关系。x86-64使用4级页表结构虚拟地址48位 ┌─────┬─────┬─────┬─────┬─────┐ │ PML4│ PDPT│ PDT │ PTE │ 偏移 │ └─────┴─────┴─────┴─────┴─────┘ 9位 9位 9位 9位 12位 每次查找一级 → 最终得到物理页号这看起来很复杂但硬件MMU内存管理单元会自动完成这个翻译过程速度非常快。为了更快CPU有TLBTranslation Lookaside Buffer——一个缓存存储最近的地址翻译结果。来看一个真实的进程内存布局写个小程序看看自己的内存长什么样#includestdio.h#includestdlib.h#includeunistd.hintglobal_var100;// 数据段intuninit_var;// BSS段voidprint_address(constchar*name,void*addr){printf(%-20s: %p\n,name,addr);}intmain(){intlocal_var200;// 栈上int*heap_varmalloc(sizeof(int));// 堆上*heap_var300;printf( 内存地址演示 \n\n);print_address(代码段,(void*)main);print_address(数据段,global_var);print_address(BSS段,uninit_var);print_address(堆,heap_var);print_address(栈,local_var);printf(\n按回车查看 /proc/self/maps...\n);getchar();// 让程序一直运行方便查看printf(PID: %d\n,getpid());printf(查看内存映射: cat /proc/%d/maps\n,getpid());printf(按回车退出...\n);getchar();free(heap_var);return0;}编译运行gcc -o memory_demo memory_demo.c ./memory_demo输出类似 内存地址演示 代码段 : 0x55c8c7b2d14a 数据段 : 0x55c8c7c4a010 BSS段 : 0x55c8c7c4a068 堆 : 0x55c8c92312a0 栈 : 0x7ffd8e9b646c你会发现栈地址很高接近用户空间顶部堆地址在中间代码、数据、BSS在较低的位置保持程序运行在另一个终端查看# 查看完整的内存映射cat/proc/$(pgrep memory_demo)/maps# 或者用 pmap如果安装了pmap$(pgrep memory_demo)输出格式55c8c7b2d000-55c8c7b2e000 r-xp 00000000 08:01 123456 /path/to/memory_demo 55c8c7b2e000-55c8c7b2f000 r--p 00001000 08:01 123456 /path/to/memory_demo 55c8c7b2f000-55c8c7b30000 rw-p 00002020 08:01 123456 /path/to/memory_demo ...每列含义虚拟地址范围权限r读w写x执行p私有偏移量设备和inode文件路径缺页中断按需加载你可能好奇程序很大启动时要把所有代码都加载到内存吗答案是不用当程序启动时操作系统只设置好页表并不真的加载所有页面。当程序真正访问某个地址时CPU发现该页不在内存中触发缺页中断操作系统把需要的页从磁盘加载到内存更新页表重新执行指令这叫按需加载Demand Paging节省了内存和时间。Copy-on-Write聪明的内存共享当你用fork()创建子进程时内核不会立即复制父进程的所有内存。相反父子进程共享相同的物理页所有页标记为只读当任何一方尝试写入时触发缺页中断复制该页重新设置权限这大大提高了性能fork()几乎是瞬间完成的因为只是复制了页表指针。第四章三者关系——完整的图景现在让我们把所有线索串起来。从命令行到程序运行的旅程当你在终端输入./myprogram arg1 arg2时1. Shell解析命令 ↓ 2. Shell调用fork()创建子进程 ↓ 3. 子进程调用execve()替换自己 - 读取程序文件 - 创建新的虚拟地址空间 - 设置页代码、数据、BSS - 设置栈放入参数和环境变量 ↓ 4. 调度器将新进程放入就绪队列 ↓ 5. 新进程开始执行 main() 函数 ↓ 6. 每次内存访问 虚拟地址 → 页表翻译 → 物理地址 → 访问内存操作系统如何协调一切用一个比喻总结组件角色职责进程工人执行具体任务虚拟内存私人办公室每个工人有自己的空间页表地址簿虚拟地址和真实地址的对应内核管理者分配资源调度任务调度器时间管理员决定谁什么时候工作MMU翻译官自动完成地址翻译完整的数据流图┌─────────────────────────────────────────────────────────────┐ │ 用户空间 │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 进程 A │ │ 进程 B │ │ │ │ ┌────────┐ │ │ ┌────────┐ │ │ │ │ │ 代码段 │ │ │ │ 代码段 │ ││ │ │ ├────────┤ │ │ ├────────┤ │ │ │ │ │ 栈 │ │ │ │ 栈 │ │ │ │ │ └────────┘ │ │ └────────┘ │ │ │ │ 虚拟地址: │ │ 虚拟地址: │ │ │ │ 0x1000 │ │ 0x1000 │ (相同虚拟地址!) │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ └─────────┼─────────────────────────┼────────────────────────────┘ │ │ │ 系统调用/中断 │ │ │ ┌─────────┴─────────────────────────┴────────────────────────────┐ │ 内核空间 │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 页表A: 0x1000 → 物理页 0xABC000 │ │ │ │ 页表B: 0x1000 → 物理页 0xDEF000 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 调度器 │ │ │ │ 决定谁在CPU上运行 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ └────────────────────┬─────────────────────────────────────────┘ │ MMU自动翻译 ↓ ┌─────────────────────────────────────────────────────────────┐ │ 物理内存 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 0xABC000 │ │ 0xDEF000 │ │ 其他页 │ │ │ │ 进程A │ │ 进程B │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────┘关键点进程A和进程B都使用虚拟地址0x1000但它们映射到不同的物理页面第五章深入实践——动手探索使用/proc文件系统/proc是Linux提供的一个神奇目录它不是真正的文件系统而是内核信息的窗口。# 查看所有进程ls/proc# 查看当前进程的命令行cat/proc/self/cmdline# 查看当前进程的内存映射cat/proc/self/maps# 查看当前进程的完整状态cat/proc/self/status# 统计所有进程的内存使用forpidin$(ls/proc|grep-E^[0-9]$);domem$(cat/proc/$pid/status2/dev/null|grepVmRSS|awk{print $2})name$(cat/proc/$pid/comm2/dev/null)echo$name:$memKBdone|sort-k2 -rn|head-10观察Copy-on-Write写个小程序验证写时复制#includestdio.h#includestdlib.h#includeunistd.h#includesys/mman.h#includesys/wait.hintglobal1;intmain(){printf(开始: global %p, global %d\n,global,global);pid_tpidfork();if(pid0){// 子进程printf(子进程: 修改前 global %d\n,global);global2;printf(子进程: 修改后 global %d\n,global);printf(子进程: global %p\n,global);return0;}else{// 父进程wait(NULL);// 等待子进程完成printf(父进程: global %d\n,global);printf(父进程: global %p\n,global);}return0;}编译运行你会发现父子进程修改了同一个变量但值不同因为修改时发生了写时复制。性能优化技巧了解这些原理后你可以写出更高效的代码1. 减少缺页中断尽量连续访问内存更好的局部性使用mlock()锁定关键内存页2. 合理使用栈和堆小对象用栈自动释放、快大对象用堆栈空间有限3. 内存对齐对齐的内存访问更快posix_memalign()分配对齐内存4. 了解你的内存# 查看系统内存信息cat/proc/meminfo# 查看页面大小getconf PAGESIZE总结核心概念进程程序的动态执行实例有自己独立的虚拟地址空间虚拟内存每个进程独享的假内存通过页表映射到物理内存页表虚拟地址到物理地址的映射表CPU的MMU硬件自动翻译内核操作系统的核心管理进程、内存、硬件等所有资源调度器决定哪个进程在CPU上运行实现多任务幻觉三者的关系它们不是独立的概念而是一个完整的系统进程 ←─ 虚拟内存每个进程有自己的一套 ↑ │ 操作系统内核 ─→ 管理所有进程和内存 │ └──→ 调度器决定谁运行虚拟内存是连接进程和物理内存的桥梁内核是管理一切的指挥家。为什么理解这些很重要理解这些底层原理你就能更好地调试内存问题写出更高效的代码理解操作系统行为学习更高级的主题如容器、虚拟化继续探索的路径书籍推荐《深入理解Linux内核》——经典之作《Linux内核设计与实现》——更容易入门《操作系统概念》——理论基础在线资源Linux内核官方文档man页面man proc、man mmap等LWN.net——Linux内核新闻实践项目写一个简单的shell实现fork、exec、pipe实现一个简单的内存分配器研究strace工具追踪系统调用最后的话操作系统是程序员最好的老师。理解了Linux是如何管理进程和内存的你就掌握了计算机系统最核心的魔法。这些知识不仅有趣还能让你写出更好的程序。现在打开终端运行几个命令感受一下那些在你电脑上默默工作的进程吧# 最后的探索echo 你的系统在运行什么 psaux --sort-%mem|head-5echo-e\n 内存使用情况 free-hecho-e\n 祝你探索愉快 Happy Hacking!参考资源Linux Kernel Documentationman pages: proc(5)Understanding the Linux KernelLinux Memory ManagementWhat Every Programmer Should Know About Memory下面是ima生成的脑图总结感谢各位对本篇文章的支持。谢谢各位点个三连吧

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询