主要内容:

  1. 什么是系统调用
  2. Linux上的系统调用实现原理
  3. 一个简单的系统调用的实现

1. 什么是系统调用

简单来说,系统调用就是用户程序和硬件设备之间的桥梁。

用户程序在需要的时候,通过系统调用来使用硬件设备。

系统调用的存在,有以下重要的意义:

1)用户程序通过系统调用来使用硬件,而不用关心具体的硬件设备,这样大大简化了用户程序的开发。

比如:用户程序通过write()系统调用就可以将数据写入文件,而不必关心文件是在磁盘上还是软盘上,或者其他存储上。

2)系统调用使得用户程序有更好的可移植性。

只要操作系统提供的系统调用接口相同,用户程序就可在不用修改的情况下,从一个系统迁移到另一个操作系统。

3)系统调用使得内核能更好的管理用户程序,增强了系统的稳定性。

因为系统调用是内核实现的,内核通过系统调用来控制开放什么功能及什么权限给用户程序。

这样可以避免用户程序不正确的使用硬件设备,从而破坏了其他程序。

4)系统调用有效的分离了用户程序和内核的开发。

用户程序只需关心系统调用API,通过这些API来开发自己的应用,不用关心API的具体实现。

内核则只要关心系统调用API的实现,而不必管它们是被如何调用的。

用户程序,系统调用,内核,硬件设备的调用关系如下图:

2. Linux上的系统调用实现原理

要想实现系统调用,主要实现以下几个方面:

  1. 通知内核调用一个哪个系统调用
  2. 用户程序把系统调用的参数传递给内核
  3. 用户程序获取内核返回的系统调用返回值

下面看看Linux是如何实现上面3个功能的。

2.1 通知内核调用一个哪个系统调用

每个系统调用都有一个系统调用号,系统调用发生时,内核就是根据传入的系统调用号来知道是哪个系统调用的。

在x86架构中,用户空间将系统调用号是放在eax中的,系统调用处理程序通过eax取得系统调用号。

系统调用号定义在内核代码:arch/alpha/include/asm/unistd.h 中,可以看出linux的系统调用不是很多。

2.2 用户程序把系统调用的参数传递给内核

系统调用的参数也是通过寄存器传给内核的,在x86系统上,系统调用的前5个参数放在ebx,ecx,edx,esi和edi中,如果参数多的话,还需要用个单独的寄存器存放指向所有参数在用户空间地址的指针。

一般的系统调用都是通过C库(最常用的是glibc库)来访问的,Linux内核提供一个从用户程序直接访问系统调用的方法。

参见内核代码:arch/cris/include/arch-v10/arch/unistd.h

里面定义了6个宏,分别可以调用参数个数为0~6的系统调用

_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)

超过6个参数的系统调用很罕见,所以这里只定义了6个。

2.3 用户程序获取内核返回的系统调用返回值

获取系统调用的返回值也是通过寄存器,在x86系统上,返回值放在eax中。

3. 一个简单的系统调用的实现

了解了Linux上系统调用的原理,下面就可以自己来实现一个简单的系统调用。

3.1 环境准备

为了不破坏现有系统,我是用虚拟机来实验的。

主机:fedora16 x86_64系统 + kvm(一种虚拟技术,就像virtualbox,vmware等)

虚拟机: 也是安装fedora16 x86_64系统(通过virt-manager很容易安装一个系统)

下载内核源码:www.kernel.org  下载最新的就行

3.2 修改内核源码中的相应文件

主要修改以下文件:

arch/x86/ia32/ia32entry.S
arch/x86/include/asm/unistd_32.h
arch/x86/include/asm/unistd_64.h
arch/x86/kernel/syscall_table_32.S
include/asm-generic/unistd.h
include/linux/syscalls.h
kernel/sys.c

我在sys.c中追加了2个函数:sys_foo和sys_bar

如果是在x86_64的内核中增加一个系统调用,只需修改 arch/x86/include/asm/unistd_64.h,比如sys_bar。

修改内容参见下面的diff文件:

diff -r new/arch/x86/ia32/ia32entry.S old/arch/x86/ia32/ia32entry.S
855d854
< .quad sys_foo
diff -r new/arch/x86/include/asm/unistd_32.h old/arch/x86/include/asm/unistd_32.h
357d356
< #define __NR_foo 349
361c360
< #define NR_syscalls 350
---
> #define NR_syscalls 349
diff -r new/arch/x86/include/asm/unistd_64.h old/arch/x86/include/asm/unistd_64.h
689,692d688
< #define __NR_foo 312
< __SYSCALL(__NR_foo, sys_foo)
< #define __NR_bar 313
< __SYSCALL(__NR_bar, sys_bar)
diff -r new/arch/x86/kernel/syscall_table_32.S old/arch/x86/kernel/syscall_table_32.S
351d350
< .long sys_foo
diff -r new/include/asm-generic/unistd.h old/include/asm-generic/unistd.h
694,695d693
< #define __NR_foo 272
< __SYSCALL(__NR_foo, sys_foo)
698c696
< #define __NR_syscalls 273
---
> #define __NR_syscalls 272
diff -r new/kernel/sys.c old/kernel/sys.c
1920,1928d1919
<
< asmlinkage long sys_foo(void)
< {
< return 1112223334444555;
< }
< asmlinkage long sys_bar(void)
< {
< return 1234567890;
< }

3.3 编译内核

#cd linux-3.2.28
#make menuconfig (选择要编译参数,如果不熟悉内核编译,用默认选项即可)
#make all (这一步真的时间很长......)
#make modules_install
#make install (这一步会把新的内核加到启动项中)
#reboot (重启系统进入新的内核)

3.4 编写调用的系统调用的代码

