99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

一文帶你掌握Linux字符設(shè)備架構(gòu)

璟琰乀 ? 來源:一口Linux ? 作者:一口Linux ? 2020-12-22 16:14 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、Linux設(shè)備分類

Linux系統(tǒng)為了管理方便,將設(shè)備分成三種基本類型:

字符設(shè)備

塊設(shè)備

網(wǎng)絡(luò)設(shè)備

字符設(shè)備:

字符(char)設(shè)備是個(gè)能夠像字節(jié)流(類似文件)一樣被訪問的設(shè)備,由字符設(shè)備驅(qū)動(dòng)程序來實(shí)現(xiàn)這種特性。字符設(shè)備驅(qū)動(dòng)程序通常至少要實(shí)現(xiàn)open、close、read和write的系統(tǒng)調(diào)用。

字符終端(/dev/console)和串口(/dev/ttyS0以及類似設(shè)備)就是兩個(gè)字符設(shè)備,它們能很好的說明“流”這種抽象概念。

字符設(shè)備可以通過文件節(jié)點(diǎn)來訪問,比如/dev/tty1和/dev/lp0等。這些設(shè)備文件和普通文件之間的唯一差別在于對(duì)普通文件的訪問可以前后移動(dòng)訪問位置,而大多數(shù)字符設(shè)備是一個(gè)只能順序訪問的數(shù)據(jù)通道。然而,也存在具有數(shù)據(jù)區(qū)特性的字符設(shè)備,訪問它們時(shí)可前后移動(dòng)訪問位置。例如framebuffer就是這樣的一個(gè)設(shè)備,app可以用mmap或lseek訪問抓取的整個(gè)圖像。

在/dev下執(zhí)行l(wèi)s -l ,可以看到很多創(chuàng)建好的設(shè)備節(jié)點(diǎn):yqqymm.png

字符設(shè)備文件(類型為c),設(shè)備文件是沒有文件大小的,取而代之的是兩個(gè)號(hào)碼:主設(shè)備號(hào)5 +次設(shè)備號(hào)1 。

塊設(shè)備:

和字符設(shè)備類似,塊設(shè)備也是通過/dev目錄下的文件系統(tǒng)節(jié)點(diǎn)來訪問。塊設(shè)備(例如磁盤)上能夠容納filesystem。在大多數(shù)的Unix系統(tǒng)中,進(jìn)行I/O操作時(shí)塊設(shè)備每次只能傳輸一個(gè)或多個(gè)完整的塊,而每塊包含512字節(jié)(或2的更高次冪字節(jié)的數(shù)據(jù))。

Linux可以讓app像字符設(shè)備一樣地讀寫塊設(shè)備,允許一次傳遞任意多字節(jié)的數(shù)據(jù)。因此,塊設(shè)備和字符設(shè)備的區(qū)別僅僅在于內(nèi)核內(nèi)部管理數(shù)據(jù)的方式,也就是內(nèi)核及驅(qū)動(dòng)程序之間的軟件接口,而這些不同對(duì)用戶來講是透明的。在內(nèi)核中,和字符驅(qū)動(dòng)程序相比,塊驅(qū)動(dòng)程序具有完全不同的接口。

塊設(shè)備文件(類型為b):

neQJRb.png

網(wǎng)絡(luò)設(shè)備:

任何網(wǎng)絡(luò)事物都需要經(jīng)過一個(gè)網(wǎng)絡(luò)接口形成,網(wǎng)絡(luò)接口是一個(gè)能夠和其他主機(jī)交換數(shù)據(jù)的設(shè)備。接口通常是一個(gè)硬件設(shè)備,但也可能是個(gè)純軟件設(shè)備,比如回環(huán)(loopback)接口。

網(wǎng)絡(luò)接口由內(nèi)核中的網(wǎng)絡(luò)子系統(tǒng)驅(qū)動(dòng),負(fù)責(zé)發(fā)送和接收數(shù)據(jù)包。許多網(wǎng)絡(luò)連接(尤其是使用TCP協(xié)議的連接)是面向流的,但網(wǎng)絡(luò)設(shè)備卻圍繞數(shù)據(jù)包的傳送和接收而設(shè)計(jì)。網(wǎng)絡(luò)驅(qū)動(dòng)程序不需要知道各個(gè)連接的相關(guān)信息,它只要處理數(shù)據(jù)包即可。

