copy_to_user,copy_from_user,get_user,put_user函数详解

字符设备驱动驱动的读,写。如:
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
等函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不能直接读写,count是
要读写的字节数,f_pos是读的位置相对于文件开头的偏移。

copy_from_user()和copy_to_user()函数的作用:由于内核空间与用户空间的内存不能直接互访,因此借助
函数copy_to_user()完成内核空间到用户空间的复制,函数copy_from_user()完成用户空间到内核空间的复制。

 源码:linux/arch/i386/lib/usercopy.c

 copy_to_user: - Copy a block of data into user space.
 @to:   Destination address, in user space.
 @from: Source address, in kernel space.
 @n:    Number of bytes to copy.
 unsigned long    copy_to_user(void __user *to, const void *from, unsigned long n)

 {

          if (access_ok(VERIFY_WRITE, to, n))

                  n = __copy_to_user(to, from, n);
          return n;
  }


 copy_from_user: - Copy a block of data from user space.
 @to:   Destination address, in kernel space.
 @from: Source address, in user space.
 @n:    Number of bytes to copy.

  unsigned long   copy_from_user(void *to, const void __user *from, unsigned long n)
  {
          if (access_ok(VERIFY_READ, from, n))
                 n = __copy_from_user(to, from, n);
          else
                  memset(to, 0, n);
         return n;
  }
 


其中access_ok(VERIFY_READ, from, n)检查当前需要访问的地址空间,有没有被其他空间访问,
如果当前进程被允许访问地址addr处的内存,函数返回真(1),否则为假(0)。

 源码定义:linux/include/asm-m32r/uaccess.h
 #define access_ok(type,addr,size) (likely(__range_ok(addr,size) == 0))
 #else
 static inline int access_ok(int type, const void *addr, unsigned long size)
 {
         extern unsigned long memory_start, memory_end;
         unsigned long val = (unsigned long)addr;
 
         return ((val >= memory_start) && ((val + size) < memory_end));
 }

  
  其中__copy_to_user(to, from, n);__copy_from_user(to, from, n);才函数实现部分。
  

源码:linux/include/asm-frv/uaccess.h
  static inline unsigned long __must_check
 __copy_to_user(void __user *to, const void *from, unsigned long n)
 {
        might_sleep();
         return __copy_to_user_inatomic(to, from, n);
 }
 #define __copy_to_user_inatomic(to, from, n)    (memcpy(____force(to), (from), (n)), 0)


 static inline unsigned long
 __copy_from_user(void *to, const void __user *from, unsigned long n)
  {
        might_sleep();
         return __copy_from_user_inatomic(to, from, n);
  }
 #define __copy_from_user_inatomic(to, from, n)  (memcpy((to), ____force(from), (n)), 0)


 函数源码定义在:linux/arch/alpha/lib/memcpy.c
 原型:extern void *memcpy(void *dest, void *src, unsigned int count);
  功能:由src所指内存区域复制count个字节到dest所指内存区域。
  说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针
  
  注:上述系列函数均返回不能复制的字节数,因此,完全复制成功,返回值0。
  且__copy_to_user()和__copy_from_user()如果直接调用的时候,不会调用
  access_ok(VERIFY_READ, from, n)检查函数,也就是不会检查当前需要访问的空间,
  有没有被其他空间访问。

而get_user(),put_user()的作用:
复制的内存是简单类型,如char,int ,long等,则可以使用简单的put_user()和get_user()
get_user --  Get a simple variable from user space.
put_user --  Write a simple value into user space.

