[转] 添加新的系统调用 _syscall0(int, mysyscall)

时间:2022-09-06 23:56:01

实验目的
阅读 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&period;NET MVC 5 - 给电影表和模型添加新字段

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

  3. Asp&period;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&num; DataGridView控件 动态添加新行

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

  7. C&num; DataGridView添加新行的2个方法

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

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

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

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

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

随机推荐

  1. &lbrack;LeetCode&rsqb; Longest Valid Parentheses 最长有效括号

    Given a string containing just the characters '(' and ')', find the length of the longest valid (wel ...

  2. 利用Eclipse使用Java OpenCV(Using OpenCV Java with Eclipse)

    最近在上计算机视觉这门课程用到了OpenCV,于是找到了"Using OpenCV Java with Eclipse"这篇博文,是英文的,我将它翻译如下与大家分享 正文: 从2. ...

  3. html 之input标签height设置问题

    今天针对这个问题纠结了很久 <input type="button" value="xx" style="height:99px;" ...

  4. Java基础——异常处理

    异常的层次结构 所有的异常类都是 java.lang.Exception 类的子类型.异常类都是 Throwable 类的子类.除了异常类 Error 类也是由 Throwable 类产生的的子类1. ...

  5. jstring&comma; String&comma; char&ast; 变换函数

    #include <malloc.h> #include <string.h> #include <stdlib.h> #include <vcclr.h&g ...

  6. PHP数组的基本操作及遍历数组的经典操作

    <?php 索引数组//数组第一种定义 $arr = array(1,2,3);var_dump($arr); //数组第二种定义$arr = [1,2,3];var_dump($arr); / ...

  7. jdk8中的stream

    https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/ Java 8 中的 Streams API 详解 Streams 的背景 ...

  8. 从头调试stm32 HID

    目录: 第1部分:参照“正点原子USB虚拟串口工程移植步骤”移植ST的USB HID工程(失败了): 第2部分:在1的基础上,替换USB HID初始化代码为ST 例程中的代码,编译后根据报错调试(失败 ...

  9. mysql 定时备份任务

    备份方案: 本地备份并同步至远程服务器,保留30天数据 1. 本地数据库备份,备份数据库gold_ecooy,naiang#!/bin/bash#xliang#Created Time: 2018-1 ...

  10. Maven中使用Jetty容器

    1.在pom.xml中添加Jetty的插件 <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId ...