1. 程式人生 > >騰訊後臺開發面試題及答案

騰訊後臺開發面試題及答案

簡單歸納:fd只是一個整數,在open時產生。起到一個索引的作用,程序通過PCB中的檔案描述符表找到該fd所指向的檔案指標filp。

檔案描述符的操作(如: open)返回的是一個檔案描述符,核心會在每個程序空間中維護一個檔案描述符表, 所有開啟的檔案都將通過此表中的檔案描述符來引用; 
流(如: fopen)返回的是一個FILE結構指標, FILE結構是包含有檔案描述符的,FILE結構函式可以看作是對fd直接操作的系統呼叫的封裝, 它的優點是帶有I/O快取

每個程序在PCB(Process Control Block)即程序控制塊中都儲存著一份檔案描述符表,檔案描述符就是這個表的索引,檔案描述表中每個表項都有一個指向已開啟檔案的指標

,現在我們明確一下:已開啟的檔案在核心中用file結構體表示,檔案描述符表中的指標指向file結構體

linux和os:

netstat :顯示網路狀態

Netstat 命令用於顯示各種網路相關資訊,如網路連線,路由表,介面狀態 (Interface Statistics),masquerade 連線,多播成員 (Multicast Memberships) 等等。

從整體上看,netstat的輸出結果可以分為兩個部分:

一個是Active Internet connections,稱為有源TCP連線其中"Recv-Q"和"Send-Q"指%0A的是接收佇列和傳送佇列。這些數字一般都應該是0。如果不是則表示軟體包正在佇列中堆積

。這種情況只能在非常少的情況見到。

另一個是Active UNIX domain sockets,稱為有源Unix域套介面(和網路套接字一樣,但是隻能用於本機通訊,效能可以提高一倍)。
Proto顯示連線使用的協議,RefCnt表示連線到本套介面上的程序號,Types顯示套介面的型別,State顯示套介面當前的狀態,Path表示連線到套介面的其它程序使用的路徑名。

tcpdump主要是截獲通過本機網路介面的資料,用以分析。能夠截獲當前所有通過本機網絡卡的資料包。它擁有靈活的過濾機制,可以確保得到想要的資料。

用簡單的話來定義tcpdump,就是:dump the traffic on a network,根據使用者的定義對網路上的資料包進行截獲的包分析工具。 tcpdump可以將網路中傳送的資料包的“頭”完全截獲下來提供分析。它支援針對網路層、協議、主機、網路或埠的過濾,並提供and、or、not等邏輯語句來幫助你去掉無用的資訊。

ipcs:檢查系統上共享記憶體的分配

用途

報告程序間通訊設施狀態。

ipcs 命令往標準輸出寫入一些關於活動程序間通訊設施的資訊。如果沒有指定任何標誌,ipcs 命令用簡短格式寫入一些關於當前活動訊息佇列共享記憶體段、訊號量、遠端佇列和本地佇列標題。

Linux下ipcs指令的用法詳解。ipcs是Linux下顯示程序間通訊設施狀態的工具。可以顯示訊息佇列、共享記憶體和訊號量的資訊。對於程式設計師可能更有用些,普通的系統管理員一般用不到此指令。

ipcrm:手動解除系統上共享記憶體的分配

用途
  刪除訊息佇列、訊號集、或者共享記憶體標識。

(如果這四個命令沒聽說過或者不能熟練使用,基本上可以回家,通過的概率較小 ^_^ ,這四個命令的熟練掌握程度基本上能體現面試者實際開發和除錯程式的經驗)

檢視cpu資訊

   #cat /proc/cpuinfo

   # cat /proc/meminfo

檢視硬碟資訊

        # df -lh

更詳細的資訊

       # cat /proc/scsi/scsi

檢視網絡卡資訊

        # dmesg | grep eth

更常用的命令(顯示系統核心版本號、名稱、機器型別等)

       # uname -a

cpu 記憶體硬碟等等與系統性能除錯相關的命令必須熟練掌握,設定修改許可權 tcp網路狀態檢視各程序狀態抓包相關等相關命令必須熟練掌握

awk sed需掌握

共享記憶體的使用實現原理(必考必問,然後共享記憶體段被對映進程序空間之後,存在於程序空間的什麼位置?共享記憶體段最大限制是多少?)

$sysctl kern.ipc.shmmax
kern.ipc.shmmax: 33554432

Linux的2.2.x以後的核心版本支援多種共享記憶體方式,比如:記憶體對映mmap、POSIX共享記憶體、System V共享記憶體;

共享記憶體定義:共享記憶體是最快的可用IPC(程序間通訊)形式。它允許多個不相關的程序去訪問同一部分邏輯記憶體。共享記憶體是由IPC為一個程序建立的一個特殊的地址範圍,它將出現在程序的地址空間中。其他程序可以把同一段共享記憶體段“連線到”它們自己的地址空間裡去。所有程序都可以訪問共享記憶體中的地址。如果一個程序向這段共享記憶體寫了資料,所做的改動會立刻被有訪問同一段共享記憶體的其他程序看到。因此共享記憶體對於資料的傳輸是非常高效的。

共享記憶體的原理:共享記憶體是最有用的程序間通訊方式之一,也是最快的IPC形式。兩個不同程序A、B共享記憶體的意思是,同一塊實體記憶體被對映到程序A、B各自的程序地址空間。程序A可以即時看到程序B對共享記憶體中資料的更新,反之亦然。

c++程序記憶體空間分佈(注意各部分的記憶體地址誰高誰低,注意棧從高到低分配,堆從低到高分配)

ELF是什麼?其大小與程式中全域性變數的是否初始化有什麼關係(注意未初始化的資料放在bss段)

Linux ELF  ELF = Executable and Linkable Format,可執行連線格式,是UNIX系統實驗室(USL)作為應用程式二進位制介面(Application Binary Interface,ABI)而開發和釋出的。副檔名為elf。工具介面標準委員會(TIS)選擇了正在發展中的ELF標準作為工作在32位INTEL體系上不同作業系統之間可移植的二進位制檔案格式。假定開發者定義了一個二進位制介面集合,ELF標準用它來支援流線型的軟體發展。應該減少不同執行介面的數量。因此可以減少重新程式設計重新編譯的程式碼。

BSS段

可執行程式包括BSS段、資料段程式碼段(也稱文字段)

BSS(Block Started by Symbol)通常是指用來存放程式中未初始化的全域性變數靜態變數的一塊記憶體區域。特點是:可讀寫的,在程式執行之前BSS段會自動清0。所以,未初始的全域性變數在程式執行之前已經成0了。

注意和資料段的區別,BSS存放的是未初始化的全域性變數靜態變數資料段存放的是初始化後的全域性變數和靜態變數。

UNIX下可使用size命令檢視可執行檔案的段大小資訊。如size a.out

可執行檔案:包含了程式碼和資料。具有可執行的程式。

可重定位檔案:包含了程式碼和資料(這些資料是和其他重定位檔案和共享的 
object檔案一起連線時使用的)

共享object檔案(又可叫做共享庫):包含了程式碼和資料(這些資料是在連線
時候被聯結器ld和執行時動態聯結器使用的)。

使建立共享庫容易,使動態裝載和共享庫的結合更加容易。在ELF下,在C++ 
中,全域性的建構函式和解構函式在共享庫和靜態庫中用同樣方法處理。

使用過哪些程序間通訊機制,並詳細說明(重點)

makefile編寫,雖然比較基礎,但是會被問到

mkdir mf

cd mf

vim makefile

hello.o:hello.c hello.h

       gcc –c hello.o -Lm

make

./hello

gdb除錯相關的經驗,會被問到

GDB是GNU開源組織釋出的一個強大的UNIX下的程式除錯工具。或許,各位比較喜歡那種圖形介面方式的,像VC、BCB等IDE的除錯,但如果你是在 UNIX平臺下做軟體,你會發現GDB這個除錯工具有比VC、BCB的圖形化偵錯程式更強大的功能。所謂“寸有所長,尺有所短”就是這個道理

一般來說,GDB主要幫助你完成下面四個方面的功能:

1、啟動你的程式,可以按照你的自定義的要求隨心所欲的執行程式。

2、可讓被除錯的程式在你所指定的調置的斷點處停住。(斷點可以是條件表示式

3、當程式被停住時,可以檢查此時你的程式中所發生的事。

4、動態的改變你程式的執行環境。

《GDB使用手冊》

gcc -g -o hello hello.c 。

GDB常用命令簡介
  GDB的命令很多,本文不會全部介紹,僅會介紹一些最常用的。在介紹之前,先介紹GDB中的一個非常有用的功能:補齊功能。它就如同Linux下SHELL中的命令補齊一樣。當你輸入一個命令的前幾個字元,然後輸入TAB鍵,如果沒有其它命令的前幾個字元與此相同,SHELL將補齊此命令。如果有其它命令的前幾個字元與此相同,你會聽到一聲警告聲,再輸入TAB鍵,SHELL將所有前幾個字元與此相同的命令全部列出。而GDB中的補齊功能不僅能補齊GDB命令,而且能補齊引數。
  本文將先介紹常用的命令,然後結合一個具體的例子來演示如何實際使用這些命令。下面的所有命令除了第一條啟動GDB命令是在SHELL下輸入的,其餘都是GDB內的命令。大部分GDB內的命令都可以僅輸入前幾個字元,只要不與其它指令衝突。如quit可以簡寫為q,因為以q打頭的命令只有quit。List可以簡寫為l,等等
3.1 啟動GDB
  你可以輸入GDB來啟動GDB程式。GDB程式有許多引數,在此沒有必要詳細介紹,但一個最為常用的還是要介紹的:如果你已經編譯好一個程式,我們假設檔名為hello,你想用GDB除錯它,可以輸入gdb hello來啟動GDB並載入你的程式。如果你僅僅啟動了GDB,你必須在啟動後,在GDB中再載入你的程式。
3.2 載入程式 === file
  在GDB內,載入程式很簡單,使用file命令。如file hello。當然,程式的路徑名要正確。
  退出GDB === quit
  在GDB的命令方式下,輸入quit,你就可以退出GDB。你也可以輸入'C-d'來退出GDB。
3.3 執行程式 === run
  當你在GDB中已將要除錯的程式載入後,你可以用run命令來執行。如果你的程式需要引數,你可以在run指令後接著輸入引數,就象你在SHELL下執行一個需要引數的命令一樣。
3.4 檢視程式資訊 === info
  info指令用來檢視程式的資訊,當你用help info檢視幫助的話,info指令的引數足足佔了兩個螢幕,它的引數非常多,但大部分不常用。我用info指令最多的是用它來檢視斷點資訊
3.4.1 檢視斷點資訊
info br
br是斷點break的縮寫,記得GDB的補齊功能吧。用這條指令,你可以得到你所設定的所有斷點的詳細資訊。包括斷點號,型別,狀態,記憶體地址,斷點在源程式中的位置等。
3.4.2 檢視當前源程式
info source
3.4.3 檢視堆疊資訊
info stack
用這條指令你可以看清楚程式的呼叫層次關係。
3.4.4 檢視當前的引數
info args
3.5 列出源一段源程式 === list
3.5.1 列出某個函式
list FUNCTION
3.5.2 以當前原始檔的某行為中間顯示一段源程式
list LINENUM
3.5.3 接著前一次繼續顯示
list
3.5.4 顯示前一次之前的源程式
list -
3.5.5 顯示另一個檔案的一段程式
list FILENAME:FUNCTION 或 listFILENAME:LINENUM
3.6 設定斷點 === break
  現在我們將要介紹的也許是最常用和最重要的命令:設定斷點。無論何時,只要你的程式已被載入,並且當前沒有正在執行,你就能設定,修改,刪除斷點。設定斷點的命令是break。有許多種設定斷點的方法。如下:
3.6.1 在函式入口設定斷點

如何定位記憶體洩露?

記憶體洩漏是指堆記憶體的洩漏。堆記憶體是指程式從堆中分配的、大小任意的(記憶體塊的大小可以在程式執行期決定)、使用完後必須顯示釋放的記憶體。應用程式一般使用malloc、realloc、new等函式從堆中分配到一塊記憶體,使用完後,程式必須負責相應的呼叫free或delete釋放該記憶體塊。否則,這塊記憶體就不能被再次使用,我們就說這塊記憶體洩漏了。

C++程式缺乏相應的手段來檢測記憶體資訊,只能使用top指令觀察程序的動態記憶體總額。而且程式退出時,我們無法獲知任何記憶體洩漏資訊

使用Linux命令回收記憶體,可以使用ps、kill兩個命令檢測記憶體使用情況和進行回收。在使用超級使用者許可權時使用命令“ps”,它會列出所有正在執行的程式名稱和對應的程序號(PID)。kill命令的工作原理是向Linux作業系統的核心送出一個系統操作訊號和程式的程序號(PID)

動態連結和靜態連結的區別

動態連結是指在生成可執行檔案時不將所有程式用到的函式連結到一個檔案,因為有許多函式在作業系統帶的dll檔案中,當程式執行時直接從作業系統中找。 而靜態連結就是把所有用到的函式全部連結到exe檔案中。
動態連結是隻建立一個引用的介面,而真正的程式碼和資料存放在另外的可執行模組中,在執行時再裝入;而靜態連結是把所有的程式碼和資料都複製到本模組中,執行時就不再需要庫了。

32位系統一個程序最多有多少堆記憶體

多執行緒和多程序的區別(重點面試官最最關心的一個問題,必須從cpu排程,上下文切換,資料共享,多核cup利用率,資源佔用,等等各方面回答,然後有一個問題必須會被問到:哪些東西是一個執行緒私有的?答案中必須包含暫存器,否則悲催)

我們按照多個不同的維度,來看看多程序和多執行緒的對比(注:都是相對的,不是說一個好得不得了,另一個差的無法忍受)

維度

多程序

多執行緒

總結

資料共享、同步

資料是分開的:共享複雜,需要用IPC;同步簡單

多執行緒共享程序資料:共享簡單;同步複雜

各有優勢

記憶體、CPU

佔用記憶體多,切換複雜,CPU利用率低

佔用記憶體少,切換簡單,CPU利用率高

執行緒佔優

建立銷燬、切換

建立銷燬、切換複雜,速度慢 

建立銷燬、切換簡單,速度快 

執行緒佔優 

程式設計除錯

程式設計簡單,除錯簡單

程式設計複雜,除錯複雜

程序佔優 

可靠性

程序間不會相互影響 

一個執行緒掛掉將導致整個程序掛掉

程序佔優

分散式 

適應於多核、多機分佈;如果一臺機器不夠,擴充套件到多臺機器比較簡單

適應於多核分佈

程序佔優

然後我們來看下執行緒和程序間的比較

子程序繼承父程序的屬性:

子執行緒繼承主執行緒的屬性:

實際使用者ID,實際組ID,有效使用者ID,有效組ID;

附加組ID;

程序組ID;

會話ID;

控制終端;

設定使用者ID標誌和設定組ID標誌;

當前工作目錄;

根目錄;

檔案模式建立遮蔽字(umask);

訊號遮蔽和安排;

針對任一開啟檔案描述符的在執行時關閉(close-on-exec)標誌;

環境;

連線的共享儲存段;

儲存對映;

資源限制;

程序中的所有資訊對該程序的所有執行緒都是共享的;

可執行的程式文字;

程式的全域性記憶體;

堆記憶體;

棧;

檔案描述符;

訊號的處理是程序中所有執行緒共享的(注意:如果訊號的預設處理是終止該程序那麼即是把訊號傳給某個執行緒也一樣會將程序殺掉);

父子程序之間的區別:

子執行緒特有的:

fork的返回值(=0子程序);

程序ID不同;

兩個程序具有不同的父程序ID;

子程序的tms_utime,tms_stime,tms_cutime以及tms_ustime均被設定為0;

不繼承父程序設定的檔案鎖;

子程序的未處理鬧鐘被清除;

子程序的未處理訊號集設定為空集;

執行緒ID;

一組暫存器值;

棧;

排程優先順序和策略;

訊號遮蔽字;

errno變數;

執行緒私有資料;


1)需要頻繁建立銷燬的優先用執行緒。
例項:web伺服器。來一個建立一個執行緒,斷了就銷燬執行緒。要是用程序,建立和銷燬的代價是很難承受的。
2)需要進行大量計算的優先使用執行緒。
所謂大量計算,當然就是要消耗很多cpu,切換頻繁了,這種情況先執行緒是最合適的。
例項:影象處理、演算法處理
3)強相關的處理用執行緒,若相關的處理用程序。
什麼叫強相關、弱相關?理論上很難定義,給個簡單的例子就明白了。
一般的server需要完成如下任務:訊息收發和訊息處理。訊息收發和訊息處理就是弱相關的任務,而訊息處理裡面可能又分為訊息解碼、業務處理,這兩個任務相對來說相關性就要強多了。因此訊息收發和訊息處理可以分程序設計,訊息解碼和業務處理可以分執行緒設計。
4)可能擴充套件到多機分佈的用程序,多核分佈的用執行緒。
5)都滿足需求的情況下,用你最熟悉、最拿手的方式。

至於”資料共享、同步“、“程式設計、除錯”、“可靠性”這幾個維度的所謂的“複雜、簡單”應該怎麼取捨,只能說:沒有明確的選擇方法。一般有一個選擇原則:如果多程序和多執行緒都能夠滿足要求,那麼選擇你最熟悉、最拿手的那個。

一般執行一個程式稱為一個程序。

程序可以建立執行緒,也可以建立程序。

執行緒是由程序管理的,執行緒之間、執行緒和父程序(建立執行緒的程序)之間可以共享記憶體變數(需要使用策略的)。

程序之間一般不可以直接共享記憶體變數,需要使用一些程序間的控制共享記憶體變數。

如果你使用平行計算,建議使用執行緒。

程序是個容器或者說資源管理者,有獨立的記憶體地址空間。
執行緒依賴於它所在的程序,共享程序的資源和記憶體地址空間。

unix特別是linux裡面,執行緒與程序接近;windows的程序完全是個容器,執行緒更輕量級。具體可以瞭解linux下的fork以及clone,windows的createprocess、createthread等

什麼是程序。最直觀的就是一個個pid,官方的說法就:程序是程式在計算機上的一次執行活動。

主要參看2篇博文:

還有就是謝老師寫的《計算機網路》第五版,.TCP/IP詳解(卷一,卷二)以及《Unix網路程式設計》以及Linux原始碼之外,RFC

一、概念:

key:TCP是一種面向連線的、可靠的、位元組流服務

1.面向連結:TCP面向連結,面向連線意味著兩個使用TCP的應用(通常是一個客戶和一個伺服器)在彼此交換資料之前必須通過三次握手先建立一個TCP連線。在一個TCP中僅有兩方彼此通訊,多播和廣播不能用於TCP。UDP是不可靠的傳輸,傳輸前不需要建立連結,可以應用多播和廣播實現一對多的通訊。

 
2.可靠性:TCP提供端到端的流量控制,對收到的資料進行確認,採用超時重發,對失序的資料進行重新排序等機制保證資料通訊的可靠性。而UDP是一種不可靠的服務,接收方可能不能收到傳送方的資料報。

 
3.TCP是一種流模式的協議,UDP是一種資料報模式的協議。程序的每個輸出操作都正好產生一個UDP資料報,並組裝成一份待發送的IP資料報。TCP應用程式產生的全體資料與真正傳送的單個IP資料報可能沒有什麼聯絡。TCP會有粘包和半包的現象。

 
4.效率上:速度上,一般TCP速度慢,傳輸過程中需要對資料進行確認,超時重發,還要對資料進行排序。UDP沒有這些機制所以速度快。資料比例,TCP頭至少20個位元組,UDP頭8個位元組,相對效率高。組裝效率上:TCP頭至少20個位元組,UDP頭8個位元組,系統組裝上TCP相對慢。

 
5.用途上:用於TCP可靠性,http,ftp使用。而由於UDP速度快,視訊,線上遊戲多用UDP,保證實時性

 
對於第三點的理解。TCP可能傳送100個“包”,而接收到50個“包”,不是丟“包”了,而是每次接受的“包”都比傳送的多,其實TCP並沒有包的概念。例如,每次發10個位元組,可能讀得時候一次讀了20個位元組。TCP是一種流模式的協議,在接收到的快取中按照發送的包得順序自動按照順序拼接好,因為資料基本來自同一個主機,而且是按照順序傳送過來的,TCP的快取中存放的就是,連續的資料。感覺好像是多封裝了一步比UDP。而UDP因為可能兩個不同的主機,給同一個主機發送,(一個埠可能收到多個應用程式的資料),或者按照TCP那樣合併資料,必然會造成資料錯誤。我覺得關鍵的原因還是,TCP是面向連線,而UDP是無連線的,這就導致,TCP接收的資料為一個主機發來且有序無誤的,而UDP可能是多個主機發來的無序,可能錯誤的。

寫一個c程式辨別系統是16位or32位

法一:int k=~0;

if((unsigned int)k >63356) cout<<"at least 32bits"<<endl;

else cout<<"16 bits"<<endl;

法二://32為系統

int i=65536;

cout<<i<<endl;

int j=65535;

cout<<j<<endl;

寫一個c程式辨別系統是大端or小端位元組序

用聯合體:如char型別的,可以看他輸出的是int的高位元組還是低位元組

訊號:列出常見的訊號,訊號怎麼處理?

訊號是Linux程式設計中非常重要的部分,本文將詳細介紹訊號機制的基本概念、Linux對訊號機制的大致實現方法、如何使用訊號,以及有關訊號的幾個系統呼叫。 

訊號機制是程序之間相互傳遞訊息的一種方法,訊號全稱為軟中斷訊號,也有人稱作軟中斷。從它的命名可以看出,它的實質和使用很象中斷。所以,訊號可以說是程序控制的一部分。 

一、訊號的基本概念 

本節先介紹訊號的一些基本概念,然後給出一些基本的訊號型別和訊號對應的事件。基本概念對於理解和使用訊號,對於理解訊號機制都特別重要。下面就來看看什麼是訊號。 

1、基本概念 

軟中斷訊號(signal,又簡稱為訊號)用來通知程序發生了非同步事件。程序之間可以互相通過系統呼叫kill傳送軟中斷訊號。核心也可以因為內部事件而給程序傳送訊號,通知程序發生了某個事件。注意,訊號只是用來通知某程序發生了什麼事件,並不給該程序傳遞任何資料。 

收 到訊號的程序對各種訊號有不同的處理方法。處理方法可以分為三類:第一種是類似中斷的處理程式,對於需要處理的訊號,程序可以指定處理函式,由該函式來處 理。第二種方法是,忽略某個訊號,對該訊號不做任何處理,就象未發生過一樣。第三種方法是,對該訊號的處理保留系統的預設值,這種預設操作,對大部分的信 號的預設操作是使得程序終止。程序通過系統呼叫signal來指定程序對某個訊號的處理行為。 

在程序表的表項中有一個軟中斷訊號域,該域中每一位對應一個訊號,當有訊號傳送給程序時,對應位置位。由此可以看出,程序對不同的訊號可以同時保留,但對於同一個訊號,程序並不知道在處理之前來過多少個。 

2、訊號的型別 

發出訊號的原因很多,這裡按發出訊號的原因簡單分類,以瞭解各種訊號: 

(1) 與程序終止相關的訊號。當程序退出,或者子程序終止時,發出這類訊號。 
(2) 與程序例外事件相關的訊號。如程序越界,或企圖寫一個只讀的記憶體區域(如程式正文區),或執行一個特權指令及其他各種硬體錯誤。 
(3) 與在系統呼叫期間遇到不可恢復條件相關的訊號。如執行系統呼叫exec時,原有資源已經釋放,而目前系統資源又已經耗盡。 
(4) 與執行系統呼叫時遇到非預測錯誤條件相關的訊號。如執行一個並不存在的系統呼叫。 
(5) 在使用者態下的程序發出的訊號。如程序呼叫系統呼叫kill向其他程序傳送訊號。 
(6) 與終端互動相關的訊號。如使用者關閉一個終端,或按下break鍵等情況。 
(7) 跟蹤程序執行的訊號。 

Linux支援的訊號列表如下。很多訊號是與機器的體系結構相關的,首先列出的是POSIX.1中列出的訊號: 

訊號 值 處理動作 發出訊號的原因 
---------------------------------------------------------------------- 
SIGHUP 1 A 終端掛起或者控制程序終止 
SIGINT 2 A 鍵盤中斷(如break鍵被按下) 
SIGQUIT 3 C 鍵盤的退出鍵被按下 
SIGILL 4 C 非法指令 
SIGABRT 6 C 由abort(3)發出的退出指令 
SIGFPE 8 C 浮點異常 
SIGKILL 9 AEF Kill訊號 
SIGSEGV 11 C 無效的記憶體引用 
SIGPIPE 13 A 管道破裂: 寫一個沒有讀埠的管道 
SIGALRM 14 A 由alarm(2)發出的訊號 
SIGTERM 15 A 終止訊號 
SIGUSR1 30,10,16 A 使用者自定義訊號1 
SIGUSR2 31,12,17 A 使用者自定義訊號2 
SIGCHLD 20,17,18 B 子程序結束訊號 
SIGCONT 19,18,25 程序繼續(曾被停止的程序) 
SIGSTOP 17,19,23 DEF 終止程序 
SIGTSTP 18,20,24 D 控制終端(tty)上按下停止鍵 
SIGTTIN 21,21,26 D 後臺程序企圖從控制終端讀 
SIGTTOU 22,22,27 D 後臺程序企圖從控制終端寫 

下面的訊號沒在POSIX.1中列出,而在SUSv2列出 

訊號 值 處理動作 發出訊號的原因 
-------------------------------------------------------------------- 
SIGBUS 10,7,10 C 匯流排錯誤(錯誤的記憶體訪問) 
SIGPOLL A Sys V定義的Pollable事件,與SIGIO同義 
SIGPROF 27,27,29 A Profiling定時器到 
SIGSYS 12,-,12 C 無效的系統呼叫 (SVID) 
SIGTRAP 5 C 跟蹤/斷點捕獲 
SIGURG 16,23,21 B Socket出現緊急條件(4.2 BSD) 
SIGVTALRM 26,26,28 A 實際時間報警時鐘訊號(4.2 BSD) 
SIGXCPU 24,24,30 C 超出設定的CPU時間限制(4.2 BSD) 
SIGXFSZ 25,25,31 C 超出設定的檔案大小限制(4.2 BSD) 

(對於SIGSYS,SIGXCPU,SIGXFSZ,以及某些機器體系結構下的SIGBUS,Linux預設的動作是A (terminate),SUSv2 是C (terminate and dump core))。 

下面是其它的一些訊號 

訊號 值 處理動作 發出訊號的原因 
---------------------------------------------------------------------- 
SIGIOT 6 C IO捕獲指令,與SIGABRT同義 
SIGEMT 7,-,7 
SIGSTKFLT -,16,- A 協處理器堆疊錯誤 
SIGIO 23,29,22 A 某I/O操作現在可以進行了(4.2 BSD) 
SIGCLD -,-,18 A 與SIGCHLD同義 
SIGPWR 29,30,19 A 電源故障(System V) 
SIGINFO 29,-,- A 與SIGPWR同義 
SIGLOST -,-,- A 檔案鎖丟失 
SIGWINCH 28,28,20 B 視窗大小改變(4.3 BSD, Sun) 
SIGUNUSED -,31,- A 未使用的訊號(will be SIGSYS) 

(在這裡,- 表示訊號沒有實現;有三個值給出的含義為,第一個值通常在Alpha和Sparc上有效,中間的值對應i386和ppc以及sh,最後一個值對應mips。訊號29在Alpha上為SIGINFO / SIGPWR ,在Sparc上為SIGLOST。) 

處理動作一項中的字母含義如下 
A 預設的動作是終止程序 
B 預設的動作是忽略此訊號 
C 預設的動作是終止程序並進行核心映像轉儲(dump core) 
D 預設的動作是停止程序 
E 訊號不能被捕獲 
F 訊號不能被忽略 

上 面介紹的訊號是常見系統所支援的。以表格的形式介紹了各種訊號的名稱、作用及其在預設情況下的處理動作。各種預設處理動作的含義是:終止程式是指程序退 出;忽略該訊號是將該訊號丟棄,不做處理;停止程式是指程式掛起,進入停止狀況以後還能重新進行下去,一般是在除錯的過程中(例如ptrace系統調 用);核心映像轉儲是指將程序資料在記憶體的映像和程序在核心結構中儲存的部分內容以一定格式轉儲到檔案系統,並且程序退出執行,這樣做的好處是為程式設計師提 供了方便,使得他們可以得到程序當時執行時的資料值,允許他們確定轉儲的原因,並且可以除錯他們的程式。 

注意 訊號SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。訊號SIGIOT與SIGABRT是一個訊號。可以看出,同一個訊號在不同的系統中值可能不一樣,所以建議最好使用為訊號定義的名字,而不要直接使用訊號的值。 

二、信 號 機 制 

上 一節中介紹了訊號的基本概念,在這一節中,我們將介紹核心如何實現訊號機制。即核心如何向一個程序傳送訊號、程序如何接收一個訊號、程序怎樣控制自己對信 號的反應、核心在什麼時機處理和怎樣處理程序收到的訊號。還要介紹一下setjmp和longjmp在訊號中起到的作用。 

1、核心對訊號的基本處理方法 

內 核給一個程序傳送軟中斷訊號的方法,是在程序所在的程序表項的訊號域設定對應於該訊號的位。這裡要補充的是,如果訊號傳送給一個正在睡眠的程序,那麼要看 該程序進入睡眠的優先順序,如果程序睡眠在可被中斷的優先順序上,則喚醒程序;否則僅設定程序表中訊號域相應的位,而不喚醒程序。這一點比較重要,因為程序檢 查是否收到訊號的時機是:一個程序在即將從核心態返回到使用者態時;或者,在一個程序要進入或離開一個適當的低排程優先順序睡眠狀態時。 

核心處理一個程序收到的訊號的時機是在一個程序從核心態返回使用者態時。所以,當一個程序在核心態下執行時,軟中斷訊號並不立即起作用,要等到將返回使用者態時才處理。程序只有處理完訊號才會返回使用者態,程序在使用者態下不會有未處理完的訊號。 

內 核處理一個程序收到的軟中斷訊號是在該程序的上下文中,因此,程序必須處於執行狀態。前面介紹概念的時候講過,處理訊號有三種類型:程序接收到訊號後退 出;程序忽略該訊號;程序收到訊號後執行使用者設定用系統呼叫signal的函式。當程序接收到一個它忽略的訊號時,程序丟棄該訊號,就象沒有收到該訊號似 的繼續執行。如果程序收到一個要捕捉的訊號,那麼程序從核心態返回使用者態時執行使用者定義的函式。而且執行使用者定義的函式的方法很巧妙,核心是在使用者棧上創 建一個新的層,該層中將返回地址的值設定成使用者定義的處理函式的地址,這樣程序從核心返回彈出棧頂時就返回到使用者定義的函式處,從函式返回再彈出棧頂時, 才返回原先進入核心的地方。這樣做的原因是使用者定義的處理函式不能且不允許在核心態下執行(如果使用者定義的函式在核心態下執行的話,使用者就可以獲得任何權 限)。 

在訊號的處理方法中有幾點特別要引起注意。第一,在一些系統中,當一個程序處理完中斷訊號返回使用者態之前,核心清除使用者區中設 定的對該訊號的處理例程的地址,即下一次程序對該訊號的處理方法又改為預設值,除非在下一次訊號到來之前再次使用signal系統呼叫。這可能會使得程序 在呼叫signal之前又得到該訊號而導致退出。在BSD中,核心不再清除該地址。但不清除該地址可能使得程序因為過多過快的得到某個訊號而導致堆疊溢 出。為了避免出現上述情況。在BSD系統中,核心模擬了對硬體中斷的處理方法,即在處理某個中斷時,阻止接收新的該類中斷。 

第二個要 引起注意的是,如果要捕捉的訊號發生於程序正在一個系統呼叫中時,並且該程序睡眠在可中斷的優先順序上,這時該訊號引起程序作一次longjmp,跳出睡眠 狀態,返回使用者態並執行訊號處理例程。當從訊號處理例程返回時,程序就象從系統呼叫返回一樣,但返回了一個錯誤程式碼,指出該次系統呼叫曾經被中斷。這要注 意的是,BSD系統中核心可以自動地重新開始系統呼叫。 

第三個要注意的地方:若程序睡眠在可中斷的優先順序上,則當它收到一個要忽略的訊號時,該程序被喚醒,但不做longjmp,一般是繼續睡眠。但使用者感覺不到程序曾經被喚醒,而是象沒有發生過該訊號一樣。 

第 四個要注意的地方:核心對子程序終止(SIGCLD)訊號的處理方法與其他訊號有所區別。當程序檢查出收到了一個子程序終止的訊號時,預設情況下,該程序 就象沒有收到該訊號似的,如果父程序執行了系統呼叫wait,程序將從系統呼叫wait中醒來並返回wait呼叫,執行一系列wait呼叫的後續操作(找 出僵死的子程序,釋放子程序的程序表項),然後從wait中返回。SIGCLD訊號的作用是喚醒一個睡眠在可被中斷優先順序上的程序。如果該程序捕捉了這個 訊號,就象普通訊號處理一樣轉到處理例程。如果程序忽略該訊號,那麼系統呼叫wait的動作就有所不同,因為SIGCLD的作用僅僅是喚醒一個睡眠在可被 中斷優先順序上的程序,那麼執行wait呼叫的父程序被喚醒繼續執行wait呼叫的後續操作,然後等待其他的子程序。 

如果一個程序呼叫signal系統呼叫,並設定了SIGCLD的處理方法,並且該程序有子程序處於僵死狀態,則核心將向該程序發一個SIGCLD訊號。 

2、setjmp和longjmp的作用 

前面在介紹訊號處理機制時,多次提到了setjmp和longjmp,但沒有仔細說明它們的作用和實現方法。這裡就此作一個簡單的介紹。 

在 介紹訊號的時候,我們看到多個地方要求程序在檢查收到訊號後,從原來的系統呼叫中直接返回,而不是等到該呼叫完成。這種程序突然改變其上下文的情況,就是 使用setjmp和longjmp的結果。setjmp將儲存的上下文存入使用者區,並繼續在舊的上下文中執行。這就是說,程序執行一個系統呼叫,當因為資 源或其他原因要去睡眠時,核心為程序作了一次setjmp,如果在睡眠中被訊號喚醒,程序不能再進入睡眠時,核心為程序呼叫longjmp,該操作是核心 為程序將原先setjmp呼叫儲存在程序使用者區的上下文恢復成現在的上下文,這樣就使得程序可以恢復等待資源前的狀態,而且核心為setjmp返回1,使 得程序知道該次系統呼叫失敗。這就是它們的作用。 