源码定义:linux/include/asm-i386/uaccess.h
  #define put_user(x,ptr)                                         \
 ({      int __ret_pu;                                           \
          __typeof__(*(ptr)) __pu_val;                            \
          __chk_user_ptr(ptr);                                    \
          __pu_val = x;                                           \
          switch(sizeof(*(ptr))) {                                \
          case 1: __put_user_1(__pu_val, ptr); break;             \
          case 2: __put_user_2(__pu_val, ptr); break;             \
          case 4: __put_user_4(__pu_val, ptr); break;             \
         case 8: __put_user_8(__pu_val, ptr); break;             \
          default:__put_user_X(__pu_val, ptr); break;             \
          }                                                       \
          __ret_pu;                                               \
  })


 在put_user(x,ptr)定义如下:
 x:用户内存空间的整型变量
 ptr:用户空间的地址
 
 下面只对put_user()函数分析:
 

 # define __chk_user_ptr(x) (void)0   取得ptr指向的0地址,然后
 __pu_val = x;可以得到x的偏移地址,这样就可以指向x。

 #define container_of(ptr, type, member) ({                      \
          const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
          (type *)( (char *)__mptr - offsetof(type,member) );})

 和这个宏定义很类似。
 
 该代码switch()执行的代码:

   #define __put_user_1(x, ptr) __asm__ __volatile__("call __put_user_1":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr))
  #define __put_user_2(x, ptr) __asm__ __volatile__("call __put_user_2":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr))
  #define __put_user_4(x, ptr) __asm__ __volatile__("call __put_user_4":"=a" (__ret_pu):"0" ((typeof(*(ptr)))(x)), "c" (ptr))
  #define __put_user_8(x, ptr) __asm__ __volatile__("call __put_user_8":"=a" (__ret_pu):"A" ((typeof(*(ptr)))(x)), "c" (ptr))
 #define __put_user_X(x, ptr) __asm__ __volatile__("call __put_user_X":"=a" (__ret_pu):"c" (ptr))


 get_user()原理相同,请自己分析。
 
 而__get_user(),__put_user()函数与上面一样,只是不会调用
  access_ok(VERIFY_READ, from, n)检查函数,也就是不会检查当前需要访问的空间,
  有没有被其他空间访问。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面给您提供一个简单的ioctl例子,展示用户层和内核层如何使用_WO/_RO通信,不使用copy_from_usercopy_to_user函数。 首先,我们需要定义一个ioctl命令码,用于用户层和内核层之间的通信。可以在头文件中定义如下: ```c #define MY_IOCTL_CMD _IOW('k', 1, unsigned int) ``` 这个命令码表示用户层向内核层发送一个无符号整数,使用_IOW表示这个数据是从用户层到内核层传输的。 在内核层,我们需要实现一个ioctl操作函数,用于处理这个命令。具体代码如下: ```c static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int data = 0; if (cmd == MY_IOCTL_CMD) { // 从用户层获取数据 get_user(data, (unsigned int __user *)arg); // 在这里可以根据需要进行一些处理 // 将处理后的结果写回用户层 put_user(data, (unsigned int __user *)arg); } return 0; } ``` 这个函数中,我们首先定义一个无符号整数变量data,用于存储从用户层传来的数据。然后判断命令码是否为我们定义的MY_IOCTL_CMD,如果是,就使用get_user函数获取用户层传来的数据,并进行一些处理。最后,使用put_user函数将处理后的结果写回到用户层。 在用户层,我们可以使用ioctl函数来发送命令码,并获取内核层处理后的结果。具体代码如下: ```c int main() { int fd; unsigned int data = 1234; // 打开设备文件 fd = open("/dev/my_device", O_RDWR); // 发送命令码并获取处理后的结果 ioctl(fd, MY_IOCTL_CMD, &data); // 关闭设备文件 close(fd); return 0; } ``` 这个代码中,我们首先打开设备文件,然后定义一个无符号整数变量data,给它赋初值1234。接着,使用ioctl函数发送命令码,并将data的地址传递给ioctl函数,以便内核层将处理结果写回到data中。最后,关闭设备文件,结束程序。 需要注意的是,这个例子中我们使用了_WO/_RO标志,这意味着我们只需要使用put_user和get_user函数即可完成数据的读写操作,而不需要使用copy_from_usercopy_to_user函数,这样可以提高程序的性能。 希望这个例子能够帮助您更好地理解ioctl的使用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值