#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <errno.h> #define __NR_foo 312
#define __NR_bar 313 int main()
{
printf ("result foo is %ld\n", syscall(__NR_foo));
printf("%s\n", strerror(errno));
printf ("result bar is %ld\n", syscall(__NR_bar));
printf("%s\n", strerror(errno));
return 0;
}

编译运行上面的代码:

#gcc test.c -o test
#./test

运行结果如下:

result foo is 1112223334444555
Success
result bar is 1234567890
Success

《Linux内核设计与实现》读书笔记(五)- 系统调用的更多相关文章

  1. Linux内核设计与实现 读书笔记 转

    Linux内核设计与实现  读书笔记: http://www.cnblogs.com/wang_yb/tag/linux-kernel/ <深入理解LINUX内存管理> http://bl ...

  2. Linux内核设计与实现 读书笔记

    第三章 进程管理 1. fork系统调用从内核返回两次: 一次返回到子进程,一次返回到父进程 2. task_struct结构是用slab分配器分配的,2.6以前的是放在内核栈的栈底的:所有进程的ta ...

  3. Linux内核设计与实现读书笔记(8)-内核同步方法【转】

    转自:http://blog.chinaunix.net/uid-10469829-id-2953001.html 1.原子操作可以保证指令以原子的方式执行——执行过程不被打断.内核提供了两组原子操作 ...

  4. Linux内核设计与实现——读书笔记2:进程管理

    1.进程: (1)处于执行期的程序,但不止是代码,还包括各种程序运行时所需的资源,实际上进程是正在执行的 程序的实时结果. (2)程序的本身并不是进程,进程是处于执行期的程序及其相关资源的总称. (3 ...

  5. Linux内核设计与实现——读书笔记1:内核简介

    内核:有的时候被称管理者或者操作系统核心,通常内核负责响应中断的中断服务程序, 负责管理多个进程从而分享处理器时间的调度程序,负责管理进程地址空间德内存管理程序 和网络,进程间通信等系统服务程序共同组 ...

  6. 《Linux内核设计与实现》第五周读书笔记——第十一章

    <Linux内核设计与实现>第五周读书笔记——第十一章 20135301张忻 估算学习时间:共2.5小时 读书:2.0 代码:0 作业:0 博客:0.5 实际学习时间:共3.0小时 读书: ...

  7. 《Linux内核设计与实现》第五章学习笔记

    <Linux内核设计与实现>第五章学习笔记 姓名:王玮怡  学号:20135116 一.与内核通信     在Linux中,系统调用是用户空间访问内核的唯一手段:除异常和陷入外,它们是内核 ...

  8. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...

  9. 【读书笔记】Linux内核设计与实现(第五章)

    5.1 内核通信 系统调用在用户空间和硬件设备之间添加了一个中间层. 该层主要作用: 1.为用户空间提供了一种硬件的抽象接口. 2.保证了系统的稳定和安全. 3.每个进程都运行在虚拟系统中. 在Lin ...

  10. 《Linux内核设计与实现》第五章读书笔记

    第五章  系统调用 5.1与内核通信 1. 系统调用 让应用程序受限的访问硬件设备 提供创建新进程并与已有进程通信的机制 提供申请操作系统其他资源能力是用户空间进程和硬件设备之间的中间层 2. 系统调 ...

随机推荐

  1. linux开发核心理解

    目录 授权 致谢 序言 更新纪录 导读 如何写作科技文档 I. 气候 1. GUI? CLI? 2. UNIX 缩写风格 3. 版本号的迷雾 4.   Vim 还是 Emacs 5.   DocBoo ...

  2. hdu 1028 && hdu 1398 && hdu 1085 && hdu 1171 ——生成函数

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1028 就是可以用任意个1.2.3....,所以式子写出来就是这样:(1+x+x^2+...)(1+x^2+ ...

  3. MyBatis 批量插入数据对插入记录数的限制

    <基于 MyBatis 框架的批量数据插入的性能问题的探讨>(作者:魏静敏 刘欢杰 来源:<计算机光盘软件与应用> 2013 年第 19 期)中提到批量插入的记录数不能超过10 ...

  4. Debian 7开启ssh、telnet

    SSH 1. 安装ssh服务 apt-get install openssh-server 2. 开启ssh /etc/init.d/ssh  start Telnet 1. 安装telnet apt ...

  5. Centos 7.2 编译安装 git

    一. 下载最新版GIT安装包: https://www.kernel.org/pub/software/scm/git/ 选择想要安装的版本,下载,解压 命令: .tar.gz $ cd git- 二 ...

  6. HDLM工具介绍

    HDLM提供了以下一些工具,以方便HDLM多路径管理. 1. dlmgetras hdlm信息收集工具,用来收集hdlm相关的各种日志.trace.配置等文件,以方便进行hdlm故障分析. 命令格式 ...

  7. 10-10C#基础---数据类型之间的转换

    10-10  C#基础数据类型转换(熟练掌握) 第一课 数据类型之间的转换 基本类型的转换:自动转换(隐式转换)和强制转换(显示转换) 装箱转换:允许值类型隐式转换成引用类型. 拆箱转换:允许将引用类 ...

  8. 2016上海浦东汽车展览会C+罩杯 车模名单

    只能帮你们到这了

  9. java判断一个字符串中是否包含全角

    public static boolean isAngle(String str){ if(str.getBytes().length==str.length()){ //全是半角 return tr ...

  10. css中的hack

    1.什么是CSS hack? CSS hack是通过在CSS样式中加入一些特殊的符号,让不同的浏览器识别不同的符号(什么样的浏览器识别什么样的符号是有标准的,CSS hack就是让你记住这个标准),以 ...