由于不是面向流的設(shè)備,因此將網(wǎng)絡(luò)接口映射到filesystem中的節(jié)點(diǎn)(比如/dev/tty1)比較困難。

Unix訪問網(wǎng)絡(luò)接口的方法仍然是給它們分配一個(gè)唯一的名字(比如eth0),但這個(gè)名字在filesystem中不存在對(duì)應(yīng)的節(jié)點(diǎn)。內(nèi)核和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序間的通信,完全不同于內(nèi)核和字符以及塊驅(qū)動(dòng)程序之間的通信,內(nèi)核調(diào)用一套和數(shù)據(jù)包相關(guān)的函數(shù)socket,也叫套接字。

查看網(wǎng)絡(luò)設(shè)備使用命令ifconfig:

6zyqAr.png

二、字符設(shè)備架構(gòu)是如何實(shí)現(xiàn)的?

在Linux的世界里面一切皆文件,所有的硬件設(shè)備操作到應(yīng)用層都會(huì)被抽象成文件的操作。我們知道如果應(yīng)用層要訪問硬件設(shè)備,它必定要調(diào)用到硬件對(duì)應(yīng)的驅(qū)動(dòng)程序。Linux內(nèi)核中有那么多驅(qū)動(dòng)程序,應(yīng)用層怎么才能精確的調(diào)用到底層的驅(qū)動(dòng)程序呢?

在這里我們字符設(shè)備為例,來看一下應(yīng)用程序是如何和底層驅(qū)動(dòng)程序關(guān)聯(lián)起來的。必須知道的基礎(chǔ)知識(shí):

1.在Linux文件系統(tǒng)中,每個(gè)文件都用一個(gè)struct inode結(jié)構(gòu)體來描述,這個(gè)結(jié)構(gòu)體里面記錄了這個(gè)文件的所有信息,例如:文件類型,訪問權(quán)限等。

2.在Linux操作系統(tǒng)中,每個(gè)驅(qū)動(dòng)程序在應(yīng)用層的/dev目錄下都會(huì)有一個(gè)設(shè)備文件和它對(duì)應(yīng),并且該文件會(huì)有對(duì)應(yīng)的主設(shè)備號(hào)和次設(shè)備號(hào)。

3.在Linux操作系統(tǒng)中,每個(gè)驅(qū)動(dòng)程序都要分配一個(gè)主設(shè)備號(hào),字符設(shè)備的設(shè)備號(hào)保存在struct cdev結(jié)構(gòu)體中。

struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops;//接口函數(shù)集合 struct list_head list;//內(nèi)核鏈表 dev_t dev; //設(shè)備號(hào) unsigned int count;//次設(shè)備號(hào)個(gè)數(shù) };

4.在Linux操作系統(tǒng)中,每打開一次文件,Linux操作系統(tǒng)在VFS層都會(huì)分配一個(gè)struct file結(jié)構(gòu)體來描述打開的這個(gè)文件。該結(jié)構(gòu)體用于維護(hù)文件打開權(quán)限、文件指針偏移值、私有內(nèi)存地址等信息。

注意:

常常我們認(rèn)為struct inode描述的是文件的靜態(tài)信息,即這些信息很少會(huì)改變。而struct file描述的是動(dòng)態(tài)信息,即在對(duì)文件的操作的時(shí)候,struct file里面的信息經(jīng)常會(huì)發(fā)生變化。典型的是struct file結(jié)構(gòu)體里面的f_pos(記錄當(dāng)前文件的位移量),每次讀寫一個(gè)普通文件時(shí)f_ops的值都會(huì)發(fā)生改變。

這幾個(gè)結(jié)構(gòu)體關(guān)系如下圖所示:

通過上圖我們可以知道,如果想訪問底層設(shè)備,就必須打開對(duì)應(yīng)的設(shè)備文件。也就是在這個(gè)打開的過程中,Linux內(nèi)核將應(yīng)用層和對(duì)應(yīng)的驅(qū)動(dòng)程序關(guān)聯(lián)起來。

