【秋招】嵌入式面試八股文 - 操作系統(tǒng) 篇
本文為 第六章操作系統(tǒng) 部分,具體整篇目錄可以看前言!
第一部分(純八股)
1 什么是進程?什么是線程?
進程(Process):
- 操作系統(tǒng)資源分配的基本單位
- 擁有獨立的內(nèi)存空間和系統(tǒng)資源
- 包含代碼、數(shù)據(jù)、堆棧等
線程(Thread):
- CPU調(diào)度的基本單位
- 共享所屬進程的內(nèi)存空間和資源
- 只擁有必要的運行資源(如程序計數(shù)器、寄存器和棧)
主要區(qū)別:
資源占用 |
占用獨立內(nèi)存空間 |
共享進程內(nèi)存空間 |
通信開銷 |
較大(需IPC機制) |
較小(直接訪問共享數(shù)據(jù)) |
創(chuàng)建開銷 |
較大 |
較小 |
切換開銷 |
較大(需保存/恢復更多上下文) |
較小 |
安全性 |
較高(相互獨立) |
較低(共享內(nèi)存可能導致沖突) |
并發(fā)性 |
多進程并發(fā) |
多線程并發(fā) |
2 內(nèi)核線程和用戶線程的區(qū)別?
- 內(nèi)核支持線程是OS內(nèi)核可感知的,而用戶級線程是OS內(nèi)核不可感知的。
- 用戶級線程的創(chuàng)建、撤消和調(diào)度不需要OS內(nèi)核的支持,是在語言(如Java)這一級處理的;而內(nèi)核支持線程的創(chuàng)建、撤消和調(diào)度都需OS內(nèi)核提供支持,而且與進程的創(chuàng)建、撤消和調(diào)度大體是相同的。
- 用戶級線程執(zhí)行系統(tǒng)調(diào)用指令時將導致其所屬進程被中斷,而內(nèi)核支持線程執(zhí)行系統(tǒng)調(diào)用指令時,只導致該線程被中斷。
- 在只有用戶級線程的系統(tǒng)內(nèi),CPU調(diào)度還是以進程為單位,處于運行狀態(tài)的進程中的多個線程,由用戶程序控制線程的輪換運行;在有內(nèi)核支持線程的系統(tǒng)內(nèi),CPU調(diào)度則以線程為單位,由OS的線程調(diào)度程序負責線程的調(diào)度。
- 用戶級線程的程序?qū)嶓w是運行在用戶態(tài)下的程序,而內(nèi)核支持線程的程序?qū)嶓w則是可以運行在任何狀態(tài)下的程序。
3 進程和線程有什么區(qū)別?
3.1 根本區(qū)別
- 進程是資源分配的基本單位,線程是程序執(zhí)行的最小單位
3.2 資源開銷
- 進程有自己獨立地址空間(代碼空間和數(shù)據(jù)空間),每啟動一個進程,系統(tǒng)會為它分配地址空間。
- 線程沒有自己獨立的地址空間,線程共享進程中的數(shù)據(jù),使用相同的地址空間。每個線程都有自己的堆棧。
- 線程切換的資源開銷要比進程小。涉及頻繁切換,就選擇線程。
- 線程開銷小,但是不利于進行資源包含;進程開銷大,但是有利于資源保護。
3.3 關(guān)于通信
- 線程之間的通信更方便,同一進程下的線程共享全局變量、靜態(tài)變量等數(shù)據(jù),而進程之間的通信需要以通信的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難點。(ftok函數(shù))
- 但是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也跟著死掉了,而一個進程死掉并不會對另外一個進程造成影響,因為進程有自己獨立的地址空間。
3.4 執(zhí)行過程
- 每個獨立的進程有一個程序運行的入口、順序執(zhí)行序列和程序入口。但是線程不能獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。
4 何時使用多進程,何時使用多線程?
- 對資源的管理和保護要求高,不限制開銷和效率時,使用多進程。
- 要求效率高,頻繁切換時,資源的保護管理要求不是很高時,使用多線程。
5 進程的幾種狀態(tài)?
?關(guān)于IO:
- 用戶程序進行IO的讀寫,會用到read&write兩大系統(tǒng)調(diào)用。read系統(tǒng)調(diào)用,是把數(shù)據(jù)從內(nèi)核緩沖區(qū)復制到進程緩沖區(qū);而write系統(tǒng)調(diào)用,是把數(shù)據(jù)從進程緩沖區(qū)復制到內(nèi)核緩沖區(qū)。
同步IO:
- 是指用戶空間線程是主動發(fā)起IO請求的一方,內(nèi)核空間是被動接受方。
- 是指內(nèi)核kernel是主動發(fā)起IO請求的一方,用戶線程是被動接受方。
- R (Running/Runnable):正在運行或就緒狀態(tài)
- S (Sleeping):可中斷睡眠,等待某事件完成
- D (Disk Sleep):不可中斷睡眠,通常是等待I/O
- Z (Zombie):僵尸狀態(tài),進程已終止但父進程未回收
- T (Stopped):進程被暫停,如收到SIGSTOP信號
- X (Dead):進程即將被銷毀
狀態(tài)轉(zhuǎn)換:
- R → S:進程等待資源或事件
- S → R:等待的事件發(fā)生,進程被喚醒
- R → D:進程請求不可中斷的操作,如磁盤I/O
- D → R:不可中斷操作完成
- R → Z:進程終止,但父進程未調(diào)用wait()
- R/S → T:進程收到SIGSTOP信號
- T → R:進程收到SIGCONT信號
6 進程創(chuàng)建的方式?
6.1 使用fork創(chuàng)建
fork()
是Unix/Linux中創(chuàng)建進程的傳統(tǒng)方法:
#include <unistd.h> pid_t fork(void);
特點:
- 創(chuàng)建調(diào)用進程的副本(子進程)
- 子進程獲得父進程數(shù)據(jù)空間、堆、棧的副本
- 父子進程共享代碼段(只讀)
- 采用寫時復制(Copy-On-Write)技術(shù)優(yōu)化內(nèi)存使用
- 返回值:父進程中返回子進程PID,子進程中返回0,失敗返回-1
示例:
#include <stdio.h> #include <unistd.h> int main() { pid_t pid = fork(); if (pid < 0) { // 創(chuàng)建失敗 perror("fork failed"); return 1; } else if (pid == 0) { // 子進程 printf("子進程,PID: %d,父進程PID: %d\n", getpid(), getppid()); } else { // 父進程 printf("父進程,PID: %d,子進程PID: %d\n", getpid(), pid); } return 0; }
6.2 vfork系統(tǒng)調(diào)用
vfork()
是fork()
的一種特殊形式:
#include <unistd.h> pid_t vfork(void);
特點:
- 創(chuàng)建進程的目的是執(zhí)行exec系列函數(shù)
- 子進程與父進程共享地址空間(不使用寫時復制)
- 父進程會被掛起,直到子進程調(diào)用exec或exit
- 子進程必須小心不修改共享的變量
- 現(xiàn)代系統(tǒng)中,fork的性能已經(jīng)很好,vfork使用較少
示例:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { pid_t pid = vfork(); if (pid < 0) { // 創(chuàng)建失敗 perror("vfork failed"); return 1; } else if (pid == 0) { // 子進程 printf("子進程,PID: %d\n", getpid()); // 子進程必須調(diào)用exec或exit _exit(0); // 注意使用_exit而非exit } else { // 父進程 printf("父進程,PID: %d,子進程PID: %d\n", getpid(), pid); } return 0; }
6.3 clone系統(tǒng)調(diào)用
clone()
是Linux特有的系統(tǒng)調(diào)用,提供更細粒度的控制:
#define _GNU_SOURCE #include <sched.h> int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...);
特點:
- 允許選擇父子進程間共享的資源(如文件描述符、信號處理等)
- 是實現(xiàn)線程的基礎(chǔ)(pthread庫在底層使用clone)
- 通過flags參數(shù)控制共享程度
- 常用flags: CLONE_FILES:共享文件描述符表CLONE_FS:共享文件系統(tǒng)信息CLONE_VM:共享內(nèi)存空間CLONE_SIGHAND:共享信號處理函數(shù)
示例:
#define _GNU_SOURCE #include <stdio.h> #include <sched.h> #include <stdlib.h> #include <unistd.h> #define STACK_SIZE (1024 * 1024) // 1MB??臻g // 子進程執(zhí)行的函數(shù) int child_func(void *arg) { printf("子進程,PID: %d\n", getpid()); return 0; } int main() { // 分配棧空間,棧向下增長 void *stack = malloc(STACK_SIZE); if (!stack) { perror("malloc failed"); return 1; } // 創(chuàng)建子進程 pid_t pid = clone(child_func, (char*)stack + STACK_SIZE, CLONE_VM | SIGCHLD, NULL); if (pid < 0) { perror("clone failed"); free(stack); return 1; } printf("父進程,PID: %d,子進程PID: %d\n", getpid(), pid); // 等待子進程結(jié)束 waitpid(pid, NULL, 0); free(stack); return 0; }
6.2 fork和vfork的區(qū)別?
- void exit( int status) 結(jié)束當前進程并將status返回;exit結(jié)束進程會刷新緩沖區(qū)
- void _exit(int status) 這個不會進行緩沖區(qū)刷新
注意:
- 內(nèi)存共享: fork():子進程獲得父進程內(nèi)存的副本(寫時復制)vfork():子進程與父進程共享內(nèi)存空間
- 執(zhí)行順序: fork():父子進程執(zhí)行順序不確定vfork():父進程掛起,直到子進程調(diào)用exec或exit
- 使用場景: fork():通用進程創(chuàng)建vfork():創(chuàng)建進程后立即執(zhí)行exec
- 安全性: vfork()更危險,子進程可能破壞父進程的數(shù)據(jù)
- 性能: 歷史上vfork()更高效,現(xiàn)代系統(tǒng)中差異不大
6.3 為什么子進程中使用exit()可能導致問題,而應(yīng)該使用_exit()?
exit()
會執(zhí)行清理操作,包括刷新標準IO緩沖區(qū)和調(diào)用atexit()注冊的函數(shù)- 在vfork()創(chuàng)建的子進程中,由于共享地址空間,使用exit()可能會: 清空父進程的IO緩沖區(qū)執(zhí)行父進程注冊的atexit()函數(shù)關(guān)閉父進程需要的文件描述符
_exit()
直接調(diào)用系統(tǒng)調(diào)用退出,不執(zhí)行用戶空間清理操作- 因此,vfork()后的子進程應(yīng)使用_exit()或exec(),避免干擾父進程
6.4 調(diào)度策略和優(yōu)先級
Linux支持多種調(diào)度策略:
- SCHED_OTHER(CFS):普通進程的默認策略
- SCHED_BATCH:批處理進程,不需要交互
- SCHED_IDLE:優(yōu)先級最低的進程
- SCHED_FIFO:實時進程,先進先出,不會被搶占(除非被更高優(yōu)先級進程)
- SCHED_RR:實時進程,時間片輪轉(zhuǎn)
優(yōu)先級系統(tǒng):
- nice值:范圍-20到19,值越小優(yōu)先級越高,默認為0
剩余60%內(nèi)容,訂閱專欄后可繼續(xù)查看/也可單篇購買
雙非本,211碩。本碩均為機械工程,自學嵌入式,在校招過程中拿到小米、格力、美的、比亞迪、海信、???、大華、江波龍等offer。八股文本質(zhì)是需要大家理解,因此里面的內(nèi)容一定要詳細、深刻!這個專欄是我個人的學習筆記總結(jié),是對很多面試問題進行的知識點分析,專欄保證高質(zhì)量,讓大家可以高效率理解與吸收里面的知識點!掌握這里面的知識,面試絕對無障礙!