---------------------------
linux是个具备保护模式的操作系统。他一直工作在i386 cpu的保护模式之下。
内存被分为两个单元: 内核区域和用户区域。(译者注:我觉得还是这样叫比较顺口)内核区域存放并运行着核心代码,当然,顾名思义,用户区域也存放并运行用户程式。当然,作为用户进程来讲他是不能访问内核区域内存空间连同其他用户进程的地址空间的。
不幸地是, 核心进程也有同样的情况。核心代码也同样不能访问用户区地地址空间。 那么,这样做到底有什么意义呢?好, 我们假设当一个硬件驱动试图去写数据到一个用户内存空间的程式里的时候, 他是不能够直接去完成的, 但是他能够利用一些特别的核心函数来间接完成。同样, 当参数需要传递地址到核心函数中时,核心函数也不能直接的来读取该参数。同样的,他能够利用一些特别的核心函数来传递参数。
这里有一些比较有用的核心函数用来作为内核区和用户区相互传递参数用。
#include <asm/segment.h>
get_user(ptr)
从用户内存获取给定的字节, 字,或长整形。这只是个宏(在核心代码里面有此宏的周详定义),并且他依据参数类型来确定传输数量。所以您必须巧妙地利用他。
put_user(ptr)和get_user()很相似, 但,他不是从用户内存读取数据,而是想用户内存写数据。
memcpy_fromfs(void *to, const void *from,unsigned long n)
从用户内存中的*from拷贝n个字节到指向核心内存的指针*to。
memcpy_tofs(void *to,const *from,unsigned long n)
从核心内存中的*from拷贝n个字节数据到用户内存中的*to。
/*译者注:这四个函数足以在2.0.x中解决内核和用户区的参数传递问题,在2.0.x以上
的版本有新的实现,即copy_user_to(...)连同copy_user_from(...)根据内核版本这些
特别函数会有不同,请关注核心代码的实现方法。*/
系统调用
大部分的c函数库的调用都依赖于系统调用, 就是一些使用户程式能够调用的简单核心包装函数。 这些系统调用运行在内核本身或在可加载内核模块中, 就是一些可动态的加载卸载的核心代码。
就象MS-DOS和其他许多系统相同, linux中的系统调用依赖一个给定的中断来调用多个系统调用。linux系统中,这个中断就是int 0x80。当调用'int 0x80'中断的时候,控制权就转交给了内核(或,我们确切点地说, 交给_system_call()这个函数), 并且实际上是个正在进行的单处理过程。
* _system_call()是如何工作的 ?
首先, 任何的寄存器被保存并且陎寄存器全面检查系统调用表, 这张表列举了任何的系统调用和他们的地址信息。他能够通过extern void *sys_call_table[]来被访问到。该表中的每个定义的数值和内存地址都对应每个系统调用。大家能够在/usr/include/sys/syscall.h这个头中找到系统调用的标示数。他们对应相应的SYS_systemcall名。假如一个系统调用不存在, 那么他在sys_call_table中相应的标示就为0, 并且返回一个出错信息。否则,系统调用存在并在表里相应的入口为系统调用代码的内存地址。
这儿是个有问题的系统调用例程:
[root@plaguez kernel]# cat no1.c
#include <linux/errno.h>
#include <sys/syscall.h>
#include <errno.h>
extern void *sys_call_table[];
sc()
{ // 165这个系统调用号是不存在的。
__asm__(
"movl $165,陎
int $0x80");
}
main()
{
errno = -sc();
perror("test of invalid syscall");
}
[root@plaguez kernel]# gcc no1.c
[root@plaguez kernel]# ./a.out
test of invalid syscall: Function not implemented
[root@plaguez kernel]# exit
系统控制权就会转向真正的系统调用, 用来完成您的请求并返回。 然后_system_call()调用_ret_from_sys_call()来检查不同的返回值, 并且最后返回到用户内存。
* libc
这int $0x80 并不是直接被用作系统调用; 更确切地是, libc函数,经常用来包装0x80中断,这样使用的。
libc通常利用_syscallX()宏来描述系统调用, X是系统调用的总参数个数。
举个例子吧, libc中的write(2)就是利用_syscall3这个系统调用宏来实现的, 因为实际的write(2)原型需要3个参数。在调用0x80中断之前,这个_syscallX宏假定系统调用的堆栈结构和需要的参数列表,最后,当 _system_call()(通过int &0x80来引发)返回的时候,_syscallX()宏将会查出错误的返回值(在陎)并且为其配置errno。
让我们看一下另一个write(2)例程并看看他是如何进行预处理的。
[root@plaguez kernel]# cat no2.c
#include <linux/types.h>
#include <linux/fs.h>
#include <sys/syscall.h>
#include <asm/unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
_syscall3(ssize_t,write,int,fd,const void *,buf,size_t,count);/*构建一个write调用*/
main()
{
char *t = "this is a test.n";
write(0, t, strlen(t));
}
[root@plaguez kernel]# gcc -E no2.c > no2.C
[root@plaguez kernel]# indent no2.C -kr
indent:no2.C:3304: Warning: old style assignment ambiguity in "=-". Assuming "= -"
[root@plaguez kernel]# tail -n 50 no2.C
#9 "no2.c" 2
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




