惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
PCI Perspectives
PCI Perspectives
Webroot Blog
Webroot Blog
Help Net Security
Help Net Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News: Ask HN
Hacker News: Ask HN
Security Latest
Security Latest
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
S
Securelist
V2EX - 技术
V2EX - 技术
Schneier on Security
Schneier on Security
P
Proofpoint News Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
Cloudbric
Cloudbric
GbyAI
GbyAI
A
About on SuperTechFans
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Vercel News
Vercel News
P
Proofpoint News Feed
SecWiki News
SecWiki News
T
Tailwind CSS Blog
腾讯CDC
C
Cybersecurity and Infrastructure Security Agency CISA
The Hacker News
The Hacker News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
M
MIT News - Artificial intelligence
爱范儿
爱范儿
Microsoft Azure Blog
Microsoft Azure Blog
T
Troy Hunt's Blog
G
Google Developers Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cisco Talos Blog
Cisco Talos Blog
D
DataBreaches.Net
V
Vulnerabilities – Threatpost
博客园 - 叶小钗
C
Check Point Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Know Your Adversary
Know Your Adversary
T
Tor Project blog
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
Y
Y Combinator Blog
H
Help Net Security
Latest news
Latest news

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 Golang CAS 操作是怎么实现的 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例) 读《为什么学生不喜欢上学》 OpenAI Prompt Engineering 摘录和总结 读《打造真正的新产品》 VueJS 总结 Linux 自动挂载 alist 提供的webdav FreeBSD 使用 vm-bhyve 安装Debian虚拟机 FreeBSD 和 Linux 网卡聚合实现提速 GPT 帮我搞定了时区转换问题 长任务系统如何处理? macOS/Linux 编译 InputLeap 使用开源软KVM - synergy-core 解决 macOS 终端hostname一直变化问题 KVM 共享 Intel 集成显卡 PromQL 备忘 读《格鲁夫给经理人的第一课》 读《打开心智》 为什么要把复杂的联表操作拆成多个单表查询? 红包系统的设计 MySQL Index Condition Pushdown Optimization Go mod 简明教程 OpenWRT 使用 Android/iOS USB 网络 搭建旁路由 Golang gRPC 错误处理 编写可维护的单元测试代码 OAuth 2 详解(六):Authorization Code Flow with PKCE OAuth 2 详解(五):Device Authorization Flow OAuth 2 详解(三):Resource Owner Password Credentials Grant OAuth 2 详解(四):Client Credentials Flow OAuth 2 详解(二):Implict Grant Flow OAuth 2 详解(一):简介及 Authorization Code 模式 ElasticSearch 学习笔记 三种git流程以及发版模型 错误处理实践 权限模型(RBAC/ABAC) OIDC(OpenID Connect) 简介 任务队列简介 PostgreSQL 操作笔记 使用Drone CI构建CI/CD系统 Golang migrate 做数据库变更管理 使用PostgreSQL做搜索引擎 Nginx 源码阅读(三): 连接池、内存池 Nginx 源码阅读(二): 请求处理 Nginx 源码阅读(一): 启动流程 Go 泛型简明教程 KVM 显卡穿透给 Windows 使用 HTTP Router 处理 Telegram Bot 按钮回调 使用反射(reflect)对结构体赋值 GIN 是如何绑定参数的 你好 2022(2021 年终总结) 用Go导入大型CSV到PostgreSQL 使用 OpenWRT 搭建软路由 使用软KVM切换器 barrier 共享键鼠 SQL 防注入及原理 使用 gomock 测试 Go 代码 gevent不是黑魔法(二): gevent 实现 gevent不是黑魔法(一): greenlet 实现 用 entgo 替代 gorm 应用内使用crontab不是那么方便 单测时要不要 mock 数据库? Sentry 自建指南 用selenium完成自动化任务 用闲置的安卓手机做垃圾电话短信过滤 推荐三个时间管理工具 一次事故反思 当JS遇到uint64:JS整数溢出问题 SQLite3 存储以及ACID原理 Redis源码阅读:pub/sub实现 Redis源码阅读:zset实现 Redis源码阅读:bitmap 位图的运算 Redis源码阅读:set是怎么做交并集运算的?
操作系统也是CRUD
Jiajun Huang · 2020-06-15 · via Jiajun的技术笔记

Andrew S·Tanenbaum 现代操作系统

