11、do_mmap()/do_ummap()' F9 K8 U: Q: m5 R
8 l. F% f( K" J' t% e' ` 内核使用do_mmap()函数为进程创建一个新的线性地址区间。但是说该函数创建了一个新VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况, do_mmap()函数都会将一个地址区间加入到进程的地址空间中--无论是扩展已存在的内存区域还是创建一个新的区域。. O5 B! ]% F5 R# _% z
6 p/ k* H: Z. h 同样,释放一个内存区域应使用函数do_ummap(),它会销毁对应的内存区域。+ @0 ], b, B/ y/ n9 Z) i
" v% D' b& a6 \, ^; a 12、get_user_pages()4 n% g" E3 R; V4 S$ N" p
, C7 ~* p2 S8 O4 c
作用是在内核空间获取用户空间内存的page 描述,之后可以通过函数kmap() 获取page 对应到内核的虚拟地址。 n7 W4 g$ z5 s- [
; U( E6 y) y/ N% t
int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
# ] n. {# y6 ^; {6 w6 K: ?9 u , M# y& k- y" @
unsigned long start, int len, int write, int force,5 {4 R# i" W9 N- d! w7 R5 t
! u; \ m7 u. Z* y. k3 ]: t
struct page **pages, struct vm_area_struct **vmas)" ~9 p4 T/ Y% k t T' l1 f( f
1 x1 I) o9 X# t; M2 D 参数说明
% h9 ?1 P: E1 a0 a8 V ( ^; f9 V7 e4 H* m" d% @+ j0 S+ C
参数tsk:指示用户空间对应进程的task_struct数据结构。只是为了记录错误信息用,该参数可以为空。9 j/ O6 ?1 n6 o; `/ w
/ e1 h. u" i" k4 J 参数mm:从该mm struct中获取start 指示的若干页面。# \- D: [( @- ~, a# |
2 U0 f# C) Z: G s 参数start:参数mm空间的起始地址,即用户空间的虚拟地址。
9 i0 K& C$ h( O; g4 K/ `6 ^ * Y- R9 }" m% C; e" W
参数len:需要映射的页数。- ?% ~" h/ ?$ g0 F" H& ?
8 D$ ? N0 X0 b
参数write:可以写标志。
) a5 b6 `; ], z" Z
& K x4 v. f! ^* B 参数force:强制可以写标志。
7 o9 z. Z. G& g% E
3 t/ D+ u6 O0 \$ V 参数pages:输出的页数据结构。/ o6 m5 y/ _, D: F. R, m+ R) ~( z
2 |3 H6 p# m) j6 Z# y v4 p4 v V+ W
参数vmas:对应的需要存储区,(没有看明白对应的代码)
4 X5 a6 t+ Q: g+ I. n" W' y
1 `' U0 u) O6 U7 n$ y 返回值:数返回实际获取的页数,貌似对每个实际获取的页都是给页计数值增1,如果实际获取的页不等于请求的页,要放弃操作则必须对已获取的页计数值减1.7 T! ]( ^ ?! r5 }+ n! {9 A
* x) a" F+ T4 Q+ o; F; k 13、copy_from_user()和copy_to_user()
' |7 i2 e. q( u- f
' y4 S9 y: [* `. T 主要应用于设备驱动中读写函数中,通过系统调用触发,在当前进程上下文内核态运行(即当前进程通过系统调用触发)。% ]" i4 u$ {: e
, W ~# u* N( y+ s6 N" Q* p copy_from_user的目的是防止用户程序欺骗内核,将一个非法的地址传进去,如果没有它,这一非法地址就检测不到,内和就会访问这个地址指向的数据。因为在内核中访问任何地址都没有保护,如果不幸访问一个错误的内存地址会搞死内核或发生更严重的问题) e; |; c' g1 ?- C4 k9 Z
% a( A( A: s( g& K7 [# q( j! N copy_from_user调用了access_ok,所以才有"自己判断功能"
# M }- i& {. A2 V+ l& L" H9 _9 [ % y% m: s4 \( A# }
access_ok(),可以检查访问的空间是否合法。8 |6 j2 N# f+ p+ r7 K3 R- ~
" J8 f4 ~8 K! s 注意:中断代码时不能用copy_from_user,因为其调用了might_sleep()函数,会导致睡眠。& I9 J" T+ Y, Y3 Z$ b3 d
" I% ?7 D6 I" J" D* p# a
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
- @, ] Y) Y) }2 ^( {& K # A+ f/ Z8 q5 \1 t# Z
通常用在设备读函数或ioctl 中获取参数的函数中:其中"to"是用户空间的buffer地址,在本函数中将内核buffer"from"除的n个字节拷贝到用户空间的"to"buffer.
# M9 t6 T! B5 g' Q) m8 w, h: ~" j
( P! C6 A, J e" V unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
7 _* O, r. F, q! [6 N ( S \ a/ ?& ~2 P$ A
通常用在设备写函数或ioctl中设置参数的函数中:"to"是内核空间的buffer指针,要写入的buffer;"from"是用户空间的指针,数据源buffer.5 E5 x; z" Z9 `
3 A: s5 |, O7 i, J9 z; z; M! y
14、get_user(x, ptr)
$ a: X0 ]. F; ^7 h4 j4 c+ l
" T: {/ q5 w( d2 P4 W 本函数的作用是获取用户空间指定地址的数值并保存到内核变量x中,ptr为用户空间的地址。用法举例如下。
) P+ d. ^, r5 o7 c
# F8 m& |- c% c+ q get_user(val, (int __user *)arg)" C( L. J4 }6 F3 W7 T$ v# e
: x, }6 u6 `. m6 x
注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。4 r# \9 x, t) r+ \* j# K+ `% y! j
+ p$ N2 A r5 `, l
15、put_user(x, ptr)# g7 @, y a0 W3 V& q9 ?2 v8 l* C
0 U; E6 |% f8 Z* b) i 本函数的作用是将内核空间的变量x的数值保存到用户空间指定地址处,prt为用户空间地址。用法举例如下。" z L- H2 u3 h
/ B, n- l, o' t( @# Z! k: t
put_user(val, (int __user *)arg)
6 Q( x. e8 x& l, c , n2 O+ r& Y+ Y5 n; J3 ^! j q
注明:函数用户进程上下文内核态,即通常在系统调用函数中使用该函数。 |