日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > 嵌入式 > 嵌入式軟件
[導(dǎo)讀]linux dma cache

說(shuō)到DMA,就會(huì)想到Cache,兩者本身似乎是好不相關(guān)的事物。的確,假設(shè)DMA針對(duì)內(nèi)存的目的地址和Cache緩存的對(duì)象沒(méi)有重疊區(qū)域,DMA和Cache之間就相安無(wú)事,但是,如果有重疊呢,經(jīng)過(guò)DMA操作,Cache緩存對(duì)應(yīng)的內(nèi)存的數(shù)據(jù)已經(jīng)被修改,而CPU本身并不知道,它仍然認(rèn)為Cache中的數(shù)據(jù)仍然還是內(nèi)存中的數(shù)據(jù),以后訪問(wèn)Cache映射的內(nèi)存時(shí),它仍然使用陳舊的Cache數(shù)據(jù),這就會(huì)發(fā)生Cache與內(nèi)存之間數(shù)據(jù)"不一致性"的錯(cuò)誤。一旦出現(xiàn)這樣的情況,沒(méi)有處理好,驅(qū)動(dòng)就將無(wú)法正常運(yùn)行。那么怎樣解決呢?最簡(jiǎn)單的方法是直接禁止DMA目標(biāo)地址范圍內(nèi)內(nèi)存的Cache功能,當(dāng)然這是犧牲性能的,但卻高可靠。不是嗎,所以這兩者之間究竟怎么平衡,還真不好解決。  其實(shí)啊,Cache不一致的情況在其他地方也是可能發(fā)生的,比如,對(duì)于帶MMU功能的arm處理器,在開(kāi)啟MMU之前,需要設(shè)置Cache無(wú)效,TLB也是如此。

說(shuō)了,那么多DMA理論的東西,剩下的來(lái)點(diǎn)Linux下DMA編程的東西,當(dāng)然,這里也是點(diǎn)一下,具體怎么操作,我可要賣個(gè)關(guān)子到下節(jié)了。 在內(nèi)存中用于與外設(shè)交互數(shù)據(jù)的一塊區(qū)域被稱作DMA緩沖區(qū),在設(shè)備不支持scatter/gatherCSG,分散/聚集操作的情況下,DMA緩沖區(qū)必須是物理上聯(lián)系的。

對(duì)于ISA設(shè)備而言,其DMA操作只能在16MB以下的內(nèi)存進(jìn)行,因此,在使用kmalloc()和__get_free_pages()及其類似函數(shù)申請(qǐng)DMA緩沖區(qū)時(shí)應(yīng)使用GFP_DMA標(biāo)志,這樣能保證獲得的內(nèi)存是具備DMA能力的。內(nèi)核中定義了__get_free_pages()針對(duì)DMA的"快捷方式"__get_dma_pages(),它在申請(qǐng)標(biāo)志中添加了GFP_DMA,如下所示:

#define __get_dma__pages(gfp_mask, order) 

__get_free_pages((gfp_mask) | GFP_DMA, (order))

"我不想使用order為參數(shù)的申請(qǐng)DMA內(nèi)存,感覺(jué)就是怪怪的,那咋辦?"

那?這樣吧,你就用另外一個(gè)函數(shù)dma_mem_alloc()源代碼如下:

/* dma_mem_alloc()返回值為虛擬地址 */

static unsigned long dma_mem_alloc(int size)

{

int order = get_order(size);//大小->指數(shù)

return __get_dma_pages(GFP_KERNEL, order);

}

上節(jié)我們說(shuō)到了dma_mem_alloc()函數(shù),需要說(shuō)明的是DMA的硬件使用總線地址而非物理地址,總線地址是從設(shè)備角度上看到的內(nèi)存地址,物理地址是從CPU角度上看到的未經(jīng)轉(zhuǎn)換的內(nèi)存地址(經(jīng)過(guò)轉(zhuǎn)換的那叫虛擬地址)。在PC上,對(duì)于ISA和PCI而言,總線即為物理地址,但并非每個(gè)平臺(tái)都是如此。由于有時(shí)候接口總線是通過(guò)橋接電路被連接,橋接電路會(huì)將IO地址映射為不同的物理地址。例如,在PRep(PowerPC Reference Platform)系統(tǒng)中,物理地址0在設(shè)備端看起來(lái)是0X80000000,而0通常又被映射為虛擬地址0xC0000000,所以同一地址就具備了三重身份:物理地址0,總線地址0x80000000及虛擬地址0xC0000000,還有一些系統(tǒng)提供了頁(yè)面映射機(jī)制,它能將任意的頁(yè)面映射為連續(xù)的外設(shè)總線地址。內(nèi)核提供了如下函數(shù)用于進(jìn)行簡(jiǎn)單的虛擬地址/總線地址轉(zhuǎn)換:

unsigned long virt_to_bus(volatile void *address);

void *bus_to_virt(unsigned long address);

在使用IOMMU或反彈緩沖區(qū)的情況下,上述函數(shù)一般不會(huì)正常工作。而且,這兩個(gè)函數(shù)并不建議使用。

需要說(shuō)明的是設(shè)備不一定能在所有的內(nèi)存地址上執(zhí)行DMA操作,在這種情況下應(yīng)該通過(guò)下列函數(shù)執(zhí)行DMA地址掩碼:

int dma_set_mask(struct device *dev, u64 mask);

比如,對(duì)于只能在24位地址上執(zhí)行DMA操作的設(shè)備而言,就應(yīng)該調(diào)用dma_set_mask(dev, 0xffffffff)。DMA映射包括兩個(gè)方面的工作:分配一片DMA緩沖區(qū);為這片緩沖區(qū)產(chǎn)生設(shè)備可訪問(wèn)的地址。結(jié)合前面所講的,DMA映射必須考慮Cache一致性問(wèn)題。內(nèi)核中提供了一下函數(shù)用于分配一個(gè)DMA一致性的內(nèi)存區(qū)域:

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

這個(gè)函數(shù)的返回值為申請(qǐng)到的DMA緩沖區(qū)的虛擬地址。此外,該函數(shù)還通過(guò)參數(shù)handle返回DMA緩沖區(qū)的總線地址。與之對(duì)應(yīng)的釋放函數(shù)為:

void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);

以下函數(shù)用于分配一個(gè)寫(xiě)合并(writecombinbing)的DMA緩沖區(qū):

void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);

與之對(duì)應(yīng)的是釋放函數(shù):dma_free_writecombine(),它其實(shí)就是dma_free_conherent,只不過(guò)是用了#define重命名而已。

此外,Linux內(nèi)核還提供了PCI設(shè)備申請(qǐng)DMA緩沖區(qū)的函數(shù)pci_alloc_consistent(),原型為:

void *pci_alloc_consistent(struct pci_dev *dev, size_t size, dma_addr_t *dma_addrp);  對(duì)應(yīng)的釋放函數(shù)為:

void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr, dma_addr_t dma_addr);

相對(duì)于一致性DMA映射而言,流式DMA映射的接口較為復(fù)雜。對(duì)于單個(gè)已經(jīng)分配的緩沖區(qū)而言,使用dma_map_single()可實(shí)現(xiàn)流式DMA映射:

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);  如果映射成功,返回的是總線地址,否則返回NULL.最后一個(gè)參數(shù)DMA的方向,可能取DMA_TO_DEVICE, DMA_FORM_DEVICE, DMA_BIDIRECTIONAL和DMA_NONE;

與之對(duì)應(yīng)的反函數(shù)是:

void dma_unmap_single(struct device *dev,dma_addr_t *dma_addrp,size_t size,enum dma_data_direction direction);

通常情況下,設(shè)備驅(qū)動(dòng)不應(yīng)該訪問(wèn)unmap()的流式DMA緩沖區(qū),如果你說(shuō)我就愿意這么做,我又說(shuō)寫(xiě)什么呢,選擇了權(quán)利,就選擇了責(zé)任,對(duì)吧。這時(shí)可先使用如下函數(shù)獲得DMA緩沖區(qū)的擁有權(quán):

void dma_sync_single_for_cpu(struct device *dev,dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);

在驅(qū)動(dòng)訪問(wèn)完DMA緩沖區(qū)后,應(yīng)該將其所有權(quán)還給設(shè)備,通過(guò)下面的函數(shù):[!--empirenews.page--]

