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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

面经——嵌入式软件工程师面试遇到的经典题目

發布時間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面经——嵌入式软件工程师面试遇到的经典题目 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

參考:嵌入式軟件工程師面試遇到的經典題目
作者:一只青木呀
發布時間: 2020-11-04 23:43:16
網址:https://blog.csdn.net/weixin_45309916/article/details/109499825

目錄

  • 1、找錯誤
  • 2、下面的代碼輸出是什么,為什么?
  • 3、C語言編譯時動態鏈接和靜態鏈接得區別是什么?
  • 4、C語言關鍵字static的作用是什么?
  • 5、分別說明一下三個變量聲明得含義:
  • 6、簡述TCP/IP鏃包含哪些分段,每一層有哪些常用協議?
  • 7、從在瀏覽器地址欄中輸入www.baidu.com到看到百度首頁,這個過程中間經歷了什么?都涉及到哪些網絡協議?
  • 8、編寫strcat函數
  • 9、使用C語言中的#define來定義一個常量來表示一年有多少秒?
  • 10、實現把字符串轉化成整數
  • 11、寫一個程序驗證系統的大小端存儲格式
  • 12、如何判斷一個byte數據中有多少bit為1?
  • 13、 C語言中關鍵字volatile的含義
  • 14、進程間通信的方式有哪些?
  • 15、堆和棧的區別
  • 16、分別給出bool,int,float,指針變量 與“零值”比較的 if 語句(假設變量名為var)
  • 17、 進程和線程的區別
  • 18.寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個。
  • 19.預處理器標識#error的目的是什么?
  • 20. 嵌入式系統中經常要用到無限循環,你怎么樣用C編寫死循環呢?
  • 21. 用變量a給出下面的定義
  • 22. 嵌入式系統總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼 ,第一個設置a的bit 3,第二個清除a的bit 3。在以上兩個操作中,要保持其它位不變。
  • 23. 嵌入式系統經常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa55。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。
  • 24. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什么?
  • 25. 整型數組作為參數傳遞時,無法在子函數中獲得其長度!只有字符串可以,因為它有一個尾巴標識(‘\0’)!所以,整型的數組長度,必須與數組名一同傳遞到子函數才可以!
  • 26.unsigned int compzero = ~0;與unsigned int compzero = 0xFFFF; 的區別
  • 27.使用#define和typedef 來聲明一個已經存在的數據類型的同義字的區別
  • 28.linux中斷處理的上半部和下半部
  • 29.嵌入式設備,為加快啟動速度,可以做哪些方面的優化?
  • 30.PSRAM、SDRAM、DDR、DDR2的時序特性?
  • 31.驅動中操作物理絕對地址為什么要先ioremap?

1、找錯誤

char * s1="hello"; char * s2="world"; char * s3=strcat(s1,s2);

這樣做對嗎,如果不對請說明原因。

解答:
不對,s1與s2都為常量指針,其內容不可修改,運行就會產生段錯誤。
s1可以是一個數組,注意給足夠容量大的數組。

嚴格地說,C中是沒有字符串變量的,一般采用字符數組或字符指針的方式才實現字符串的操作,二者在使用中有諸多類似,但并不是完全等價的,注意下面兩句的區別:

char a[] ="Hello world";//存放在棧中,可以修改a數據組的任意值 char*s ="Hello world";//指針s存放在棧中,字符串常量在代碼段,不可修改

下面的代碼有何問題?

#include<stdio.h> #include<string.h> #include<stdlib.h>void test1() {char string1[10];char* str1 = "0123456789";strcpy( string1, str1 );printf("%s",string1);}int main() {test1();return 0; }

字符串strl的末尾是以’ \0 ’結尾的,所以他的長度是11,而string的長度不夠。。。。

但是我自己寫了這樣的程序,他是可以拷貝的。。。。

改成9的話,編譯通過,運行出錯!

*** stack smashing detected ***: <unknown> terminated Aborted (core dumped)

2、下面的代碼輸出是什么,為什么?

#include<stdio.h> #include<string.h> #include<stdlib.h>void foo(void){long int ret = 0;unsigned int a=6;int b=-20;(a+b>6)?puts(">6"):puts("<6");ret = a+b;printf("%ld\n",ret);printf("%d\n",a+b); }int main() {foo();return 0; }

輸出結果:

>6 4294967282 -14

輸出“>6”,應為無符號數和有符號相加,①有符號的整形數會轉化成一個無符號的整型數;②而且負的有符號整型數轉換后會變得非常大,所以相加會大于6。

3、C語言編譯時動態鏈接和靜態鏈接得區別是什么?

