Linux/Windows 应用程序开发
一、基础知识
虽然写的都是代码,但是代码运行在哪个级别什么位置,还是需要做好定位,这样才心中有数。Linux和Windows都是宏内核,内核自己管理硬件软件资源,对外提供“系统调用”给程序员编程。
操作系统按照一定的协议把我们写的程序加载进内存,为其分配内存等资源,让其诞生;然后程序自己管自己,自己运行;最后C/C++/Java的mian函数都能会返回,返回后这个程序就结束了(还可能有其他方式,进程被迫结束),所占用的资源(比如内存堆和栈、任务时间片、外设等)返还给了操作系统。
1.1 Linux 环境
1 Linux系统调用
所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口获得操作系统内核提供的服务。
Linux/Unix 的设计策略将是内核空间与用户空间隔绝开,使得内核空间对象或资源“不能”被用户空间直接访问。用户能在用户空间操作用户自己的对象,如果需要使用内核资源,可以直接调用系统调用(system call)或者通过“库(比如C库),也可以叫APIs”间接地调系统调用。系统调用像一个屏障,它将内核空间与用户空间隔开。
因为核心态和用户态用的是两个不同的栈,切换时需要切换特权级别,并且不同的硬件平台(arm/ibm/intel)有不同的硬件栈指针(StackPointer)实现机制,所以系统调用的实现需要用到汇编语言,实现的功能包括切换SP、参数传递、上下文切换等。如果提到汇编怎么通过cpu寄存器传递参数,这些细节都有其标准,不必深究。如果提及StackPointer,stm32就有两个独立的StackPointer(SP),MSP与PSP。需要指出Linux用户空间、内核空间与中断是有区别的:用户空间和内核空间其实看上去就是运行在不同处理器特权级别上的普通程序(相对中断而言),只不过内核空间运行着整个软件系统的核心LinuxKernel,里面装的是正在运行的Linux Objects;中断程序一般针对硬件,短小而严谨,不能睡眠。
Linux系统调用非常精简(只有250个左右),它继承了UNIX系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、存储管理、网络管理、套接字控制、用户管理等几类。
2 系统库/通用库
这里系统库指Linux/Unix之上的通用库,比如C/C++库。
编程人员可以通过调用C/C++库继而调用Linux/Unix的系统调用。当然,这些库不仅仅涉及到操作系统(也就是系统调用),还涉及到与系统资源无关的比如数值计算等库函数,而这种类型的库函数可以在用户空间执行。
3 系统命令
Shell 实际上也是一个可执行程序,与其他普通程序并无二致,它是通过命令行方式运行其它程序的程序。
4 系统内核、系统调用、通用库、shell、应用程序之间的关系
见下图。
1.2 Windows 环境
Windows和Linux类似都有一套系统内核对象(或者叫组件、模块,比如进程、内存、文件系统等)。Windows提供了 Windows APIs -> Microsoft Fundation(MFC) -> [todo]
MFC 是利用面向对象思想的C++语言对 WindowsAPIs 进行的一次封装。MFC提高了编程的效率,但是降低了灵活度。MFC还有一个莫名其妙的别名,叫 Visual C++,是指“用 Visual Studio IDE 、C++ 和 MFC 技术”开发 windows 程序的意思,侧重在“MFC”。这句话出自《深入浅出MFC》第二版侯俊杰。终于知道多年的VC++是什么意思了。这个世界太乱了,乱起名,让人厌恶、不负责任。
《深入浅出MFC》(第二版)侯俊杰
Winconsole程序是没有GUI的WinAPIs程序。
1.3 C/C++/Java 标准库函数
我认为不管是基于操作系统还是基于裸机,你只要编写出符合标准库函数规范的函数即可, 即标准对于如何实现无要求,因此不同的实现方式,可能性能上有所差异。
以malloc为例,malloc invokes either brk or mmap syscall to obtain memory.
https://www.linuxjournal.com/article/6390
https://sploitfun.wordpress.com/2015/02/11/syscalls-used-by-malloc/amp/
关于malloc in linux 的说明:
When a process needs memory, some room is created by moving the upper bound of the heap forward, using the brk() or sbrk() system calls. Because a system call is expensive in terms of CPU usage, a better strategy is to call brk() to grab a large chunk of memory and then split it as needed to get smaller chunks. This is exactly what malloc() does. It aggregates a lot of smaller malloc() requests into fewer large brk() calls. Doing so yields a significant performance improvement. The malloc() call itself is much less expensive than brk(), because it is a library call, not a system call. Symmetric behavior is adopted when memory is freed by the process. Memory blocks are not immediately returned to the system, which would require a new brk() call with a negative argument. Instead, the C library aggregates them until a sufficiently large, contiguous chunk can be freed at once.
For very large requests, malloc() uses the mmap() system call to find addressable memory space. This process helps reduce the negative effects of memory fragmentation when large blocks of memory are freed but locked by smaller, more recently allocated blocks lying between them and the end of the allocated space. In this case, in fact, had the block been allocated with brk(), it would have remained unusable by the system even if the process freed it.