Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1911178
  • 博文数量: 383
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 4061
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-24 18:53
文章分类

全部博文(383)

文章存档

2011年(1)

2010年(9)

2009年(276)

2008年(97)

我的朋友

分类: LINUX

2008-11-30 14:23:22

、系统调用的一些基础知识

1)基本概念

系统调用是操作系统为程序设计人员提供的接口服务,是进入系统空间的一种方法。使用系统调用可以充分利用计算机资源,使编写的程序更加灵活,功能更加强大。

不同的操作系统有各自的系统调用方法。如Windows API,便是Windows的系统调用。Linux的系统调用与之不同在于Linux内核代码完全公开,所以可以细致的分析其系统调用的机制。

2)系统调用和普通函数的区别

u      运行于不同的系统状态

用户程序可以通过系统调用进入系统空间,在核心态执行;而普通程序只能在用户空间当中运行。

u      通过软中断切换

由于用户程序使用系统调用后要进入系统空间,所以需要调用一个软中断;而普通程序在被调用过程时没有这个过程。

3)系统调用的类型

u      进程控制类

u      文件操作类

u      进程通信类

u      信息维护类

4)系统调用的实现机制

Linux实现其系统调用的步骤分为以下两步:

STEP1:设置系统调用号

在系统当中,往往设置多条系统调用命令,并赋予每条系统调用命令一个唯一的系统调用号。

STEP2:处理系统调用

操作系统有一张系统调用入口表。表中的每个表目都对应一条系统调用命令,它包含该系统调用自带参数的数目、系统调用命令处理程序的入口地址等等。操作系统就是根据所输入的系统调用号在该表中查找到相应的系统调用,进而转入它的入口地址去执行系统调用程序。

 

、编写一个程序来调用现有的系统调用

本节将编写一个简单程序来调用一个系统调用fork( ),此程序可以很清楚的显示出fork( )系统调用生成了子进程,从而产生的分叉作用:

#include

void main()

{

int iUid;

iUid=fork();

if(iUid==0)

for(;;)

{

printf("This is parent.\n");

sleep(1);

}

if(iUid>0)

for(;;)

{

printf("This is child.\n");

sleep(1);

}

if(iUid<0)

printf("Can not use system call.\n");

 

}

 

下面是可能得到的一种结果:

This is child.

This is parent.

This is child.

This is parent.

This is parent.

This is child.

This is child.

 

、创建自己的系统调用

1)添加源代码

第一个任务是编写添加到内核中的源程序,即添加到内核文件中的一个函数。该函数的名称应该在新的系统调用名称之前加上sys_标志。假设新加的系统调用为foo( ),功能为原值返回输入的整型数。在/usr/src/linux/kernel/sys.c文件中添加源代码,如下所示:

asmlinkage int sys_foo(int x)

{

printf("%d\n",x);

}

2)分配系统调用号

进入目录/usr/src/linux/include/asm-i386/,打开文件unistd.h。这个文件包含了系统调用的清单,用来给每个系统调用分配一个唯一的号码。系统调用号的定义格式如下:

# define __NR _name  NNN

其中,name以系统调用名称代替,而NNN是该系统调用对应的号码。假如调用号已经用到了257,则应将新的系统调用名称放到清单的最后,并分配给系统调用号258

# define __NR _foo  258

3)连接新的系统调用

进入目录/usr/src/linux/arch/i386/kernel/,打开文件entry.S。该文件中有类似下面的清单:

ENTRY(sys_call_table)

.long SYSMBOL_NAME(sys_ni_syscall)

.long SYSMBOL_NAME(sys_exit)

.long SYSMBOL_NAME(sys_fork)

在该表的最后加上:

.long SYSMBOL_NAME(sys_foo)

4)重新编译内核

# make menuconfig             // 配置新内核

# make dep                   // 创建新内核

# make modules_install       // 加入模块

# make clean                // 清除多余创建的文件

# make bzImage             // 生成可执行内核引导文件

5)使用新编译的内核

# cp a /usr/src/linux-2.4.2/arch/i386/boot/bzImage /boot

6)重新配置/etc/lino.conf文件

使用vi编辑器编辑/etc/lino.conf文件

vi /etc/lilo.conf

在其中加入如下几行:

image=/boot/bzImage  #启动内核的位置, 即自己新配置的内核所在目录

label=xhlinux       #给内核起一个名称, 配置完成。

read_only         #定义新的内核为只读

root=/dev/hda5  #定义硬盘的启动位置是/dev/hda5,在该设计中没有变

7)重启系统

完成以上配置后,重新启动系统进入自己的新系统。

8)编写一个程序来测试新添加的系统调用

/*******test.c******/

#include

_syscall1(int,foo,int,ret)

int main()

{

 int a,b;

 a=100;

 b=0;

 b=foo(a);

 printf("a=%d\n",a);

 printf("b=%d\n",b);

 return 0;

}

程序编译:

     gcc o I /usr/src/linux-2.4.20-8/include test.c

注意:由于要引入内核头文件unistd.h,不能简单地用普通编译命令,而应该安上面那样设置参数。

9)运行测试程序:

    # ./test

   解释:当函数还没有定义在内核中时,如果运行以上程序,系统将显示一个未定义的值-1;而在定义了以后运行该程序,系统将显示100,说明我们的内核添加系统调用已经成功!
阅读(1053) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~