起初学习UNIX环境下的编程觉得非常难,毕竟这是一个陌生的东西,但是工作后,照面打得多了,自然就熟悉了些,在此总结一些经验。

其实要是写了几年代码的人,回头总结一下用代码一般都做什么事,会得到四个字母:CRUD,也就是增删查改。

  • 后端程序员:对数据库的增删查改
  • 系统程序员:对OS数据结构的增删查改
  • 嵌入式程序员:对硬件数据的增删查改
  • 前端程序员:对JSON和用户输入的增删查改

那既然大家归根结底,都是增删查改,学习UNIX是否可以从另外一个角度来看待呢?

开始之前

远古时期,所有程序员都是直接操纵硬件的,但是这有很大的问题,你也想用100M内存,我也想用100M,但是一共只有100M,怎么办? 你也想播放音乐,我也想,又怎么办?你也想写磁盘,我也想写磁盘,怎么办?

除了上述冲突,更麻烦的是,所有人都要知道所有硬件怎么操作,才能写出一个程序,这太难了。于是有了操作系统,它帮我们屏蔽了所有的硬件设备的 区别,提供了统一的接口,并且抽象了一些东西,比如文件系统,通过文件系统,我们不再需要操心这个文件是写到磁盘具体的哪个位置, 而只需要告诉操作系统:我要写文件,写什么,写多长即可。

这一切都是通过操作系统提供的系统调用(system call)来完成的。

我们还是要了解一下UNIX操作系统的结构:

UNIX 架构

可以看到,最内部的,就是内核,它帮我们处理各种各样的硬件设备,然后抽象成一个简单的概念。 比如我们不用担心是HDD还是SSD,我们只管用文件系统,我们只需要调用操作文件的系统调用,操作系统会帮我们找到对应的磁盘位置并且把数据写进去, 即使失败了,也会返回一个错误告诉我们为什么。

稍往外层,就是系统调用,系统调用的外层有shell和library routines,另外applications也可以和它直接接触,这是什么意思呢?

shell可以简单理解为命令行,但是用熟了之后,应当知道,bash,zsh这类才是shell,他封装了很多系统调用,然后以命令或者图形界面的方式提供 给用户,比如Windows上,很多按钮点一下,背后其实就调用了一些系统调用。 而library routines,比如glibc,帮我们封装了系统调用,加了一些代码,让我们调用更加轻松,因为我们的代码不可能是通过执行各种命令行 来完成,那样抗不住并发,所以我们得通过调用函数的方式, 我们可以直接调用系统调用,这就是图中applications直接和system call接触的那部分,也可以调用library routines,然后后者帮我们调用system call。

模块分类

相信很多人毕业设计做的就是XXX管理系统,内核其实也是一个XXX管理系统,只不过它是多个管理系统的集合:

  • 文件管理系统
  • 用户管理系统
  • 权限管理系统
  • 进程管理系统
  • 内存管理系统
  • 硬件设备管理系统

其实内核自己就可以做这些事情,但是因为每个人每个公司的需求都不一样,所以内核暴露了系统调用来让程序员可以借助它的能力, 做定制化操作。

内核为了安全,会通过CPU的能力,将进程分为两个类别:

  • 在内核里跑的
  • 在用户空间跑的

显然前者拥有更大的权利,干啥都可以,后者的权力自然是受限的。

文件管理系统

我们知道,XXX管理系统的主要内容就是CRUD。我们来看看,我们自然是要:

  • 创建文件(增)
  • 删除文件(删)
  • 查找文件内容(查)
  • 更新文件内容(写入)

由于除了文件,还有文件夹,所以它也有CRUD。由于UNIX中把一切都抽象成文件,因此,对他们也有CRUD,不过,幸好由于UNIX把一切都抽象成文件,因此 对一切文件,也就这么几个系统调用(其实是C库封装过后的库函数,但是其底层是系统调用,后同):

  • creat 创建文件
  • remove 删除文件
  • close 关闭文件
  • read 读取文件
  • write 写文件
  • lseek 设置写入的位置

当然,UNIX发展这么久了,其历史分支多的不得了,自然就会有分裂和不统一的情况,因此除了上述系统调用,你还会看到一些标新立异、特殊使用的,比如针对网络的 recvsend,没有关系,理解一下,毕竟几十年 的系统了。

说起文件,特别重要的一个概念就是文件描述符(file descriptor,简称fd)。这是啥呢?你看我们平时操作数据库的每一行,是不是还有个Primary ID?对的,这玩意儿就跟它类似,用于定位一个文件,这样大家都知道我们说的是哪个文件。

