操作系统是如何看待进程的
在第一章回顾了一些重要主题:CPU、内存、程序、进程后,第二章将正式开始操作系统系列主题,本章主要来讨论操作系统是如何来对进程进行控制的,以下为本篇目录。
不简单的HelloWorld
系统调用(System Call)
为什么需要系统调用?
操作系统是如何看待进程的
总结
在经过第一章的铺垫之后,我们将正式进入操作系统课程,首先我们来谈一下进程。
进程是操作系统中最重要的概念,没有之一,操作系统中大部分设计都是围绕“进程”这个概念实现的。因此能否彻底理解进程将直接影响程序员对于操作系统的理解。
在这一章中我们重点关注进程是如何与操作系统进行交互的。
不简单的HelloWorld
可能许多程序员都没有意识到我们的代码中有许多操作必须依赖操作才能完成,比如在网络通信程序中,从socket进行数据的读取与写入实际上是操作系统替我们完成的。由于操作系统对复杂的操作过程进行了封装,因此在程序员眼里这仅仅就是一个函数调用没什么大不了的。不过这也从设计的角度说明了操作系统的作用,那就是封装复杂的硬件操作过程,对外界提供一个友好的接口供程序员使用。比如C语言中最简单的helloworld程序:
include <stdio.h>
int main(){
printf("Hello World."); //这里需要操作系统的帮助
return 0;
}
即便是如此简单的一个程序也是需要操作系统来帮忙才能完成的。当执行printf这句时话时我们的程序把参数“Hello World.”这几个字符告诉操作系统,之后执行的就不再是我们的程序了,而是操作系统,操作系统拿到字符串“Hello World.”后把这几个字符输出到屏幕上,操作系统完成这一任务后把控制权又交给我们的程序,这样HelloWorld又得以继续运行,如下图所示(还记得吗,在进程眼里,自己独占内存,一部分留给了操作系统使用,剩下的自己使用)。
实际上看似简单的HelloWorld程序,实际运行起来非常不简单,这是一个比较复杂的过程,我们们能在屏幕上看到“HelloWorld”这几个字符需要用户程序,操作系统,硬件三方通力合作才能实现。
在这里,作为程序员你需要意识到,当你的程序在运行时,CPU不是一直在执行你的程序的,而是要时不时的跳转到操作系统,通过运行操作系统来完成程序中某些函数调用。至于哪些函数调用会需要操作系统的帮助,学完这章课程你就明白啦。
系统调用(System Call)
在程序员看来,用户程序向操作系统发起请求仅仅是一个普通的函数调用,比如这里的printf(实际上,无论你使用什么语言写helloworld程序,C/C++、Java、Python等等,最终都需要操作系统辅助完成)。但这仅仅从表面上看是这样的,实际上这个过程不同于普通的函数调用,这个过程有一个专有名词,那就是系统调用(System Call)(实际上HelloWorld程序中的printf真正调用的是一个叫做write的系统调用)。系统调用是用户程序和操作系统通信的媒介。用户程序向操作系统发起请求实际上是通过系统调用来完成的,因此我们可以说:
系统调用是用户程序与操作系统的信使
也就是说,我们的程序是通过系统调用来向操作系统发起请求的,这也是本章要重点讲解的内容。
为什么需要系统调用?
到目前为止,也许你对系统调用没什么概念,不过没有关系,现在你只需要记住,当我们的程序在访问硬件,比如读写文件(磁盘)、收发网络数据(网卡)、接收键盘按键、响应鼠标点击事件、又或者创建线程、创建进程、查看系统时间等操作时都需要依赖操作系统完成这些任务。操作系统对这些复杂的过程进行了很好的封装,在程序员看来这仅仅就是一个简单的函数调用。不过从现在起,你要开始意识到,当这些看起来很简单的函数调用实际上是操作系统来实现的,看似简单的表面实际上隐藏了非常复杂的操作。
什么叫做“这个过程是操作系统来实现的呢”?
我们之前也说过,操作系统就是一个大的C程序,本质上和我们的程序没有任何区别,当CPU开始执行我们的代码时就表现为进程在运行,当CPU执行操作系统的代码时就表现为操作系统在运行。那么所谓“操作系统来实现”指的是当用户程序进行文件读写调用比如read函数时,CPU开始时执行的是read函数,但在调用过程中CPU跳转到操作系统的代码,这时操作系统替我们去进行文件读取,当操作系统完成文件读取后,CPU跳转回来继续执行read函数的后半部分。因此在程序员看来read就是一个普通的函数调用,实际上read函数的执行必须依赖操作系统才能完成。
到目前为止,你需要记住两点:1,当我们调用涉及到比如硬件的函数时,实际上是操作系统替我们完成的,这个过程叫系统调用2,系统调用和普通的函数调用是不同的
你可能会想,为什么我要这么麻烦的使用系统调用而不直接的函数调用呢?答案就存在于操作系统是如何看待用户程序的。当理解了这一点后你就能很自然的理解为什么操作系统中会提出系统调用这样的设计。
操作系统是如何看待进程的
在操作系统眼里,用户程序是一个充满了bug随时会崩溃的定时炸弹(必须承认,我们写的代码里藏有很多bug :) ),或者干脆就是某些天才程序员用来恶意控制整个计算机的破坏者。
操作系统面对的就是这样一个恶劣的环境。因此作为操作系统,应该把用户程序当做囚犯一样关在牢笼里面。
现在你应该明白了吧,因为程序员无心的bug或者恶意破坏,在操作系统看来,用户程序是极其不靠谱的甚至是可疑的,操作系统看用户程序就像警察看待关在大牢里的犯人一样。犯人在大牢中绞尽脑汁想尽一切办法试图逃出牢笼上演一把肖申克的救赎,道高一尺魔高一丈,操作系统就要严加防范,对犯人的一举一动进行严格的审查。操作系统需要竭尽全力把用户程序带来的破坏降低到最低程度以及最小范围。从设计角度上看操作系统就按照这一原则来实现的,在后面的课程中你会发现,操作系统成功的把用户进程关在了牢笼里面,从而实现了控制用户进程的目的,使得用户进程不得任意妄为。
总结起来就是,操作系统不信任应用程序,操作系统只信任自己。
总结
从现在开始,作为程序员你需要意识到,你的某些函数调用实际上是需要操作系统辅助来完成的。能意识到这一点是成为编程高手的开始。
同时操作系统看待用户进程就像看待犯人一样,因此操作系统对用户程序进行了严格的限制,在接下来的文章中,我们将会看到操作系统是如何通过系统调用做到这一点的。
操作系统系列
基础篇 | 1,什么是程序 |
3,程序员应如何理解内存:上篇 | |
4,程序员应如何理解内存:中篇 | |
5,程序员应如何理解内存:下篇 | |
6,程序员应如何理解CPU:上篇 | |
7,程序员应如何理解CPU:下篇 |
PS:微信公众号从去年开始限制了留言功能,如果你有任何问题欢迎直接在公众号留言。