当前位置: 首页 > 编程日记 > 正文

程序员的自我修养--链接、装载与库笔记:系统调用与API

系统调用(System Call)是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口,它决定了应用程序是如何与内核打交道的。无论程序是直接进行系统调用,还是通过运行库,最终还是会到达系统调用这个层面上。

1. 系统调用介绍

什么是系统调用:在现代的操作系统里,程序运行的时候,本身是没有权利访问多少系统资源的。由于系统有限的资源有可能被多个不同的应用程序同时访问,因此,如果不加以保护,那么各个应用程序难免产生冲突。所以现代操作系统都将可能产生冲突的系统资源给保护起来,阻止应用程序直接访问。这些系统资源包括文件、网络、IO、各种设备等。举个例子,无论在Windows下还是Linux下,程序员都没有机会擅自去访问硬盘的某扇区上面的数据,而必须通过文件系统;也不能擅自修改任意文件,所有的这些操作都必须经由操作系统所规定的方式来进行,比如我们使用fopen去打开一个没有权限的文件就会发生失败。此外,有一些行为,应用程序不借助操作系统是无法办到或不能有效地办到的。为了让应用程序有能力访问系统资源,也为了让程序借助操作系统做一些必须由操作系统支持的行为,每个操作系统都会提供一套接口,以供应用程序使用。这些接口往往通过中断来实现,比如Linux使用0x80号中断作为系统调用的入口,Windows采用0x2E号中断作为系统调用入口。

系统调用覆盖的功能很广,有程序运行所必需的支持,例如创建/退出进程和线程、进程内存管理,也有对系统资源的访问,例如文件、网络、进程间通信、硬件设备的访问,也可能有对图形界面的操作支持,例如Windows下的GUI机制。

系统调用既然作为一个接口,而且是非常重要的接口,它的定义将十分重要。因为所有的应用程序都依赖于系统调用,那么,首先系统调用必须有明确的定义,即每个调用的含义、参数、行为都需要有严格而清晰的定义,这样应用程序(运行库)才可以正确地使用它;其次它必须保持稳定和向后兼容,如果某次系统更新导致系统调用接口发生改变,新的系统调用接口与之前版本完全不同,这是无法想象的,因为所有之前能正常运行的程序都将无法使用。所以操作系统的系统调用往往从一开始定义后就基本不做改变,而仅仅是增加新的调用接口,以保持向后兼容。

Linux系统调用:在x86下,系统调用由0x80中断完成,各个通用寄存器用于传递参数,EAX寄存器用于表示系统调用的接口号,每个系统调用都对应于内核源代码中的一个函数,它们都是以”sys_”开头的,比如exit调用对应内核中的sys_exit函数。Linux内核版本提供了很多个系统调用,这些系统调用都可以在程序里面直接使用,它的C语言形式被定义在/usr/include/unistd.h中,比如我们完全可以绕过glibc的fopen、fread、fclose打开读取和关闭文件,而直接使用open()、read()和close()来实现文件的读取,使用write向屏幕输出字符串(标准输出的文件句柄为0),使用read系统调用来实现读取用户输入(标准输入的文件句柄为1)。不过由于绕过了glibc的文件读取机制,所以所有位于glibc中的缓冲、按行读取文本文件等这些机制都没有了,读取的就是文件的原始数据。我们也可以使用Linux的man命令查看每个系统调用的详细说明,如: man 2 read。

系统调用的弊端:系统调用完成了应用程序和内核交流的工作,事实上,包括Linux,大部分操作系统的系统调用都有两个特点:(1). 使用不便:操作系统提供的系统调用接口往往过于原始,程序员需要了解很多与操作系统相关的细节。如果没有进行很好的包装,使用起来不方便。(2). 各个操作系统之间系统调用不兼容:首先Windows系统和Linux系统之间的系统调用就基本上完全不同,虽然它们的内容很多都一样,但是定义和实现大不一样。即使是同系列的操作系统的系统调用都不一样,比如Linux和UNIX就不相同。

“解决计算机的问题可以通过增加层来实现”,于是运行库挺身而出,它作为系统调用与程序之间的一个抽象层可以保持着这样的特点:(1). 使用简便:因为运行库本身就是语言级别的,它一般都设计相对比较友好。(2). 形式统一:运行库有它的标准,叫做标准库,凡是所有遵循这个标准的运行库理论上都是相互兼容的,不会随着操作系统或编译器的变化而变化。

运行时库将不同的操作系统的系统调用包装为统一固定的接口,使得同样的代码,在不同的操作系统下都可以直接编译,并产生一致的效果。这就是源代码级上的可移植性。但是运行库也有运行库的缺陷,比如C语言的运行库为了保证多个平台之间能够相互通用,于是它只能取各个平台之间功能的交集。

2. 系统调用原理

特权级与中断:现代的CPU常常可以在多种截然不同的特权级别下执行指令,在现代操作系统中,通常也据此有两种特权级别,分别为用户模式(User Mode)和内核模式(Kernel Mode),也被称为用户态和内核态。由于有多种特权模式的存在,操作系统就可以让不同的代码运行在不同的模式上,以限制它们的权力,提供稳定性和安全性。系统调用是运行在内核态的,而应用程序基本都是运行在用户态的。用户态的程序如何运行内核态的代码呢?操作系统一般是通过中断(Interrupt)来从用户态切换到内核态。什么是中断呢?中断是一个硬件或软件发出的请求,要求CPU暂停当前的工作转手去处理更加重要的事情

中断一般具有两个属性,一个称为中断号(从0开始),一个称为中断处理程序(Interrupt Service Routine, ISR)。不同的中断具有不同的中断号而同时一个中断处理程序一一对应一个中断号。在内核中,有一个数组称为中断向量表(Interrupt Vector Table),这个数组的第n项包含了指向第n号中断的中断处理程序的指针。当中断到来时,CPU会暂停当前执行的代码,根据中断的中断号,在中断向量表中找到对应的中断处理程序,并调用它。中断处理程序执行完成之后,CPU会继续执行之前的代码。

通常意义上,中断有两种类型,一种称为硬件中断,这种中断来自于硬件的异常或其它事件的发生,如电源掉电、磁盘被按下等。另一种称为软件中断,软件中断通常是一条指令(i386下是int),带有一个参数记录中断号,使用这条指定用户可以手动触发某个中断并执行其中断处理程序。由于中断号是很有限的,操作系统不会舍得用一个中断号来对应一个系统调用,而更倾向于用一个或少数几个中断号来对应所有的系统调用。

基于int的Linux的经典系统调用实现:

(1). 触发中断:首先当程序在代码里调用一个系统调用时,是以一个函数的形式调用的。

(2). 切换堆栈:在实际执行中断向量表的第0x80号元素所对应的函数之前,CPU首先还要进行栈的切换。在Linux中,用户态和内核态使用的是不同的栈,两者各自负责各自的函数调用,互不干扰。但在应用程序调用0x80号中断时,程序的执行流程从用户态切换到内核态,这时程序的当前栈必须也相应地从用户栈切换到内核栈。从中断处理函数中返回时,程序的当前栈还要从内核栈切换回用户栈。

(3). 中断处理程序:在int指令合理地切换了栈之后,程序的流程就切换到了中断向量表中记录的0x80号中断处理程序。内核里的系统调用函数往往以sys_加上系统调用函数名来命名,例如sys_fork、sys_open等。

Linux的新型系统调用机制:使用ldd来获取一个可执行文件ls的共享库的依赖情况,如下表所示,可以看到linux-vdso.so.1没有与任何实际的文件相对应,这个共享库是Linux用于支持新型系统调用的”虚拟”共享库。linux-vdso.so.1并不存在实际的文件,它只是操作系统生成的一个虚拟动态共享库(Virtual Dynamic Shared Library, VDSO)。可以通过Linux的proc文件系统来查看一个可执行程序的内存映像。命令cat  /proc/self/maps可以查看cat命令自己的内存布局。我们可以看见地址0x7ffdc0d43000到0x7ffdc0d45000的地方被映射了vdso,也就是linux-vdso.so.1。

3. Windows API

API的全称为Application Programming Interface,即应用程序编程接口。Windows API是指Windows操作系统提供给应用程序开发者的最底层的、最直接与Windows打交道的接口。在Windows操作系统下,CRT是建立在Windows API之上的。另外还有很多对Windows API的各种包装库,MFC就是很著名的一种以C++形式封装的库。很多操作系统是以系统调用作为应用程序最底层的,而Windows的最底层接口是Windows API。Windows API是Windows编程的基础,尽管Windows的内核提供了数百个系统调用(Windows又把系统调用称作系统服务(System Service)),但是出于种种原因,微软并没有将这些系统调用公开,而在这些系统调用之上,建立了这样一个API层,让程序员只能调用API层的函数,而不是如Linux一般直接使用系统调用。

Windows API概览:Windows API是以DLL导出函数的形式暴露给应用程序开发者的。它被包含在诸多的系统DLL内,规模上非常庞大。微软把这些Windows API DLL导出函数的声明的头文件、导出库、相关文件和工具一起提供给开发者,并让它们成为Software Development Kit(SDK)。SDK可以单独地在微软的官方网站下载,也可以被集成到Visual Studio这样的开发工具中。当我们安装了Visual Studio后,可以在SDK的安装目录下找到所有的Windows API函数声明。其中有一个头文件”Windows.h”包含了Windows API的核心部分,只要我们在程序里面包含了它,就可以使用Windows API的核心部分了。

Windows API随着Windows版本的升级也经历了好几个版本,每次Windows进行大升级的时候,也会引入新版本的API。Windows API现在的数量已经十分庞大,它们按照功能被划分成了几大类别,如下图所示:

由于Windows API所提供的接口还是相对比较原始的,所以直接使用API进行程序开发往往效率较低。Windows系统在API之上建立了很多应用模块,这些应用模块是对Windows API的功能的扩展。

为什么要使用Windows API:系统调用实际上是非常依赖于硬件结构的一种接口,它受到硬件的严格控制,比如寄存器的数量、调用时的参数传递、中断号、堆栈切换等,都与硬件密切相关。如果硬件结构稍微发生改变,大量的应用程序可能就会出现问题(特别是那些与CRT静态链接在一起的)。为了尽量隔离硬件结构的不同而导致的程序兼容性问题,Windows系统把系统调用包装了起来,使用DLL导出函数作为应用程序的唯一可用的接口暴露给用户。除了隔离硬件结构不同之外,Windows本身也有可能使用不同版本的内核,所以系统调用的接口自然也是不一样的。

GitHub:https://github.com/fengbingchun/Messy_Test

相关文章:

iOS下JS与OC互相调用(四)--JavaScriptCore

前面讲完拦截URL的方式实现JS与OC互相调用,终于到JavaScriptCore了。它是从iOS7开始加入的,用 Objective-C 把 WebKit 的 JavaScript 引擎封装了一下,提供了简单快捷的方式与JavaScript交互。 关于JavaScriptCore的使用有两篇很好的文章&…

围巾都这么黑科技了,是我见识少了

有一个永恒的话题:北方冷一点还是南方冷一点?答案是:哪里都冷!冬天最痛苦的莫过于走出空调房——刺骨的风直直的从领口处灌进去那叫一个“透心凉,心飞扬”缠了好几圈的大围巾却根本没什么保暖效果每当这时候&#xff0…

【教程】【FLEX】#004 反射机制

总结: 目前用到反射的主要有两个方法 1. getDefinitionByName //根据类名,返回对象(反射实例化对象) 2. describeType //根据对象,返回XML格式的属性,方法等信息(反射得到…

iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge

WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000star。我去翻看了它的第一版本已经是4年前了,在版本V4.1.4以及之前,该库只有一个类和一个js 的txt文件,所以旧版…

OpenCV代码提取:Windows上通过DShow获取Camera视频

在OpenCV 3.1中获取视频的模块在videoio(video input and output module)中,调用VideoCapture类接口,除了videoio模块外还依赖core、highgui、imgproc、imgcodecs四个模块,而OpenCV 2.4.13.6仅需要core、highgui、imgproc三个模块。3.1中的vi…

迁移学习与图神经网络“合力”模型:用DoT-GNN克服组重识别难题

作者 | Ziling Huang、Zheng Wang、Wei Hu、Chia-Wen Lin、Shin’ichi Satoh译者 | 刘畅编辑 | Jane出品 | AI科技大本营(ID:rgznai100)【导读】目前,大多数行人重识别(ReID)方法主要是从收集的单个人图像数…

struts2 select 默认选中

jsp:<s:select list"#{1:男,2:女}" name"sex"/> action:private String sex;sex属性有get/set方法。在业务方法中设置sex "2";select会默认选中。

通过Windows DShow获取设备名、支持的编解码及视频size列表实现

之前在https://blog.csdn.net/fengbingchun/article/details/102641967中介绍过通过DShow获取Camera视频的实现&#xff0c;即调用VideoCapture类。在OpenCV的VideoCapture类中并没有提供获取Camera设备列表、支持的编解码类型列表及支持的video size列表接口&#xff0c;这里基…