1.當(dāng)open函數(shù)打開設(shè)備文件時(shí),可以根據(jù)設(shè)備文件對(duì)應(yīng)的struct inode結(jié)構(gòu)體描述的信息,可以知道接下來要操作的設(shè)備類型(字符設(shè)備還是塊設(shè)備)。還會(huì)分配一個(gè)struct file結(jié)構(gòu)體。

2.根據(jù)struct inode結(jié)構(gòu)體里面記錄的設(shè)備號(hào),可以找到對(duì)應(yīng)的驅(qū)動(dòng)程序。這里以字符設(shè)備為例。在Linux操作系統(tǒng)中每個(gè)字符設(shè)備有一個(gè)struct cdev結(jié)構(gòu)體。此結(jié)構(gòu)體描述了字符設(shè)備所有的信息,其中最重要一項(xiàng)的就是字符設(shè)備的操作函數(shù)接口。

3.找到struct cdev結(jié)構(gòu)體后,Linux內(nèi)核就會(huì)將struct cdev結(jié)構(gòu)體所在的內(nèi)存空間首地記錄在struct inode結(jié)構(gòu)體的i_cdev成員中。將struct cdev結(jié)構(gòu)體的中記錄的函數(shù)操作接口地址記錄在struct file結(jié)構(gòu)體的f_op成員中。

4.任務(wù)完成,VFS層會(huì)給應(yīng)用層返回一個(gè)文件描述符(fd)。這個(gè)fd是和struct file結(jié)構(gòu)體對(duì)應(yīng)的。接下來上層的應(yīng)用程序就可以通過fd來找到strut file,然后在由struct file找到操作字符設(shè)備的函數(shù)接口了。

三、字符驅(qū)動(dòng)相關(guān)函數(shù)分析

/** * cdev_init() - initialize a cdev structure * @cdev: the structure to initialize * @fops: the file_operations for this device * * Initializes @cdev, remembering @fops, making it ready to add to the * system with cdev_add()。 */void cdev_init(struct cdev *cdev, const struct file_operations *fops)功能: 初始化cdev結(jié)構(gòu)體參數(shù): @cdev cdev結(jié)構(gòu)體地址 @fops 操作字符設(shè)備的函數(shù)接口地址返回值: 無

/** * register_chrdev_region() - register a range of device numbers * @from: the first in the desired range of device numbers; must include * the major number. * @count: the number of consecutive device numbers required * @name: the name of the device or driver. * * Return value is zero on success, a negative error code on failure. */ int register_chrdev_region(dev_t from, unsigned count, const char *name)功能: 注冊(cè)一個(gè)范圍()的設(shè)備號(hào)參數(shù): @from 設(shè)備號(hào) @count 注冊(cè)的設(shè)備個(gè)數(shù) @name 設(shè)備的名字返回值: 成功返回0,失敗返回錯(cuò)誤碼(負(fù)數(shù))

/** * cdev_add() - add a char device to the system * @p: the cdev structure for the device * @dev: the first device number for which this device is responsible * @count: the number of consecutive minor numbers corresponding to this * device * * cdev_add() adds the device represented by @p to the system, making it * live immediately. A negative error code is returned on failure. */int cdev_add(struct cdev *p, dev_t dev, unsigned count)功能: 添加一個(gè)字符設(shè)備到操作系統(tǒng)參數(shù): @p cdev結(jié)構(gòu)體地址 @dev 設(shè)備號(hào) @count 次設(shè)備號(hào)個(gè)數(shù)返回值: 成功返回0,失敗返回錯(cuò)誤碼(負(fù)數(shù))

/** * cdev_del() - remove a cdev from the system * @p: the cdev structure to be removed * * cdev_del() removes @p from the system, possibly freeing the structure * itself. */void cdev_del(struct cdev *p)功能: 從系統(tǒng)中刪除一個(gè)字符設(shè)備參數(shù): @p cdev結(jié)構(gòu)體地址返回值: 無

static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)功能: 注冊(cè)或者分配設(shè)備號(hào),并注冊(cè)fops到cdev結(jié)構(gòu)體, 如果major》0,功能為注冊(cè)該主設(shè)備號(hào), 如果major=0,功能為動(dòng)態(tài)分配主設(shè)備號(hào)。參數(shù): @major : 主設(shè)備號(hào) @name : 設(shè)備名稱,執(zhí)行 cat /proc/devices顯示的名稱 @fops : 文件系統(tǒng)的接口指針返回值 如果major》0 成功返回0,失敗返回負(fù)的錯(cuò)誤碼 如果major=0 成功返回主設(shè)備號(hào),失敗返回負(fù)的錯(cuò)誤碼

