long new_read(struct inode *inode, struct file *filp, char *buf,
unsigned long count);
{
/* identify your data (device-specific code) */
if (__copy_to_user(buf, new_data, count))
return -EFAULT;
return count;
}
注意使用不进行检查的__copy_to_user是因为调用者在把数据传输分派到文件操作之前已经检查了用户空间。这就象2.0,read和write不需要调用verify_area。类似地,典型的ioctl实现看起来如下:
int new_ioctl(struct inode *inode, struct file *filp, unsigned int
cmd, unsigned long arg);
{
/* device-specific checks, if needed */
switch(cmd){
case NEW_GETVALUE:
put_user_ret(new_value, (int *)arg, -EFAULT);
break;
case NEW_SETVALUE:
get_user_ret(new_value, (int *)arg, -EFAULT);
default:
return –EINVAL;
}
return 0;
}
版本2.0的对应者不同的是,这个函数在switch语句之前并不需要检查参数,因为每个get_user或put_user会进行检查。另一方面,当你想写可以同时在2.0和2.1上编译的代码时,问题变得稍微复杂一些,因为在老的核心上,你不能用C预处理器伪装新的行为。你不能简单地#define一个接收两个参数的get_user宏,因为实际的get_user实现在2.0中已经是个宏。我在写既可移植有高效率的代码的选择是设置sysdep-2.1.h以提供具有下列函数的源码。下面只列出了读取数据的函数;写数据的函数行为完全一样。
int access_ok(type, address, size);
当在2.0上编译时,这个函数以verify_area的名义实现。int verify_area_20(type, address, size);通常,当为Linux2.1写代码时,你不需调用access_ok。另一方面,当在Linux2.0上编译时,必须调用verify_area。这个函数就是要填平这个不同:当为Linux2.1编译时,它扩展为空;而为2.0编译时,它扩展为原来的verify_area。这个函数不能被称做verify_area,因为2.1已经有一个宏叫这个名字了。在2.1中定义的verify_area宏实现了access_ok的老的语义,它的存在是为了简化源码从2.0到2.1的转换。(从理论上说,你可以在你的模块中留下verify_area,只是将函数名改一下;这种简单移植技巧的缺点是新版本不能在2.0上编译。)
int GET_USER(var, add);
int __GET_USER(var, add);
GET_USER_RET(var, add, ret);
当在2.1上编译时,这些宏扩展为实际的get_user函数,即上面解释过的那些。当在2.0上编译时,get_user的2.0实现被用来实现与2.1中同样的功能。
int copy_from_user(to, from, size);
int copy_from_user(to, from, size);
int __copy_from_user(to, from, size);
copy_from_user_ret(to, from, size);
当在2.0上编译时,这些扩展为memcpy_fromfs;而在2.1上,则使用本身的函数。_ret一类在2.0上从不会返回,因为复制函数不会失败。
我个人比较喜欢这种实现兼容性的方法,但这并不是唯一的方法。在我的示例代码中,任何用户空间的访问(除了用来read或write的缓冲区,它们已经事先检查过了)之前,verify_area_20必须被调用。另一种方法更加忠实于2.1的语义,即当用2.0时,在每个get_user 和copy_from_user之前自动生成一个verify_area。这个选择在源码级要更清晰一些,但在版本2.0上编译时效率相当低,包括代码大小和执行时间。可以同时在2.0和2.1上编译的示例代码,如scull模块,可以在目录v2.1/scull中找到。我不觉得这个代码足够有趣,因此不在这里给出。
任务队列
从2.1.30开始的Linux版本不再定义函数queue_task_irq和queue_task_irq_off,因为在queue_task上的实际加速不值得花精力维护两个独立的函数。当新机制被加到核心时,这就变得明显了。在源码级,这是2.0和2.1之间唯一的区别;头文件定义了消失的函数简化了从2.0移植驱动程序。感兴趣的读者可以查看以获得更多的细节。
中断管理
在2.1的开发中,有些Linux内部被修改了。新核心提供了对内部锁的很好的管理;通过使用几个细粒度的锁,而不是全局的锁,竞争条件被避免了,这样也获得了更好的性能----特别是SMP配置下。更细的锁机制的一个结果是intr_count不再存在了。2.1.34抛弃了这个全局变量,而布尔函数in_interrupt可以取而代之(这个函数从2.1.30开始存在)。目前,in_interrupt是在头文件中声明的宏,这个头文件又包含在中。头文件sysdep-2.1.h用intr_count的名义定义了in_interrupt以获得对2.0的向后兼容性。
注意虽然in_interrupt是个整数,intr_count却是个unsigned long,因此,如果你想打印这个值,并在2.0和2.1间可移植,你必须强制将这个值转换为一个显式的类型,并在调用printk时指定一个合适的格式。在2.1.37中中断管理又引入了一个不同:快和慢中断处理程序不再存在了。SA_INTERRUPT不被新版本的request_irq使用,但它在处理程序执行以前仍然控制着中断是否被打开。如果几个处理程序共享一个中断线,每个可以是个不同的“类型”。中断打开与否依赖于第一个被调用的处理程序。当中断处理程序存在时,下半部总是执行。
位操作
2.1.37稍微改变了在中定义的位操作的作用。现在函数set_bit及其相关者返回void,而新的类似test_and_set_bit的函数已被引入。新的函数集有如下原型:
void set_bit(int nr, volatile void * addr);
void clear_bit(int nr, volatile void * addr);
void change_bit(int nr, volatile void * addr);
int test_and_set_bit(int nr, volatile void * addr);
int test_and_clear_bit(int nr, volatile void * addr);
int test_and_change_bit(int nr, volatile void * addr);
int test_bit(nr, addr);
如果你想获得与2.0的后向兼容性,你可以在你的模块中包含sysdep-2.1.h,并使用新的原型。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




