linux 内核修炼之道——系统调用
1、问:什么是系统调用?
用户应用程序访问并使用内核所提供的各种服务的途径即是系统调用,也称系统调用接口层。
2、问:为什么需要系统调用?
① 系统调用作为内核和应用程序之间的中间层,扮演了一个桥梁角色,系统调用把应用程序的请求传达给内核,待内核处理完之后再将结果返回给应用程序
② 系统调用可以为用户空间提供访问硬件资源的统一接口,以至于应用程序不必关心具体的硬件访问操作
③ 系统调用可以对系统进行保护,保证系统的稳定和安全
3、系统调用、API、POSIX、C库、系统命令、内核函数的区别?
①在通常情况下,应用程序是通过操作系统提供的应用编程接口(API)而不是直接通过系统调用来编程
②在UNIX世界里,最通用的操作系统API基于POSIX(Portable Operating System Interface of UNIX,可移植操作系统接口)标准,Linux 兼容于POSIX标准,提供了根据POSIX而定义的API函数,这些API函数和系统调用之间有着直接关系,一个API函数可以由一个系统调用实现,也可以通过多个系统调用实现,还可以完全不用系统调用实现
③API通常以C库的方式提供,C库提供了POSIX的绝大部分API,同时,内核提供的每个系统调用在C库中都有相应的封装函数
④系统命令位于C库的更上层,利用C库实现的可执行程序,strace工具可以追踪命令的执行过程
⑤应用程序通过系统调用进入内核后,会执行各个系统调用对应的内核函数,即系统调用服务例程,除了系统调用服务例程之外,内核还有许多内核函数,有些内核函数局限于某个内核文件自己使用,有些则是用export导出来供内核其他部分使用,可以使用ksyms命令或通过/proc/ksyms 文件查看
4、系统调用表
系统调用表sys_call_table 存储了所有系统调用对应的服务例程的函数地址
5、系统调用号
用户通过系统调用号作为下标去获取系统调用表sys_call_table中的服务例程的函数地址来指明要执行哪个系统调用
系统调用号定义在include/asm-i386/unistd.h文件中
6、系统调用服务例程
系统调用最终由系统调用服务例程来完成明确的操作,它的命名遵守一些规则,函数名都具有“sys_”前缀,函数定义中必须添加asmlinkage标记,通知编译器仅从堆栈中获取该函数的参数,通常返回0表示成功,负数表示失败
7、如何使用系统调用
第一种方式是通过C库函数,包括系统调用在C库中的封装函数和其他普通函数
第二种方式是使用_syscall 宏。2.6.18版本之前的内核,在include/asm-i386/unistd.h文件中定义7个宏分别是:
_syscall0(type,name)
_syscall1(type, name, type1, arg1)
_syscall2(type, name, type1, arg1, type2, arg2)
_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)
_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4)
_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5)
_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5, type6, arg6)
其中,type表示所生成系统调用的返回值类型,name表示该系统调用的名称,typeN、argN分别表示第N个参数的类型和名称,它们的数目和_syscall后面的数字一样大,这些宏的作用是创建名为name的函数,_syscall后面跟的数字指明了该函数的参数的个数。例如:sysinfo系统调用用于获取系统总体统计信息,使用_syscall宏定义如下:
_syscall1(int, sysinfo, struct sysinfo *, info);
展开后的形式为:
int sysinfo(struct sysinfo* info)
{
long _res;
__asm__ volatile("int $0x80" : "=a" (_res) : "0" (116), "b" ((long) (info)));
do {
if ((unsigned long)(_res) >= (unsigned long)(-(128 + 1))) {
errno = -(_res);
_res = -1;
}
return (int)(_res);
} while (0);
}
在程序文件里使用_syscall宏定义需要的系统调用,就可以在接下来的代码中通过系统调用名称直接调用该系统调用,例如:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <linux/unistd.h>
5 #include <linux/kernel.h>
6
7 _syscall1(int, sysinfo, struct sysinfo *, info);
8
9 int main(void)
10 {
11 struct sysinfo s_info;
12 int error;
13
14 error = sysinfo(&s_info);
15 printf("code error = %d\n", error);
16 printf("Uptime = %lds\n", s_info.uptime);
17
18 return 0;
19 }
但是,自2.6.19版本开始,_syscall 宏被废除,我们需要使用syscall函数,通过制定系统调用号和一组参数来调用系统调用
syscall的函数原型为:
int syscall(int number, ...);
其中,number是系统调用号,number后面应顺序接上该系统调用的所有参数,下面是gettid系统调用的实例:
1 #include <unistd.h>
2 #include <sys/syscall.h>
3 #include <sys/types.h>
4
5 #define _NR_gettid 224
6
7 int main(void)
8 {
9 pid_t tid;
10 tid = syscall(_NR_gettid);
11 return 0;
12 }
大部分系统调用都包括了一个SYS_符号常量来制定自己到系统调用号的映射,因此上面第10行可重写为:
tid = syscall(SYS_gettid);
8、系统调用执行过程

如图所示,系统调用的执行需要一个指令完成从用户空间到内核空间的状态转换,这种指令被称为操作系统陷入指令,linux通过软中断来实现这种陷入,对于X86来说,是软中断0x80,也即int $0x80汇编指令,通过软中断0x80,系统会跳到一个预设的内核空间地址,它指向了系统调用处理程序,即在arch/i386/kernel/entry.S文件中使用汇编语言编写的system_call函数