void dma_sync_single_for_device(struct device *dev,dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);如果設(shè)備要求較大的DMA緩沖區(qū),在其支持SG模式的情況下,申請(qǐng)多個(gè)不連續(xù)的,相對(duì)較小的DMA緩沖區(qū)通常是防止申請(qǐng)?zhí)蟮倪B續(xù)物理空間的方法,在Linux內(nèi)核中,使用如下函數(shù)映射SG:

int dma_map_sg(struct device *dev,struct scatterlist *sg, int nents,enum dma_data_direction direction); 其中nents是散列表入口的數(shù)量,該函數(shù)的返回值是DMA緩沖區(qū)的數(shù)量,可能小于nents。對(duì)于scatterlist中的每個(gè)項(xiàng)目,dma_map_sg()為設(shè)備產(chǎn)生恰當(dāng)?shù)目偩€地址,它會(huì)合并物理上臨近的內(nèi)存區(qū)域。下面在給出scatterlist結(jié)構(gòu):

struct scatterlist

{

struct page *page;

unsigned int offset;   //偏移量

dma_addr_t dma_address;   //總線地址

unsigned int length;   //緩沖區(qū)長(zhǎng)度

}

執(zhí)行dma_map_sg()后,通過(guò)sg_dma_address()后可返回scatterlist對(duì)應(yīng)緩沖區(qū)的總線結(jié)構(gòu),sg_dma_len()可返回scatterlist對(duì)應(yīng)的緩沖區(qū)的長(zhǎng)度,這兩個(gè)函數(shù)的原型是:

dma_addr_t sg_dma_address(struct scatterlist *sg);   unsigned int sg_dma_len(struct scatterlist *sg);

在DMA傳輸結(jié)束后,可通過(guò)dma_map_sg()的反函數(shù)dma_unmap_sg()去除DMA映射:

void dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);   SG映射屬于流式DMA映射,與單一緩沖區(qū)情況下流式DMA映射類似,如果設(shè)備驅(qū)動(dòng)一定要訪問(wèn)映射情況下的SG緩沖區(qū),應(yīng)該先調(diào)用如下函數(shù):

int dma_sync_sg_for_cpu(struct device *dev,struct scatterlist *sg, int nents,enum dma_data_direction direction);

訪問(wèn)完后,通過(guò)下列函數(shù)將所有權(quán)返回給設(shè)備:

int dma_map_device(struct device *dev,struct scatterlist *sg, int nents,enum dma_data_direction direction);

Linux 系統(tǒng)中可以有一個(gè)相對(duì)簡(jiǎn)單的方法預(yù)先分配緩沖區(qū),那就是同步"mem="參數(shù)預(yù)留內(nèi)存。例如,對(duì)于內(nèi)存為64MB的系統(tǒng),通過(guò)給其傳遞mem=62MB命令行參數(shù)可以使得頂部的2MB內(nèi)存被預(yù)留出來(lái)作為IO內(nèi)存使用,這2MB內(nèi)存可以被靜態(tài)映射,也可以執(zhí)行ioremap()。

相應(yīng)的函數(shù)都介紹完了:說(shuō)真的,好費(fèi)勁啊,我都想放棄了,可為了小王,我繼續(xù)哈在linux設(shè)備驅(qū)動(dòng)中如何操作呢:

像使用中斷一樣,在使用DMA之前,設(shè)備驅(qū)動(dòng)程序需要首先向系統(tǒng)申請(qǐng)DMA通道,申請(qǐng)DMA通道的函數(shù)如下:

int request_dma(unsigned int dmanr, const char * device_id);  同樣的,設(shè)備結(jié)構(gòu)體指針可作為傳入device_id的最佳參數(shù)。

使用完DMA通道后,應(yīng)該使用如下函數(shù)釋放該通道:void free_dma(unsinged int dmanr);

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

CPU親和度通過(guò)限制進(jìn)程或線程可以運(yùn)行的CPU核心集合,使得它們只能在指定的CPU核心上執(zhí)行。這可以減少CPU緩存的失效次數(shù),提高緩存命中率,從而提升系統(tǒng)性能。

關(guān)鍵字: Linux 嵌入式

