您好,欢迎来到榕意旅游网。
搜索
您的当前位置:首页操作系统-09-程序员应如何理解系统调用<上>

操作系统-09-程序员应如何理解系统调用<上>

来源:榕意旅游网

操作系统主要有两项功能:

1.向用户程序提供一个友好的编程接口,即系统调用

2.管理计算机资源(包括CPU、内存、磁盘、网卡等外设,以及进程管理,线程管理,文件管理等)

通常操作系统如何管理计算资源对于程序员来说是不可见的,应用程序想要使用系统资源必须通过操作系统,从这个角度讲操作系统更像是server,我们的应用程序是client,client只需要向server发出request然后得到response,至于server如何处理请求并不要client关心。同样的道理,应用程序只需要进行系统调用,而操作系统通过系统调用来屏蔽了处理细节。

作为程序员应该意识到,我们的程序在运行时,CPU不仅仅在执行我们的程序,当涉及到文件、网络、进程控制、线程控制、I/O等时,单单依靠我们的代码是没有办法来完成这些操作的,这些只能依靠操作系统,对于程序员来说就是调用系统调用。当系统调用开始执行时,CPU从用户模式切换到内存模式并开始运行操作系统的代码,即操作系统开始运行来完成上述用户程序请求。

虽然系统调用在程序员眼里仅仅是一个普通的函数调用,但是深刻理解系统调用对于理解操作系统的运行方式来说是非常重要的。简而言之,要想成为编程高手,你需要理解系统调用。

API与系统调用
一般情况下,程序员不会直接使用系统调用进行编程,而是使用对系统调用进行了封装的API来编程,这个API在Unix/Linux下是libc来提供的,也就是我们熟悉的C标准库;在Windows下这个API叫做Win32 API,相信在Windows下编程的同学对此不会陌生。

实际上作为程序员我们使用的基本上是使用C标准库或Win32 API进行编程,而这些API又封装了系统调用(所谓封装,也就是这些API最终会调用到系统调用),这也是为什么很多程序员根本没有意识到自己的程序在进行系统调用的原因。

你可能会想,为什么我们要使用C标准库或者Win32 API(以下统称API)而不直接使用系统调用来进行编程呢?

一方面是因为这些系统调用的接口不是很易用;另一方面比较重要的是,API的使用对程序员屏蔽了系统调用,也就是说我们的程序并不直接依赖系统调用,这一点是极为重要,因此这些API可以选择使用某个系统调用或者使用多个系统调用或者不使用系统调用。这就给API的设计带了了极大的灵活性。同时只要API的接口是不变的,那么如果两个不同的操作系统提供了同样的API,我们的程序就可以不加修改的在另一种操作系统上运行了,这是非常棒的一种设计。比如,如果Windows上实现了C标准库,那么我们在Unix/Linux上基于C标准库的程序就可以不加修改的在Windows上运行。

通常来说一个系统调用会对应一个API,但是反过来不一定正确,也就是说一个API中不一定会调用系统调用,比如我们使用的memcpy,这里面就没有调用任何系统调用。而且多个API可能会调用同一个系统调用,比如在Linux下我们进行内存分配释放常用的几个函数malloc(),calloc(),free(),这些函数实际上都是调用的一个叫做brk()的系统调用来完成的。

由于Windows是闭源的商业操作系统,因此Win32 API有很好的兼容性,很古老的Windows程序放到现在的Win10上依然可以运行的好好的。

但是对于Unix来说情况就不一样了,由于历史原因,现存有很多基于Unix的操作系统,但是又包含了自己的实现。因此为了方便在这些系统是进行软件开发,提出了POSIX标准,POSIX标准主要是用来统一各个Unix平台上的API而不是这些平台上的系统调用,这些Unix系统可以有不同的系统调用,但是对外的API要提供一个大家都认可的统一的格式,POSIX就是来规定这些格式的。有了POSIX标准,基于该标准的编写的程序就可以运行在不同的Unix平台上了。顺便说一下,虽然我们经常把Unix和Linux放在一起阐述,但是Linux是和Unix完全不同的一个操作系统,仅仅是Linux在设计哲学上借鉴了Unix,Linux上已经提供了符合POSIX规范的API。

一般来说这些API(Unix/Linux下的C标准库或者Windows下的Win32 API)已经足够大部分程序员使用了,因此作为程序员很少会遇到需要直接使用系统调用的情况。

接下来我们就来看看一个系统调用的完整过程。

系统调用的过程
在这里我们依然以我们熟悉的HelloWorld程序为例来说明,同时我们假定运行的操作系统是Linux(其它系统下这个过程依然适用)。在Linux下printf其实是C标准库中的函数,当调用printf时最终会调用到一个叫做write()的系统调用。

include <stdio.h>

int main(){
   printf("Hello World."); //调用系统调用write
   return 0;
}

所有的系统调用都是按照这种过程完成的。

原来这些系统调用都有一个唯一的编号,这样当CPU执行trap命令切换的内核模式后,操作系统就能通过系统调用编号知道需要执行什么样的函数啦。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- nryq.cn 版权所有 赣ICP备2024042798号-6

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务