北京理工大学操作系统课程设计的代码
不同操作系统的实验放在了Linux
和Windows
的EX_*
文件夹中
- 实验一: 编译内核
- 实验二(EX_2):启动进程
start process
- 实验三(EX_3):生产者消费者
- 实验四(EX_4):实现进程资源查看器
- 实验五(EX_5):实现递归移动整个文件夹的
myCp
Visual Studio2019;控制台子系统;MSVC编译
实验一:WZC专属的实验一,通过Visual Studio编译了NT内核- 实验二:Visual Studio
- 实验三:vscode + mingw-w64;编写完毕后在Visual Studio中进行语义检查。
- 实验四:Visual Studio
- 实验五:Visual Studio
WSL2中使用g++编译,gdb调试
调用曾经从未用过的系统API是一件非常有挑战性的事情。许多函数接口需要输入的参数难以一眼就分辨出来(尤其是Win32 API)。
推荐直接在MSDN上查看Win32 Api的一手资料。https://docs.microsoft.com/en-us/windows/win32/api/
建议的路线是:查看老师的PPT课件后,知道本次设计需要利用到哪些函数,接着再到MSDN处查对应函数的接口。
教学PPT找到函数名->在MSDN的搜索框里粘贴函数名->查看对应的编程手册->仔细查看函数接口的重要部分(比如需要设置数值的、需要传入字符串的),稍微忽略一些不重要部分,并提醒自己用的时候稍后再看(比如各种flag)
虽然Win32的API文档是全英文的可能会有点劝退,但英语语法很基本,用心看是能看懂的,更何况还有谷歌翻译的力量。另一方面其实文档真正需要看的地方并不多,一些函数传入的flag快速扫一遍即可,因为它们大多通过变量名就能看出用途。
推荐直接看一手文档的根本原因是:微软的文档实在是写的太好了。非常详细,并且关键部分会给你新的链接让你拓展阅读,文档中展示示例代码更是常规操作。假如你能看完,那么课上验收时,无论老师怎么提问都能轻而易举的答出来,并且还会获得许多超越课本的知识涉猎与感悟。文档的很多地方是可以和操作系统理论课的Windows部分的知识串起来的。粗读ppt+精读msdn,大体和细节组合连击,是一种非常美妙的体验。
对于Win32 API,我觉得许多人在最初时都会和我有一样的感觉:Windows的Api传入参数的命名实在是太丑了!!
以CreateProcess
为例,lpApplicationName
、bInheritHandles
、dwCreationFlags
这些传入参数前的lp
、b
、dw
等着实令人很在意[问题1]。
除此之外,甚至连传入的变量的类型都令人看不懂,比如LPCSTR
、LPSTR
、DWORD
等[问题2]。
事实上,对于[问题1],这个是Windows变量的命名法则:匈牙利命名法。在每个变量名的最前面引入这个变量类型的缩写,可以令程序员瞬间理解应该传入什么类型的变量,大大的减少出错的概率(因为在早期,代码编辑工具的语法检测还没那么强大)。
比如,lp
代表long pointer
,告诉你要传入一个指针变量(因为曾经Windows是16位的,所以大于16位的指针就相对成为了“long”指针)、b
则指Boolean
、dw
则是DWord
(什么是DWord等会再说)。再举一个常见的例子:常见的szXXX
前面的sz
指的是“以0结尾的字符串”(大概可以理解为"string returns zero"?)。
总之,很大程度上通过变量名就可以知道这个函数的参数应该传一个指针(lp,字符串一般也是lp)、一个数(dw)、或者是一个布尔值。特别说明一下,常见的h
,指的是句柄(HANDLE
)。
匈牙利命名法的详情可见 https://docs.microsoft.com/zh-cn/windows/win32/learnwin32/windows-coding-conventions?redirectedfrom=MSDN
对于[问题2],你则可以理解为Windows为了实现无敌的向前向后的史诗级别的兼容性,自己定义了很多变量,并将其映射到原始C语言的类型。比如,我们大一就知道,很多变量类型的大小会随着编译器(以及机器字长)的不同而变得不同(比如int),这样必然会对程序的移植性产生困难:比如原本一个好好的变量,可能换一个编译器运行就运算溢出了。由于有这个情况的存在,微软干脆就自己typedef了一堆变量,使得即使后续系统架构迁移(比如32位迁移到64位、x86迁移到arm),也最多只需要改一下typdef映射的对象,而无需在源代码处一个个修改(比如把int
手动替换为long
等)就可以方便程序移植并且不出错。
事实上,但凡你在使用Win32 Api的时候感觉到了任何一点的不优雅性,都可以理解这都是Windows拓展性和兼容性上所做出的努力。Windows的设计是最优雅的!
一些常见的变量类型如下:
Data type | Size | Signed? |
---|---|---|
BYTE | 8 bits | Unsigned |
DWORD | 32 bits | Unsigned |
INT32 | 32 bits | Signed |
INT64 | 64 bits | Signed |
LONG | 32 bits | Signed |
LONGLONG | 64 bits | Signed |
UINT32 | 32 bits | Unsigned |
UINT64 | 64 bits | Unsigned |
ULONG | 32 bits | Unsigned |
ULONGLONG | 64 bits | Unsigned |
WORD | 16 bits | Unsigned |
比如DWORD是无符号整形,所以Windows内经常把它当各种Flag来用。
对于各种字符串的话,上面给出的链接也有说明,它们通常都是各种缩写。比如LPWSTR
是“Long Pointer of Wide Char(宽字符,正好可以兼容Windows常用的UTF-16编码)”,而LPCWSTR
表示“Long Pointer of Const Wide Char(比上面一个多了个const。Windows有个潜规则,凡是不带const的变量,你都不要传个const的"字符串"
进去,Windows通常会对非const的地址的内容进行修改(虽然改完后又会复原))”
关于句柄的使用,用户并不用操心操心了也没用,我们几乎无法对句柄进行任何操作。只用知道句柄的本质是地址,负责链接各种内核需要的对象的数据,我们只负责把句柄传给操作系统,让操作系统决定这个句柄对应的对象该怎么处理,就可以玩的很爽了。(Windows真的优雅!)
对了,Windows比Linux优雅的另一个显著的地方在于:一个#include <windows.h>
可以囊括万物。(除了一些需要用到特定dll的函数,比如实验4)
再多提一句,无论是使用VS code,还是Dev C++之类的开源IDE,它们用的编译器都是mingw,其中的win32的API是mingw自行实现的,在许多表现上和微软官方的Visual Studio所使用MSVC编译器有所不同(比如缺少了很多64位的库,LPWSTR是char而不是wchar_t,并且L""转化宽字符不能用等)。虽然课程并没有要求,但是如果要使用正统的Win32 Api最好在VS中编写、编译、调试Windows版本的程序!(但是VS实在是太难用了!)
Linux的编程就很简单了。不仅文档多,接口也很简单。
POSIX接口虽然在一致性上比较差(比如IPC类型的system V风格对比其他API),但是调用起来都很简单,也很符合直觉。
不过我还是建议阅读一手文档。 https://www.kernel.org/doc/man-pages/
相当于Linux世界的MSDN,我们需要的函数都能在其中第二章的 https://man7.org/linux/man-pages/dir_section_2.html 也就是System Call
中找到。直接Ctrl
+F
查找到你要的函数名点进去即可。
这个网页的本质等同于Linux命令的man
,你甚至可以直接在Linux的命令行下输入自己要找的命令,比如man fork
,就能轻易的找到这个函数所在的头文件、用法、返回值等信息。不过使用网页直接看的话,有个好处是超链接的跳转会很方便。