三、有關訊號的系統呼叫 

前面兩節已經介紹了有關訊號的大部分知 識。這一節我們來了解一下這些系統呼叫。其中,系統呼叫signal是程序用來設定某個訊號的處理方法,系統呼叫kill是用來發送訊號給指定程序的。這 兩個呼叫可以形成訊號的基本操作。後兩個呼叫pause和alarm是通過訊號實現的程序暫停和定時器,呼叫alarm是通過訊號通知程序定時器到時。所 以在這裡,我們還要介紹這兩個呼叫。 

1、signal 系統呼叫 

系統呼叫signal用來設定某個訊號的處理方法。該呼叫宣告的格式如下: 
void (*signal(int signum, void (*handler)(int)))(int); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include <signal.h> 

上述宣告格式比較複雜,如果不清楚如何使用,也可以通過下面這種型別定義的格式來使用(POSIX的定義): 
typedef void (*sighandler_t)(int); 
sighandler_t signal(int signum, sighandler_t handler); 
但這種格式在不同的系統中有不同的型別定義,所以要使用這種格式,最好還是參考一下聯機手冊。 

在呼叫中,引數signum指出要設定處理方法的訊號。第二個引數handler是一個處理函式,或者是 
SIG_IGN:忽略引數signum所指的訊號。 
SIG_DFL:恢復引數signum所指訊號的處理方法為預設值。 

傳遞給訊號處理例程的整數引數是訊號值,這樣可以使得一個訊號處理例程處理多個訊號。系統呼叫signal返回值是指定訊號signum前一次的處理例程或者錯誤時返回錯誤程式碼SIG_ERR。下面來看一個簡單的例子: 

#include <signal.h> 
#include <unistd.h> 
#include <stdio.h> 
void sigroutine(int dunno) { /* 訊號處理例程,其中dunno將會得到訊號的值 */ 
switch (dunno) { 
case 1: 
printf("Get a signal -- SIGHUP "); 
break; 
case 2: 
printf("Get a signal -- SIGINT "); 
break; 
case 3: 
printf("Get a signal -- SIGQUIT "); 
break; 

return; 


int main() { 
printf("process id is %d ",getpid()); 
signal(SIGHUP, sigroutine); //* 下面設定三個訊號的處理方法 
signal(SIGINT, sigroutine); 
signal(SIGQUIT, sigroutine); 
for (;;) ; 


其中訊號SIGINT由按下Ctrl-C發出,訊號SIGQUIT由按下Ctrl-發出。該程式執行的結果如下: 

localhost:~$ ./sig_test 
process id is 463 
Get a signal -SIGINT //按下Ctrl-C得到的結果 
Get a signal -SIGQUIT //按下Ctrl-得到的結果 
//按下Ctrl-z將程序置於後臺 
[1]+ Stopped ./sig_test 
localhost:~$ bg 
[1]+ ./sig_test & 
localhost:~$ kill -HUP 463 //向程序傳送SIGHUP訊號 
localhost:~$ Get a signal – SIGHUP 
kill -9 463 //向程序傳送SIGKILL訊號,終止程序 
localhost:~$ 

2、kill 系統呼叫 

系統呼叫kill用來向程序傳送一個訊號。該呼叫宣告的格式如下: 
int kill(pid_t pid, int sig); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include <sys/types.h> 
#include <signal.h> 

該 系統呼叫可以用來向任何程序或程序組傳送任何訊號。如果引數pid是正數,那麼該呼叫將訊號sig傳送到程序號為pid的程序。如果pid等於0,那麼信 號sig將傳送給當前程序所屬程序組裡的所有程序。如果引數pid等於-1,訊號sig將傳送給除了程序1和自身以外的所有程序。如果引數pid小於- 1,訊號sig將傳送給屬於程序組-pid的所有程序。如果引數sig為0,將不傳送訊號。該呼叫執行成功時,返回值為0;錯誤時,返回-1,並設定相應 的錯誤程式碼errno。下面是一些可能返回的錯誤程式碼: 
EINVAL:指定的訊號sig無效。 
ESRCH:引數pid指定的程序或程序組不存在。注意,在程序表項中存在的程序,可能是一個還沒有被wait收回,但已經終止執行的僵死程序。 
EPERM: 程序沒有權力將這個訊號傳送到指定接收訊號的程序。因為,一個程序被允許將訊號傳送到程序pid時,必須擁有root權力,或者是發出呼叫的程序的UID 或EUID與指定接收的程序的UID或儲存使用者ID(savedset-user-ID)相同。如果引數pid小於-1,即該訊號傳送給一個組,則該錯誤 表示組中有成員程序不能接收該訊號。 

3、pause系統呼叫 

系統呼叫pause的作用是等待一個訊號。該呼叫的宣告格式如下: 
int pause(void); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include <unistd.h> 

該呼叫使得發出呼叫的程序進入睡眠,直到接收到一個訊號為止。該呼叫總是返回-1,並設定錯誤程式碼為EINTR(接收到一個訊號)。下面是一個簡單的範例: 

#include <unistd.h> 
#include <stdio.h> 
#include <signal.h> 
void sigroutine(int unused) { 
printf("Catch a signal SIGINT "); 


int main() { 
signal(SIGINT, sigroutine); 
pause(); 
printf("receive a signal "); 


在這個例子中,程式開始執行,就象進入了死迴圈一樣,這是因為程序正在等待訊號,當我們按下Ctrl-C時,訊號被捕捉,並且使得pause退出等待狀態。 

4、alarm和 setitimer系統呼叫 

系統呼叫alarm的功能是設定一個定時器,當定時器計時到達時,將發出一個訊號給程序。該呼叫的宣告格式如下: 
unsigned int alarm(unsigned int seconds); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include <unistd.h> 

系 統呼叫alarm安排核心為呼叫程序在指定的seconds秒後發出一個SIGALRM的訊號。如果指定的引數seconds為0,則不再發送 SIGALRM訊號。後一次設定將取消前一次的設定。該呼叫返回值為上次定時呼叫到傳送之間剩餘的時間,或者因為沒有前一次定時呼叫而返回0。 

注意,在使用時,alarm只設定為傳送一次訊號,如果要多次傳送,就要多次使用alarm呼叫。 

對於alarm,這裡不再舉例。現在的系統中很多程式不再使用alarm呼叫,而是使用setitimer呼叫來設定定時器,用getitimer來得到定時器的狀態,這兩個呼叫的宣告格式如下: 
int getitimer(int which, struct itimerval *value); 
int setitimer(int which, const struct itimerval *value, struct itimerval*ovalue); 
在使用這兩個呼叫的程序中加入以下標頭檔案: 
#include <sys/time.h> 

該系統呼叫給程序提供了三個定時器,它們各自有其獨有的計時域,當其中任何一個到達,就傳送一個相應的訊號給程序,並使得計時器重新開始。三個計時器由引數which指定,如下所示: 
TIMER_REAL:按實際時間計時,計時到達將給程序傳送SIGALRM訊號。 
ITIMER_VIRTUAL:僅當程序執行時才進行計時。計時到達將傳送SIGVTALRM訊號給程序。 
ITIMER_PROF:當程序執行時和系統為該程序執行動作時都計時。與ITIMER_VIR-TUAL是一對,該定時器經常用來統計程序在使用者態和核心態花費的時間。計時到達將傳送SIGPROF訊號給程序。 

定時器中的引數value用來指明定時器的時間,其結構如下: 
struct itimerval { 
struct timeval it_interval; /* 下一次的取值 */ 
struct timeval it_value; /* 本次的設定值 */ 
}; 

該結構中timeval結構定義如下: 
struct timeval { 
long tv_sec; /* 秒 */ 
long tv_usec; /* 微秒,1秒 = 1000000 微秒*/ 
}; 

在setitimer 呼叫中,引數ovalue如果不為空,則其中保留的是上次呼叫設定的值。定時器將it_value遞減到0時,產生一個訊號,並將it_value的值設 定為it_interval的值,然後重新開始計時,如此往復。當it_value設定為0時,計時器停止,或者當它計時到期,而it_interval 為0時停止。呼叫成功時,返回0;錯誤時,返回-1,並設定相應的錯誤程式碼errno: 
EFAULT:引數value或ovalue是無效的指標。 
EINVAL:引數which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一個。 

下面是關於setitimer呼叫的一個簡單示範,在該例子中,每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM訊號: 

#include <signal.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <sys/time.h> 
int sec; 

void sigroutine(int signo) { 
switch (signo) { 
case SIGALRM: 
printf("Catch a signal -- SIGALRM "); 
break; 
case SIGVTALRM: 
printf("Catch a signal -- SIGVTALRM "); 
break; 

return; 


int main() { 
struct itimerval value,ovalue,value2; 
sec = 5; 

printf("process id is %d ",getpid()); 
signal(SIGALRM, sigroutine); 
signal(SIGVTALRM, sigroutine); 

value.it_value.tv_sec = 1; 
value.it_value.tv_usec = 0; 
value.it_interval.tv_sec = 1; 
value.it_interval.tv_usec = 0; 
setitimer(ITIMER_REAL, &value, &ovalue); 

value2.it_value.tv_sec = 0; 
value2.it_value.tv_usec = 500000; 
value2.it_interval.tv_sec = 0; 
value2.it_interval.tv_usec = 500000; 
setitimer(ITIMER_VIRTUAL, &value2, &ovalue); 

for (;;) ; 


該例子的螢幕拷貝如下: 

localhost:~$ ./timer_test 
process id is 579 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal –GVTALRM 

本文簡單介紹了Linux下的訊號,如果希望瞭解其他呼叫,請參考聯機手冊或其他文件。

i++是否原子操作?並解釋為什麼?

說出你所知道的linux系統的各類同步機制(重點),什麼是死鎖?如何避免死鎖(每個技術面試官必問)

死鎖的條件。(互斥條件(Mutualexclusion):1、資源不能被共享,只能由一個程序使用。2、請求與保持條件(Hold andwait):已經得到資源的程序可以再次申請新的資源。3、非剝奪條件(Nopre-emption):已經分配的資源不能從相應的程序中被強制地剝奪。4、迴圈等待條件(Circularwait):系統中若干程序組成環路,該環路中每個程序都在等待相鄰程序正佔用的資源。處理死鎖的策略:1.忽略該問題。例如鴕鳥演算法,該演算法可以應用在極少發生死鎖的的情況下。為什麼叫鴕鳥演算法呢,因為傳說中鴕鳥看到危險就把頭埋在地底下,可能鴕鳥覺得看不到危險也就沒危險了吧。跟掩耳盜鈴有點像。2.檢測死鎖並且恢復。3.仔細地對資源進行動態分配,以避免死鎖。4.通過破除死鎖四個必要條件之一,來防止死鎖產生。)

列舉說明linux系統的各類非同步機制

exit()與_exit()的區別?

_exit終止呼叫程序,但不關閉檔案,不清除輸出快取,也不調用出口函式。exit函式將終止呼叫程序。在退出程式之前,所有檔案關閉,緩衝輸出內容將重新整理定義,並呼叫所有已重新整理的“出口函式”(由atexit定義)。

‘exit()’與‘_exit()’有不少區別在使用‘fork()’,特別是‘vfork()’時變得很突出。

 ‘exit()’與‘_exit()’的基本區別在於前一個呼叫實施與呼叫庫裡使用者狀態結構(user-mode constructs)有關的清除工作(clean-up),而且呼叫使用者自定義的清除程式

如何實現守護程序?

守護程序(Daemon)是執行在後臺的一種特殊程序。它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。守護程序是一種很有用的程序。 Linux的大多數伺服器就是用守護程序實現的。比如,Internet伺服器inetd,Web伺服器httpd等。同時,守護程序完成許多系統任務。比如,作業規劃程序crond,列印程序lpd等。

守護程序的程式設計本身並不複雜,複雜的是各種版本的Unix的實現機制不盡相同,造成不同 Unix環境下守護程序的程式設計規則並不一致。需要注意,照搬某些書上的規則(特別是BSD4.3和低版本的System V)到Linux會出現錯誤的。下面將給出Linux下守護程序的程式設計要點和詳細例項。

一. 守護程序及其特性

守護程序最重要的特性是後臺執行。在這一點上DOS下的常駐記憶體程式TSR與之相似。其次,守護程序必須與其執行前的環境隔離開來。這些環境包括未關閉的檔案描述符,控制終端,會話和程序組,工作目錄以及檔案建立掩模等。這些環境通常是守護程序從執行它的父程序(特別是shell)中繼承下來的。最後,守護程序的啟動方式有其特殊之處。它可以在Linux系統啟動時從啟動指令碼/etc/rc.d中啟動,可以由作業規劃程序crond啟動,還可以由使用者終端(shell)執行。

總之,除開這些特殊性以外,守護程序與普通程序基本上沒有什麼區別。因此,編寫守護程序實際上是把一個普通程序按照上述的守護程序的特性改造成為守護程序。如果對程序有比較深入的認識就更容易理解和程式設計了。

二. 守護程序的程式設計要點

前面講過,不同Unix環境下守護程序的程式設計規則並不一致。所幸的是守護程序的程式設計原則其實都一樣,區別在於具體的實現細節不同。這個原則就是要滿足守護程序的特性。同時,Linux是基於Syetem V的SVR4並遵循Posix標準,實現起來與BSD4相比更方便。程式設計要點如下;

1. 在後臺執行。

為避免掛起控制終端將Daemon放入後臺執行。方法是在程序中呼叫fork使父程序終止,讓Daemon在子程序中後臺執行。

if(pid=fork())
exit(0); //是父程序,結束父程序,子程序繼續
2. 脫離控制終端,登入會話和程序組

有必要先介紹一下Linux中的程序與控制終端,登入會話和程序組之間的關係:程序屬於一個程序組,程序組號(GID)就是程序組長的程序號(PID)。登入會話可以包含多個程序組。這些程序組共享一個控制終端。這個控制終端通常是建立程序的登入終端。控制終端,登入會話和程序組通常是從父程序繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎上,呼叫setsid()使程序成為會話組長:

setsid();

說明:當程序是會話組長時setsid()呼叫失敗。但第一點已經保證程序不是會話組長。setsid()呼叫成功後,程序成為新的會話組長和新的程序組長,並與原來的登入會話和程序組脫離。由於會話過程對控制終端的獨佔性,程序同時與控制終端脫離。

3. 禁止程序重新開啟控制終端

現在,程序已經成為無終端的會話組長。但它可以重新申請開啟一個控制終端。可以通過使程序不再成為會話組長來禁止程序重新開啟控制終端:

if(pid=fork()) exit(0); //結束第一子程序,第二子程序繼續(第二子程序不再是會話組長)

4. 關閉開啟的檔案描述符

程序從建立它的父程序那裡繼承了開啟的檔案描述符。如不關閉,將會浪費系統資源,造成程序所在的檔案系統無法卸下以及引起無法預料的錯誤。按如下方法關閉它們:

for(i=0;i 關閉開啟的檔案描述符close(i);>

5. 改變當前工作目錄

程序活動時,其工作目錄所在的檔案系統不能卸下。一般需要將工作目錄改變到根目錄。對於需要轉儲核心,寫執行日誌的程序將工作目錄改變到特定目錄如 /tmpchdir("/")

6. 重設檔案建立掩模

程序從建立它的父程序那裡繼承了檔案建立掩模。它可能修改守護程序所建立的檔案的存取位。為防止這一點,將檔案建立掩模清除:umask(0);

7. 處理SIGCHLD訊號

處理SIGCHLD訊號並不是必須的。但對於某些程序,特別是伺服器程序往往在請求到來時生成子程序處理請求。如果父程序不等待子程序結束,子程序將成為殭屍程序(zombie)從而佔用系統資源。如果父程序等待子程序結束,將增加父程序的負擔,影響伺服器程序的併發效能。在Linux下可以簡單地將 SIGCHLD訊號的操作設為SIG_IGN。

signal(SIGCHLD,SIG_IGN);

這樣,核心在子程序結束時不會產生殭屍程序。這一點與BSD4不同,BSD4下必須顯式等待子程序結束才能釋放殭屍程序。

三. 守護程序例項

守護程序例項包括兩部分:主程式test.c和初始化程式init.c。主程式每隔一分鐘向/tmp目錄中的日誌test.log報告執行狀態。初始化程式中的init_daemon函式負責生成守護程序。讀者可以利用init_daemon函式生成自己的守護程序。

linux的記憶體管理機制是什麼?

Linux虛擬記憶體的實現需要6種機制的支援:地址對映機制、記憶體分配回收機制、快取和重新整理機制、請求頁機制、交換機制和記憶體共享機制

記憶體管理程式通過對映機制把使用者程式的邏輯地址對映到實體地址。當用戶程式執行時,如果發現程式中要用的虛地址沒有對應的實體記憶體,就發出了請求頁要求。如果有空閒的記憶體可供分配,就請求分配記憶體(於是用到了記憶體的分配和回收),並把正在使用的物理頁記錄在快取中(使用了快取機制)。如果沒有足夠的記憶體可供分配,那麼就呼叫交換機制;騰出一部分記憶體。另外,在地址對映中要通過TLB(翻譯後援儲存器)來尋找物理頁;交換機制中也要用到交換快取,並且把物理頁內容交換到交換檔案中,也要修改頁表來對映檔案地址。

linux的任務排程機制是什麼?

在每個程序的task_struct結構中有以下四項:policy、priority、counter、rt_priority。這四項是選擇程序的依據。其中,policy是程序的排程策略,用來區分實時程序和普通程序,實時程序優先於普通程序執行;priority是程序(包括實時和普通)的靜態優先順序;counter是程序剩餘的時間片,它的起始值就是priority的值;由於counter在後面計算一個處於可執行狀態的程序值得執行的程度goodness時起重要作用,因此,counter 也可以看作是程序的動態優先順序。rt_priority是實時程序特有的,用於實時程序間的選擇。 
Linux用函式goodness()來衡量一個處於可執行狀態的程序值得執行的程度。該函式綜合了以上提到的四項,還結合了一些其他的因素,給每個處於可執行狀態的程序賦予一個權值(weight),排程程式以這個權值作為選擇程序的唯一依據。關於goodness()的情況在後面將會詳細分析。

五種I/O 模式——

阻塞(預設IO模式),

非阻塞(常用於管道),

I/O多路複用(IO多路複用的應用場景),

訊號I/O,

非同步I/O  

五種I/O 模式:
【1】       阻塞I/O           (Linux下的I/O操作預設是阻塞I/O,即open和socket建立的I/O都是阻塞I/O)
【2】       非阻塞 I/O        (可以通過fcntl或者open時使用O_NONBLOCK引數,將fd設定為非阻塞的I/O)
【3】       I/O 多路複用     (I/O多路複用,通常需要非阻塞I/O配合使用)
【4】       訊號驅動 I/O    (SIGIO)
【5】        非同步 I/O

一般來說,程式進行輸入操作有兩步:
1.等待有資料可以讀
2.將資料從系統核心中拷貝到程式的資料區。

對於sock程式設計來說:

         第一步:   一般來說是等待資料從網路上傳到本地。當資料包到達的時候,資料將會從網路層拷貝到核心的快取中;

         第二步:   是從核心中把資料拷貝到程式的資料區中。

阻塞I/O模式                           //程序處於阻塞模式時,讓出CPU,進入休眠狀態
        阻塞 I/O 模式是最普遍使用的 I/O 模式。是Linux系統下預設的IO模式。

       大部分程式使用的都是阻塞模式的 I/O 。

       一個套接字建立後所處於的模式就是阻塞 I/O 模式。(因為Linux系統預設的IO模式是阻塞模式)


對於一個UDP 套接字來說,資料就緒的標誌比較簡單:
(1)已經收到了一整個資料報
(2)沒有收到。
而 TCP 這個概念就比較複雜,需要附加一些其他的變數。

       一個程序呼叫 recvfrom  ,然後系統呼叫並不返回知道有資料報到達本地系統,然後系統將資料拷貝到程序的快取中。(如果系統呼叫收到一箇中斷訊號,則它的呼叫會被中斷)

   我們稱這個程序在呼叫recvfrom一直到從recvfrom返回這段時間是阻塞的。當recvfrom正常返回時,我們的程序繼續它的操作。

非阻塞模式I/O                          //非阻塞模式的使用並不普遍,因為非阻塞模式會浪費大量的CPU資源。
       當我們將一個套接字設定為非阻塞模式,我們相當於告訴了系統核心: “當我請求的I/O 操作不能夠馬上完成,你想讓我的程序進行休眠等待的時候,不要這麼做,請馬上返回一個錯誤給我。”
      我們開始對 recvfrom 的三次呼叫,因為