Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15531154
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2007-06-30 21:32:07

简析linux系统调用

  在i386系统中,每个系统调用都是通过一个单一的入口点多路传入内核。eax寄存器用来标识应当调用的某个系统调用,这在C库中做了指定(来自用户空间应用程序的每个调用)。当加载了系统的C库调用索引和参数时,就会调用一个软件中断(0x80中断),他将执行system_call函数(通过中断处理程序),这个函数会按照eax内容中的标识处理所有的系统调用。内核使用system_call_table和eax中包含的索引来执行真正的系统调用。从系统调用中返回后,最终执行syscall_exit,并调用resume_userspace返回用户空间。然后继续在C库中执行,它将返回到用户程序中。要添加一个新的系统调用,需要执行三个步骤:
1 添加新函数。即更改源文件,添加新的内核函数。
2 更新头文件。典型的是文件unistd.h。在此中主要是将系统调用编号和函数本身建立一种对应关系。如:

#define __NR_getjiffies 320
这里的__NR是符号常量,在在 /usr/include/asm/unistd.h 中定义。
3 根据新函数更新系统调用表。即文件:linux/arch/i386/kernel/syscall_table.S 文件
对新的系统调用的使用有两种方法:1 使用syscall系统调用。2 使用_syscall0~_syscall6宏。
对第一种方法,例子为:
#include <linux/unistd.h>
#include <sys/syscall.h>
#define __NR_getjiffies 320
int main()
{
   long jiffies;
   jiffies = syscall( __NR_getjiffies );
   printf( "Current jiffies is %lx\n", jiffies );
   return 0;
}
对第二种方法,例子为:
#include <stdio.h>
#include <linux/unistd.h>
#include <sys/syscall.h>

#define __NR_getjiffies 320
#define __NR_diffjiffies 321
#define __NR_pdiffjiffies 322

_syscall0( long, getjiffies );
_syscall1( long, diffjiffies, long, ujiffies );
_syscall2( long, pdiffjiffies, long, ujiffies, long*, presult );

int main()
{
   long jifs, result;
   int err;
   jifs = getjiffies();
   printf( "difference is %lx\n", diffjiffies(jifs) );
   err = pdiffjiffies( jifs, &result );
   if (!err) {
     printf( "difference is %lx\n", result );
   } else {
     printf( "error\n" );
   }
   return 0;
}
_syscall 宏的参数格式为_syscall2(return-type,func-name,arg1-type,arg1-name,arg2-type,arg2-name)。它最多有6个参数。

linux提供了几个函数用来将系统调用参数移动到用户空间中,或从中移出。例如:
int access_ok(type,,address,size);用来验证给定操作的用户空间指针。其中type取值为:VERIFY_READ或VERIFY_WRITE。如果调用成功就返回0。
int get_user( var, ptr );
int put_user( var, ptr );用来在用户和内核空间移动一些简单类型(int 或long)。
unsigned long copy_from_user( void *to, const void __user *from, unsigned long n );
unsigned long copy_to_user( void *to, const void __user *from, unsigned long n );
这些用来在用户和内核空间中移动较大的对象,如数组或结构。
long strncpy_from_user( char *dst, const char __user *src, long count );
strlen_user( str );
我们可以可以使用 strncpy_from_user 函数将一个以 NULL 结尾的字符串从用户空间移动到内核空间中。
在调用这个函数之前,您可以通过调用 strlen_user 宏来获得用户空间字符串的大小。
有关内核的一些别的概念:
内核 jiffies
Linux 内核具有一个名为 jiffies 的全局变量,它代表从机器启动时算起的时间滴答数。这个变量最初被初始化为 0,每次时钟中断时都会加 1。您可以使用get_jiffies_64 函数来读取 jiffies 的值,然后使用jiffies_to_msecs 将其换算成毫秒或使用 jiffies_to_usecs 将其换算成微秒。jiffies 的全局定义和相关函数是在 ./linux/include/linux/jiffies.h 中提供的。系统调用多路分解有些系统调用会由内核进一步进行多路分解。例如,BSD(Berkeley Software Distribution)socket 调用(socket、bind、 connect 等)都与一个单独的系统调用索引(__NR_socketcall)关联在一起,不过在内核中会进行多路分解,通过另外一个参数进入适当的调用。请参看 ./linux/net/socket.c 中的 sys_socketcall 函数。

阅读(1564) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~