該函數(shù)實(shí)現(xiàn)了對(duì)cdev的初始化和注冊(cè)的封裝,所以調(diào)用該函數(shù)之后就不需要自己操作cdev了。

相對(duì)的注銷函數(shù)為unregister_chrdev

static inline void unregister_chrdev(unsigned int major, const char *name)

四、如何編寫字符設(shè)備驅(qū)動(dòng)

aeYnIj.png

參考上圖,編寫字符設(shè)備驅(qū)動(dòng)步驟如下:

1. 實(shí)現(xiàn)模塊加載和卸載入口函數(shù)

module_init (hello_init);module_exit (hello_exit);

2. 申請(qǐng)主設(shè)備號(hào)

申請(qǐng)主設(shè)備號(hào) (內(nèi)核中用于區(qū)分和管理不同字符設(shè)備)

register_chrdev_region (devno, number_of_devices, “hello”);

3. 創(chuàng)建設(shè)備節(jié)點(diǎn)

創(chuàng)建設(shè)備節(jié)點(diǎn)文件 (為用戶提供一個(gè)可操作到文件接口--open()) 創(chuàng)建設(shè)備節(jié)點(diǎn)有兩種方式:手動(dòng)方式創(chuàng)建,函數(shù)自動(dòng)創(chuàng)建。手動(dòng)創(chuàng)建:

mknod /dev/hello c 250 0

自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn)

除了使用mknod命令手動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn),還可以利用linux的udev、mdev機(jī)制,而我們的ARM開發(fā)板上移植的busybox有mdev機(jī)制,那么就使用mdev機(jī)制來自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn)。

在etc/init.d/rcS文件里有一句:

echo /sbin/mdev 》 /proc/sys/kernel/hotplug

該名命令就是用來自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn)。

udev 是一個(gè)工作在用戶空間的工具,它能根據(jù)系統(tǒng)中硬件設(shè)備的狀態(tài)動(dòng)態(tài)的更新設(shè)備文件,包括設(shè)備文件的創(chuàng)建,刪除,權(quán)限等。這些文件通常都定義在/dev 目錄下,但也可以在配置文件中指定。udev 必須有內(nèi)核中的sysfs和tmpfs支持,sysfs 為udev 提供設(shè)備入口和uevent 通道,tmpfs 為udev 設(shè)備文件提供存放空間。

udev 運(yùn)行在用戶模式,而非內(nèi)核中。udev 的初始化腳本在系統(tǒng)啟動(dòng)時(shí)創(chuàng)建設(shè)備節(jié)點(diǎn),并且當(dāng)插入新設(shè)備——加入驅(qū)動(dòng)模塊——在sysfs上注冊(cè)新的數(shù)據(jù)后,udev會(huì)創(chuàng)新新的設(shè)備節(jié)點(diǎn)。

注意,udev 是通過對(duì)內(nèi)核產(chǎn)生的設(shè)備文件修改,或增加別名的方式來達(dá)到自定義設(shè)備文件的目的。但是,udev 是用戶模式程序,其不會(huì)更改內(nèi)核行為。也就是說,內(nèi)核仍然會(huì)創(chuàng)建sda,sdb等設(shè)備文件,而udev可根據(jù)設(shè)備的唯一信息來區(qū)分不同的設(shè)備,并產(chǎn)生新的設(shè)備文件(或鏈接)。

例如:

如果驅(qū)動(dòng)模塊可以將自己的設(shè)備號(hào)作為內(nèi)核參數(shù)導(dǎo)出,在sysfs文件中就有一個(gè)叫做uevent文件記錄它的值。

iAJbia.png

由上圖可知,uevent中包含了主設(shè)備號(hào)和次設(shè)備號(hào)的值以及設(shè)備名字。