動態庫:
1、鏈接時不復制,程序運行時由系統動態加載到內存,供程序調用,系統只加載一次,多個程序可以共用,節省內存。
2、程序升級簡單,因為app里面沒有庫的源代碼,升級之后只要庫的名字不變,函數名以及參數不變,只是實現做了優化,就能加載成功。
3、. 加載速度比靜態庫慢
4、發布程序需要提供依賴的動態庫

靜態庫:

  • 1、靜態庫被打包到程序中加載速度
  • 2 、發布程序無需提供靜態庫,應為已經在app中,移植方便
  • 3 、 鏈接時完整的拷貝至可執行文件中,被多次使用就會有多次冗余拷貝
  • 4、 更新,部署,發布麻煩

4、C語言關鍵字static的作用是什么?

  • 隱藏作用, 可以在不同的文件中定義同名變量和同名函數。
  • ②對于變量來說, 保持變量持久, 靜態數據區的變量會在程序剛剛運行時就完成初始化, 也是唯一一次初始化; 儲存在靜態數據區, 靜態存儲區只有兩種變量(全局變量和 static 靜態變量) 。
  • ③默認初始化為 0x00,和全局變量一樣的屬性, 減少程序員的工作量。

5、分別說明一下三個變量聲明得含義:

int const *p;int * const p;int const *p const;

int const*p=const int *p; const修飾的是指針 p,表示指針p的值不能改變,而p(即地址)是可以改變的;

int * const p; const修飾的是p(即地址)是常量,不可改變,但是*p的值可以改變。

int const *p const ; 上面兩種情況兼得,表示只讀,其地址以及地址中的值都不可改變

6、簡述TCP/IP鏃包含哪些分段,每一層有哪些常用協議?

應用層:http dns telnet ftp TFTP 。。。。
傳輸層:tcp udp
網絡層:ip ICMP ARP rarp
數據鏈路層:ethnet ethnet2 802.3 ppp fr x.25 hdlc
物理層:比特流

7、從在瀏覽器地址欄中輸入www.baidu.com到看到百度首頁,這個過程中間經歷了什么?都涉及到哪些網絡協議?

按照時間順序:
1.客戶端瀏覽器獲取用戶在地址欄輸入的域名。
2.客戶端瀏覽器將域名發送給DNS域名系統,請求解析。
3.DNS解析域名得到相應的IP,返回給客戶端瀏覽器。
4.客戶端瀏覽器根據IP向服務器發起TCP三次握手,建立TCP連接。
5.客戶端瀏覽器向服務器發送HTTP請求,請求百度首頁。
6.服務器通過HTTP響應向客戶端瀏覽器返回百度首頁文件。
7.釋放TCP連接。
8.客戶端瀏覽器解析HTML文件,根據文件內容獲取CSS、JS等資源文件,將頁面渲染展示給用戶。

TCP/IP五層模型中網絡層及以上用到的協議:
1.應用層:HTTP、DNS、HTTPS
2.傳輸層:TCP、UDP
3.網絡層:IP、ARP

8、編寫strcat函數

2.strcat函數原型char *my_strcat(char *dest,const char *src) //將源字符串加const,表明其為輸入參數 {char *strDest=dest;assert(dest!=NULL && src!=NULL); //對源地址和目的地址加非0斷言//若使用while(*Dest++),則會出錯,指向'\0'之后,會出現dest++,則指向了個'\0'的下一個位置,while(*dest !='\0'){dest++; //循環體內的++可以使指向字符串結束標志'\0'}while((*dest++=*src++)!='\0');return strDest; }

之后會問為什么要char *的返回值:

主要是為了實現鏈式表達式。直接返回char *的手段而不是返回void就是為了后來函數調用者方便而設計的,不用你這么麻煩用上述方法去使用了,而直接可以使用拷貝后的dest字符串了,這種方便的實現方法,看起來就像鏈子鏈在一起的,所以稱為鏈式表達式。

如 strcpy(buf, strcat(dest, src) )

9、使用C語言中的#define來定義一個常量來表示一年有多少秒?

#define SECONDS_PER_YEAR (unsigned long)(365 * 24 * 60 * 60)

一定要加括號,宏定義只是替換,不加括號會出錯。。。。

10、實現把字符串轉化成整數

int my_atoi(char *str) {int sum=0,status=1;if(str == NULL){return 0;}if(*str == '-'){status = -1;str++;}while((*str)!='\0'){sum=sum*10+ ((*str)-'0');str++;}return sum*status;}

11、寫一個程序驗證系統的大小端存儲格式

