实验目的
阅读 Linux 内核源代码,通过添加一个简单的系统调用实验,进一步理解
Linux操作系统处理系统调用的统一流程。通过用kernel module的方法来实现一
个系统调用实验,进一步理解Linux的内核模块和Linux系统调用机制,对通过
module方法添加一个系统调用的步骤有所了解。
实验内容
1、在现有的系统中添加一个不用传递参数的系统调用。调用这个系统调
用,使用户的uid变成0。
2、用kernel module的方法来实现一个系统调用
实验提示 1
怎么样?觉得困难还是觉得太简单?我们首先承认,每个人接触Linux的时间长短不一
样,因此基础也会有一点差别。那么对于觉得太简单的人呢,请你迅速地合上书本,然后跑
到电脑前面,开始实现这个题目。来吧,不要眼高手低,做完之后,你就可以跳过这一节,
直接进入下一节的学习了。对于觉得有点困难的人呢,不用着急,这一节就是专门为你准备
的。我们会列出详细的实现步骤,你一定也没有问题的。
如果你前面对整个系统调用的过程有一个清晰的理解的话,我们就顺着系统调用的流程
思路,给出一个添加新的系统调用的步骤:
一、决定你的系统调用的名字
这个名字就是你编写用户程序想使用的名字,比如我们取一个简单的名字:mysyscall。
一旦这个名字确定下来了,那么在系统调用中的几个相关名字也就确定下来了。
l 系统调用的编号名字:__NR_mysyscall;
l 内核中系统调用的实现程序的名字:sys_mysyscall;
现在在你的用户程序中出现了:
#include <linux/unistd.h>
int main()
{
mysyscall();
}
流程转到标准C库。
二、利用标准C 库进行包装吗
编译器怎么知道这个mysyscall 是怎么来的呢?在前面我们分析的时候,我们知道那时
标准C 库给系统调用作了一层包装,给所有的系统调用做出了定义。但是显然,我们可能
不愿意去改变标准C库,也没有必要去改变。那么我们在自己的程序中来做:
#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意这里没有分号*/
int main()
{
mysyscall();
}
好,由于有了_syscall0 这个宏,mysyscall 将得到定义。但是现在系统会去找系统调用号,
以放入eax。所以,接下来我们定义系统调用号。
三、添加系统调用号
系统调用号在文件unistd.h里面定义。这个文件可能在你的系统上会有两个版本:一个
是C库文件版本,出现的地方是在/usr/include/unistd.h和/usr/include/asm/unistd.h;另外还有
一个版本是内核自己的unistd.h,出现的地方是在你解压出来的2.4.18 内核代码的对应位置
(比如/usr/src/linux/include/linux/unistd.h和/usr/include/asm-i386/unistd.h)。当然,也有可能
这个C 库文件只是一个到对应内核文件的连接。至于为什么会存在两个版本的头文件,这
个问题将会在5-6较高级主题一节进行说明。现在,你要做的就是在文件unistd.h中添加我
们的系统调用号:__NR_mysyscall,如下所示:
include/asm-i386/unistd.h
/usr/include/asm/unistd.h
240 #define __NR_llistxattr 233
241 #define __NR_flistxattr 234
242 #define __NR_removexattr 235
243 #define __NR_lremovexattr 236
244 #define __NR_fremovexattr 237
245 #define __NR_mysyscall 238 /* mysyscall adds here */
添加系统调用号之后,系统才能根据这个号,作为索引,去找syscall_table中的相应表项。
所以说,我们接下来的一步就是:
四、在系统调用表中添加相应表项
我们前面讲过,系统调用处理程序(system_call)会根据eax 中的索引到系统调用表
(sys_call_table)中去寻找相应的表项。所以,我们必须在那里添加我们自己的一个值。
arch/i386/kernel/entry.S
398 ENTRY(sys_call_table)
399 .long SYMBOL_NAME(sys_ni_syscall)
400 .long SYMBOL_NAME(sys_exit)
401 .long SYMBOL_NAME(sys_fork)
402 .long SYMBOL_NAME(sys_read)
403 .long SYMBOL_NAME(sys_write)
……
……
634 .long SYMBOL_NAME(sys_ni_syscall)
635 .long SYMBOL_NAME(sys_ni_syscall)
636 .long SYMBOL_NAME(sys_ni_syscall)
637 .long SYMBOL_NAME(sys_mysyscall)
638
639 .rept NR_syscalls-(.-sys_call_table)/4
640 .long SYMBOL_NAME(sys_ni_syscall)
641 .endr
到现在为止,系统已经能够正确地找到并且调用sys_mysyscall。剩下的就只有一件事情,那
就是sys_mysyscall的实现。
五、sys_mysyscall 的实现
我们把这一小段程序添加在kernel/sys.c 里面。在这里,我们没有在kernel 目录下另外
添加自己的一个文件,这样做的目的是为了简单,而且不用修改Makefile,省去不必要的麻
烦。
asmlinkage int sys_mysyscall(void)
{
current->uid = current->euid = current->suid = current->fsuid = 0;
return 0;
}
这个系统调用中,把标志进程身份的几个变量uid、euid、suid和fsuid都设为0。
到这里为止,我们所要做的添加一个新的系统调用的所有工作就完成了,是不是非常简
单?的确如此。因为Linux 内核结构的层次性还是非常清楚的,这就使得每一个开发者可以
把精力放在怎么样实现具体的功能上,而不用在一些接口函数上伤脑筋。
现在所有的代码添加已经结束,那么要使得这个系统调用真正在内核中运行起来,我们
就需要对内核进行重新编译。这个问题我们在第一章的时候就讨论到了,应该没有问题,因
此我们在这里略过。
六、编写用户态程序
要测试我们新添加的系统调用,我们可以编写一个用户程序调用我们自己的系统调用。
我们对自己的系统调用的功能已经很清楚了:使得自己的uid变成0。那么我们看看是不是
如此:
用户态程序
#include <linux/unistd.h>
_syscall0(int,mysyscall) /* 注意这里没有分号*/
int main()
{
mysyscall(); /* 这个系统调用的作用是使得自己的uid为0 */
printf(“em…, this is my uid: %d. \n”, getuid());
}
实验提示2:
在这里,我们把系统调用的知识和Kernel Module的知识结合起来,用kernel module的
方法来实现一个系统调用。这个系统调用是gettimeofday的简化版本。实验的目的只是为了
使你对通过module方法添加一个系统调用的步骤有所了解。
具体代码示例如下:
/* pedagogictime.c */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
/* 在这个头文件里面包含了所有的系统调用号__NR_... */
#include <linux/unistd.h>
/* for struct time* */
#include <linux/time.h>
/* for copy_to_user() */
#include <asm/uaccess.h>
/* for current macro */
#include <linux/sched.h>
#define __NR_pedagogictime 238
MODULE_DESCRIPTION("My sys_pedagogictime()");
MODULE_AUTHOR("Your Name :), (C) 2002, GPLv2 or later");
/* 用来保存旧系统调用的地址*/
static int (*anything_saved)(void);
/* 这个是我们自己的系统调用函数sys_pedagogictime(). */
static int sys_pedagogictime(struct timeval *tv)
{
struct timeval ktv;
/* 这里我们需要增加模块使用计数。*/
MOD_INC_USE_COUNT;
do_gettimeofday(&ktv);
if (copy_to_user(tv, &ktv, sizeof(ktv))){
MOD_DEC_USE_COUNT;
return -EFAULT;
}
printk(KERN_ALERT"Pid %ld called sys_gettimeofday().\n",(long)current->pid);
MOD_DEC_USE_COUNT;
return 0;
}
/* 这里是初始化函数。__init标志表明这个函数使用后就可以丢弃了。*/
int __init init_addsyscall(void)
{
extern long sys_call_table[];
/* 保存原来系统调用表中此位置的系统调用*/
anything_saved = (int(*)(void))(sys_call_table[__NR_pedagogictime]);
/* 把我们自己的系统调用放入系统调用表,注意进行类型转换*/
sys_call_table[__NR_pedagogictime] = (unsigned long)sys_pedagogictime;
return 0;
}
/* 这里是退出函数。__exit标志表明如果我们不是以模块方式编译这段程序,则这个标志后的
* 函数可以丢弃。也就是说,模块被编译进内核,只要内核还在运行,就不会被卸载。
*/
void __exit exit_addsyscall(void)
{
extern long sys_call_table[];
/* 恢复原先的系统调用*/
sys_call_table[__NR_pedagogictime] = (unsigned long)anything_saved;
}
/* 这两个宏告诉系统我们真正的初始化和退出函数*/
module_init(init_addsyscall);
module_exit(exit_addsyscall);
使用kernel module的方法来实现的这个系统调用,我们需要做的只是用这条命令:
gcc -Wall -O2 -DMODULE -D__KERNEL__ -DLINUX -c pedagogictime.c.
编译成.o文件,然后使用insmod pedagogictime.o把它动态地加载到正在运行的内核中。显
然,这样的做法比起我们先前的那种要重新编译内核的办法更加灵活,更加方便。这也正是
Linux kernel module program如此受欢迎的原因。
测试用这个使用kernel module编写的系统调用的用户程序:
测试用的用户程序
/* for struct timeval */
#include <linux/time.h>
/* for _syscall1 */
#include <linux/unistd.h>
#define __NR_pedagogictime 238
_syscall1(int, pedagogictime, struct timeval *, thetime)
int main()
{
struct timeval tv;
pedagogictime(&tv);
printf("tv_sec : %ld\n", tv.tv_sec);
printf("tv_nsec: %ld\n", tv.tv_usec);
printf("em..., let me sleep for 2 second.:)\n");
sleep(2);
pedagogictime(&tv);
printf("tv_sec : %ld\n", tv.tv_sec);
printf("tv_nsec: %ld\n", tv.tv_usec);
}
假设这个程序是test.c,那么使用gcc -o test test.c得到test可执行文件,然后你可以执
行这个test看看结果。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/unbutun/archive/2009/09/08/4532583.aspx