在Linux應(yīng)用層啟動(dòng)一個(gè)udev程序,這個(gè)程序的第一次運(yùn)行的時(shí)候,會(huì)遍歷/sys目錄,尋找每個(gè)子目錄的uevent文件,從這些uevent文件中獲取創(chuàng)建設(shè)備節(jié)點(diǎn)的信息,然后調(diào)用mknod程序在/dev目錄下創(chuàng)建設(shè)備節(jié)點(diǎn)。結(jié)束之后,udev就開始等待內(nèi)核空間的event。這個(gè)設(shè)備模型的東西,我們?cè)诤竺嬖僭敿?xì)說。這里大就可以這樣理解,在Linux內(nèi)核中提供了一些函數(shù)接口,通過這些函數(shù)接口,我們可在sysfs文件系統(tǒng)中導(dǎo)出我們的設(shè)備號(hào)的值,導(dǎo)出值之后,內(nèi)核還會(huì)向應(yīng)用層上報(bào)event。此時(shí)udev就知道有活可以干了,它收到這個(gè)event后,就讀取event對(duì)應(yīng)的信息,接下來就開始創(chuàng)建設(shè)備節(jié)點(diǎn)啦。

如何創(chuàng)建一個(gè)設(shè)備類?

第一步 :通過宏class_create() 創(chuàng)建一個(gè)class類型的對(duì)象;

/* This is a #define to keep the compiler from merging different * instances of the __key variable */#define class_create(owner, name) ({ static struct lock_class_key __key; __class_create(owner, name, &__key); })參數(shù): @owner THIS_MODULE @name 類名字返回值 可以定義一個(gè)struct class的指針變量cls接受返回值,然后通過IS_ERR(cls)判斷 是否失敗,如果成功這個(gè)宏返回0,失敗返回非9值(可以通過PTR_ERR(cls)來獲得 失敗返回的錯(cuò)誤碼)

在Linux內(nèi)核中,把設(shè)備進(jìn)行了分類,同一類設(shè)備可以放在同一個(gè)目錄下,該函數(shù)啟示就是創(chuàng)建了一個(gè)類,例如:

qYvE7f.png

第二步:導(dǎo)出我們的設(shè)備信息到用戶空間

/** * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added * @drvdata: the data to be added to the device for callbacks * @fmt: string for the device‘s name * * This function can be used by char device classes. A struct device * will be created in sysfs, registered to the specified class. * * A “dev” file will be created, showing the dev_t for the device, if * the dev_t is not 0,0. * If a pointer to a parent struct device is passed in, the newly created * struct device will be a child of that device in sysfs. * The pointer to the struct device will be returned from the call. * Any further sysfs files that might be required can be created using this * pointer. * * Returns &struct device pointer on success, or ERR_PTR() on error. * * Note: the struct class passed to this function must have previously * been created with a call to class_create()。 */struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, 。..)

自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn)使用實(shí)例:

static struct class *cls;static struct device *test_device; devno = MKDEV(major,minor); cls = class_create(THIS_MODULE,“helloclass”); if(IS_ERR(cls)) { unregister_chrdev(major,“hello”); return result; } test_device = device_create(cls,NULL,devno,NULL,“hellodevice”); if(IS_ERR(test_device )) { class_destroy(cls); unregister_chrdev(major,“hello”); return result; }

4 實(shí)現(xiàn)file_operations

static const struct file_operations fifo_operations = { .owner = THIS_MODULE, .open = dev_fifo_open, .read = dev_fifo_read, .write = dev_fifo_write, .unlocked_ioctl = dev_fifo_unlocked_ioctl,};

open、release對(duì)應(yīng)應(yīng)用層的open()、close()函數(shù)。實(shí)現(xiàn)比較簡單,

直接返回0即可。 其中read、write、unloched_ioctrl 函數(shù)的實(shí)現(xiàn)需要涉及到用戶空間 和內(nèi)存空間的數(shù)據(jù)拷貝。

在Linux操作系統(tǒng)中,用戶空間和內(nèi)核空間是相互獨(dú)立的。也就是說內(nèi)核空間是不能直接訪問用戶空間內(nèi)存地址,同理用戶空間也不能直接訪問內(nèi)核空間內(nèi)存地址。

如果想實(shí)現(xiàn),將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間或?qū)?nèi)核空間數(shù)據(jù)拷貝到用戶空間,就必須借助內(nèi)核給我們提供的接口來完成。