/*方法1*/ typedef union {int i;char c; }my_union;int checkSystem1(void) {my_union u;u.i = 1;return (u.i == u.c); } /*方法2*/ int checkSystem2(void) {int i = 0x12345678;char *c = &i;//字符串地址給到指針,指針按照地址大小順序遍歷輸出return ((c[0] == 0x78) && (c[1] == 0x56) && (c[2] == 0x34) && (c[3] == 0x12));//指針可以寫成數組的形式 }int main(void) { if(checkSystem2()) printf("little endian\n"); else printf("big endian\n"); return 0;

12、如何判斷一個byte數據中有多少bit為1?

int select(unsigned char data) {int count=0,i=1;while(data!=0){count += (data & i);data >>= 1;}return count; }

13、 C語言中關鍵字volatile的含義

volatile 的意思是“易失的,易改變的”。這個限定詞的含義是向編譯器指明變量的內容可能會由于其他程序的修改而變化。
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份

一般說來,volatile用在如下的幾個地方:

1、中斷服務程序中修改的供其它程序檢測的變量需要加volatile;

2、多任務環境下各任務間共享的標志應該加volatile;

3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;

一個參數既可以是const還可以是volatile嗎?解釋為什么。
是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

14、進程間通信的方式有哪些?

1.無名管道( pipe ): 管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。

2.有名管道 (named pipe) : 有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。

3.消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中并由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩沖區大小受限等缺點。

4.信號量( semophore ) : 信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。

5.信號 ( sinal ) : 信號是一種比較復雜的通信方式,用于通知接收進程某個事件已經發生。

6.共享內存( shared memory ) : 共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號兩,配合使用,來實現進程間的同步和通信。

7.套接字( socket ) : 套解字也是一種進程間通信機制,與其他通信機制不同的是,它可用于不同機器間的進程通信。

15、堆和棧的區別

①、申請方式不同:棧由系統自動分配,堆是由人為自行開辟(malloc,new)
② 、申請的大小不同:棧是從高地址像低地址分配的,分配空間較小,堆是由地址向高地分配的,空間較大
③ 、申請效率不同:棧由系統分配,分配速度較快,堆一般較慢
④ 、棧是連續的地址空間,堆不是連續的地址空間,很容易產生內存碎片,浪費內存。

16、分別給出bool,int,float,指針變量 與“零值”比較的 if 語句(假設變量名為var)

Bool: if(!var)Int : if(var==0)Float: const float val=0.00000001If((var >= -val) && (var <= val))

17、 進程和線程的區別

進程和線程的根本區別是進程是操作系統資源分配的基本單位,而線程是處理器任務調度和執行的基本單位。另外區別還有資源開銷、包含關系、內存分配、影響關系、執行過程等。

  • 資源開銷:每個進程都有獨立的代碼和數據空間(程序上下文),程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,同一類線程共享代碼和數據空間,每個線程都有自己獨立的運行棧和程序計數器(PC),線程之間切換的開銷小。

  • 包含關系:如果一個進程內有多個線程,則執行過程不是一條線的,而是多條線(線程)共同完成的;線程是進程的一部分,所以線程也被稱為輕權進程或者輕量級進程。

  • 內存分配:同一進程的線程共享本進程的地址空間和資源,而進程之間的地址空間和資源是相互獨立的。

  • 影響關系:一個進程崩潰后,在保護模式下不會對其他進程產生影響,但是一個線程崩潰整個進程都死掉。所以多進程要比多線程健壯。

  • 執行過程:每個獨立的進程有程序運行的入口、順序執行序列和程序出口。但是線程不能獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制,兩者均可并發執行。

進程和線程的根本區別是進程是操作系統資源分配的基本單位,而線程是處理器任務調度和執行的基本單位

18.寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個。

#define MIN(A, B) ((A) <= (B)? (A) : (B))

這個測試是為下面的目的而設的:
(1)標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符 變為標準C的一部分,宏是方便產生嵌入代碼的唯一方法,對于嵌入式系統來說,為 了能達到要求的性能,嵌入代碼經常是必須的方法。
(2)三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產生比if -then-else更優化的代碼,了解這個用法是很重要的。
(3)懂得在宏中小心地把參數用括號括起來。
(4)我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發生什么事?
least = MIN(*p++, b);
宏定義#define MIN(A, B) ((A) <= (B) ? (A) : (B))對MIN(*p++, b)的作用結果是:
((*p++) <= (b) ? (*p++) : (b))這個表達式會產生副作用,指針p會作兩次++自增操作。

19.預處理器標識#error的目的是什么?

這問題對區分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種問題的答案。當然如果你不是在找一個書呆子,那么應試者最好希望自己不要知道答案。
編譯程序時,只要遇到 #error 就會跳出一個編譯錯誤當程序比較大時,往往有些宏定義是在外部指定的(如makefile),或是在系統頭文件中指定的,當你不太確定當前是否定義了 XXX 時,就可以改成如下這樣進行編譯:
#ifdef XXX
#error “XXX has been defined”
#else

#endif
這樣,如果編譯時出現錯誤,輸出了XXX has been defined,表明宏XXX已經被定義了。

20. 嵌入式系統中經常要用到無限循環,你怎么樣用C編寫死循環呢?

這個問題用幾個解決方案。我首選的方案是:

while(1) { }

一些程序員更喜歡如下方案:

for(;;) { }

這個實現方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什么?!边@會給我留下一個壞印象。
第三個方案是用 goto

Loop: ... goto Loop;

應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。

21. 用變量a給出下面的定義

(1)一個整型數(An integer): int a;
(2)一個指向整型數的指針(A pointer to an integer): int *a;
(3)一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an
integer): int **a;
(4)一個有10個整型數的數組(An array of 10 integers) :int a[10];
(5)一個有10個指針的數組,該指針是指向一個整型數的(An array of 10 pointers to
integers): int *a[10];
(6)一個指向有10個整型數數組的指針(數組指針):int (*a)[10];int *p[n](指針數組)
(7)指向函數的指針,該函數有一個整型參數并返回一個整型數(函數指針):int (*a)(int);
(8)一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數并返回一個整
型數( An array of ten pointers to functions that take an integer argument and return an
integer ): int (*a[10])(int)。
人們經常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。 但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道所有的答案(或至少大部分答案),那么也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那么他又能為什么做準備呢?

22. 嵌入式系統總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼 ,第一個設置a的bit 3,第二個清除a的bit 3。在以上兩個操作中,要保持其它位不變。

對這個問題有三種基本的反應
(1)不知道如何下手。該被面者從沒做過任何嵌入式系統的工作。
(2)用bit fields。bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間
是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到Infineon為其較復雜的通信芯片寫的驅動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式來實現bit fields的。從道德講:永遠不要讓一個非嵌入式的家伙沾實際硬件的邊。
(3)用#define和bit masks操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:

#define BIT3 (0x1 < <3) static int a; void set_bit3(void) { a |= BIT3; } void clear_bit3(void) { a &= ~BIT3; }

一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數,這也是可以接受的我希望看到幾個要點:說明常數、|=和&=~操作。

23. 嵌入式系統經常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa55。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。

這一問題測試你是否知道為了訪問一絕對地址把一個整型數強制轉換(typecast)為一指針是合法的。這一問題的實現方式隨著個人風格不同而不同。典型的類似代碼如下:

int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa55;

一個較晦澀的方法是:

*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。

24. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什么?

int a = 5, b = 7, c;
c = a+++b;
這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:
c = a++ + b;
因此, 這段代碼持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當作問題。我發現這個問題的最大好處是:這是一個關于代碼編寫風格,代碼的可讀性,代碼的可修改性。

25. 整型數組作為參數傳遞時,無法在子函數中獲得其長度!只有字符串可以,因為它有一個尾巴標識(‘\0’)!所以,整型的數組長度,必須與數組名一同傳遞到子函數才可以!

26.unsigned int compzero = ~0;與unsigned int compzero = 0xFFFF; 的區別

unsigned int zero = 0; unsigned int compzero = 0xFFFF;

對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:

unsigned int compzero = ~0;

unsigned int compzero = 0xFFFF; 只寫了2個字節,16位的才符合 。

32位的可以寫:

unsigned int compzero = 0xFFFFFFFF;

但unsigned int compzero = ~0;更安全,不管有多少位,直接取反,把所有的0都變成1了。

27.使用#define和typedef 來聲明一個已經存在的數據類型的同義字的區別

Typedef 在C語言中頻繁用以聲明一個已經存在的數據類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:

#define dPS struct s *typedef struct s * tPS;

以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指針。哪種方法更好呢?(如果有的話)為什么?

這是一個非常微妙的問題,任何人答對這個問題(正當的原因)是應當被恭喜的。答案是:typedef更好。思考下面的例子:

dPS p1,p2;
tPS p3,p4;

第一個擴展為:struct s * p1, p2;
由于是宏替換,上面的代碼定義p1為一個指向結構體的指針,p2為一個實際的結構,這也許不是你想要的。

第二個例子正確地定義了p3 和p4 兩個指針。

struct s *p3,*p4;

28.linux中斷處理的上半部和下半部