15篇论文全面概览BERT压缩方法

作者 | Mitchell A. Gordon译者 | 孙薇出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;模型压缩可减少受训神经网络的冗余——由于几乎没有BERT或者BERT-Large模型可用于GPU及智能手机上&#xff0c;这一点就非常有用了。另外&#xff0c;内存与推理速度的提高也能节…

iOS下JS与OC互相调用(七)--Cordova 基础

Cordova 简介 在介绍Cordova之前&#xff0c;必须先提一下PhoneGap。PhoneGap 是Nitobi软件公司2008年推出的一个框架&#xff0c;旨在弥补web 和iOS 之间的不足&#xff0c;使得web 和 iPhone SDK 之间的交互更容易。后来又加入了Android SDK 和BlackBerry SDK&#xff0c;再然…

在linux上MySQL的三种安装方式

安装MySQL的方式常见的有三种&#xff1a;方式一&#xff1a;rpm安装(1) 操作系统发行商提供的(2) MySQL官方提供的&#xff08;版本更新&#xff0c;修复了更多常见BUG&#xff09;www.mysql.com/downloads关于MySQL中rpm包类型的介绍&#xff1a;MySQL-client 客户端…

通过libjpeg-turbo实现对jpeg图像的解码

之前在https://blog.csdn.net/fengbingchun/article/details/89715416中介绍过通过libjpeg-turbo接口实现将数据编码或压缩成jpeg数据并通过FILE的fwrite接口将其直接保存成*.jpg图像&#xff0c;当时用的是libjpeg的接口&#xff0c;其实还可以使用turbojpeg api的接口即tjCom…

AI+大数据顶级技术盛会开幕在即,6.6折特惠票限时抢购

2019年12月5-7日&#xff0c;由中国计算机学会主办&#xff0c;CCF 大数据专家委员会承办&#xff0c;CSDN、中科天玑数据科技股份有限公司协办的中国大数据技术大会&#xff08;BDTC 2019&#xff09;将于北京长城饭店隆重举行。届时&#xff0c;超过百位顶尖技术专家将齐聚于…

iOS下JS与OC互相调用(八)--Cordova详解+实战

1.新建工程&#xff0c;添加Cordova 关键类 我这里用Xcode 8 新建了一个工程&#xff0c;叫 JS_OC_Cordova,然后将Cordova关键类添加进工程。 有哪些关键类呢&#xff1f; 这里添加config.xml 、Private 和 Public 两个文件夹里的所有文件。工程目录结构如下&#xff1a; 然后…

iOS多线程编程之NSOperation和NSOperationQueue的使用

使用 NSOperation的方式有两种&#xff0c; 一种是用定义好的两个子类&#xff1a; NSInvocationOperation 和 NSBlockOperation。 另一种是继承NSOperation 如果你也熟悉Java&#xff0c;NSOperation就和java.lang.Runnable接口很相似。和Java的Runnable一样&#xff0c;NSOpe…

Swift - 使用SwiftHTTP通过HTTPS进行网络请求,及证书的使用

&#xff08;本文代码已升级至Swift3&#xff09;一&#xff0c;证书的生成&#xff0c;以及服务器配置参考我前面写的这篇文章&#xff1a;Tomcat服务器配置https双向认证&#xff08;使用keytool生成证书&#xff09;文章详细介绍了HTTPS&#xff0c;SSL/TLS。还有使用key to…

Linux下通过v4l2获取视频设备名、支持的编解码及视频size列表实现

早些时候给出了在Windows下通过dshow获取视频设备信息的实现&#xff0c;包括获取视频设备名、获取每种视频设备支持的编解码格式列表、每种编解码格式支持的video size列表&#xff0c;见&#xff1a;https://blog.csdn.net/fengbingchun/article/details/102806822 下面给出…

12种主流编程语言输出“ Hello World ”,把我给难住了!

作为一名程序员&#xff0c;在初步学习编程想必都绕不开一个最为基础的入门级示例“Hello World”&#xff0c;那么&#xff0c;你真的了解各个语言“Hello World”的正确写法吗&#xff1f;在我们刚开始打开编程世界的时候&#xff0c;往往写的第一个程序都是简单的文本输出&a…

军哥lnmp一键安装包nginx支持pathinfo配置