为了高性能,我们的文件管理系统自然就要引入缓存,引入缓存,那就要引入把缓存写到磁盘的操作,因此就有了:

  • sync
  • fsync
  • fdatasync

另外文件还有一些属性什么的,因此就有了:

  • stat
  • fstat
  • lstat

权限管理系统和用户管理系统

UNIX是一个多用户的系统,既然是多用户,那就要做好权限控制,我不能随意看别人的东西,别人也不能随意看我的,要不然大家岂不是都在 裸奔了吗?

首先要做的就是在文件系统里引入权限控制,也就同时引入了用户管理系统。

UNIX中,每个用户都有自己的id,每个用户都可以加入多个组,每个组也可以有多个用户,因此UNIX把权限分为三类:

  • 对本人的控制
  • 对本组的控制
  • 对其他人(也就是其他组)的控制

然后控制,又细分为三个:

  • 可读
  • 可写
  • 可执行(对于文件来说是可执行,对于文件夹来说,就是是否可以列出其下文件)

由于UNIX设计的时候,内存都是很宝贵的(今天没那么贵了但是也还挺宝贵的),所以用位来保存权限,可读、可写、可执行分别是二进制的 00000100,00000010和00000001,因此对应10进制分别是4,2,1。

注意,4+2+1 = 7。

所以如果你给一个文件,权限为777,那么就是对本人可读可写可执行,对组内成员可读可写可执行,对其他所有人可读可写可执行。

如果是467,那就是对本人可读,对组内成员可读可写,对其他人可读可写可执行。

以此类推。

当然,我们得有系统调用来对权限进行CRUD:

  • access
  • chmod 改、增、删

刚才说了,有用户和组这两个概念。UNIX下,用户信息存储在 /etc/passwd 上,组的信息存储在 /etc/group 上。

用户有密码,以前密码也是存在 /etc/passwd 上,不过不太安全,后来就把密码以更严格的文件权限存储在 /etc/shadow 里。

$ ll -h /etc/passwd /etc/shadow /etc/group
-rw-r--r-- 1 root root    910 Jun 12 15:29 /etc/group
-rw-r--r-- 1 root root   2.2K Jun 12 15:29 /etc/passwd
-rw-r----- 1 root shadow 1.3K Jun 12 15:29 /etc/shadow

进程管理系统和内存管理系统

我们的代码,最终编译为程序,它就是一堆0和1,躺在磁盘里。但是当它被操作系统加载,运行之后,在内存里,就叫进程。当然,进程也得有一个唯一ID,我们管他叫 pid(process id)

进程和内存分不开,进程要申请内存、销毁内存等,因此就有了这些CRUD:

  • malloc 申请内存
  • free 释放内存

至于改和查嘛,这是在代码逻辑里就做好了的,无需操心。

而对进程本身的一些增删查改,则是对它的运行环境什么的操作,比如:

  • getenv 获取环境变量
  • getpid 获取当前进程id
  • getppid 获取父进程id
  • getrlimitsetrlimit 获取和设置进程的一些资源限制
  • fork 创建新的进程
  • exec 替换当前进程所要执行的代码
  • wait 等待子进程

进程管理本身就还包含很多东西,比如进程所属的用户和组,进程的资源限制,进程之间的通信,进程的优先级等等,每一个子系统都有它的CRUD。

后来又引入了线程这个概念,线程是CPU调度的最小单位,同一个进程内的多个线程, 共享所在进程里的一些资源,对于这些东西,又有很多CRUD函数。同时还引入了并发和并行这两个概念,因此也就有一些锁、竞争等概念,所以又有一批 对应的CRUD函数。

硬件管理系统

程序要做的很重要的一件事情,其实是和其他进程通信,和其他硬件设备打交道。

同步I/O无法应对并发,因此就有了I/O多路复用,也就有了:

  • select
  • epoll
  • kqueue

还有POSIX规定的aio系列函数,这又是一堆CRUD。

总结

一个操作系统,要做的事情很多,直到今天,Linux内核也还在非常活跃的开发维护中,操作系统要做的事情远远不止上面所列的这么 几个管理系统,但是我们可以把操作系统要做的事情,分门别类来看,就会发现,原来它也是CRUD。当然,这个CRUD的技术含量,可就 比较高了。