在應用程式看來,字元裝置隻是一個裝置檔案,應用程式可以像操作普通檔案一樣對硬體裝置進行操作。應用層對裝置的操作都在裝置驅動程式的file_operations結構中有對應的接口,比如應用層的read函數對應驅動層的file_operations-> read,而應用層的write函數對應驅動層的file_operations-> write。本節介紹字元裝置核心空間與使用者空間資料互動的方法。
先看file_operations中的讀寫接口:
- ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
- ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
它們的第二個參數實際上是使用者空間的資料位址。由于核心态和使用者态使用不同的記憶體定義,是以二者之間不能直接通路對方的記憶體,而應該使用Linux中的使用者和核心态記憶體互動函數,這些函數在include/asm/uaccess.h中聲明。
從核心空間複制資料到使用者空間使用copy_to_user函數:
- unsigned long copy_to_user (void __user * to, const void * from, unsigned long n);
而要從使用者空間複制資料到核心空間可以用copy_from_user函數:
- unsigned long copy_from_user (void * to, const void* from, unsigned long n);
此外核心空間和使用者空間之間也可進行單值互動(如char、int、long類型):
- int put_user(dataum,ptr); //向使用者空間傳單值
- int get_user(local,ptr); //向核心空間傳單值
當一個指針指向使用者空間時,必須確定指向的使用者位址是合法的,而且對應的頁面也已經映射,這一點可以使用access_ok函數檢測。access_ok函數的type參數有兩個選項:VERIFY_READ、VERIFY_ WRITE,分别對應記憶體讀、寫。
- int access_ok(int type, const void *addr, unsigned long size);
在通路使用者空間的記憶體時,可以使用下面的方法先檢查使用者空間的指針是否合法:
- char kernelbuffer[100];
- static ssize_t demo_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
- {
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- if (copy_to_user(buffer, kernelbuffer, count))
- return -EFAULT;
- return count;
- }