應用層和內核層數據傳輸-Linux驅動學習(3)


應用層和內核層的數據傳輸

【學習筆記】

1、應用層和內核層數據傳輸常用的函數

在Linux中,文件對應的操作有:打開、關閉、讀寫,同樣與文件類似,設備節點對應的操作有:打開、關閉、讀寫

如果我們在應用層使用系統IO對設備節點進行打開、關閉、讀寫等操作會發生什么?

【注】:
下面這些函數都定義在linux內核文件夾,比如:linux-4.9.268/include/linux/fs.h中的 struct file_operations {};結構體中

(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這個函數。

unsigned_t(*poll)(struct file*, struct poll_table_struct*);

(4)當我們在應用層對設備節點進行ioctl操作時,就會觸發驅動里邊的ioctl這個函數。

long(*unlocked_ioctl)(struct file*, unsigned int, unsigned long);

(5)當我們在應用層對設備節點進行open操作時,就會觸發驅動里邊的open這個函數。

int(*open)(struct inode*, struct file*);

(6)當我們在應用層對設備節點進行close操作時,就會觸發驅動里邊的release這個函數。

int(*release)(struct inode*, struct file*);

【注意】read和write參數中的__user*前面是兩個下划線,寫成一個編譯時會報錯

2、應用層調用驅動的例子

驅動層:驅動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;
}

如下圖,設備節點是內核層和應用層的橋梁

image

如果驅動文件中相應的操作集沒有填充,而應用層又調用了對應的函數,那么將什么都不會發生,也不會報錯。

應用層和數據層不能直接進行數據傳輸,需要使用其他函數

下面兩個函數定義在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;
}

整理自嵌入式學習之Linux驅動篇


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM