在内核代码中经常看到下面的用法:
/**
* copy_to_user_nofault(): safely attempt to write to a user-space location
* @dst: address to write to
* @src: pointer to the data that shall be written
* @size: size of the data chunk
*
* Safely write to address @dst from the buffer at @src. If a kernel fault
* happens, handle that and return -EFAULT.
*/
long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
{
long ret = -EFAULT;
if (access_ok(dst, size)) {
pagefault_disable();
ret = __copy_to_user_inatomic(dst, src, size);
pagefault_enable();
}
if (ret)
return -EFAULT;
return 0;
}
上面的pagefault_disable的实现如下:
/*
* These routines enable/disable the pagefault handler. If disabled, it will
* not take any locks and go straight to the fixup table.
*
* User access methods will not sleep when called from a pagefault_disabled()
* environment.
*/
static inline void pagefault_disable(void)
{
pagefault_disabled_inc();
/*
* make sure to have issued the store before a pagefault
* can hit.
*/
barrier();
}
static inline void pagefault_enable(void)
{
/*
* make sure to issue those last loads/stores before enabling
* the pagefault handler again.
*/
barrier();
pagefault_disabled_dec();
}
根据注释,如果缺页被关闭,当发生缺页后,在缺页处理程序中会直接利用fixup表来修复,
并不会去处理缺页。所以__copy_to_user_inatomic会返回实际要拷贝的数size。
下面是一个测试程序:
- 驱动
点击查看代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
static long mem_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
char buf[64] = {0};
int ret;
pagefault_disable();
ret = copy_from_user(buf, (void *)arg, sizeof(buf));
printk("pagefault_disable: arg: 0x%lx ret: %d, buf: %s\n", arg, ret, buf);
pagefault_enable();
ret = copy_from_user(buf, (void *)arg, sizeof(buf));
printk("pagefault_enable: arg: 0x%lx ret: %d, buf: %s\n", arg, ret, buf);
return 0;
}
static struct file_operations memcpy_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = mem_ioctl,
.compat_ioctl = mem_ioctl,
};
static struct miscdevice mem_miscdev = {
.fops = &memcpy_fops,
.name = "test_memcpy",
.minor = MISC_DYNAMIC_MINOR,
};
static int __init test_memcpy_init(void)
{
int ret = 0;
ret = misc_register(&mem_miscdev);
if (ret)
pr_err("misc register failed: %d\n", ret);
return ret;
}
static void __exit test_memcpy_exit(void)
{
misc_deregister(&mem_miscdev);
}
module_init(test_memcpy_init);
module_exit(test_memcpy_exit);
MODULE_LICENSE("GPL");
- 应用
点击查看代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd, fd1;
char *buf;
int count = 0;
fd = open("/dev/test_memcpy", O_RDWR);
if (fd < 0) {
perror("open failed");
exit(-1);
}
fd1 = open("./test.bin", O_RDWR);
if (fd1 < 0) {
perror("open failed");
exit(-1);
}
restart:
system("vmtouch -ve ./test.bin");
buf = mmap(NULL, 0x800000, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);
if (buf == MAP_FAILED)
perror("map failed\n");
ioctl(fd, 0x5a5a5a5a, buf);
munmap(buf, 0x800000);
count++;
if (count < 2)
goto restart;
close(fd1);
close(fd);
return 0;
}
- 测试结果
# /mnt/test
Evicting ./test.bin
Files: 1
Directories: 0
Evicted Pages: 4096 (16M)
Elapsed: 0.001014 seconds
Evicting ./test.bin
Files: 1
Directories: 0
Evicted Pages: 4096 (16M)
Elapsed: 0.001705 seconds
内核日志:
[199215.187377] pagefault_disable: arg: 0x7fd349400000 ret: 64, buf:
[199215.188557] pagefault_enable: arg: 0x7fd349400000 ret: 0, buf: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
[199215.219537] pagefault_disable: arg: 0x7fd349400000 ret: 64, buf:
[199215.220701] pagefault_enable: arg: 0x7fd349400000 ret: 0, buf: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