ssh里执行&#xff1a;cat > /usr/local/nginx/conf/pathinfo.conf << EOF set $real_script_name $fastcgi_script_name; if ($fastcgi_script_name ~ "(.?\.php)(/.*)") { set $real_script_name $1; set $path_info $2; } fastcgi_param SCRIPT_FILENAM…

Effective STL 50条有效使用STL的经验笔记

Scott Meyers大师Effective三部曲&#xff1a;Effective C、More Effective C、Effective STL&#xff0c;这三本书出版已很多年&#xff0c;后来又出版了Effective Modern C。 Effective C的笔记见&#xff1a;https://blog.csdn.net/fengbingchun/article/details/102761542…

HTTPS网络加密双向验证-使用AFNetworking封装

1.首先使用OC封装请求头 #import <Foundation/Foundation.h> #import "AFNetworking.h" interface HttpsHandler : NSObject (AFHTTPSessionManager *)setHttpsMange; end 2.实现方法 (AFHTTPSessionManager *)setHttpsMange; { NSString *certFilePath […

30分钟搞定数据竞赛刷分夺冠神器LightGBM!

作者 | 梁云1991来源 | Python与算法之美&#xff08;ID:Python_Ai_Road)【导读】LightGBM可以看成是XGBoost的升级加强版本&#xff0c;2017年经微软推出后&#xff0c;便成为各种数据竞赛中刷分夺冠的神兵利器。一&#xff0c;LightGBM和XGBoost对比正如其名字中的Light所蕴含…

js模块化例子

最近在看一本书&#xff0c;里面提到js的模块化&#xff0c;觉得很有必要&#xff0c;所以记录下来 Game.js /*** This is the main class that handles the game life cycle. It initializes* other components like Board and BoardModel, listens to the DOM events and* tr…

swift3.0提示框新用法

var alert: UIAlertController! alert UIAlertController(title: "提示", message: "添加照片", preferredStyle: UIAlertControllerStyle.actionSheet) let cleanAction UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel,han…

FFmpeg在Windows上通过dshow编解码方式设置为mjpeg并实时显示测试代码

Windows上默认的内置摄像头一般支持两种编解码格式&#xff1a;rawvideo和mjpeg。在调用FFmpeg接口时默认的采用rawvideo。这里通过DirectShow实现为mjpeg进行编解码。 通过命令行调用FFmpeg可执行文件&#xff1a; (1). 可获取Windows上连接的视频设备&#xff0c;命令如下&…

基于深度学习的低光照图像增强方法总结(2017-2019)| CSDN博文精选

扫码参与CSDN“原力计划”作者 | hyk_1996来源 | CSDN博客精选之前在做光照对于高层视觉任务的影响的相关工作&#xff0c;看了不少基于深度学习的低光照增强&#xff08;low-light enhancement&#xff09;的文章[3,4,5,7,8,9,10]&#xff0c;于是决定简单梳理一下。光照估计&…

ios多线程和进程的区别(转载)

很想写点关于多进程和多线程的东西&#xff0c;我确实很爱他们。但是每每想动手写点关于他们的东西&#xff0c;却总是求全心理作祟&#xff0c;始终动不了手。 今天终于下了决心&#xff0c;写点东西&#xff0c;以后可以再修修补补也无妨。 一.为何需要多进程&#xff08;或者…

OC封装的轮播图-只用调用即可

先来使用方法 1.//创建显示本地图片view UIView *imageScorll[WTImageScroll ShowLocationImageScrollWithFream:CGRectMake(0, 0, SCREENWIDTH, 200) andImageArray:array andBtnClick:^(NSInteger tagValue) { NSLog("点击的图片----%",(tagValue)); self.didSele…

多核时代,并行编程为何“臭名昭著”?

作者 | Yan Gu来源 | 转载自知乎用户Yan Gu【导读】随着计算机技术的发展&#xff0c;毫无疑问现代计算机的处理速度和计算能力也越来越强。然而细心的同学们可能早已注意到&#xff0c;从2005年起&#xff0c;单核的 CPU 性能就没有显著的提升了。究其原因&#xff0c;是人们发…

Linux下获取usb视频设备vendor id和product id的8种方法

在使用usb摄像头获取视频时&#xff0c;有时需要获取此摄像头供应商ID(vendor id, vid)和产品ID(product id, pid)&#xff0c;这里在Linux下提供获取vid和pid的8种方法&#xff1a; 1. 通过v4l2中结构体v4l2_capability的成员变量card&#xff1a;此变量中会包含设备名、vid、…