日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

發(fā)布時(shí)間:2023/12/2 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

哈嘍,我是老吳,繼續(xù)記錄我的學(xué)習(xí)心得。

一、保持專注的幾個(gè)技巧

  • 將最重要的事放在早上做。

  • 待在無(wú)干擾環(huán)境下,比如圖書(shū)館。

  • 意識(shí)到剛坐下開(kāi)始投入工作前,有點(diǎn)負(fù)面小情緒是特別正常的現(xiàn)象。

  • 讓“開(kāi)心一刻”成為計(jì)劃的一部分。

  • 擁有合情合理的日計(jì)劃和周計(jì)劃。


二、Linux 字符設(shè)備驅(qū)動(dòng)內(nèi)幕 (1)

正文目錄:

1. 什么是字符設(shè)備驅(qū)動(dòng)?
2.?快速體驗(yàn)字符設(shè)備驅(qū)動(dòng)和應(yīng)用程序?(超簡(jiǎn)單的?demo)
3.?字符設(shè)備在內(nèi)核里的抽象
????3.1?字符設(shè)備核心代碼概覽
????3.2?對(duì)字符設(shè)備進(jìn)行抽象:?struct?cdev
??? 3.3 對(duì)字符設(shè)備的操作進(jìn)行抽象:struct file_operations
4.?更多值得學(xué)習(xí)的知識(shí)點(diǎn)
5.?相關(guān)參考

寫(xiě)作目的:

  • 探索 Linux 字符設(shè)備驅(qū)動(dòng)。

測(cè)試環(huán)境:

  • Ubuntu 16.04
  • Gcc 5.4.0

1. 什么是字符設(shè)備驅(qū)動(dòng)?

  • 現(xiàn)實(shí)世界中存在著成千上萬(wàn)的硬件設(shè)備,這些設(shè)備在硬件特性和使用方式上都各不相同。Linux 系統(tǒng)的大牛們從這些形形色色的設(shè)備中提取出共性,將它們抽象為三大類:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備

  • 基于代碼質(zhì)量和復(fù)用性的考慮,Linux 內(nèi)核針對(duì)每一類硬件設(shè)備都提供了對(duì)應(yīng)的驅(qū)動(dòng)模型框架,一般包括基本的內(nèi)核設(shè)施和文件系統(tǒng)接口。開(kāi)發(fā)人員在寫(xiě)某類設(shè)備驅(qū)動(dòng)程序時(shí),能一套完整的驅(qū)動(dòng)模型框架可以使用,從而將精力放在硬件設(shè)備本身的控制上。
  • 簡(jiǎn)單的 Linux 設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)圖:

  • 詳細(xì)一點(diǎn)的 Linux 設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)圖:

2. 快速體驗(yàn)字符設(shè)備驅(qū)動(dòng)和應(yīng)用程序 (超簡(jiǎn)單的 demo)

1) 字符設(shè)備驅(qū)動(dòng) (chrdev_drv.c):

字符設(shè)備的打開(kāi)和讀函數(shù):

static?struct?cdev?chr_dev;?//?字符設(shè)備抽象
static?dev_t?ndev;??????????//?設(shè)備號(hào)
static?int?chr_open(struct?inode?*nd,?struct?file?*filp){
????printk("chr_open,?major=%d,?minor=%d\n",?MAJOR(nd->i_rdev),?MINOR(nd->i_rdev));
????return?0;
}

static?ssize_t?chr_read(struct?file?*filp,?char?__user?*u,?size_t?sz,?loff_t?*off){
????printk("In?chr_read()\n");
????return?0;
}

static?int?chr_release(struct?inode?*nd,?struct?file?*filp){
????printk("In?chr_release()\n");
????return?0;
}

文件操作函數(shù)集:

struct?file_operations?chr_ops?=
{
????.owner?=?THIS_MODULE,
????.open?=?chr_open,
????.read?=?chr_read,
????.release?=?chr_release,
};

模塊加載和卸載:

static?int?demo_init(void){
????int?ret;
????cdev_init(&chr_dev,?&chr_ops);
????ret?=?alloc_chrdev_region(&ndev,?0,?1,?"chr_dev");
????if(ret?0)
????????return?ret;

????printk("demo_init():major=%d,?minor=%d\n",?MAJOR(ndev),?MINOR(ndev));

????ret?=?cdev_add(&chr_dev,?ndev,?1);
????if(ret?0)
????????return?ret;

????return?0;
}
static?void?demo_exit(void){
????printk("demo_exit...\n");

????cdev_del(&chr_dev);
????unregister_chrdev_region(ndev,?1);
}

2) 訪問(wèn)字符設(shè)備的應(yīng)用程序:

int?main(){
????int?ret;
????char?buf[32];
????int?fd?=?open("/dev/chr_dev",?O_RDONLY|O_NDELAY);
????if(fd?0)
????{
????????printf("open?file?%s?failed!\n",?CHR_DEV_NAME);
????????return?-1;
????}
????read(fd,?buf,?32);
????close(fd);

????return?0;
}

3) 不用完全理解代碼的含義,直接看運(yùn)行效果:

#?編譯驅(qū)動(dòng)程序
$?make?KERNELDIR=XXX/linux?ARCH=arm?CROSS_COMPILE=arm-linux-

#?編譯應(yīng)用程序
$?arm-linux-gcc?chrdev_app.c?-o?chrdev_app

#?加載驅(qū)動(dòng)模塊
$?insmod?chrdev_drv.ko
demo_init():major=242,?minor=0

#?手動(dòng)創(chuàng)建字符設(shè)備文件節(jié)點(diǎn)
$?mknod?/dev/chr_dev?c?242?0

#?運(yùn)行應(yīng)用程序
$?./chrdev_app
chr_open,?major=242,?minor=0
In?chr_read()
In?chr_release()

從上面測(cè)運(yùn)行結(jié)果可知,應(yīng)用程序調(diào)用 chrdev_app.c / open() 會(huì)導(dǎo)致驅(qū)動(dòng)程序 chrdev_drv.c / struct file_operations chr_ops->open() 被調(diào)用,read 操作也是類似。

內(nèi)核是如何實(shí)現(xiàn)上述功能的?

帶著這個(gè)困惑來(lái)了解字符設(shè)備驅(qū)動(dòng)的框架,才不會(huì)迷失在內(nèi)核里各種復(fù)雜的代碼細(xì)節(jié)里。

3 字符設(shè)備在內(nèi)核里的抽象

3.1 字符設(shè)備核心代碼概覽

在深入閱讀各種代碼之前,先整體地概覽一遍將會(huì)涉及到的程序文件,找出核心主干,能有效地避免陷入繁雜的代碼細(xì)節(jié)中。

1) 分解 C source 文件,fs/char_dev.c (Linux-4.14):

作用:
char_dev.c 是字符設(shè)備驅(qū)動(dòng)框架的核心實(shí)現(xiàn)文件,它位于 fs 目錄中,說(shuō)明了字符設(shè)備驅(qū)動(dòng)和文件系統(tǒng)是緊密聯(lián)系在一起的。

內(nèi)容(以重要性排序):1> public 函數(shù):

