@
- 嵌入式軟件工程師面試題目整理(二)
- linux中內核空間及用戶空間的區別?用戶空間與內核通信方式有哪些?
- 字符設備和塊設備的區別,請分別列舉一些實際的設備說出它們是屬於哪一類設備
- linux中系統調用過程?如:應用程序中read()在linux中執行過程即從用戶空間到內核空間?
- 查看驅動模塊中打印信息應該使用什么命令?如何查看內核中已有的字符設備的信息?如何查看正在使用的有哪些中斷號?
- copy_to_user()和copy_from_user()主要用於實現什么功能?一般用於file_operations結構的哪些函數里面?
- 請簡述主設備號和次設備號的用途。如果執行mknod chartest c 4 64,創建chartest設備。請分析chartest使用的是那一類設備驅動程序。
- 設備驅動程序中如何注冊一個字符設備?分別解釋一下它的幾個參數的含義。
- 字符型驅動設備怎么創建設備文件?
- insmod 一個驅動模塊,會執行模塊中的哪個函數?rmmod呢?這兩個函數在設計上要注意哪些?遇到過卸載驅動出現異常沒?是什么問題引起的?
- 設備驅動模型三個重要成員是?platform總線的匹配規則是?在具體應用上要不要先注冊驅動再注冊設備?有先后順序沒?
- 內核函數mmap的實現原理,機制?
- 申請內存的方式
- IIC原理,總線框架,設備編寫方法
- Linux中的用戶模式和內核模式是什么含意
- 怎樣申請大塊內核內存?
- 用戶進程間通信主要哪幾種方式
- 內核配置編譯及Makefile?
- 談談對Volatile關鍵字的理解
- framebuffer機制
- spinlock與信號量的區別
- linux中的同步機制
- 自旋鎖和信號量在互斥使用時需要注意哪些?在中斷服務程序里面的互斥是使用自旋鎖還是信號量?還是兩者都能用?為什么?
- 驅動里面為什么要有並發、互斥的控制?如何實現?講個例子?
- linux中斷實現機制、tasklet和workqueue的區別和底層實現的區別,為什么要區分中斷上半部和中斷下半部
- 中斷和輪詢哪個效率高?怎樣決定是采用中斷方式還是采用輪詢方式去實現驅動?
- 寫一個中斷服務需要注意哪些?如果中斷產生之后要做比較多的事情你是怎么做的?
- IRQ和FIQ有什么區別,在CPU里面是是怎么做的?
- Linux軟中斷和工作隊列的作用是什么
嵌入式軟件工程師面試題目整理(二)
linux中內核空間及用戶空間的區別?用戶空間與內核通信方式有哪些?
區別:
1.內和空間和用戶空間的划分
Linux簡化了分段機制,使得虛擬地址與線性地址總是一致,因此,Linux的虛擬地址空間也為0~4G.Linux內核將這4G字節的空間分為兩部分。將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱為"內核空間".而將較低的3G字節(從虛擬地址 0x00000000到0xBFFFFFFF),供各個進程使用,稱為"用戶空間)。因為每個進程可以通過系統調用進入內核,因此,Linux內核由系統內的所有進程共享。於是,從具體進程的角度來看,每個進程可以擁有4G字節的虛擬空間。
2.存儲內容不同
內核空間中存放的是內核代碼和數據,而進程的用戶空間中存放的是用戶程序的代碼和數據。不管是內核空間還是用戶空間,它們都處於虛擬空間中。
下面總結了7種方式,主要對以前不是很熟悉的方式做了編程實現,以便加深印象。
3.其他
內核空間和用戶空間上不同太多了,說不完,比如用戶態的鏈表和內核鏈表不一樣;用戶態用printf,內核態用printk;用戶態每個應用程序空間是虛擬的,相對獨立的,內核態中卻不是獨立的,所以編程要非常小心。等等。
用戶空間與內核通信方式
見前Linux部分
字符設備和塊設備的區別,請分別列舉一些實際的設備說出它們是屬於哪一類設備
字符設備:字符設備是個能夠像字節流(類似文件)一樣被訪問的設備,由字符設備驅動程序來實現這種特性。字符設備驅動程序通常至少實現open,close,read和write系統調用。字符終端、串口、鼠標、鍵盤、攝像頭、聲卡和顯卡等就是典型的字符設備。
塊設備:和字符設備類似,塊設備也是通過/dev目錄下的文件系統節點來訪問。塊設備上能夠容納文件系統,如:u盤,SD卡,磁盤等。
字符設備和塊設備的區別僅僅在於內核內部管理數據的方式,也就是內核及驅動程序之間的軟件接口,而這些不同對用戶來講是透明的。在內核中,和字符驅動程序相比,塊驅動程序具有完全不同的接口。
公眾號:嵌入式與Linux那些事 CSDN: 嵌入式與Linux那些事 來源網絡,轉載聲明
linux中系統調用過程?如:應用程序中read()在linux中執行過程即從用戶空間到內核空間?
必須知道的知識:
(1) 在Linux文件系統中,每個文件都用一個struct inode結構體來描述,這個結構體記錄了這個文件的所有信息,例如文件類型,訪問權限等。
(2) 在linux操作系統中,每個驅動程序在應用層的/dev目錄或者其他如/sys目錄下都會有一個文件與之對應。
(3) 在linux操作系統中, 每個驅動程序都有一個設備號。
(4) 在linux操作系統中,每打開一次文件,Linux操作系統會在VFS層分配一個struct file結構體來描述打開的文件。
注意:常常我們認為,struct inode描述的是文件的靜態信息,即這些信息很少會改變,而struct file描述的是動態信息,即對文件的操作的時候,struct file里面的信息經常會發生變化。典型的是struct file結構體里面的f_ops(記錄當前文件的位移量),每次讀寫一個普通文件時f_ops的值都會發生改變。
通過上圖我們可以知道,如果想訪問底層設備,就必須打開對應的設備文件。也就是在這個打開的過程中,Linux內核將應用層和對應的驅動程序關聯起來。
(1) 當open函數打開設備文件時,可以根據設備文件對應的struct inode結構體描述的信息,可以知道接下來要操作的設備類型(字符設備還是塊設備),還會分配一個struct file結構體。
(2) 根據struct inode結構體里面記錄的設備號,可以找到對應的驅動程序。這里以字符設備為例。在Linux操作系統中每個字符設備都有一個struct cdev結構體。此結構體描述了字符設備所有信息,其中最重要的一項就是字符設備的操作函數接口。
(3) 找到struct cdev結構體后,linux內核就會將struct cdev結構體所在的內存空間首地址記錄在struct inode結構體i_cdev成員中,將struct cdev結構體中的記錄的函數操作接口地址記錄在struct file結構體的f_ops成員中。
(4) 任務完成,VFS層會給應用返回一個文件描述符(fd)。這個fd是和struct file結構體對應的。接下來上層應用程序就可以通過fd找到struct file,然后在有struct file找到操作字符設備的函數接口了。
公眾號:嵌入式與Linux那些事 CSDN: 嵌入式與Linux那些事 來源網絡,轉載聲明
總結
1.應用層調用open函數,在VFS層中找到struct inode結構體->>判斷是字符設備還是塊設備,根據設備號,可以找到對應的驅動程序
2.在驅動層中,每個字符設備都有一個struct cdev結構體,這個結構體通過struct inode結構體中的i_cdev把連接起VFS層和驅動層,struct cdev結構體描述了字符設備所有信息,其中最重要的一項就是字符設備的操作函數接口
3.struct cdev結構體中的struct file結構體記錄了操作字符設備的一些函數,比如open read write函數等。
struct file結構體其實是在VFS層的,通過struct file結構體指針指向驅動層的struct file結構體將驅動層函數和VFS層鏈接起來
4.任務完成,VFS層會給應用返回一個文件描述符(fd)。這個fd是和struct file結構體對應的。
查看驅動模塊中打印信息應該使用什么命令?如何查看內核中已有的字符設備的信息?如何查看正在使用的有哪些中斷號?
1) 查看驅動模塊中打印信息的命令:dmesg
2) 查看字符設備信息可以用lsmod 和modprobe,ismod可以查看模塊的依賴關系,modprobe在加載模塊時會加載其他依賴的 模塊。
3) 顯示當前使用的中斷號cat /proc/interrupt
copy_to_user()和copy_from_user()主要用於實現什么功能?一般用於file_operations結構的哪些函數里面?
由於內核空間和用戶空間是不能互相訪問的,如果需要訪問就必須借助內核函數進行數據讀寫。copy_to_user():完成內核空間到用戶空間的復制,copy_from_user():是完成用戶空間到內核空間的復制。一般用於file_operations結構里的read,write,ioctl等內存數據交換作用的函數。當然,如果ioctl沒有用到內存數據復制,那么就不會用到這兩個函數。
請簡述主設備號和次設備號的用途。如果執行mknod chartest c 4 64,創建chartest設備。請分析chartest使用的是那一類設備驅動程序。
1)主設備號:主設備號標識設備對應的特定的驅動程序。雖然現代的linux內核允許多個驅動程序共享主設備號,但我們看待的大多數設備仍然按照“一個主設備對應一個驅動程序”的原則組織。
次設備號:次設備號由內核使用,用於確定由主設備號對應驅動程序中的各個設備。。依賴於驅動程序的編寫方式,我們可以通過次設備號獲得一個指向內核設備的直接指針,也可將此設備號當作設備本地數組的索引。
2)chartest 表示設備節點,4表示主設備號,64表示次設備號。(感覺類似於串口終端或者字符設備終端)。
設備驅動程序中如何注冊一個字符設備?分別解釋一下它的幾個參數的含義。
注冊一個字符設備驅動有兩種方法:
1) void cdev_init(struct cdev *cdev, struct file_operations *fops)
該注冊函數可以將cdev結構嵌入到自己的設備特定的結構中。cdev是一個指向結構體cdev的指針,而fops是指向一個類似於f file_operations結構(可以是file_operations結構,但不限於該結構)的指針。
2) int register_chrdev(unsigned int major, const char *namem , struct file operations *fopen);
該注冊函數是早期的注冊函數,major是設備的主設備號,name是驅動程序的名稱,而fops是默認的file_operations結構(這是只限於file_operations結構)。對於register_chrdev的調用將為給定的主設備號注冊0-255作為次設備號,並為每個 設備建 立一個對應的默認cdev結構。
字符型驅動設備怎么創建設備文件?
手動創建:mknod /dev/led c 250 0 其中dev/led 為設備節點 ,c 代表字符設備, 250代表主設備號, 0代表次設備號。
還有UDEV/MDEV自動創建設備文件的方式,UDEV/MDEV是運行在用戶態的程序,可以動態管理設備文件,包括創建和刪除設備文件,運行在用戶態意味着系統要運行之后。在 /etc/init.d/rcS 腳本文件中會執行 mdev -s 自動創建設備節點。
insmod 一個驅動模塊,會執行模塊中的哪個函數?rmmod呢?這兩個函數在設計上要注意哪些?遇到過卸載驅動出現異常沒?是什么問題引起的?
答: insmod調用init函數,rmmod調用exit函數。這兩個函數在設計時要注意什么?卸載模塊時曾出現卸載失敗的情形,原因是存在進程正在使用模塊,檢查代碼后發現產生了死鎖的問題。
要注意在init函數中申請的資源在exit函數中要釋放,包括存儲,ioremap,定時器,工作隊列等等。也就是一個模塊注冊進內核,退出內核時要清理所帶來的影響,帶走一切不留下一點痕跡。
在LCD驅動的 file_opreations結構體中有個relase函數,就算沒有用到也要定義
設備驅動模型三個重要成員是?platform總線的匹配規則是?在具體應用上要不要先注冊驅動再注冊設備?有先后順序沒?
設備驅動模型三個重要成員是 總線、設備、驅動;
platfoem總線的匹配規則是:要匹配的設備和驅動都要注冊,設備可以在設備樹里注冊,也可以通過代碼注冊設備,匹配成功會去調用驅動程序里的probe函數(probe函數在這個platform_driver結構體中注冊)。
內核函數mmap的實現原理,機制?
mmap系統調用(功能)
void* mmap ( void * addr , size_t len , int prot , int flags ,int fd , off_t offset )
內存映射函數mmap, 負責把文件內容映射到進程的虛擬內存空間, 通過對這段內存的讀取和修改,來實現對文件的讀取和修改,而不需要再調用read,write等操作。
列舉最少3種你所知道的嵌入式的體系結構,並請說明什么是ARM體系結構。
arm,mips,x86
三種架構的區別
1. ARM
ARM是高級精簡指令集的簡稱(Advanced RISC Machine),它是一個32位的精簡指令集架構,但也配備16位指令集,一般來講比等價32位代碼節省達35%,卻能保留32位系統的所有優勢。
ARM處理器的主要特點是:
體積小、低功耗、低成本、高性能——ARM被廣泛應用在嵌入式系統中的最重要的原因。
支持Thumb(16位)/ARM(32位)雙指令集,能很好的兼容8位/16位器件;
大量使用寄存器,指令執行速度更快;
大多數數據操作都在寄存器中完成;
尋址方式靈活簡單,執行效率高;
指令長度固定;
Load_store結構:在RISC中,所有的計算都要求在寄存器中完成。而寄存器和內存的通信則由單獨的指令來完成。而在CSIC中,CPU是可以直接對內存進行操作的。
流水線處理方式。
2. MIPS
MIPS架構(英語:MIPS architecture,為Microprocessor without interlocked piped stages architecture的縮寫,亦為Millions of Instructions Per Second的相關語),是一種采取精簡指令集(RISC)的處理器架構,1981年出現,由MIPS科技公司開發並授權,廣泛被使用在許多電子產品、網絡設備、個人娛樂裝置與商業裝置上。最早的MIPS架構是32位,最新的版本已經變成64位。
它的基本特點是:
包含大量的寄存器、指令數和字符;
可視的管道延時時隙;
這些特性使MIPS架構能夠提供最高的每平方毫米性能和當今SoC設計中最低的能耗。
3. X86
X86架構是芯片巨頭Intel設計制造的一種微處理器體系結構的統稱。如果這樣說你不理解,那么當我說出8086,80286等這樣的詞匯時,相信你肯定馬上就理解了,正是基於此,X86架構這個名稱被廣為人知。
如今,我們所用的PC絕大部分都是X86架構。可見X86架構普及程度,這也和Intel的霸主地位密切相關。
x86采用CISC(Complex Instruction Set Computer,復雜指令集計算機)架構。與采用RISC不同的是,在CISC處理器中,程序的各條指令是按順序串行執行的,每條指令中的各個操作也是按順序串行執行的。順序執行的優點是控制簡單,但計算機各部分的利用率不高,執行速度慢。
總結:
申請內存的方式
見之前Linux部分整理
IIC原理,總線框架,設備編寫方法
見之前Linux部分整理
Linux中的用戶模式和內核模式是什么含意
見之前Linux部分整理
怎樣申請大塊內核內存?
vmalloc
用戶進程間通信主要哪幾種方式
見之前操作系統部分整理
內核配置編譯及Makefile?
最近在學習Linux內核的配置、編譯及Makefile文件。今天總結一下學習成果,分享給大家_
1.解壓縮打補丁
首先是解壓縮你獲取到的Linux內核。這里我用到的是linux.2.22.6版本的內核。在Linux下命令行通過tar xjf linux.2.22.6.tar.bz2解壓內核。然后,如果你需要對這個內核打補丁的話,用patch命令:patch -px <../linux.2.22.6.patch。這里的px指的是忽略掉補丁文件中描述的第幾個斜杠。也就是忽略前x個目錄。
--- linux-2.6.22.6/arch/arm/configs/s3c2410_defconfig
+++ linux-2.6.22.6_jz2440/arch/arm/configs/s3c2410_defconfig
如果你此刻就在內核的根目錄下,即linux-2.6.22.6下,也就是說打補丁需要忽略掉一個斜杠的目錄。那么打補丁的命令就是patch -p1 <../linux.2.22.6.patch。
2.配置內核
現在補丁已經打好了,接下來就是配置內核了。這里配置有3種方法:
1>直接進行make menuconfig。這是最麻煩的一種方法,所有的配置都需要你來操作。
2>在默認配置上自己修改,也就是修改defconfig文件。使用 find -name "defconfig"查找你的架構對應的默認配置文件。我是在arch/arm/configs找到自己板子的默認配置文件。執行defconfig文件: make XXX_defconfig。XXX是你具體使用的板子型號。執行這一操作后,結果保存在.config文件。然后再執行make menuconfig命令。這時的配置就是在默認配置上稍加修改就可以了。
3>使用廠家的配置文件。如果你的硬件有廠家提供的config文件那是最輕松的。直接cp XXX .config。然后執行make menuconfig。
這里詳細給大家講一下內核的配置。Linux的內核配置,就是為了生成.config文件。因為在編譯時需要用.config文件生成其他相關配置文件。我們的配置項大多是例如CONFIG_XXXDRIVER,這里的XXXDRIVER指的是各種驅動。我們需要告訴內核,這些驅動是編譯進內核,還是編譯成模塊。通過查找CONFIG_XXXDRIVER,我們可以發現,它出現在四個地方:
1>C源代碼
2>子目錄Makefile:drivers/XXX/Makefile
3>include/config/auto.conf
4>include/linux/autoconf.h
這里首先說明:.config文件在進行內核編譯時(make uImage)生成了include/config/auto.conf和include/linux/autoconf.h。通過查看C源代碼我們發現CONFIG_XXXDRIVER是一個宏定義,等於一個常量。在include/linux/autoconf.h中宏定義CONFIG_XXXDRIVER為一個常量,可能是0或1。那么現在有一個問題,就是CONFIG_XXXDRIVER到底被編譯進內核還是編譯成一個模塊呢?這在C語言中是無法進行區分的,這種區分體現在哪里呢?這種區分體現在子目錄的Makefile文件中。在子目錄的Makefile中,若有 obj -y += XXX.o則表示XXX.c被編譯進內核;obj -m +=XXX.o則表示XXX被編譯成模塊,為XXX.ko。include/config/auto.conf文件則是對CONFIG_XXXDRIVER進行賦值,為y時表示編譯進內核,為m時表示編譯成獨立模塊。
#這里是include/config/auto.conf的部分內容
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.22.6
# Sun Nov 27 18:34:38 2016
#
CONFIG_CPU_S3C244X=y
CONFIG_CPU_COPY_V4WB=y
CONFIG_CRYPTO_CBC=y
CONFIG_CPU_S3C2410_DMA=y
CONFIG_CRYPTO_ECB=m
CONFIG_SMDK2440_CPU2440=y
復制代碼
復制代碼
#這里是drivers/i2c/Makefile
# Makefile for the i2c core.
#
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-y += busses/ chips/ algos/
ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
endif
3.編譯內核
通過上面的描述,我們可以知道,在每個driver下,都有一個Makefile文件。來定義這個驅動是編譯進內核還是編譯成模塊。這里稍稍提一下。上面我們講到了在Makefile中單個文件怎樣編譯進內核和編譯成模塊。但是如果有兩個以上的文件該如何書寫呢?這里舉個例子:obj -y += a.o b.o就表示將a.c和b.c編譯進內核。
obj -m += ab.o
ab -objs := a.o b.o
就可以表示將a.c和b.c共同編譯成為一個模塊。過程就是 a.c生成a.o , b.c生成b.o。a.o 和b.o共同生成ab.ko。在以往的對uboot啟動內核的代碼分析中,我們提到過編譯內核生成的uImage是由兩部分組成的:頭部+Linux內核。這個頭部包含了很多初始化的參數信息,例如內核的加載地址和入口地址。在編譯內核時,我們直接執行make uImage即可。那么在文件中是怎樣定義uImage的呢?又是怎樣生成uImage的呢?
首先,我們通過查找Makefile,發現uImage在arch/arm/Makefile中,而這個架構目錄下的Makefile被包含進頂層目錄的Makefile中。這樣我們在執行 make uImage時,頂層目錄的Makefile就可以調用架構子目錄下的Makefile,實現對內核的編譯,生成uImage。
頂層目錄下Makefile中相關命令:
include $(srctree)/arch/$(ARCH)/Makefile
架構目錄下Makefile相關命令:
zImage Image xipImage bootpImage uImage: vmlinux
這就是剛剛所說的頂層Makefile調用架構目錄下Makefile,架構目錄下Makefile生成uImage,而且依賴於vmlinux文件。下面我們就開始講解如何生成vmlinux文件。在頂層Makefile中,我們找到了有關生成vmlinux的大部分命令。
頂層目錄Makefile:
init-y := init/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/built-in.o, $(core-y))
libs-y := lib/
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
drivers-y := drivers/ sound/
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds
export KBUILD_VMLINUX_OBJS := $(vmlinux-all)
架構目錄Makefile:
zImage Image xipImage bootpImage uImage: vmlinux
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
我已經把頂層目錄和架構目錄下生成vmlinux的命令摘選出來。首先,我們看要想生成vmlinux,需要vmlinux-lds文件、vmlinux-init文件、vmlinux-main文件。其中,vmlinux-lds是鏈接腳本文件,定義了代碼段,數據段的存放位置。這里我們接着往下看,vmlinux-init需要head-y和init-y,通過查看兩個Makefile,我們可以得到經過轉換后的結果:
head-y :=
arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
init-y := $(patsubst %/, %/built-in.o, $(init-y)) = init/built-in.o
core-y := $(patsubst %/, %/built-in.o, $(core-y))
= usr/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
libs-y := $(libs-y1) $(libs-y2) =lib/lib.a lib/built-in.o
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) = drivers/built-in.o sound/built-in.o
net-y := $(patsubst %/, %/built-in.o, $(net-y)) = net/built-in.o
現在已經分析了內核編譯的全部過程。那怎樣知道我們分析的到底對不對,通過實際執行make uImage我們就可以看到執行過程。這是執行make uImage過程中的部分相關命令:
arm-linux-ld -EL -p --no-undefined -X -o vmlinux
-T arch/arm/kernel/vmlinux.lds
arch/arm/kernel/head.o
arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o
可以看到,首先目標要生成vmlinux,然后是鏈接腳本為vmlinux.lds。開始生成第一個文件:head.o,第二個文件:init_task.o。這和我們分析的完全一致。接下來以此類推,和我們分析的相同,也就是說我們分析的是正確的。
SECTIONS
{
. = (0xc0000000) + 0x00008000;
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}
.init : { /* Init code and data */
*(.init.text)
_einittext = .;
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
. = ALIGN(16);
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(.early_param.init)
__early_end = .;
__initcall_start = .;
這是鏈接腳本vmlinux.lds中的部分內容。首先定義了虛擬地址:(0xc0000000) + 0x00008000。 然后是首先執行頭部文件,這與我們分析的完全一致。代碼段,初始化代碼段等等。
這就是Linux內核的從配置到編譯的全部分析了_
談談對Volatile關鍵字的理解
見之前C語言部分整理
framebuffer機制
Linux抽象出FrameBuffer這個設備來供用戶態進程實現直接寫屏。Framebuffer機制模仿顯卡的功能,將顯卡硬件結構抽象掉,可以通過Framebuffer的讀寫直接對顯存進行操作。用戶可以將Framebuffer看成是顯示內存的一個映像,通過mmap將其映射到進程地址空間之后,就可以直接進行讀寫操作,而寫操作可以立即反應在屏幕上。這種操作是抽象的,統一的。用戶不必關心物理顯存的位置、換頁機制等等具體細節,這些都是由Framebuffer設備驅動來完成的。通過mmap調用把顯卡的物理內存空間映射到用戶空間。
spinlock與信號量的區別
見之前操作系統部分整理
linux中的同步機制
說出你所知道的各類linux系統的各類同步機制(重點),什么是死鎖?如何避免死鎖(每個技術面試官必問)
原子操作,不會被任何事務給打斷,通常用於資源計數,引用計數。TCP/IP協議棧的IP碎片計數。
信號量。就像一個房間有好幾把鑰匙,拿到鑰匙就能進去訪問。設置為1的時候變為了mutex。絕大部分情況下作為互斥鎖使用。
讀寫信號量。可以允許多個讀,一個寫。一旦有人在寫,就大家都不能讀。但是如果沒人在寫,就可以允許很多一起讀。
自旋鎖。自旋鎖與互斥鎖的區別在於不會導致睡眠,如果自旋鎖被其他執行單元持有了,那么調用者就一直自旋在那循環地看持有者是否已經釋放,而不睡眠。在持有時間短的情況下使用會比互斥鎖高效。
順序鎖(seqlock)。讀者可以在寫的時候讀,寫者也可以在讀的時候寫,但是寫與寫之間還是互斥的。利用了一個序號,寫的時候要對序號加1,這樣讀的人可以知道讀的期間有沒有人寫。
什么是死鎖及死鎖的必要條件和解決方法
死鎖。就是幾個進程申請資源,出現了無限循環等待的現象。
四個必要條件
資源是互斥的
不可搶占
占有且申請
循環等待
解決方案也從這四個條件來着手
1. 資源的互斥是客觀的,要改變的話不現實。
2. 申請資源不可滿足的時候釋放已經申請到的資源。
3. 只有在全部資源都可以得到的情況下才一次性分配。
4. 資源有序分配。給所有資源編號,進程對資源的請求必須是嚴格遞增的序列,只有在占有了小號資源的情況下才能申請了大號資源。(假設打印機是小號資源,CDROM是大號資源)
自旋鎖和信號量在互斥使用時需要注意哪些?在中斷服務程序里面的互斥是使用自旋鎖還是信號量?還是兩者都能用?為什么?
使用自旋鎖的進程不能睡眠,使用執行時間短的任務,使用信號量的進程可以睡眠,適合於執行時間較長的任務。中斷服務例程中的互斥使用的是自旋鎖,原因是在中斷處理例程中,硬中斷是關閉的,這樣會丟失可能到來的中斷。
驅動里面為什么要有並發、互斥的控制?如何實現?講個例子?
並發(concurrency)指的是多個執行單元同時、並行被執行,而並發的執行單元對共 享資源(硬件資源和軟件上的全局變量、靜態變量等)的訪問則很容易導致競態(race conditions)。
解決競態問題的途徑是保證對共享資源的互斥訪問,所謂互斥訪問就是指一個執行單元 在訪問共享資源的時候,其他的執行單元都被禁止訪問。
訪問共享資源的代碼區域被稱為臨界區,臨界區需要以某種互斥機 制加以保護,中斷屏蔽,原子操作,自旋鎖,和信號量都是linux設備驅動中可采用的互斥途徑。
linux中斷實現機制、tasklet和workqueue的區別和底層實現的區別,為什么要區分中斷上半部和中斷下半部
見之前Linux部分整理
中斷和輪詢哪個效率高?怎樣決定是采用中斷方式還是采用輪詢方式去實現驅動?
中斷是CPU處於被動狀態下來接受設備的信號,而輪詢是CPU主動去查詢該設備是否有請求。凡事都是兩面性,所以,看效率不能簡單的說那個效率高。如果是請求設備是一個頻繁請求cpu的設備,或者有大量數據請求的網絡設備,那么輪詢的效率是比中斷高。如果是一般設備,並且該設備請求cpu的頻率比較底,則用中斷效率要高一些。主要是看請求頻率。
寫一個中斷服務需要注意哪些?如果中斷產生之后要做比較多的事情你是怎么做的?
第一: 中斷處理例程應該盡量短,把能放在后半段(tasklet,等待隊列等)的任務盡量放在后半段。
寫一個中斷服務程序要注意快進快出,在中斷服務程序里面盡量快速采集信息,包括硬件信息,然后退出中斷,要做其它事情可以使用工作隊列或者tasklet方式。也就是中斷上半部和下半部。
第二:中斷服務程序中不能有阻塞操作。應為中斷期間是完全占用CPU的(即不存在內核調度),中斷被阻塞住,其他進程將無法操作;
第三:中斷服務程序注意返回值,要用操作系統定義的宏做為返回值,而不是自己定義的OK,FAIL之類的。
IRQ和FIQ有什么區別,在CPU里面是是怎么做的?
FIQ和IRQ是兩種不同類型的中斷,ARM為了支持這兩種不同的中斷,提供了對應的叫做FIQ和IRQ處理器模式(ARM有7種處理模式)。
一般的中斷控制器里我們可以配置與控制器相連的某個中斷輸入是FIQ還是IRQ,所以一個中斷是可以指定為FIQ或者IRQ的,為了合理,要求系統更快響應,自身處理所耗時間也很短的中斷設置為FIQ,否則就設置了IRQ。
如果該中斷設置為了IRQ,那么當該中斷產生的時候,中斷處理器通過IRQ請求線告訴ARM,ARM就知道有個IRQ中斷來了,然后ARM切換到IRQ模式運行。類似的如果該中斷設置為FIQ,那么當該中斷產生的時候,中斷處理器通過FIQ請求線告訴ARM,ARM就知道有個FIQ中斷來了,然后切換到FIQ模式運行。
簡單的對比的話就是FIQ比IRQ快,為什么快呢?
ARM的FIQ模式提供了更多的banked寄存器,r8到r14還有SPSR,而IRQ模式就沒有那么多,R8,R9,R10,R11,R12對應的banked的寄存器就沒有,這就意味着在ARM的IRQ模式下,中斷處理程序自己要保存R8到R12這幾個寄存器,然后退出中斷處理時程序要恢復這幾個寄存器,而FIQ模式由於這幾個寄存器都有banked寄存器,模式切換時CPU自動保存這些值到banked寄存器,退出FIQ模式時自動恢復,所以這個過程FIQ比IRQ快.
FIQ比IRQ有更高優先級,如果FIQ和IRQ同時產生,那么FIQ先處理。
在symbian系統里,當CPU處於FIQ模式處理FIQ中斷的過程中,預取指令異常,未定義指令異常,軟件中斷全被禁止,所有的中斷被屏蔽。所以FIQ就會很快執行,不會被其他異常或者中斷打斷,所以它又比IRQ快了。而IRQ不一樣,當ARM處理IRQ模式處理IRQ中斷時,如果來了一個FIQ中斷請求,那正在執行的IRQ中斷處理程序會被搶斷,ARM切換到FIQ模式去執行這個FIQ,所以FIQ比IRQ快多了。
另外FIQ的入口地址是0x1c,IRQ的入口地址是0x18。
Linux軟中斷和工作隊列的作用是什么
見之前Linux部分