在Linux系統(tǒng)性能優(yōu)化中,內(nèi)存管理與網(wǎng)絡(luò)連接處理是兩大核心領(lǐng)域。vm.swappiness與net.core.somaxconn作為關(guān)鍵內(nèi)核參數(shù),直接影響系統(tǒng)在高負(fù)載場(chǎng)景下的穩(wěn)定性與響應(yīng)速度。本文通過(guò)實(shí)戰(zhàn)案例解析這兩個(gè)...

關(guān)鍵字: Linux 內(nèi)存管理

《帶得走的智能制造》暑期課程圓滿落幕 北京2025年7月25日 /美通社/ -- 近日,由國(guó)際獨(dú)立第三方檢測(cè)、檢驗(yàn)和認(rèn)證機(jī)構(gòu)德國(guó)萊茵TÜV大中華區(qū)(以下簡(jiǎn)稱"TÜV萊茵")與北京...

關(guān)鍵字: 智能制造 BSP DMA 信息安全

對(duì)于LLM,我使用b谷歌Gemini的免費(fèi)層,所以唯一的成本是n8n托管。在使用了n8n Cloud的免費(fèi)積分后,我決定將其托管在Railway上(5美元/月)。然而,由于n8n是開(kāi)源的,您可以在自己的服務(wù)器上托管它,而...

關(guān)鍵字: 人工智能 n8n Linux

在Linux系統(tǒng)管理中,權(quán)限控制是安全運(yùn)維的核心。本文通過(guò)解析/etc/sudoers文件配置與組策略的深度應(yīng)用,結(jié)合某金融企業(yè)生產(chǎn)環(huán)境案例(成功攔截98.7%的非法提權(quán)嘗試),揭示精細(xì)化權(quán)限管理的關(guān)鍵技術(shù)點(diǎn),包括命令別...

關(guān)鍵字: Linux 用戶權(quán)限 sudoers文件

Linux內(nèi)核中的信號(hào)量(Semaphore)是一種用于資源管理的同步原語(yǔ),它允許多個(gè)進(jìn)程或線程對(duì)共享資源進(jìn)行訪問(wèn)控制。信號(hào)量的主要作用是限制對(duì)共享資源的并發(fā)訪問(wèn)數(shù)量,從而防止系統(tǒng)過(guò)載和數(shù)據(jù)不一致的問(wèn)題。

關(guān)鍵字: Linux 嵌入式

在云計(jì)算與容器化技術(shù)蓬勃發(fā)展的今天,Linux網(wǎng)絡(luò)命名空間(Network Namespace)已成為構(gòu)建輕量級(jí)虛擬網(wǎng)絡(luò)的核心組件。某頭部互聯(lián)網(wǎng)企業(yè)通過(guò)命名空間技術(shù)將測(cè)試環(huán)境資源消耗降低75%,故障隔離效率提升90%。本...

關(guān)鍵字: Linux 云計(jì)算

在Linux內(nèi)核4.18+和主流發(fā)行版(RHEL 8/Ubuntu 20.04+)全面轉(zhuǎn)向nftables的背景下,某電商平臺(tái)通過(guò)遷移將防火墻規(guī)則處理效率提升40%,延遲降低65%。本文基于真實(shí)生產(chǎn)環(huán)境案例,詳解從ipt...

關(guān)鍵字: nftables Linux

在Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)中,等待隊(duì)列(Wait Queue)是實(shí)現(xiàn)進(jìn)程睡眠與喚醒的核心機(jī)制,它允許進(jìn)程在資源不可用時(shí)主動(dòng)放棄CPU,進(jìn)入可中斷睡眠狀態(tài),待資源就緒后再被喚醒。本文通過(guò)C語(yǔ)言模型解析等待隊(duì)列的實(shí)現(xiàn)原理,結(jié)合...

關(guān)鍵字: 驅(qū)動(dòng)開(kāi)發(fā) C語(yǔ)言 Linux

在Unix/Linux進(jìn)程間通信中,管道(pipe)因其簡(jiǎn)單高效被廣泛使用,但默認(rèn)的半雙工特性和無(wú)同步機(jī)制容易導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)。本文通過(guò)父子進(jìn)程雙向通信案例,深入分析互斥鎖與狀態(tài)機(jī)在管道同步中的應(yīng)用,實(shí)現(xiàn)100%可靠的數(shù)據(jù)傳...

關(guān)鍵字: 管道通信 父子進(jìn)程 Linux
關(guān)閉