所有的系统调用都会执行system_call 函数,当软中断指令int 0x80执行时,系统调用号会被放入eax寄存器,并且sys_call_table每一项占用4个字节,system_call函数读取eax 寄存器获得当前系统调用的系统调用号,将其乘以4得到偏移地址,然后以sys_call_table为基址,加上得到的偏移地址就是应该执行的系统调用服务例程的地址。
前面说过,系统调用服务地产定义中的asmlinkage标记表示,编译器仅从堆栈中获取该函数的参数,而不需要从寄存器中获得任何参数,在进入system_call之前,用户应用将参数放到对应寄存器中,system_call函数执行时会首先将这些寄存器压入堆栈。
有一个特殊的服务例程sys_ni_syscall,它代表了那些已经被内核中淘汰的系统调用。
系统调用通过软中断0x80陷入内核,跳转到系统调用处理程序system_call函数,并执行相应的服务例程,由于是嗲表用户进程,所以这个执行过程并不属于中断上下文,而是出于进程上下文,因此,系统调用执行过程中,可以访问用户进程的许多信息,可以被其他进程抢占,可以休眠。
9、如何实现一个新的系统调用
① 编写系统调用服务例程,并在相应的头文件(include/linux/syscalls.h)中添加原型声明
② 在相应的头文件(include/asm-i386/unistd.h)中添加系统调用号
③ 修改系统调用表
④ 重新编译内核并测试
linux 内核修炼之道——系统调用的更多相关文章
- 《Linux内核修炼之道》精华分享与讨论(5)——Kernel地图:Kconfig与Makefile
		转自:http://blog.csdn.net/fudan_abc/article/details/5340408 Makefile不是Make Love 从前在学校,混了四年,没有学到任何东西,每天 ... 
- linux内核修炼之道
		华清远见·任桥伟 人民邮电 2010 内核不学,岂能理解?今天开始正式学习内核原理 linux 发行版本Mint. cat /etc/issue # sudo lsb_release - ... 
- 《Linux内核修炼之道》 之 高效学习Linux内核
		http://blog.csdn.net/fudan_abc/article/details/5738436 
- 《Linux内核修炼之道》 系列
		http://blog.csdn.net/fudan_abc/article/category/655796 
- (转)Linux内核之进程和系统调用
		Linux内核之进程和系统调用 什么是系统调用 在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是在内核中实现的,再通过一定 ... 
- Lab1:Linux内核编译及添加系统调用(详细版)
		实验一:Linux内核编译及添加系统调用(HDU) 花了一上午的时间来写这个,良心制作,发现自己刚学的时候没有找到很详细的,就是泛泛的说了下细节地方也没有,于是自己写了这个,有点长,如果你认真的看完了 ... 
- Linux内核分析之扒开系统调用的三层皮(下)
		一.实验内容 1. 通过内核的方式使用系统调用 需要使用的命令 rm menu -rf //强制删除当前menugit clone http://github.com/mengning/menu.gi ... 
- Linux内核学习之道
		来自:http://blog.chinaunix.net/uid-26258259-id-3783679.html 内核文档 内核代码中包含有大量的文档,这些文档对于学习理解内核有着不可估量的价值,记 ... 
- Linux内核分析之扒开系统调用的三层皮(上)
		一.原理总结 本周老师讲的内容主要包括三个方面,用户态.内核态和中断,系统调用概述,以及使用库函数API获取系统当前时间.系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口,也是一种特殊的 ... 
随机推荐
- Java基础系列(23)- 打印九九乘法表
			package struct; public class ForDemo03 { public static void main(String[] args) { for (int line = 1; ... 
- 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅  | 百篇博客分析OpenHarmony源码 | v37.03
			百篇博客系列篇.本篇为: v37.xx 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ... 
- P5404-[CTS2019]重复【KMP,dp】
			正题 题目链接:https://www.luogu.com.cn/problem/P5404 题目大意 给出一个字符串\(S\),然后求有多少个长度为\(m\)的串\(T\)满足.无限多个串\(T\) ... 
- python 包(package)和模块(module)的创建和引入(import)
			python 包(package)和模块(module)的创建和引入(import) 名词解释 实际上,Python中的函数(Function).类(Class).模块(Module).包库(Pack ... 
- postgresql高可用集群部署
			一.概况 1.概念 pgsql高可用集群采用postgresql+etcd+patroni+haproxy+keepalived等软件实现,以postgresql做数据库,etcd存储集群状态,pat ... 
- ansible远程运维操作
			1.command 用于查看文件内容,查看磁盘,内存,启动命令等纯命令信息 ansible portal -m command -a "cat /test1/test"2.ping ... 
- 第21篇-加载与存储指令之iload、_fast_iload等(3)
			iload会将int类型的本地变量推送至栈顶.模板定义如下: def(Bytecodes::_iload , ubcp|____|clvm|____, vtos, itos, iload , _ ); ... 
- 随机生成文章的AI(C++)
			#include <iostream> #include <cstdlib> #include <ctime> #include <fstream> u ... 
- C# 提取PDF中的表格
			本文介绍在C#程序中(附VB.NET代码)提取PDF中的表格的方法,调用Spire.PDF for .NET提供的提取表格的类以及方法等来获取表格单元格中的文本内容:代码内容中涉及到的主要类及方法归纳 ... 
- 爬虫逆向基础,理解 JavaScript 模块化编程 webpack
			关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶.JS/安卓逆向等技术干货! 简介 在分析一些站点的 JavaScript 代码时,比较简单的代码,函数通常都是一个一个的,例 ... 
