Linux 用户和内核之间的数据传递

Last edited
Last updated July 5, 2023
Pages
Tags

1. 概述

用户空间和内核空间无法直接通过指针或者地址的形式互相访问数据,需要借助特定的函数来实现用户空间和内核空间的数据传递

2. API

2.1 内核空间到用户空间

#include <linux/uaccess.h> unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
功能:实现内核数据传到到用户空间
参数:
  • to :用户空间存放数据的 buf 首地址
  • from :内核空间存放数据的 buf 首地址
  • n :要传递的数据的长度
返回值:成功返回 0,失败返回未传递的字节个数

2.2 用户空间到内核空间

#include <linux/uaccess.h> unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
功能:实现用户空间数据传递到内核空间
参数:
  • to: 内核空间存放数据的buf首地址
  • from: 用户空间存放数据的buf首地址
  • n: 要传递的数据长度

3. 实例

3.1 测试程序

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> int main(int argc, char const *argv[]) { char buf[128] = { 0 }; int fd = open("/dev/mychrdev", O_RDWR); if (fd < 0) { printf("打开设备文件失败\n"); exit(-1); } fgets(buf, sizeof(buf), stdin); // 在终端读取 buf[strlen(buf) - 1] = '\0'; // 吃掉末尾的'\n' write(fd, buf, sizeof(buf)); memset(buf, 0, sizeof(buf)); // 清空buf read(fd, buf, sizeof(buf)); printf("buf:%s\n", buf); close(fd); return 0; }

3.2 驱动程序

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> int major; // 用于保存主设备号 char kbuf[128] = { 0 }; // 封装操作方法 int mycdev_open(struct inode *inode, struct file *file) { printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); return 0; } ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof) { int ret; if (sizeof(kbuf) < size) size = sizeof(kbuf); ret = copy_to_user(ubuf, kbuf, size); if (ret) { printk("copy_to_user filed\n"); return -EIO; } printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); return 0; } ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof) { int ret; if (sizeof(kbuf) < size) // 判断用户空间的需求是否能被驱动满足,满足不了就给能给的最好的 size = sizeof(kbuf); ret = copy_from_user(kbuf, ubuf, size); if (ret) { printk("copy_from_user filed\n"); return -EIO; // IO 错误,定义于 errno-base.h } printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); return 0; } int mycdev_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\n", __FILE__, __func__, __LINE__); return 0; } // 定义操作方法结构体变量并完成初始化 struct file_operations fops = { .open = mycdev_open, .read = mycdev_read, .write = mycdev_write, .release = mycdev_close, }; static int __init mycdev_init(void) { //字符设备驱动注册 major = register_chrdev(0, "mychrdev", &fops); if (major < 0) { printk("字符设备驱动注册失败\n"); return major; } printk("字符设备驱动注册成功major=%d\n", major); return 0; } static void __exit mycdev_exit(void) { //字符设备驱动的注销 unregister_chrdev(major, "mychrdev"); } module_init(mycdev_init); module_exit(mycdev_exit); MODULE_LICENSE("GPL");

3.3 编译运行

$ make arch=x86 modname=mychrde $ gcc test.c $ sudo insmod mychrdev.k $ sudo dmes [56410.468754] 字符设备驱动注册成功major=23 $ sudo mknod /dev/mychrdev c 238 $ sudo ./a.ou hello buf:hell $ sudo dmesg [56410.468754] 字符设备驱动注册成功major=238 [56446.777310] /mychrdev2/mychrdev.c:mycdev_open:11 [56449.953402] /mychrdev2/mychrdev.c:mycdev_write:41 [56449.953415] /mychrdev2/mychrdev.c:mycdev_read:25 [56449.953581] /mychrdev2/mychrdev.c:mycdev_close:47