//?1.?字符設(shè)備子系統(tǒng)初始化
void?__init?chrdev_init(void)?//?2.?struct?cdev?管理相關(guān)void?chrdev_show(struct?seq_file?*f,?off_t?offset)?void?cdev_put(struct?cdev?*p)?void?cd_forget(struct?inode?*inode)?int?cdev_add(struct?cdev?*p,?dev_t?dev,?unsigned?count)?void?cdev_set_parent(struct?cdev?*p,?struct?kobject?*kobj)?int?cdev_device_add(struct?cdev?*cdev,?struct?device?*dev)?void?cdev_device_del(struct?cdev?*cdev,?struct?device?*dev)?void?cdev_del(struct?cdev?*p)?
struct?cdev?*cdev_alloc(void)?void?cdev_init(struct?cdev?*cdev,?const?struct?file_operations?*fops)//?3.?設(shè)備號(hào)管理相關(guān)
__register_chrdev_region(unsigned?int?major,?unsigned?int?baseminor,?
__unregister_chrdev_region(unsigned?major,?unsigned?baseminor,?int?minorct)?int?register_chrdev_region(dev_t?from,?unsigned?count,?const?char?*name)?int?alloc_chrdev_region(dev_t?*dev,?unsigned?baseminor,?unsigned?count,?int?__register_chrdev(unsigned?int?major,?unsigned?int?baseminor,?void?unregister_chrdev_region(dev_t?from,?unsigned?count)?void?__unregister_chrdev(unsigned?int?major,?unsigned?int?baseminor,
  • 在 Linux 代碼中,雙下劃線(__)開(kāi)始的函數(shù)名表示這是一個(gè)低層接口, 應(yīng)當(dāng)小心使用。如果你調(diào)用這個(gè)函數(shù),確信你知道你在做什么。也就是說(shuō),閱讀代碼時(shí),雙下劃線開(kāi)頭的函數(shù)可以暫時(shí)放一邊。

  • struct cdev 管理相關(guān):cdev_add() / cdev_del() / cdev_init(),需重點(diǎn)關(guān)注。

  • 設(shè)備號(hào)管理相關(guān):alloc_chrdev_region() / register_chrdev_region(),需重點(diǎn)關(guān)注。

2> public 變量:

const?struct?file_operations?def_chr_fops?=?{
?.open?=?chrdev_open,
?.llseek?=?noop_llseek,
};
  • 類似于高級(jí)語(yǔ)言的多態(tài)機(jī)制,在 Linux 各個(gè)子系統(tǒng)的驅(qū)動(dòng)框架中一般用一個(gè)統(tǒng)一的 file_operations->open 函數(shù)關(guān)聯(lián)到各個(gè)具體的硬件驅(qū)動(dòng)的 struct file_operations,需要重點(diǎn)關(guān)注。

3> private 變量:

static?struct?char_device_struct?{
?struct?char_device_struct?*next;
?unsigned?int?major;
?unsigned?int?baseminor;
?int?minorct;
?char?name[64];
?struct?cdev?*cdev;??/*?will?die?*/
}?*chrdevs[CHRDEV_MAJOR_HASH_SIZE];

static?struct?kobj_map?*cdev_map;?
static?struct?kobj_type?ktype_cdev_default;
static?struct?kobj_type?ktype_cdev_dynamic;
  • struct char_device_struct *chrdevs[] 數(shù)組是字符設(shè)備驅(qū)動(dòng)框架的核心數(shù)據(jù)結(jié)構(gòu),需重點(diǎn)關(guān)注。

4> private 函數(shù):

static?inline?int?major_to_index(unsigned?major)?static?int?find_dynamic_major(void)?static?struct?kobject?*cdev_get(struct?cdev?*p)?static?int?chrdev_open(struct?inode?*inode,?struct?file?*filp)?static?void?cdev_purge(struct?cdev?*cdev)?static?struct?kobject?*exact_match(dev_t?dev,?int?*part,?void?*data)?static?int?exact_lock(dev_t?dev,?void?*data)?static?void?cdev_unmap(dev_t?dev,?unsigned?count)?static?void?cdev_default_release(struct?kobject?*kobj)?static?void?cdev_dynamic_release(struct?kobject?*kobj)?static?struct?kobject?*base_probe(dev_t?dev,?int?*part,?void?*data)
  • helper 類函數(shù),不太重要。

2) 分解 C header 文件,include/linux/cdev.h (Linux-4.14):

作用:
包含字符設(shè)備驅(qū)動(dòng)相關(guān)結(jié)構(gòu)體的定義、以及一些字符設(shè)備核心 API 的聲明。

內(nèi)容:1> struct cdev 結(jié)構(gòu)體:

struct?cdev?{
?struct?kobject?kobj;
?struct?module?*owner;
?const?struct?file_operations?*ops;
?struct?list_head?list;
?dev_t?dev;
?unsigned?int?count;
}?__randomize_layout;
  • struct cdev 是字符設(shè)備驅(qū)動(dòng)的核心抽象,需重點(diǎn)關(guān)注。

3.2 對(duì)字符設(shè)備進(jìn)行抽象: struct cdev

編寫(xiě)字符設(shè)備驅(qū)動(dòng)程序就是為了管理和控制字符設(shè)備,Linux 內(nèi)核將字符設(shè)備抽象為一 個(gè)數(shù)據(jù)結(jié)構(gòu):struct cdev。這個(gè)結(jié)構(gòu)體似乎從 Linux-2.6 到現(xiàn)在 Linux-5.8 都沒(méi)有發(fā)生變化。

1) struct cdev 成員簡(jiǎn)介:

目前沒(méi)必要完全理解這些成員的作用,有個(gè)大概印象就好:

  • struct kobject kobj: 內(nèi)嵌的內(nèi)核對(duì)象,與 Linux 設(shè)備驅(qū)動(dòng)模型相關(guān),后續(xù)需專門(mén)寫(xiě)一篇文章來(lái)介紹。

  • struct module *owner: 指向擁有這個(gè)結(jié)構(gòu)體的模塊的指針,這個(gè)成員用來(lái)在它的操作還在被使用時(shí)阻止模塊被卸載,一般初始化為 THIS_MODULE;

  • struct file_operations *ops: 在Linux通用文件模型下,字符設(shè)備的文件操作函數(shù)集,后續(xù)詳解。

  • struct list_head list:用于管理 struct cdev 的鏈表,后續(xù)需專門(mén)寫(xiě)一篇文章來(lái)介紹。

  • 字符設(shè)備的設(shè)備號(hào),由主設(shè)備號(hào)和次設(shè)備號(hào)構(gòu)成,后續(xù)需專門(mén)寫(xiě)一篇文章來(lái)介紹。

2) 兩種方式創(chuàng)建 struct cdev 對(duì)象:

這里用對(duì)象一詞,是為了引導(dǎo)大家用面向?qū)ο蟮乃季S來(lái)看待 Linux 內(nèi)核的設(shè)計(jì)

