📄 linux设备驱动程序学习(4)-高级字符驱动程序操作[(1)ioctl and llseek] - linux设备驱动程序 - tekkaman ninja.htm
字号:
</FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P><PRE class=programlisting></PRE>
<P>第一个参数应当是 VERIFY_READ(读)或VERIFY_WRITE(读写);addr
参数为用户空间地址,size 为字节数,可使用sizeof()。access_ok 返回一个布尔值:
1 是成功(存取没问题)和 0 是失败(存取有问题)。如果它返回假,驱动应当返回 -EFAULT
给调用者。</P></DIV>
<DT>
<DIV>
<P>注意:首先, <FONT
color=#ff0000>access_ok</FONT>不做校验内存存取的完整工作;
它只检查内存引用是否在这个进程有合理权限的内存范围中,且确保这个地址不指向内核空间内存。其次,大部分驱动代码不需要真正调用
access_ok,而直接使用<SPAN
class=term><SPAN>put_user(datum, ptr)和<SPAN
class=term><SPAN>get_user(local,
ptr),它们带有校验的功能,确保进程能够写入给定的内存地址,成功时返回 0, 并且在错误时返回
-EFAULT.。
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT
face=新宋体>put_user<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>datum<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> ptr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN>
<BR>__put_user<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>datum<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> ptr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN>
<BR>get_user<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>local<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> ptr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN>
<BR>__get_user<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>local<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> ptr<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN>
</FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></SPAN></SPAN></SPAN></SPAN>这些宏它们相对copy_to_user
和copy_from_user快,
并且这些宏已被编写来允许传递任何类型的指针,只要它是一个用户空间地址. 传送的数据大小依赖 prt
参数的类型, 并且在编译时使用 sizeof 和 typeof 等编译器内建宏确定。<FONT
color=#0000ff>他们只传送1、2、4或8
个字节。</FONT>如果使用以上函数来传送一个大小不适合的值,结果常常是一个来自编译器的奇怪消息,如"coversion
to non-scalar type requested". 在这些情况中,必须使用
copy_to_user 或者 copy_from_user。</P></DIV>
<DT>
<DIV>
<P>__put_user和<FONT face=新宋体>__get_user</FONT>
进行更少的检查(不调用 access_ok),
但是仍然能够失败如果被指向的内存对用户是不可写的,所以他们应只用在内存区已经用 access_ok
检查过的时候。作为通用的规则:当实现一个 read 方法时,调用 __put_user
来节省几个周期, 或者当你拷贝几个项时,因此,在第一次数据传送之前调用 access_ok
一次。</P></DIV>
<DT>
<DIV>
<P><FONT color=#0000ff
size=4><STRONG>权能与受限操作</STRONG></FONT></P></DIV>
<DT>
<DIV>
<P>Linux 内核提供了一个更加灵活的系统,
称为权能(capability)。内核专为许可管理上使用权能并导出了两个系统调用 capget 和
capset,这样可以从用户空间管理权能,其定义在 <FONT
color=#0000ff><linux/capability.h></FONT>
中。对设备驱动编写者有意义的权能如下:
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT
face=新宋体>CAP_DAC_OVERRIDE <SPAN
style="COLOR: rgb(255,153,0)">/*越过在文件和目录上的访问限制(数据访问控制或
DAC)的能力。*/</SPAN><BR><BR>CAP_NET_ADMIN <SPAN
style="COLOR: rgb(255,153,0)">/*进行网络管理任务的能力,
包括那些能够影响网络接口的任务*/</SPAN><BR><BR>CAP_SYS_MODULE
<SPAN
style="COLOR: rgb(255,153,0)">/*加载或去除内核模块的能力*/</SPAN><BR><BR>CAP_SYS_RAWIO
<SPAN style="COLOR: rgb(255,153,0)">/*进行
"raw"(裸)I/O 操作的能力. 例子包括存取设备端口或者直接和 USB
设备通讯*/</SPAN><BR><BR>CAP_SYS_ADMIN <SPAN
style="COLOR: rgb(255,153,0)">/*截获的能力,
提供对许多系统管理操作的途径*/</SPAN><BR><BR>CAP_SYS_TTY_CONFIG
<SPAN style="COLOR: rgb(255,153,0)">/*执行 tty
配置任务的能力*/</SPAN></FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<P>在进行一个特权操作之前, 一个设备驱动应当检查调用进程有合适的能力,检查是通过 capable
函数来进行的(定义在 <FONT
color=#0000ff><linux/sched.h></FONT> )范例如下:
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(0,0,255)">if</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,204)">!</SPAN> capable
<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>CAP_SYS_ADMIN<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><BR> <SPAN
style="COLOR: rgb(0,0,255)">return</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">-</SPAN>EPERM<SPAN
style="COLOR: rgb(0,0,204)">;</SPAN></FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<HR id=null>
<STRONG><FONT color=#0000ff
size=4>二、定位设备(llseek实现)</FONT></STRONG>
<P></P></DIV>
<DT>
<DIV>
<P>llseek是修改文件中的当前读写位置的系统调用。内核中的缺省的实现进行移位通过修改
filp->f_pos, 这是文件中的当前读写位置。对于 lseek
系统调用要正确工作,读和写方法必须通过更新它们收到的偏移量来配合。</P></DIV>
<DT>
<DIV>
<P>如果设备是不允许移位的,你不能只制止声明 llseek
操作,因为缺省的方法允许移位。应当在你的 open 方法中,通过调用
nonseekable_open 通知内核你的设备不支持 llseek :
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(0,0,255)">int</SPAN>
nonseekable_open<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN> inode
<SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>inode<SPAN
style="COLOR: rgb(0,0,204)">;</SPAN> <SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN> <SPAN
style="COLOR: rgb(255,0,0)">file</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>filp<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN>
</FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></P>
<P>完整起见, 你也应该在你的 file_operations 结构中设置 llseek
方法到一个特殊的帮助函数 no_llseek(定义在 <FONT
color=#0000ff><linux/fs.h></FONT> )。
具体的应用在试验程序中学习. </P>
<HR id=null>
<STRONG><FONT color=#0000ff
size=4>三、ioctl和llseek实验。</FONT></STRONG>
<P></P></DIV><FONT color=#ff0000 size=3><FONT
face=新宋体><FONT color=#000000
size=3>模块程序链接:</FONT></FONT></FONT><A
href="http://blogimg.chinaunix.net/blog/upfile2/071101182632.gz"
target=_blank>ioctl_and_llseek</A>
<P><FONT size=3>模块测试程序</FONT><FONT color=#ff0000
size=3><FONT face=新宋体><FONT color=#000000
size=3>链接</FONT></FONT></FONT><FONT
size=3>:</FONT><A
href="http://blogimg.chinaunix.net/blog/upfile2/071101182648.gz"
target=_blank>ioctl_and_llseek-test</A></P><FONT
size=4><SPAN
style="COLOR: rgb(0,1,255)">ARM9实验板的实验现象是:</SPAN></FONT><BR>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><SPAN
style="COLOR: rgb(0,0,204)">[</SPAN>Tekkaman2440@SBC2440V4<SPAN
style="COLOR: rgb(0,0,204)">]</SPAN><SPAN
style="COLOR: rgb(0,0,204)">#</SPAN>cd <SPAN
style="COLOR: rgb(0,0,204)">/</SPAN>lib<SPAN
style="COLOR: rgb(0,0,204)">/</SPAN>modules<SPAN
style="COLOR: rgb(0,0,204)">/</SPAN><BR><SPAN
style="COLOR: rgb(0,0,204)">[</SPAN>Tekkaman2440@SBC2440V4<SPAN
style="COLOR: rgb(0,0,204)">]</SPAN><SPAN
style="COLOR: rgb(0,0,204)">#</SPAN>insmod
scull<SPAN
style="COLOR: rgb(0,0,204)">.</SPAN>ko
scull_nr_devs<SPAN
style="COLOR: rgb(0,0,204)">=</SPAN>1<BR><SPAN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -