嵌入式軟件工程師面試題目整理(二)


@

目錄

嵌入式軟件工程師面試題目整理(二)

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部分


免責聲明!

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



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