面向過(guò)程還是面向?qū)ο?#xff0c;不應(yīng)該和語(yǔ)言綁定在一起,應(yīng)該理解為 2 種不同的編程思維。人腦是很美妙的,在不同的場(chǎng)景,只要你愿意,它就能應(yīng)用不同的思維方式來(lái)解決問(wèn)題。設(shè)計(jì) Linux 內(nèi)核代碼的神牛們,可謂是各個(gè)都是面向?qū)ο缶幊痰拇笈?#xff0c;練習(xí)編程就應(yīng)該練習(xí)對(duì)事物的抽象能力,C 程序員如果覺(jué)得自己缺乏這方面的能力,不如學(xué)習(xí)一下 Java 編程。

靜態(tài)定義:

static?struct?cdev?chr_dev;

動(dòng)態(tài)分配:

struct?cdev?*my_cdev?=?cdev_alloc();

cdev_alloc() 不僅會(huì)為struct cdev對(duì)象分配內(nèi)存空間,還會(huì)對(duì)該對(duì)象進(jìn)行必要的初始化:

struct?cdev?*cdev_alloc(void){
?struct?cdev?*p?=?kzalloc(sizeof(struct?cdev),?GFP_KERNEL);
?if?(p)?{
??INIT_LIST_HEAD(&p->list);
??kobject_init(&p->kobj,?&ktype_cdev_dynamic);
?}
?return?p;
}

一個(gè)值得注意的點(diǎn):
我搜索了一下內(nèi)核源碼,發(fā)現(xiàn)大多數(shù)驅(qū)動(dòng)都選擇靜態(tài)定義 struct cdev,這樣更簡(jiǎn)單省事。

3) 某個(gè)真實(shí)字符硬件的抽象:

數(shù)據(jù)結(jié)構(gòu) struct cdev 作為字符設(shè)備的抽象,僅僅是為了滿足 Linux 內(nèi)核對(duì)字符設(shè)備驅(qū)動(dòng)程序框架結(jié)構(gòu)設(shè)計(jì)的需要

現(xiàn)實(shí)中,一個(gè)具體的字符硬件設(shè)備的數(shù)據(jù)結(jié)構(gòu)的抽象往往要復(fù)雜得多,在這種情況下 struct cdev 常常作為一種內(nèi)嵌的成員變量出現(xiàn)在實(shí)際設(shè)備的數(shù)據(jù)結(jié)構(gòu)中。

例如 drvier/watchdog/watchdog_dev.c:

struct?watchdog_core_data?{
?struct?kref?kref;
?struct?cdev?cdev;
?struct?watchdog_device?*wdd;
?struct?mutex?lock;
?unsigned?long?last_keepalive;
?unsigned?long?last_hw_keepalive;
?struct?delayed_work?work;
?unsigned?long?status;??/*?Internal?status?bits?*/
};

4) 初始化 cdev 對(duì)象:

void?cdev_init(struct?cdev?*cdev,?const?struct?file_operations?*fops){
?memset(cdev,?0,?sizeof?*cdev);
?INIT_LIST_HEAD(&cdev->list);
?kobject_init(&cdev->kobj,?&ktype_cdev_default);
?cdev->ops?=?fops;
}

cdev_init() 最重要的作用就是將 struct cdev 對(duì)象和 struct file_operations 對(duì)象綁定在一起。

一些值得注意的點(diǎn)

  • cdev_init() 和 cdev_alloc() 中有一部分功能是重疊的(例如 kobject_init()),所以 cdev_init() 只能搭配靜態(tài)定義 struct cdev 的方式來(lái)使用。

  • 如果采用 cdev_alloc() 動(dòng)態(tài)分配 struct cdev 對(duì)象,則需自行 cdev->ops = fops 。

3.3 對(duì)字符設(shè)備的操作進(jìn)行抽象:struct file_operations

struct?file_operations?{
?struct?module?*owner;
?loff_t?(*llseek)?(struct?file?*,?loff_t,?int);
?ssize_t?(*read)?(struct?file?*,?char?__user?*,?size_t,?loff_t?*);
?ssize_t?(*write)?(struct?file?*,?const?char?__user?*,?size_t,?loff_t?*);
?ssize_t?(*read_iter)?(struct?kiocb?*,?struct?iov_iter?*);
?ssize_t?(*write_iter)?(struct?kiocb?*,?struct?iov_iter?*);
????
????[...]

?int?(*mmap)?(struct?file?*,?struct?vm_area_struct?*);
?int?(*open)?(struct?inode?*,?struct?file?*);
?int?(*flush)?(struct?file?*,?fl_owner_t?id);
?int?(*release)?(struct?inode?*,?struct?file?*);
?int?(*fsync)?(struct?file?*,?loff_t,?loff_t,?int?datasync);
?int?(*fasync)?(int,?struct?file?*,?int);
?int?(*lock)?(struct?file?*,?int,?struct?file_lock?*);

?[...];

}?__randomize_layout;

  • 目前沒(méi)必要完全理解這些成員的作用,有個(gè)大概印象就好。

  • 字符設(shè)備驅(qū)動(dòng)程序中一個(gè)極其關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),字符設(shè)備驅(qū)動(dòng)程序的編寫(xiě),就是是圍繞著如何實(shí)現(xiàn) struct file_operations 中的那些函數(shù)指針成員而展開(kāi)的。

  • 通過(guò)內(nèi)核文件系統(tǒng)組件在其間的穿針引線,應(yīng)用程序中對(duì)文件類函數(shù)(open、read、write)的調(diào)用,將最終被轉(zhuǎn)接到 struct file_operations 中對(duì)應(yīng)函數(shù)指針的具體實(shí)現(xiàn)上,后續(xù)需專門(mén)寫(xiě)一篇文章來(lái)介紹。

鑒于大多數(shù)人的注意力無(wú)法在一篇文章里上集中太久,更多的內(nèi)容將放在后面的文章里。建議大家可以先自行閱讀相關(guān)書(shū)籍,不是自己理解到的東西是消化不了的。

4. 更多值得學(xué)習(xí)的知識(shí)點(diǎn)

  • 字符設(shè)備號(hào)的構(gòu)成與管理

  • 字符設(shè)備的注冊(cè)

  • 生成字符設(shè)備文件的方式有哪些?

  • 創(chuàng)建字符設(shè)備文件是發(fā)生了什么?

  • 字符設(shè)備文件是如何關(guān)聯(lián)到字符設(shè)備驅(qū)動(dòng)的?

  • 分析一些真實(shí)的字符設(shè)備驅(qū)動(dòng)

5. 相關(guān)參考

  • 《Linux 內(nèi)核文檔》

  • 《Linux設(shè)備驅(qū)動(dòng)程序》(ldd) / 第 3 章節(jié)

  • 《深入Linux設(shè)備驅(qū)動(dòng)程序內(nèi)核機(jī)制》(ildd) / 第 2 章節(jié)

  • 《精通Linux設(shè)備驅(qū)動(dòng)程序開(kāi)發(fā)》(eldd) / 第 2 章節(jié)

  • 《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》(ldds) / 第 6 章節(jié)

  • 《深入Linux內(nèi)核架構(gòu)》(plka) / 第 6 章節(jié)

  • 《嵌入式應(yīng)用開(kāi)發(fā)完全手冊(cè)》


三、思考技術(shù),也思考人生

學(xué)習(xí)技術(shù),更要學(xué)習(xí)如何生活

你和我各有一個(gè)蘋(píng)果,如果我們交換蘋(píng)果的話,我們還是只有一個(gè)蘋(píng)果。但當(dāng)你和我各有一個(gè)想法,我們交換想法的話,我們就都有兩個(gè)想法了。

對(duì) 嵌入式系統(tǒng) (Linux、RTOS、OpenWrt、Android) 和 開(kāi)源軟件 感興趣,想和更多人互相交流學(xué)習(xí),請(qǐng)關(guān)注公眾號(hào):嵌入式Hacker,一起來(lái)學(xué)習(xí)吧。

無(wú)論是關(guān)注或轉(zhuǎn)發(fā),還是打賞,都是對(duì)作者莫大的支持。覺(jué)得文章對(duì)你有價(jià)值的話,不妨點(diǎn)個(gè) 在看和點(diǎn)贊 哦。

歡迎加入我的微信群:先加我,我拉你進(jìn)群,暗號(hào)(我最棒)。

祝各位工作順利,家庭幸福,財(cái)源滾滾~~~

總結(jié)

以上是生活随笔為你收集整理的linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。