設備的中斷會打斷內核中進程的正常調度和運行,系統對更高吞吐率的追求勢必要求中斷服務程序盡可能地短小精悍。但是,這個良好的愿望往往與現實并不吻合。在大多數真實的系統中,當中斷到來時,要完成的工作往往并不會是短小的,它可能要進行較大量的耗時處理。

為了在中斷執行時間盡可能短和中斷處理需完成大量工作之間找到一個平衡點,Linux 將中斷處理程序分解為兩個半部:頂半部(top half)和底半部(bottom half)。

頂半部完成盡可能少的比較緊急的功能,它往往只是簡單地讀取寄存器中的中斷狀態并清除中斷標志后就進行“登記中斷”的工作?!暗怯浿袛唷币馕吨鴮⒌装氩刻幚沓绦驋斓皆撛O備的底半部執行隊列中去。這樣,頂半部執行的速度就會很快,可以服務更多的中斷請求。

現在,中斷處理工作的重心就落在了底半部的頭上,它來完成中斷事件的絕大多數任務。底半部幾乎做了中斷處理程序所有的事情,而且可以被新的中斷打斷,這也是底半部和頂半部的最大不同,因為頂半部往往被設計成不可中斷。底半部則相對來說并不是非常緊急的,而且相對比較耗時,不在硬件中斷服務程序中執行。

盡管頂半部、底半部的結合能夠改善系統的響應能力,但是,僵化地認為 Linux設備驅動中的中斷處理一定要分兩個半部則是不對的。如果中斷要處理的工作本身很少,則完全可以直接在頂半部全部完成。

其他操作系統中對中斷的處理也采用了類似于Linux系統的方法,真正的硬件中斷服務程序都應該盡可能短。

因此,許多操作系統都提供了中斷上下文和非中斷上下文相結合的機制,將中斷的耗時工作保留到非中斷上下文去執行。

Linux 系統實現底半部的機制主要有tasklet,工作隊列和軟中斷。Linux 的中斷處理分為兩個半部,頂半部處理緊急的硬件操作,底半部處理不緊急的耗時操作。tasklet 和工作隊列都是調度中斷底半部的良好機制,tasklet 基于軟中斷實現。內核定時器也依靠軟中斷實現。內核中的延時是忙等待或者睡眠等待,為了充分利用CPU資源,使系統有更好的吞吐性能,在對延遲時間的要求并不是很精確的情況下,睡眠等待通常是值得推薦的。

29.嵌入式設備,為加快啟動速度,可以做哪些方面的優化?

答:linux默認的安裝內核相當龐大,為了保證系統的兼容性和靈活性,支持熱插拔操作,內核啟動時要進行大量的硬件檢測和初始化工作,而嵌入式的硬件都是固定的,只需要選擇需要的硬件驅動就可以,不需要全部的硬件驅動都檢測;因此可以進行適當的裁剪內核達到縮小啟動linux系統的目的;同時可以統計驅動模塊的耗時時間,對耗時較長的模塊驅動加以分析,優化。

30.PSRAM、SDRAM、DDR、DDR2的時序特性?

答:PSRAM,全稱Pseudo static random access memory。指的是偽靜態隨機存儲器。

SDRAM:Synchronous Dynamic Random Access Memory,同步動態隨機存儲器,同步是指 Memory工作需要同步時鐘,內部的命令的發送與數據的傳輸都以它為基準;動態是指存儲陣列需要不斷的刷新來保證數據不丟失;隨機是指數據不是線性依次存儲,而是自由指定地址進行數據讀寫。

DDR=Double Data Rate雙倍速率同步動態隨機存儲器。DDR SDRAM是Double Data Rate SDRAM的縮寫,是雙倍速率同步動態隨機存儲器的意思。

在同等核心頻率下,DDR2的實際工作頻率是DDR的兩倍。這得益于DDR2內存擁有兩倍于標準DDR內存的4BIT預讀取能力。換句話說,雖然DDR2和DDR一樣,都采用DDR2內存的頻率了在時鐘的上升延和下降延同時進行數據傳輸的基本方式,但DDR2擁有兩倍于DDR的預讀取系統命令數據的能力。也就是說,在同樣100MHz的工作頻率下,DDR的實際頻率為200MHz,而DDR2則可以達到400MHz。

31.驅動中操作物理絕對地址為什么要先ioremap?

答:因為內核沒有辦法直接訪問物理內存地址,必須先通過ioremap獲得對應的虛擬地址。

ioremap將一個IO地址空間映射到內核的虛擬地址空間上去,便于訪問。

總結

以上是生活随笔為你收集整理的面经——嵌入式软件工程师面试遇到的经典题目的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。