主要内容:

  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. 【LeetCode】006. ZigZag Conversion

    The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like ...

  2. RequireJS 也可以引入 VUE

    RequireJS 也可以引入 VUE 由于 FastAdmin 是使用 RequireJS 导入 JS 模块的. 有人想把 VUE 也引入进去,虽然说也是可以,VUE 还是推荐使用 Webpack ...

  3. Python collections系列之默认字典

    默认字典(defaultdict)  defaultdict是对字典的类型的补充,它默认给字典的值设置了一个类型. 1.创建默认字典 import collections dic = collecti ...

  4. Qt多选框

    1.获取并显示复选框文本内容 ui->label->setText(ui->comboBox->currentText());

  5. 机器学习:集成学习(Bagging、Pasting)

    一.集成学习算法的问题 可参考:模型集成(Enxemble) 博主:独孤呆博 思路:集成多个算法,让不同的算法对同一组数据进行分析,得到结果,最终投票决定各个算法公认的最好的结果: 弊端:虽然有很多机 ...

  6. struts2中常用constant命令配置

    struts.objectFactory这个属性用 于说明Struts2的 对象池创建工厂,Struts2也有自己的对象池,就像Spring那样,在配置文件中你可以引用对象池中的对象,你可以借助于Sp ...

  7. 在IIS下配置自定义的报错页面

    这里介绍在IIS中配置自定义出错页面的方法,主要以404为例,其他状态可类推 1.远程桌面连接IIS所在的服务器,进入控制面板>系统和安全>管理工具,双击打开IIS管理器,选择需要配置的网 ...

  8. springmvc----demo---login---bai

    web.xml配置: <?xml version="1.0" encoding="UTF-8"?> <web-app version=&quo ...

  9. paramiko 模块封装

    #!/usr/bin/env python#coding=utf-8 import paramiko, getpass,sys,traceback class ssh_utils(): def log ...

  10. 阿里云服务器ubuntu安装redis2.8.13

    阿里云服务器ubuntu安装redis2.8.13 2014-09-04 16:14 |  coding云 |  2198次阅读 | 暂无评论   一.下载redis 可以先下载到本地,然后ftp到服 ...