【學習筆記】node
在Linux中,文件對應的操做有:打開、關閉、讀寫,一樣與文件相似,設備節點對應的操做有:打開、關閉、讀寫linux
若是咱們在應用層使用系統IO對設備節點進行打開、關閉、讀寫等操做會發生什麼?app
【注】:
下面這些函數都定義在linux內核文件夾,好比:linux-4.9.268/include/linux/fs.h中的 struct file_operations {};結構體中
ide
(1)當咱們在應用層對設備節點進行read操做時,就會觸發驅動裏邊的read這個函數。函數
ssize_t(*read)(struct file*, char __user*, size_t, loff_t*);#這裏(*read)是函數名字,能夠自定義,下面的都是如此
(2)當咱們在應用層對設備節點進行write操做時,就會觸發驅動裏邊的write這個函數。學習
ssize_t(*write)(struct file*, const char __user*, size_t, loff_t*);
(3)當咱們在應用層對設備節點進行poll/select操做時,就會觸發驅動裏邊的poll這個函數。code
unsigned_t(*poll)(struct file*, struct poll_table_struct*);
(4)當咱們在應用層對設備節點進行ioctl操做時,就會觸發驅動裏邊的ioctl這個函數。blog
long(*unlocked_ioctl)(struct file*, unsigned int, unsigned long);
(5)當咱們在應用層對設備節點進行open操做時,就會觸發驅動裏邊的open這個函數。字符串
int(*open)(struct inode*, struct file*);
(6)當咱們在應用層對設備節點進行close操做時,就會觸發驅動裏邊的release這個函數。get
int(*release)(struct inode*, struct file*);
【注意】read和write參數中的__user*前面是兩個下劃線,寫成一個編譯時會報錯
驅動層:驅動file_operations.c
#include <linux/init.h> #include <linux/module.h> //雜項設備驅動須要增長兩個頭文件 #include <linux/miscdevice.h> #include <linux/fs.h> int misc_open(struct inode *inode, struct file *file){//(*open)函數實現 printk("hello misc_open\n"); return 0; } int misc_release(struct inode *inode, struct file *file){//(*release)函數實現 printk("bye bye\n"); return 0; } ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){ printk("hello read\n"); return 0; } ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){ printk("hello write\n"); return 0; } //第2步:填充文件操做集 struct file_operations misc_fops = { .owner = THIS_MODULE, //這裏簡單的填充一個owner .open = misc_open, //根據咱們自定義的函數名來填充 .release = misc_release, .read = misc_read, .write = misc_write }; //第1步:填充雜項設備結構體 struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, //次設備號,動態分配 .name = "hello_misc", //設備節點的名字 .fops = &misc_fops //填充文件操做集 }; //第3步;註冊到內核 static int misc_init(void){ int ret; ret = misc_register(&misc_dev);//存儲註冊的地址 //判斷是否註冊成功 if(ret < 0){ printk("misc registe is error\n"); return -1; } printk("misc registe is successful\n"); //內核裏不能使用c語言庫,因此不能用printf return 0; } //卸載驅動 static void misc_exit(void){ misc_deregister(&misc_dev); printk("misc bye bye\n"); } //入口和出口 module_init(misc_init); module_exit(misc_exit); //聲明許可證 MODULE_LICENSE("GPL");
應用層:app.c上層函數
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]){//若是打開設備節點成功,這會調用驅動裏邊的misc_open()函數 int fd; char buf[64] = {0}; fd = open("/dev/hello_misc",O_RDWR);//open the device node if(fd < 0){ //determine whether the opening is successful perror("open error\n");//和printf用法類似 return fd; } read(fd,buf,sizeof(buf));//讀取設備節點 //write(fd,buf,sizeof(buf));//寫設備節點 close(fd);//關閉節點 return 0; }
以下圖,設備節點是內核層和應用層的橋樑
若是驅動文件中相應的操做集沒有填充,而應用層又調用了對應的函數,那麼將什麼都不會發生,也不會報錯。
應用層和數據層不能直接進行數據傳輸,須要使用其餘函數
下面兩個函數定義在linux-4.9.268/include/linux/uaccess.h中
//用戶層向內核層傳輸數據 static inline long copy_from_user(void *to, const void _user *from, unsigned long n); //內核層向用戶層傳輸數據 static inline long copy_to_user(void *to, const void *from, unsigned long n);
注意這兩個函數只能在驅動裏邊用,不能在應用層使用。
file_operation.c
#include <linux/init.h> #include <linux/module.h> //雜項設備驅動須要增長兩個頭文件 #include <linux/miscdevice.h> #include <linux/fs.h> //增長傳輸函數所在的頭文件 #include <linux/uaccess.h> int misc_open(struct inode *inode, struct file *file){//(*open)函數實現 printk("hello misc_open\n"); return 0; } int misc_release(struct inode *inode, struct file *file){//(*release)函數實現 printk("bye bye\n"); return 0; } ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){ char kbuf[64] = "mydate";//定義字符串,可以在應用層讀取到 if(copy_to_user(ubuf, kbuf, strlen(kbuf)) != 0){//判斷是否成功嚮應用層傳輸數據 printk("copy_to_user error\n"); return -1; } return 0; } ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){ char kbuf[64] = {0}; if(copy_from_user(kbuf, ubuf, size) != 0){//判斷是否成功嚮應用層傳輸數據 printk("copy_from_user error\n"); return -1; } printk("kbuf is %s\n",kbuf); return 0; } //第2步:填充文件操做集 struct file_operations misc_fops = { .owner = THIS_MODULE, //這裏簡單的填充一個owner .open = misc_open, //根據咱們自定義的函數名來填充 .release = misc_release, .read = misc_read, .write = misc_write }; //第1步:填充雜項設備結構體 struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, //次設備號,動態分配 .name = "hello_misc", //設備節點的名字 .fops = &misc_fops //填充文件操做集 }; //第3步;註冊到內核 static int misc_init(void){ int ret; ret = misc_register(&misc_dev);//存儲註冊的地址 //判斷是否註冊成功 if(ret < 0){ printk("misc registe is error\n"); return -1; } printk("misc registe is successful\n"); //內核裏不能使用c語言庫,因此不能用printf return 0; } //卸載驅動 static void misc_exit(void){ misc_deregister(&misc_dev); printk("misc bye bye\n"); } //入口和出口 module_init(misc_init); module_exit(misc_exit); //聲明許可證 MODULE_LICENSE("GPL");
app.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]){//若是打開設備節點成功,這會調用驅動裏邊的misc_open()函數 int fd; char read_buf[64] = {0}; char write_buf[64] = "write date"// fd = open("/dev/hello_misc",O_RDWR);//open the device node if(fd < 0){ //determine whether the opening is successful perror("open error\n");//和printf用法類似 return fd; } read(fd,read_buf,sizeof(read_buf));//讀設備節點 printf("read_buf is %s\n",read_buf);//打印從內核層讀取的內容 write(fd,write_buf,sizeof(write_buf));//將數據寫入設備節點,將應用層數據傳入的內核層 close(fd);//關閉節點 return 0; }