系统调用,python常见数据结构的底层实现(list,hashmap)
操作系统没有传说中那么复杂
- 程序视角:对象 + API
- 硬件视角:一个 C 程序
理解操作系统:三个根本问题
操作系统服务谁?
- 程序 = 状态机
- 课程涉及:多线程 Linux 应用程序
(设计/应用视角) 操作系统为程序提供什么服务?
- 操作系统 = 对象 + API
- 课程涉及:POSIX + 部分 Linux 特性
(实现/硬件视角) 如何实现操作系统提供的服务?
- 操作系统 = C 程序
- 完成初始化后就成为 interrupt/trap/fault handler
数字电路与状态机
数字逻辑电路(一个计数器或者数码管)
状态 = 寄存器保存的值 (flip-flop)
初始状态 = RESET (implementation dependent)
迁移 = 组合逻辑电路计算寄存器下一周期的值
什么是程序 :程序就是状态机
C语言状态机(源代码视角):
- C 程序的状态机模型 (语义,semantics)
- 状态 = 堆 + 栈
- 初始状态 = main 的第一条语句
- 迁移 = 执行一条简单语句
- 任何 C 程序都可以改写成 “非复合语句” 的 C 代码
- 真的有这种工具 (C Intermediate Language) 和解释器
- 状态 = stack frame 的列表 (每个 frame 有 PC) + 全局变量
- 初始状态 = main(argc, argv), 全局变量初始化
- 迁移 = 执行 top stack frame PC 的语句; PC++
- 函数调用 = push frame (frame.PC = 入口)
- 函数返回 = pop frame
- 非递归的汉诺塔程序,模拟栈帧。。。。
汇编语言状态机(二进制视角):
- 状态 = 内存 MM + 寄存器 RR
- 初始状态 = (稍后回答)
- 迁移 = 执行一条指令
- 我们花了一整个《计算机系统基础》解释这件事,
gdb 同样可以观察状态和执行
如何在程序的两个视角之间切换?
- 从两个视角的状态机可以解决一个很重要的基本问题:什么是编译器?
- 编译器:源代码S(状态机)→二进制代码C(状态机)
C=compile(S)
操作系统上的程序
- 所有的指令都只能计算
- deterministic: mov, add, sub, call, ...
- non-deterministic: rdrand, ...
- 但这些指令甚至都无法使程序停下来 (NEMU: 加条 trap 指令)
操作系统中的一般程序
- 所有的程序和最小的helloworld:minimal.S 没有本质区别
- 状态机视角的程序: 程序 = 计算 → syscall → 计算 → ...
调用操作系统 syscall
- 把 (M, R)(M,R) 完全交给操作系统,任其修改
- 实现与操作系统中的其他对象交互
读写文件/操作系统状态 (例如把文件内容写入 MM)
改变进程 (运行中状态机) 的状态,例如创建进程/销毁自己
程序 = 计算 + syscall
- 问题:怎么构造一个最小的 Hello, World?
- (计算机系统不存在玄学;一切都建立在确定的机制上)
所以你应该有一个强烈的信念:这个问题是可以回答的
这门课中很重要的工具:strace
- system call trace
- 理解程序运行时使用的系统调用
- 在这门课中,你能理解 strace 的输出并在你自己的操作系统里实现相当一部分系统调用 (mmap, execve, ...)
本质上,所有的程序和 Hello World 类似
- 程序 = 状态机 = 计算 → syscall → 计算 →...
- 被操作系统加载
- 通过另一个进程执行 execve 设置为初始状态
- 状态机执行
- 进程管理:fork, execve, exit, ...
- 文件/设备管理:open, close, read, write, ...
- 存储管理:mmap, brk, ...
- 直到 _exit (exit_group) 退出
- 被操作系统加载
说好的浏览器、游戏、杀毒软件、病毒呢?都是这些 syscall API 吗?(初学者会感到惊讶)
这些 API 就是操作系统的全部:
- 编译器 (gcc),代表其他工具程序
- 主要的系统调用:execve, read, write
- strace -f gcc a.c (gcc 会启动其他进程)
- 主要的系统调用:execve, read, write
可以管道给编辑器 vim -
编辑器里还可以 %!grep (细节/技巧)
- 图形界面程序 (xedit),代表其他图形界面程序 (例如 vscode)
- 主要的系统调用:poll, recvmsg, writev
- strace xedit:
- 图形界面程序和 X-Window 服务器按照 X11 协议通信
虚拟机中的 xedit 将 X11 命令通过 ssh (X11 forwarding) 转发到 Host
- 图形界面程序和 X-Window 服务器按照 X11 协议通信
- 各式各样的应用程序都在操作系统 API (syscall) 和操作系统中的对象上构建
- 窗口管理器
- 管理设备和屏幕 (read/write/mmap)
- 进程间通信 (send, recv)
- 任务管理器
- 访问操作系统提供的进程对象 (readdir/read)
- 参考 gdb 里的 info proc *
- 杀毒软件
- 文件静态扫描 (read)
- 主动防御 (ptrace)
- 其他更复杂的安全机制……
总结:Q: 到底什么是 “程序”?
- 程序 = 状态机
- 源代码 S: 状态迁移 = 执行语句
- 二进制代码 C: 状态迁移 = 执行指令
- 编译器 C =compile(S)
- 应用视角的操作系统
- 就是一条 syscall 指令
- 计算机系统不存在玄学;一切都建立在确定的机制上
- 理解操作系统的重要工具:gcc, binutils, gdb, strace
一个小例子
在编写的c++程序中,如果是窗口,有时会一闪就消失了,如果不想让其消失,在程序结尾处添加:
system("pause");
注意:不要再return 的语句之后加,那样就执行不到了。
分析:
system()
是调用系统命令
pause
暂停命令;
这样在运行到此处时,会显示“Press any key to continue . . .”也就是“按任意键继续...”
python list的底层性质
list.append()时间复杂度是O(1),但是extend复杂度是O(k):
出处:
Time Complexity: Append has constant t!
ime complexity i.e.,O(1). Extend has a time complexity of O(k). Where k is the length of the list which need to be added.
Python dict的底层性质
python常见操作的时间复杂度官方文档
x in dict.values()的时间复杂度是O(n)很简单,现在没有可以查的哈希key了,所以一定是O(n)
系统调用和shell
作者:大宽宽
链接:https://www.zhihu.com/question/35382632/answer/803711006
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
操作系统对外提供的接口是“系统调用”,也就是一堆编程用的接口。这些接口一般以C函数的形式暴露给使用者。通过这些接口,开发者可以命令操作系统“启动一个进程”,“查找某个目录下的所有文件”,“将某个- 文件的权限配置为744”等等。
- 实际上我们平时编程用的是对系统调用的包装,比如libc里的那些库函数。但无论如何,你总是得写代码才能使用系统调用。
为什么说Windows文件管理器也是shell
Windows explorer(文件管理器)实际上是一个图形界面的shell,接受的输入是点击,但是做的事情和linux的ls
命令一模一样,内部也是用系统调用实现的。
Shell也可以做成图形界面的,比如你点击一个窗口里左边的一个目录图标,右边就展示这个目录下的所有文件。他做的事情和上面ls一模一样,只不过接受的是点击,而不是输入字符串;输出的是一幅画,而不是格式化的文本而已。内部还是转换成调用系统调用来完成工作。
shell的本质
如果你开心,你甚至都把Shell可以做成声控的,比如你说一段语音,你的“Shell”识别后翻译为调用程序的指令,执行成功后就可以把结果翻译为语音再返回给你。你一定熟悉“小爱同学”,“Hi Siri”这种东西吧。Shell的思想很普遍,并不一定限制在操作系统上。比如你自己写一个程序,有大量复杂的参数和配置。为了使用方便可以写个命令行工具将一个命令翻译成对这段程序的调用。你写的命令行工具就是你自己程序的“Shell”。比如写Java的同学肯定很熟悉mvn。一句mvn install可以产生出成百上千个下载、压缩、编译、清理、测试、上传等api的调用;使用数据库的同学也会用SQL来表达自己的查询,让数据库的“Shell”解释成对存储引擎各种api的调用。
其实大家都对 Shell 这个概念有误解,好像一定要是命令行的 Shell 才叫 Shell。其实 Shell,壳,顾名思义就是机器外面的一层壳,用于人机交互,只要是人与电脑之间交互的接口,就可以称为
Shell,所以我们熟悉的 GNOME、KDE 等图形界面也都是 Shell,只不过是 GUI Shell。所以像
Bash 等 Shell 当初发明的原因当然也就很容易理解了,就是为了人与机器之间交互的问题,只不过
当时的技术还不能做出 GUI,所以就做成了命令行的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?