[转] 添加新的系统调用 _syscall0(int, mysyscall)的更多相关文章

  1. 如何在Linux中添加新的系统调用

    系统调用是应用程序和操作系统内核之间的功能接口.其主要目的是使得用户 可以使用操作系统提供的有关设备管理.输入/输入系统.文件系统和进程控制. 通信以及存储管理等方面的功能,而不必了解系统程序的内部结 ...

  2. ASP.NET MVC 5 - 给电影表和模型添加新字段

    在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...

  3. Asp.Net MVC4入门指南(7):给电影表和模型添加新字段

    在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...

  4. 使用percona xtradb cluster的IST方式添加新节点

    使用percona xtradb cluster的IST(Incremental State Transfer)特性添加新节点,防止新节点加入时使用SST(State SnapShop Transfe ...

  5. spring AOP Bean添加新方法

    目的:为studentAdditionalDetails中添加Student的showDetails()和ExtraShowDetails()两个方法 spring  中AOP能够为现有的方法添加额外 ...

  6. C# DataGridView控件 动态添加新行

    DataGridView控件在实际应用中非常实用,特别需要表格显示数据时.可以静态绑定数据源,这样就自动为DataGridView控件添加相应的行.假如需要动态为DataGridView控件添加新行, ...

  7. C# DataGridView添加新行的2个方法

    可以静态绑定数据源,这样就自动为DataGridView控件添加 相应的行.假如需要动态为DataGridView控件添加新行,方法有很多种,下面简单介绍如何为DataGridView控件动态添加新行 ...

  8. 自己动手为PHP7添加新的语法特性

    好文章! nikic介绍了如何向PHP添加新的语法特性,原文写的非常精彩,具体是添加in语法功能,使最终实现: <?php $words = ['hello', 'world', 'foo', ...

  9. 【译】ASP.NET MVC 5 教程 - 9:添加新字段

    原文:[译]ASP.NET MVC 5 教程 - 9:添加新字段 在本节中,我们将使用Entity Framework Code First 数据迁移功能将模型类的改变应用到数据库中. 默认情况下,当 ...

随机推荐

  1. 缺少wlanapi.dll文件问题修复

    我在下载百度云盘的时候碰到了一个问题,缺少wlanapi.dll文件.下面贴出解决办法 第一步 http://d.apktop.cn/p/soft_134.html   下载wlanapi.dll 第 ...

  2. CSS基础3

    1.变形样式 transform : none | <transform-function>,改变元素的大小,透明,旋转角度,扭曲度等.<transform-function> ...

  3. git clone error: RPC failed; result=22, HTTP code = 502

    http://www.jianshu.com/p/645d3fe4e028 git克隆的工程太大用https的方式会有如下问题 hbl:tmp hubert$ git clone https://gi ...

  4. C# 图片的裁剪,两个图片合成一个图片

    图片的裁剪,两个图片合成一个图片(这是从网上摘的) /// <summary>         /// 图片裁剪,生成新图,保存在同一目录下,名字加_new,格式1.png  新图1_ne ...

  5. OC语言中BOOL 和 bool 区别

    1.类型不同 BOOL为int型: bool为布尔型: 2.长度不同 bool只有一个字节: BOOL长度视实际环境来定,一般可认为是4个字节: 3.取值不同 bool取值false和true,是0和 ...

  6. Flexbox,更优雅的布局

    在设计的眼中,排版的操作是一件很简单的事情,靠左.置中.靠右,我只要点一下,所有元素,就会乖乖的到指定的位置. 但到了前端在排版的实现上,就不是这样了. 我们常常得用一堆其实本来不是这样用的属性来做 ...

  7. 用JS修改checkbox的选中状态

    代码如下: <SCRIPT LANGUAGE="JavaScript"> <!-- function change() { var c=document.myfo ...

  8. 机器学习相关的Awesome系列

    Index Awesome 备注 1 Awesome Machine Learning 机器学习资源大全中文版 2 Awesome Artificial Intelligence 人工智能 3 Awe ...

  9. ThinkPHP模板中如何操作session,以及如果session中保存的是数组的情况

    在ThinkPHP的模板中操作session时,可以参考ThinkPHP参考文档中的“模板—>系统变量”部分,在默认模板引擎中,语法如下: {$Think.session.user} //输出s ...

  10. 练习sql语句的好去处——http://www.sqlzoo.cn/

    sql语句的编写需要按照实际的例子来练习. 如果自己来做准备,需要你自己搭好数据库,建好库和表,还要填入数据,最后自己想出题目和正确答案. 不过,现在我发现了一个好去处,http://www.sqlz ...