1. read接口實(shí)現(xiàn)

用戶空間--》內(nèi)核空間

字符設(shè)備的write接口定義如下:

ssize_t (*write)(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);參數(shù): filp:待操作的設(shè)備文件file結(jié)構(gòu)體指針 buf:待寫入所讀取數(shù)據(jù)的用戶空間緩沖區(qū)指針 count:待讀取數(shù)據(jù)字節(jié)數(shù) f_pos:待讀取數(shù)據(jù)文件位置,寫入完成后根據(jù)實(shí)際寫入字節(jié)數(shù)重新定位返回: 成功實(shí)際寫入的字節(jié)數(shù),失敗返回負(fù)值

如果該操作為空,將使得write系統(tǒng)調(diào)用返回負(fù)EINVAL失敗,正常返回實(shí)際寫入的字節(jié)數(shù)。

用戶空間向內(nèi)核空間拷貝數(shù)據(jù)需要使用copy_from_user函數(shù),該函數(shù)定義在arch/arm/include/asm/uaccess.h中。

static inline int copy_from_user(void *to, const void __user volatile *from,unsigned long n)參數(shù): to:目標(biāo)地址(內(nèi)核空間) from:源地址(用戶空間) n:將要拷貝數(shù)據(jù)的字節(jié)數(shù)返回: 成功返回0,失敗返回沒有拷貝成功的數(shù)據(jù)字節(jié)數(shù)

7ZbaUn.png

還可以使用get_user宏:

int get_user(data, ptr);參數(shù): data:可以是字節(jié)、半字、字、雙字類型的內(nèi)核變量 ptr:用戶空間內(nèi)存指針返回: 成功返回0,失敗返回非0

2. write接口實(shí)現(xiàn)

內(nèi)核空間--》用戶空間

字符設(shè)備的read接口定義如下:

ssize_t (*read)(struct file *filp, char __user *buf, size_t count, lofft *f_pos);參數(shù): filp: 待操作的設(shè)備文件file結(jié)構(gòu)體指針 buf: 待寫入所讀取數(shù)據(jù)的用戶空間緩沖區(qū)指針 count:待讀取數(shù)據(jù)字節(jié)數(shù) f_pos:待讀取數(shù)據(jù)文件位置,讀取完成后根據(jù)實(shí)際讀取字節(jié)數(shù)重新定位 __user :是一個(gè)空的宏,主要用來顯示的告訴程序員它修飾的指針變量存放的是用戶空間的地址。返回值: 成功實(shí)際讀取的字節(jié)數(shù),失敗返回負(fù)值

注意:如果該操作為空,將使得read系統(tǒng)調(diào)用返回負(fù)EINVAL失敗,正常返回實(shí)際讀取的字節(jié)數(shù)。

用戶空間從內(nèi)核空間讀取數(shù)據(jù)需要使用copy_to_user函數(shù):

static inline int copy_to_user(void __user volatile *to, const void *from,unsigned long n)參數(shù): to:目標(biāo)地址(用戶空間) from:源地址(內(nèi)核空間) n:將要拷貝數(shù)據(jù)的字節(jié)數(shù)返回: 成功返回0,失敗返回沒有拷貝成功的數(shù)據(jù)字節(jié)數(shù)

JzmUVz.png

在這里插入圖片描述

還可以使用put_user宏:

int put_user(data, prt)參數(shù): data:可以是字節(jié)、半字、字、雙字類型的內(nèi)核變量 ptr:用戶空間內(nèi)存指針返回: 成功返回0, 失敗返回非0

這樣我們就可以實(shí)現(xiàn)read、write函數(shù)了,實(shí)例如下:

ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp){ ssize_t result = 0; if (count 》 127) count = 127; if (copy_to_user (buff, data, count)) { result = -EFAULT; } else { printk (KERN_INFO “wrote %d bytes ”, count); result = count; } return result;}ssize_t hello_write (struct file *filp,const char *buf, size_t count, loff_t *f_pos){ ssize_t ret = 0; //printk (KERN_INFO “Writing %d bytes ”, count); if (count 》 127) return -ENOMEM; if (copy_from_user (data, buf, count)) { ret = -EFAULT; } else { data[count] = ’