騰訊後臺開發面試題

簡單概括:fd只是一個整數,在open時產生。起到一個索引的做用,進程經過PCB中的文件描述符表找到該fd所指向的文件指針filp。html

文件描述符的操做(如: open)返回的是一個文件描述符,內核會在每一個進程空間中維護一個文件描述符表, 全部打開的文件都將經過此表中的文件描述符來引用; 
而流(如: fopen)返回的是一個FILE結構指針, FILE結構是包含有文件描述符的,FILE結構函數能夠看做是對fd直接操做的系統調用的封裝, 它的優勢是帶有I/O緩存mysql

每一個進程在PCB(Process Control Block)即進程控制塊中都保存着一份文件描述符表,文件描述符就是這個表的索引,文件描述表中每一個表項都有一個指向已打開文件的指針,如今咱們明確一下:已打開的文件在內核中用file結構體表示,文件描述符表中的指針指向file結構體。linux

linux和os:nginx

netstat :顯示網絡狀態c++

Netstat 命令用於顯示各類網絡相關信息,如網絡鏈接,路由表,接口狀態 (Interface Statistics),masquerade 鏈接,多播成員 (Multicast Memberships) 等等。程序員

從總體上看,netstat的輸出結果能夠分爲兩個部分:

一個是Active Internet connections,稱爲有源TCP鏈接,其中"Recv-Q"和"Send-Q"指%0A的是接收隊列和發送隊列。這些數字通常都應該是0。若是不是則表示軟件包正在隊列中堆積。這種狀況只能在很是少的狀況見到。web

另外一個是Active UNIX domain sockets,稱爲有源Unix域套接口(和網絡套接字同樣,可是隻能用於本機通訊,性能能夠提升一倍)。
Proto顯示鏈接使用的協議,RefCnt表示鏈接到本套接口上的進程號,Types顯示套接口的類型,State顯示套接口當前的狀態,Path表示鏈接到套接口的其它進程使用的路徑名。面試

 

tcpdump:主要是截獲經過本機網絡接口的數據,用以分析。可以截獲當前全部經過本機網卡的數據包。它擁有靈活的過濾機制,能夠確保獲得想要的數據。算法

用簡單的話來定義tcpdump,就是:dump the traffic on a network,根據使用者的定義對網絡上的數據包進行截獲的包分析工具。 tcpdump能夠將網絡中傳送的數據包的「頭」徹底截獲下來提供分析。它支持針對網絡層、協議、主機、網絡或端口的過濾,並提供and、or、not等邏輯語句來幫助你去掉無用的信息。sql

 

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主要幫助你完成下面四個方面的功能:

一、啓動你的程序,能夠按照你的自定義的要求爲所欲爲的運行程序。

二、可以讓被調試的程序在你所指定的調置的斷點處停住。(斷點能夠是條件表達式

三、當程序被停住時,能夠檢查此時你的程序中所發生的事。

四、動態的改變你程序的執行環境。

《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,官方的說法就:進程是程序在計算機上的一次執行活動。

TCP和UDP分析

主要參看2篇博文:

http://blog.csdn.net/dog250/article/details/6612496

http://blog.csdn.net/dog250/article/details/6896949

還有就是謝老師寫的《計算機網絡》第五版,.TCP/IP詳解(卷一,卷二)以及《Unix網絡編程》以及Linux源代碼以外,RFC

1、概念:

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來指定進程對某個信號的處理行爲。 

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

二、信號的類型 

發出信號的緣由不少,這裏按發出信號的緣由簡單分類,以瞭解各類信號: 

(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是一個信號。能夠看出,同一個信號在不一樣的系統中值可能不同,因此建議最好使用爲信號定義的名字,而不要直接使用信號的值。 

2、信 號 機 制 

上 一節中介紹了信號的基本概念,在這一節中,咱們將介紹內核如何實現信號機制。即內核如何向一個進程發送信號、進程如何接收一個信號、進程怎樣控制本身對信 號的反應、內核在什麼時機處理和怎樣處理進程收到的信號。還要介紹一下setjmp和longjmp在信號中起到的做用。 

一、內核對信號的基本處理方法 

內 核給一個進程發送軟中斷信號的方法,是在進程所在的進程表項的信號域設置對應於該信號的位。這裏要補充的是,若是信號發送給一個正在睡眠的進程,那麼要看 該進程進入睡眠的優先級,若是進程睡眠在可被中斷的優先級上,則喚醒進程;不然僅設置進程表中信號域相應的位,而不喚醒進程。這一點比較重要,由於進程檢 查是否收到信號的時機是:一個進程在即將從內核態返回到用戶態時;或者,在一個進程要進入或離開一個適當的低調度優先級睡眠狀態時。 

內核處理一個進程收到的信號的時機是在一個進程從內核態返回用戶態時。因此,當一個進程在內核態下運行時,軟中斷信號並不當即起做用,要等到將返回用戶態時才處理。進程只有處理完信號纔會返回用戶態,進程在用戶態下不會有未處理完的信號。 

內 核處理一個進程收到的軟中斷信號是在該進程的上下文中,所以,進程必須處於運行狀態。前面介紹概念的時候講過,處理信號有三種類型:進程接收到信號後退 出;進程忽略該信號;進程收到信號後執行用戶設定用系統調用signal的函數。當進程接收到一個它忽略的信號時,進程丟棄該信號,就象沒有收到該信號似 的繼續運行。若是進程收到一個要捕捉的信號,那麼進程從內核態返回用戶態時執行用戶定義的函數。並且執行用戶定義的函數的方法很巧妙,內核是在用戶棧上創 建一個新的層,該層中將返回地址的值設置成用戶定義的處理函數的地址,這樣進程從內核返回彈出棧頂時就返回到用戶定義的函數處,從函數返回再彈出棧頂時, 才返回原先進入內核的地方。這樣作的緣由是用戶定義的處理函數不能且不容許在內核態下執行(若是用戶定義的函數在內核態下運行的話,用戶就能夠得到任何權 限)。 

在信號的處理方法中有幾點特別要引發注意。第一,在一些系統中,當一個進程處理完中斷信號返回用戶態以前,內核清除用戶區中設 定的對該信號的處理例程的地址,即下一次進程對該信號的處理方法又改成默認值,除非在下一次信號到來以前再次使用signal系統調用。這可能會使得進程 在調用signal以前又獲得該信號而致使退出。在BSD中,內核再也不清除該地址。但不清除該地址可能使得進程由於過多過快的獲得某個信號而致使堆棧溢 出。爲了不出現上述狀況。在BSD系統中,內核模擬了對硬件中斷的處理方法,即在處理某個中斷時,阻止接收新的該類中斷。 

第二個要 引發注意的是,若是要捕捉的信號發生於進程正在一個系統調用中時,而且該進程睡眠在可中斷的優先級上,這時該信號引發進程做一次longjmp,跳出睡眠 狀態,返回用戶態並執行信號處理例程。當從信號處理例程返回時,進程就象從系統調用返回同樣,但返回了一個錯誤代碼,指出該次系統調用曾經被中斷。這要注 意的是,BSD系統中內核能夠自動地從新開始系統調用。 

第三個要注意的地方:若進程睡眠在可中斷的優先級上,則當它收到一個要忽略的信號時,該進程被喚醒,但不作longjmp,通常是繼續睡眠。但用戶感受不到進程曾經被喚醒,而是象沒有發生過該信號同樣。 

第 四個要注意的地方:內核對子進程終止(SIGCLD)信號的處理方法與其餘信號有所區別。當進程檢查出收到了一個子進程終止的信號時,缺省狀況下,該進程 就象沒有收到該信號似的,若是父進程執行了系統調用wait,進程將從系統調用wait中醒來並返回wait調用,執行一系列wait調用的後續操做(找 出僵死的子進程,釋放子進程的進程表項),而後從wait中返回。SIGCLD信號的做用是喚醒一個睡眠在可被中斷優先級上的進程。若是該進程捕捉了這個 信號,就象普通訊號處理同樣轉處處理例程。若是進程忽略該信號,那麼系統調用wait的動做就有所不一樣,由於SIGCLD的做用僅僅是喚醒一個睡眠在可被 中斷優先級上的進程,那麼執行wait調用的父進程被喚醒繼續執行wait調用的後續操做,而後等待其餘的子進程。 

若是一個進程調用signal系統調用,並設置了SIGCLD的處理方法,而且該進程有子進程處於僵死狀態,則內核將向該進程發一個SIGCLD信號。 

二、setjmp和longjmp的做用 

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

在 介紹信號的時候,咱們看到多個地方要求進程在檢查收到信號後,從原來的系統調用中直接返回,而不是等到該調用完成。這種進程忽然改變其上下文的狀況,就是 使用setjmp和longjmp的結果。setjmp將保存的上下文存入用戶區,並繼續在舊的上下文中執行。這就是說,進程執行一個系統調用,當由於資 源或其餘緣由要去睡眠時,內核爲進程做了一次setjmp,若是在睡眠中被信號喚醒,進程不能再進入睡眠時,內核爲進程調用longjmp,該操做是內核 爲進程將原先setjmp調用保存在進程用戶區的上下文恢復成如今的上下文,這樣就使得進程能夠恢復等待資源前的狀態,並且內核爲setjmp返回1,使 得進程知道該次系統調用失敗。這就是它們的做用。 

3、有關信號的系統調用 

前面兩節已經介紹了有關信號的大部分知 識。這一節咱們來了解一下這些系統調用。其中,系統調用signal是進程用來設定某個信號的處理方法,系統調用kill是用來發送信號給指定進程的。這 兩個調用能夠造成信號的基本操做。後兩個調用pause和alarm是經過信號實現的進程暫停和定時器,調用alarm是經過信號通知進程定時器到時。所 以在這裏,咱們還要介紹這兩個調用。 

一、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:~$ 

二、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,即該信號發送給一個組,則該錯誤 表示組中有成員進程不能接收該信號。 

三、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退出等待狀態。 

四、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):一、資源不能被共享,只能由一個進程使用。二、請求與保持條件(Hold andwait):已經獲得資源的進程能夠再次申請新的資源。三、非剝奪條件(Nopre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。四、循環等待條件(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 的三次調用,由於系統尚未接收到網絡數據,因此內核立刻返回一個EWOULDBLOCK的錯誤。

      第四次咱們調用 recvfrom 函數,一個數據報已經到達了,內核將它拷貝到咱們的應用程序的緩衝區中,而後recvfrom 正常返回,咱們就能夠對接收到的數據進行處理了。
      當一個應用程序使用了非阻塞模式的套接字,它須要使用一個循環來不聽的測試是否一個文件描述符有數據可讀(稱作 polling(輪詢))。應用程序不停的 polling 內核來檢查是否 I/O操做已經就緒。這將是一個極浪費 CPU資源的操做。這種模式使用中不是很廣泛。

 

 例如:

         對管道的操做,最好使用非阻塞方式!

 

 

 

 

I/O多路複用                            //針對批量IP操做時,使用I/O多路複用,很是有好。

       在使用 I/O 多路技術的時候,咱們調用select()函數和 poll()函數或epoll函數(2.6內核開始支持),在調用它們的時候阻塞,而不是咱們來調用 recvfrom(或recv)的時候阻塞。
       當咱們調用 select函數阻塞的時候,select 函數等待數據報套接字進入讀就緒狀態。當select函數返回的時候,也就是套接字能夠讀取數據的時候。這時候咱們就能夠調用 recvfrom函數來將數據拷貝到咱們的程序緩衝區中。
        對於單個I/O操做,和阻塞模式相比較,select()和poll()或epoll並無什麼高級的地方。

       並且,在阻塞模式下只須要調用一個函數:

                            讀取或發送函數。

                  在使用了多路複用技術後,咱們須要調用兩個函數了:

                             先調用 select()函數或poll()函數,而後才能進行真正的讀寫。

       多路複用的高級之處在於::

             它能同時等待多個文件描述符,而這些文件描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select()函數就能夠返回。

 

IO 多路技術通常在下面這些狀況中被使用:
一、當一個客戶端須要同時處理多個文件描述符的輸入輸出操做的時候(通常來講是標準的輸入輸出和網絡套接字),I/O多路複用技術將會有機會獲得使用。
二、當程序須要同時進行多個套接字的操做的時候。
三、若是一個 TCP 服務器程序同時處理正在偵聽網絡鏈接的套接字和已經鏈接好的套接字。
四、若是一個服務器程序同時使用 TCP 和 UDP 協議。
五、若是一個服務器同時使用多種服務而且每種服務可能使用不一樣的協議(好比 inetd就是這樣的)。

 

 

 

異步IO模式有::

      一、信號驅動I/O模式

       二、異步I/O模式

信號驅動I/O模式                                                  //本身沒有用過。

       咱們能夠使用信號,讓內核在文件描述符就緒的時候使用 SIGIO 信號來通知咱們。咱們將這種模式稱爲信號驅動I/O 模式。

爲了在一個套接字上使用信號驅動 I/O 操做,下面這三步是所必須的。
(1)一個和 SIGIO信號的處理函數必須設定。
(2)套接字的擁有者必須被設定。通常來講是使用 fcntl 函數的 F_SETOWN 參數來
進行設定擁有者。
(3)套接字必須被容許使用異步 I/O。通常是經過調用 fcntl 函數的 F_SETFL 命令,O_ASYNC爲參數來實現。

       雖然設定套接字爲異步 I/O 很是簡單,可是使用起來困難的部分是怎樣在程序中判定產生 SIGIO信號發送給套接字屬主的時候,程序處在什麼狀態。

1.UDP 套接字的 SIGIO 信號                   (比較簡單)
在 UDP 協議上使用異步 I/O 很是簡單.這個信號將會在這個時候產生:

一、套接字收到了一個數據報的數據包。
二、套接字發生了異步錯誤。
        當咱們在使用 UDP 套接字異步 I/O 的時候,咱們使用 recvfrom()函數來讀取數據報數據或是異步 I/O 錯誤信息。
2.TCP 套接字的 SIGIO 信號                  (不會使用)
          不幸的是,異步 I/O 幾乎對 TCP 套接字而言沒有什麼做用。由於對於一個 TCP 套接字來講,SIGIO 信號發生的概率過高了,因此 SIGIO 信號並不能告訴咱們究竟發生了什麼事情。

在 TCP 鏈接中, SIGIO 信號將會在這個時候產生:
l  在一個監聽某個端口的套接字上成功的創建了一個新鏈接。
l  一個斷線的請求被成功的初始化。
l  一個斷線的請求成功的結束。
l  套接字的某一個通道(發送通道或是接收通道)被關閉。
l  套接字接收到新數據。
l  套接字將數據發送出去。

l  發生了一個異步 I/O 的錯誤。

一個對信號驅動 I/O 比較實用的方面是NTP(網絡時間協議 Network TimeProtocol)服務器,它使用 UDP。這個服務器的主循環用來接收從客戶端發送過來的數據報數據包,而後再發送請求。對於這個服務器來講,記錄下收到每個數據包的具體時間是很重要的。

由於那將是返回給客戶端的值,客戶端要使用這個數據來計算數據報在網絡上來回所花費的時間。圖 6-8 表示了怎樣創建這樣的一個 UDP 服務器。

 

 

異步I/O模式             //好比寫操做,只需用寫,不必定寫入磁盤(這就是異步I/O)的好處。異步IO的好處效率高。
      當咱們運行在異步 I/O 模式下時,咱們若是想進行 I/O 操做,只須要告訴內核咱們要進行 I/O 操做,而後內核會立刻返回。具體的 I/O 和數據的拷貝所有由內核來完成,咱們的程序能夠繼續向下執行。當內核完成全部的 I/O 操做和數據拷貝後,內核將通知咱們的程序。
異步 I/O 和  信號驅動I/O的區別是:
        一、信號驅動 I/O 模式下,內核在操做能夠被操做的時候通知給咱們的應用程序發送SIGIO 消息。

        二、異步 I/O 模式下,內核在全部的操做都已經被內核操做結束以後纔會通知咱們的應用程序。

select,poll,epoll

. Epoll 是何方神聖?

Epoll 但是當前在 Linux 下開發大規模併發網絡程序的熱門人選, Epoll 在 Linux2.6 內核中正式引入,和 select 類似,其實都 I/O 多路複用技術而已,並無什麼神祕的。

其實在Linux 下設計併發網絡程序,向來不缺乏方法,好比典型的 Apache 模型( Process Per Connection ,簡稱PPC), TPC ( ThreadPer Connection )模型,以及 select 模型和 poll 模型,那爲什麼還要再引入 Epoll 這個東東呢?那仍是有得說說的 …

2. 經常使用模型的缺點

若是不擺出來其餘模型的缺點,怎麼能對比出 Epoll 的優勢呢。

2.1 PPC/TPC 模型

這兩種模型思想相似,就是讓每個到來的鏈接一邊本身作事去,別再來煩我。只是 PPC 是爲它開了一個進程,而TPC 開了一個線程。但是別煩我是有代價的,它要時間和空間啊,鏈接多了以後,那麼多的進程 / 線程切換,這開銷就上來了;所以這類模型能接受的最大鏈接數都不會高,通常在幾百個左右。

2.2 select 模型

1. 最大併發數限制,由於一個進程所打開的 FD (文件描述符)是有限制的,www.linuxidc.com 由FD_SETSIZE 設置,默認值是 1024/2048 ,所以 Select 模型的最大併發數就被相應限制了。本身改改這個 FD_SETSIZE ?想法雖好,但是先看看下面吧 …

2. 效率問題, select 每次調用都會線性掃描所有的 FD 集合,這樣效率就會呈現線性降低,把 FD_SETSIZE 改大的後果就是,你們都慢慢來,什麼?都超時了??!!

3. 內核 / 用戶空間內存拷貝問題,如何讓內核把 FD 消息通知給用戶空間呢?在這個問題上 select 採起了內存拷貝方法。

2.3 poll 模型

基本上效率和select 是相同的,select 缺點的 2 和 3 它都沒有改掉。

3. Epoll 的提高

把其餘模型逐個批判了一下,再來看看 Epoll 的改進之處吧,其實把 select 的缺點反過來那就是 Epoll 的優勢了。

3.1. Epoll 沒有最大併發鏈接的限制,上限是最大能夠打開文件的數目,這個數字通常遠大於 2048, 通常來講這個數目和系統內存關係很大,具體數目能夠 cat /proc/sys/fs/file-max 察看。

3.2. 效率提高, Epoll 最大的優勢就在於它只管你「活躍」的鏈接,而跟鏈接總數無關,所以在實際的網絡環境中, Epoll的效率就會遠遠高於 select 和 poll 。

3.3. 內存拷貝, Epoll 在這點上使用了「共享內存 」,這個內存拷貝也省略了。


4. Epoll 爲何高效

Epoll 的高效和其數據結構的設計是密不可分的,這個下面就會提到。

首先回憶一下select 模型,當有I/O 事件到來時,select 通知應用程序有事件到了快去處理,而應用程序必須輪詢全部的FD 集合,測試每一個 FD 是否有事件發生,並處理事件;代碼像下面這樣:

int res = select(maxfd+1, &readfds,NULL, NULL, 120);

if (res > 0)

{

    for (int i = 0; i <MAX_CONNECTION; i++)

    {

       if (FD_ISSET(allConnection[i], &readfds))

       {

           handleEvent(allConnection[i]);

       }

    }

}

// if(res == 0) handle timeout, res < 0handle error


Epoll 不只會告訴應用程序有I/0事件到來,還會告訴應用程序相關的信息,這些信息是應用程序填充的,所以根據這些信息應用程序就能直接定位到事件,而沒必要遍歷整個FD 集合。

int res = epoll_wait(epfd, events, 20,120);

for (int i = 0; i < res;i++)

{

    handleEvent(events[n]);

}

5. Epoll 關鍵數據結構

前面提到Epoll 速度快和其數據結構密不可分,其關鍵數據結構就是:

struct epoll_event {

    __uint32_tevents;      // Epoll events

    epoll_data_tdata;      // User data variable

};

typedef union epoll_data {

    void *ptr;

    int fd;

    __uint32_t u32;

    __uint64_t u64;

} epoll_data_t;

可見epoll_data 是一個 union 結構體 , 藉助於它應用程序能夠保存不少類型的信息 :fd 、指針等等。有了它,應用程序就能夠直接定位目標了。

 

標準庫函數和系統調用的區別?

一、系統調用和庫函數的關係

系統調用經過軟中斷int 0x80從用戶態進入內核態。

函數庫中的某些函數調用了系統調用。

函數庫中的函數能夠沒有調用系統調用,也能夠調用多個系統調用。

編程人員能夠經過函數庫調用系統調用。

高級編程也能夠直接採用int 0x80進入系統調用,而沒必要經過函數庫做爲中介。

若是是在覈心編程,也能夠經過int 0x80進入系統調用,此時不能使用函數庫。由於函數庫中的函數是內核訪問不到的。

二、從用戶調用庫函數到系統調用執行的流程。

1) 假設用戶調用ssize_t write (int fields, cont void *buff, size_tnbytes);庫函數。

2) 庫函數會執行int 0x80中斷。由於中斷使得進程從用戶態進入內核態,因此參數經過寄存器傳送。

3) 0x80中斷對應的中斷例程被稱爲system call handler。其工做是:

 i.  存儲大多數寄存器到內核堆棧中。這是彙編代碼寫的。

 ii.  執行真正的系統調用函數――system callservice routine。這是C代碼。

 iii. 經過ret_from_sys_call()返回,回到用戶態的庫函數。這是彙編代碼。

一、系統調用

系統調用提供的函數如open, close, read, write, ioctl等,需包含頭文件unistd.h。以write爲例:其函數原型爲 size_t write(int fd, const void *buf, size_t nbytes),其操做對象爲文件描述符或文件句柄fd(file descriptor),要想寫一個文件,必須先以可寫權限用open系統調用打開一個文件,得到所打開文件的fd,例如fd=open(/"/dev/video/", O_RDWR)。fd是一個整型值,每新打開一個文件,所得到的fd爲當前最大fd加1。Linux系統默認分配了3個文件描述符值:0-standard input,1-standard output,2-standard error。

系統調用一般用於底層文件訪問(low-level file access),例如在驅動程序中對設備文件的直接訪問。

系統調用是操做系統相關的,所以通常沒有跨操做系統的可移植性。

系統調用發生在內核空間,所以若是在用戶空間的通常應用程序中使用系統調用來進行文件操做,會有用戶空間到內核空間切換的開銷。事實上,即便在用戶空間使用庫函數來對文件進行操做,由於文件老是存在於存儲介質上,所以無論是讀寫操做,都是對硬件(存儲器)的操做,都必然會引發系統調用。也就是說,庫函數對文件的操做其實是經過系統調用來實現的。例如C庫函數fwrite()就是經過write()系統調用來實現的。

這樣的話,使用庫函數也有系統調用的開銷,爲何不直接使用系統調用呢?這是由於,讀寫文件一般是大量的數據(這種大量是相對於底層驅動的系統調用所實現的數據操做單位而言),這時,使用庫函數就能夠大大減小系統調用的次數。這一結果又緣於緩衝區技術。在用戶空間和內核空間,對文件操做都使用了緩衝區,例如用fwrite寫文件,都是先將內容寫到用戶空間緩衝區,當用戶空間緩衝區滿或者寫操做結束時,纔將用戶緩衝區的內容寫到內核緩衝區,一樣的道理,當內核緩衝區滿或寫結束時纔將內核緩衝區內容寫到文件對應的硬件媒介。

二、庫函數調用

標準C庫函數提供的文件操做函數如fopen, fread, fwrite, fclose,fflush, fseek等,需包含頭文件stdio.h。以fwrite爲例,其函數原型爲size_t fwrite(const void *buffer,size_t size, size_t item_num, FILE *pf),其操做對象爲文件指針FILE *pf,要想寫一個文件,必須先以可寫權限用fopen函數打開一個文件,得到所打開文件的FILE結構指針pf,例如pf=fopen(/"~/proj/filename/",/"w/")。實際上,因爲庫函數對文件的操做最終是經過系統調用實現的,所以,每打開一個文件所得到的FILE結構指針都有一個內核空間的文件描述符fd與之對應。一樣有相應的預約義的FILE指針:stdin-standard input,stdout-standard output,stderr-standard error。

庫函數調用一般用於應用程序中對通常文件的訪問。

庫函數調用是系統無關的,所以可移植性好。

因爲庫函數調用是基於C庫的,所以也就不可能用於內核空間的驅動程序中對設備的操做

ping命令所利用的原理是這樣的:網絡上的機器都有惟一肯定的IP地址,咱們給目標IP地址發送一個數據包,對方就要返回一個一樣大小的數據包,根據返回的數據包咱們能夠肯定目標主機的存在,能夠初步判斷目標主機的操做系統等。

補充一個坑爹坑爹坑爹坑爹的問題:系統如何將一個信號通知到進程?

信號機制是異步的;當一個進程接收到一個信號時,它會馬上處理這個信號,而不會等待當前函數甚至當前一行代碼結束運行。信號有幾十種,分別表明着不一樣的意義。信號之間依靠它們的值來區分,可是一般在程序中使用信號的名字來表示一個信號。在Linux系統中,這些信號和以它們的名稱命名的常量均定義在/usr/include/bits/signum.h文件中。(一般程序中不須要直接包含這個頭文件,而應該包含<signal.h>。)

信號事件的發生有兩個來源:硬件來源(好比咱們按下了鍵盤或者其它硬件故障);軟件來源,最經常使用發送信號的系統函數是kill, raise, alarm和setitimer以及sigqueue函數,軟件來源還包括一些非法運算等操做。

發送信號的主要函數有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。

 

進程能夠經過三種方式來響應一個信號:(1)忽略信號,即對信號不作任何處理,其中,有兩個信號不能忽略:SIGKILL及SIGSTOP;(2)捕捉信號。定義信號處理函數,當信號發生時,執行相應的處理函數;(3)執行缺省操做,

 c語言:

宏定義和展開(必須精通)

#define是C語言中提供的宏定義命令,其主要目的是爲程序員在編程時提供必定的方便,並能在必定程度上提升程序的運行效率,但學生在學習時每每不能理解該命令的本質,老是在此處產生一些困惑,在編程時誤用該命令,使得程序的運行與預期的目的不一致,或者在讀別人寫的程序時,把運行結果理解錯誤,這對C語言的學習很不利。
1#define命令剖析 
1.1   #define的概念
#define命令是C語言中的一個宏定義命令,它用來將一個標識符定義爲一個字符串,該標識符被稱爲宏名,被定義的字符串稱爲替換文本。
該命令有兩種格式:一種是簡單的宏定義,另外一種是帶參數的宏定義。
(1)   簡單的宏定義:
#define   <宏名><字符串> 
   例:  #define PI 3.1415926 
(2) 帶參數的宏定義
   #define   <宏名> (<參數表>)  <宏體>
   例:#define   A(x) x 
一個標識符被宏定義後,該標識符即是一個宏名。這時,在程序中出現的是宏名,在該程序被編譯前,先將宏名用被定義的字符串替換,這稱爲宏替換,替換後才進行編譯,宏替換是簡單的替換。
1.2 宏替換髮生的時機
爲了可以真正理解#define的做用,讓咱們來了解一下對C語言源程序的處理過程。當咱們在一個集成的開發環境如TurboC中將編寫好的源程序進行編譯時,實際通過了預處理、編譯、彙編和鏈接幾個過程

 

 

這裏要說一下宏的展開次序,若是宏有參數,如TO_STRING_MACRO(x)中的x,咱們稱之爲形參,而宏實際的參數咱們稱之爲實參,如TO_STRING_MACRO(a)中的a。

宏的展開是個很複雜的過程,但能夠用如下三步來簡單描述,

首先用實參替換形參,將實參代入宏文本中;

而後若是實參也是宏,則展開實參;

最後再繼續處理宏替換後的宏文本,若宏文本也包含宏則繼續展開,不然完成展開。

可是有個例外,那就是第一步後,將實參代入宏文本後,實參以前若是遇到字符「#」或「##」,即便實參是宏,也再也不展開實參,而是看成文本處理。

 

位操做(必須精通)

在計算機程序中,數據的位是能夠操做的最小數據單位,理論上能夠用「位運算」來完成全部的運算和操做。通常的位操做是用來控制硬件的,或者作數據變換使用,可是,靈活的位操做能夠有效地提升程序運行的效率。C語言提供了位運算的功能,這使得C語言也能像彙編語言同樣用來編寫系統程序。

  位運算符C語言提供了六種位運算符:

& 按位與

| 按位或

^ 按位異或

~ 取反

<< 左移

>> 右移

 

指針操做和計算(必須精通)

&是地址操做符,用來引用一個內存地址。經過在變量名字前使用&操做符,咱們能夠獲得該變量的內存地址。

1

2

3

4

5

6

7

8

9

// 聲明一個int指針

int*ptr;

// 聲明一個int值

intval = 1;

// 爲指針分配一個int值的引用

ptr = &val;

// 對指針進行取值,打印存儲在指針地址中的內容

intderef = *ptr;

printf("%d\n", deref);

指針是一個存儲計算機內存地址的變量。在這份教程裏「引用」表示計算機內存地址。從指針指向的內存讀取數據稱做指針的取值。指針能夠指向某些具體類型的變量地址,例如 int、long 和 double。指針也能夠是 void 類型、NULL 指針和未初始化指針。本文會對上述全部指針類型進行探討。

  根據出現的位置不一樣,操做符 * 既能夠用來聲明一個指針變量,也能夠用做指針的取值。當用在聲明一個變量時,*表示這裏聲明瞭一個指針。其它狀況用到*表示指針的取值。

  &是地址操做符,用來引用一個內存地址。經過在變量名字前使用&操做符,咱們能夠獲得該變量的內存地址。

語言中指針的15個問題 
aqiaoboy 

1 指針的四要素 
  1指針變量,表示一個內存地址,一般爲邏輯地址,與實際的物理地址還有一個映射關係. 
2指針變量的長度,在WIN32下爲四個字節, 
  3指針指向的變量 
   該內存地址空間下存放的變量,具體內容多是各類類型的變量. 
  4 指針指向的變量的長度,以該內存地址空間開始的內存空間大小. 
2 Const,volatile修飾指針的含義 
const    char *cpch=」hello’; 
表示指針指向的變量不可改變,但指針自己是能夠改變的 
char * const    pchc; 
指針指向的變量能夠改變,但指針自己不可改變. 

Const  char * const pchc; 
二者都不可變. 
3 堆和棧上的指針 
指針所指向的這塊內存是在哪裏分配的,在堆上稱爲堆上的指針,在棧上爲棧上的指針. 
在堆上的指針,能夠保存在全局數據結構中,供不一樣函數使用訪問同一塊內存. 
在棧上的指針,在函數退出後,該內存即不可訪問. 

4 什麼是指針的釋放? 
具體來講包括兩個概念. 
1 釋放該指針指向的內存,只有堆上的內存才須要咱們手工釋放,棧上不須要. 
2 將該指針重定向爲NULL. 

5 near,far型指針的區別? 
老式的IBM PC兼容機纔有這種區別,由於老式機不能徹底支持32位指針, 
因此才分爲16位指針,(near),和32位指針(far) 
從386開始沒有這種區別,都是32位指針. 

6 數據結構中的指針? 
其實就是指向一塊內存的地址,經過指針傳遞,可實現複雜的內存訪問. 
7 函數指針? 
指向一塊函數的入口地址. 

8 指針做爲函數的參數? 
好比指向一個複雜數據結構的指針做爲函數變量 
這種方法避免整個複雜數據類型內存的壓棧出棧操做,提升效率. 
注意:指針自己不可變,但指針指向的數據結構能夠改變. 

9 指向指針的指針? 
指針指向的變量是一個指針,即具體內容爲一個指針的值,是一個地址. 
此時指針指向的變量長度也是4位. 

10 指針與地址的區別? 
區別: 
1指針意味着已經有一個指針變量存在,他的值是一個地址,指針變量自己也存放在一個長度爲四個字節的地址當中,而地址概念自己並不表明有任何變量存在. 
2 指針的值,若是沒有限制,一般是能夠變化的,也能夠指向另一個地址. 
   地址表示內存空間的一個位置點,他是用來賦給指針的,地址自己是沒有大小概念,指針指向變量的大小,取決於地址後面存放的變量類型. 

11 指針與數組名的關係? 
  其值都是一個地址,但前者是能夠移動的,後者是不可變的. 

12 怎樣防止指針的越界使用問題? 
  必須讓指針指向一個有效的內存地址, 
1 防止數組越界 
2 防止向一塊內存中拷貝過多的內容 
3 防止使用空指針 
4 防止改變const修改的指針 
5 防止改變指向靜態存儲區的內容 
6 防止兩次釋放一個指針 
7 防止使用野指針. 


13 指針的類型轉換? 
指針轉換一般是指針類型和void * 類型以前進行強制轉換,從而與指望或返回void指針的函數進行正確的交接. 


14 什麼是指針退化? 
若是用一個數組做爲函數入參 
好比 
void fun(char a[100]) 

cout<<SIZEOF(A)<


15 指針的移動問題? 

指針P ++具體移動的字節數等於指針指向的變量類型大小. 



寫得還比較全的 
函數參數我都用指針沒有用數組 

14 什麼是指針退化? 
若是用一個數組做爲函數入參 
好比 
void fun(char a[100]) 

cout<<SIZEOF(A)<


像這樣定義也不能保證檢查傳進來的參數對不對 
[code] 
int a(char s[100]) 

    s[100]=1; 
    return 0; 


main() 

   char s[20]="hello"; 
   a(s); 

[/code]

內存分配(必須精通)

sizeof必考

內存分配方式有三種:

  (1)從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。

  (2)在棧上建立。在執行函數時,函數內局部變量的存儲單元均可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,可是分配的內存容量有限。

  (3)從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員本身負責在什麼時候用free或delete釋放內存。動態內存的生存期由咱們決定,使用很是靈活,但問題也最多。

各種庫函數必須很是熟練的實現

 

哪些庫函數屬於高危函數,爲何?(strcpy等等)

c++:

一個String類的完整實現必須很快速寫出來(注意:賦值構造,operator=是關鍵)

虛函數的做用和實現原理(必問必考,實現原理必須很熟)

有虛函數的類內部有一個稱爲「虛表」的指針(有多少個虛函數就有多少個指針),這個就是用來指向這個類虛函數。也就是用它來肯定調用該那個函數。

實際上在編譯的時候,編譯器會自動加入「虛表」。虛表的使用方法是這樣的:若是派生類在本身的定義中沒有修改基類的虛函數,就指向基類的虛函數;若是派生類改寫了基類的虛函數(就是本身從新定義),這時虛表則將原來指向基類的虛函數的地址替換爲指向自身虛函數的指針。那些被virtual關鍵字修飾的成員函數,就是虛函數。虛函數的做用,用專業術語來解釋就是實現多態性(Polymorphism),多態性是將接口與實現進行分離;用形象的語言來解釋就是實現以共同的方法,但因個體差別而採用不一樣的策略。

每一個類都有本身的vtbl,vtbl的做用就是保存本身類中虛函數的地址,咱們能夠把vtbl形象地當作一個數組,這個數組的每一個元素存放的就是虛函數的地址,

虛函數的效率低,其緣由就是,在調用虛函數以前,還調用了得到虛函數地址的代碼。

 

sizeof一個類求大小(注意成員變量,函數,虛函數,繼承等等對大小的影響)

指針和引用的區別(通常都會問到)

相同點:1. 都是地址的概念;
指針指向一塊內存,它的內容是所指內存的地址;引用是某塊內存的別名。

區別:1. 指針是一個實體,而引用僅是個別名;

2. 引用使用時無需解引用(*),指針須要解引用;

3. 引用只能在定義時被初始化一次,以後不可變;指針可變;

4. 引用沒有 const,指針有 const;

5. 引用不能爲空,指針能夠爲空;

6. 「sizeof 引用」獲得的是所指向的變量(對象)的大小,而「sizeof 指針」獲得的是指針自己(所指向的變量或對象的地址)的大小;

7. 指針和引用的自增(++)運算意義不同;

8.從內存分配上看:程序爲指針變量分配內存區域,而引用不須要分配內存區域。

多重類構造和析構的順序

先調用基類的構造函數,在調用派生類的構造函數

先構造的後析構,後構造的先析構

stl各容器的實現原理(必考)

STL = Standard Template Library,標準模板庫

STL共有六大組件
 一、容器。二、算法。三、迭代器。四、仿函數。六、適配器。

序列式容器:
vector-數組,元素不夠時再從新分配內存,拷貝原來數組的元素到新分配的數組中。
list-單鏈表。
deque-分配中央控制器map(並不是map容器),map記錄着一系列的固定長度的數組的地址.記住這個map僅僅保存的是數組的地址,真正的數據在數組中存放着.deque先從map中央的位置(由於雙向隊列,先後均可以插入元素)找到一個數組地址,向該數組中放入數據,數組不夠時繼續在map中找空閒的數組來存數據。當map也不夠時從新分配內存看成新的map,把原來map中的內容copy的新map中。因此使用deque的複雜度要大於vector,儘可能使用vector。

stack-基於deque。
queue-基於deque。
heap-徹底二叉樹,使用最大堆排序,以數組(vector)的形式存放。
priority_queue-基於heap。
slist-雙向鏈表。

關聯式容器:
set,map,multiset,multimap-基於紅黑樹(RB-tree),一種加上了額外平衡條件的二叉搜索樹。

hash table-散列表。將待存數據的key通過映射函數變成一個數組(通常是vector)的索引,例如:數據的key%數組的大小=數組的索引(通常文本經過算法也能夠轉換爲數字),而後將數據看成此索引的數組元素。有些數據的key通過算法的轉換多是同一個數組的索引值(碰撞問題,能夠用線性探測,二次探測來解決),STL是用開鏈的方法來解決的,每個數組的元素維護一個list,他把相同索引值的數據存入一個list,這樣當list比較短時執行刪除,插入,搜索等算法比較快。

hash_map,hash_set,hash_multiset,hash_multimap-基於hashtable。

extern c 是幹啥的,(必須將編譯器的函數名修飾的機制解答的很透徹)

volatile是幹啥用的,(必須將cpu的寄存器緩存機制回答的很透徹)

volatile的本意是「易變的」 由於訪問寄存器要比訪問內存單元快的多,因此編譯器通常都會做減小存取內存的優化,但有可能會讀髒數據。當要求使用volatile聲明變量值的時候,系統老是從新從它所在的內存讀取數據,即便它前面的指令剛剛從該處讀取過數據。精確地說就是,遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就再也不進行優化,從而能夠提供對特殊地址的穩定訪問;若是不使用volatile,則編譯器將對所聲明的語句進行優化。(簡潔的說就是:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發生變化,與該變量有關的運算,不要進行編譯優化,以避免出錯)

5.volatile的本質:

1> 編譯器的優化

在本次線程內, 當讀取一個變量時,爲提升存取速度,編譯器優化時有時會先把變量讀取到一個寄存器中;之後,再取變量值時,就直接從寄存器中取值;當變量值在本線程裏改變時,會同時把變量的新值copy到該寄存器中,以便保持一致。

當變量在因別的線程等而改變了值,該寄存器的值不會相應改變,從而形成應用程序讀取的值和實際的變量值不一致。

當該寄存器在因別的線程等而改變了值,原變量的值不會改變,從而形成應用程序讀取的值和實際的變量值不一致。

2>volatile應該解釋爲「直接存取原始內存地址」比較合適,「易變的」這種解釋簡直有點誤導人。

 

static const等等的用法,(能說出越多越好)

數據結構或者算法:

《離散數學》範圍內的一切問題皆由可能被深刻問到(這個最坑爹,最重要,最體現功底,最能加分,特別是各種樹結構的實現和應用)

各種排序:大根堆的實現,快排(如何避免最糟糕的狀態?),bitmap的運用等等

hash,任何一個技術面試官必問(例如爲何通常hashtable的桶數會取一個素數?如何有效避免hash結果值的碰撞)

網絡編程:

tcp與udp的區別(必問)

1.基於鏈接與無鏈接 
2.對系統資源的要求(TCP較多,UDP少) 
3.UDP程序結構較簡單 
4.流模式與數據報模式
5.TCP保證數據正確性,UDP可能丟包,TCP保證數據順序,UDP不保證

TCP---傳輸控制協議,提供的是面向鏈接、可靠的字節流服務。當客戶和服務器彼此交換數據前,必須先在雙方之間創建一個TCP鏈接,以後才能傳輸數據。TCP提供超時重發,丟棄重複數據,檢驗數據,流量控制等功能,保證數據能從一端傳到另外一端。
UDP---用戶數據報協議,是一個簡單的面向數據報的運輸層協議。UDP不提供可靠性,它只是把應用程序傳給IP層的數據報發送出去,可是並不能保證它們能到達目的地。因爲UDP在傳輸數據報前不用在客戶和服務器之間創建一個鏈接,且沒有超時重發等機制,故而傳輸速度很快

 

udp調用connect有什麼做用?

1:UDP中能夠使用connect系統調用2:UDP中connect操做與TCP中connect操做有着本質區別.TCP中調用connect會引發三次握手,client與server創建連結.UDP中調用connect內核僅僅把對端ip&port記錄下來.3:UDP中能夠屢次調用connect,TCP只能調用一次connect.UDP屢次調用connect有兩種用途:1,指定一個新的ip&port連結.2,斷開和以前的ip&port的連結.指定新連結,直接設置connect第二個參數便可.斷開連結,須要將connect第二個參數中的sin_family設置成 AF_UNSPEC便可. 4:UDP中使用connect能夠提升效率.緣由以下:普通的UDP發送兩個報文內核作了以下:#1:創建連結#2:發送報文#3:斷開連結#4:創建連結#5:發送報文#6:斷開連結采用connect方式的UDP發送兩個報文內核以下處理:#1:創建連結#2:發送報文#3:發送報文另一點,每次發送報文內核都由可能要作路由查詢.5:採用connect的UDP發送接受報文能夠調用send,write和recv,read操做.固然也能夠調用sendto,recvfrom.調用sendto的時候第五個參數必須是NULL,第六個參數是0.調用recvfrom,recv,read系統調用只能獲取到先前connect的ip&port發送的報文. 
UDP中使用connect的好處:1:會提高效率.前面已經描述了.2:高併發服務中會增長系統穩定性.緣由:假設client A 經過非connect的UDP與serverB,C通訊.B,C提供相同服務.爲了負載均衡,咱們讓A與B,C交替通訊.A 與 B通訊IPa:PORTa<----> IPb:PORTbA 與 C通訊IPa:PORTa'<---->IPc:PORTc 
假設PORTa 與 PORTa'相同了(在大併發狀況下會發生這種狀況),那麼就有可能出現A等待B的報文,卻收到了C的報文.致使收報錯誤.解決方法內就是採用connect的UDP通訊方式.在A中建立兩個udp,而後分別connect到B,C.

tcp鏈接中時序圖,狀態圖,必須很是很是熟練

 

 

socket服務端的實現,select和epoll的區別(必問)

select的本質是採用32個整數的32位,即32*32= 1024來標識,fd值爲1-1024。當fd的值超過1024限制時,就必須修改FD_SETSIZE的大小。這個時候就能夠標識32*max值範圍的fd。

對於單進程多線程,每一個線程處理多個fd的狀況,select是不適合的。

1.全部的線程均是從1-32*max進行掃描,每一個線程處理的均是一段fd值,這樣作有點浪費

2.1024上限問題,一個處理多個用戶的進程,fd值遠遠大於1024

因此這個時候應該採用poll,

poll傳遞的是數組頭指針和該數組的長度,只要數組的長度不是很長,性能仍是很不錯的,由於poll一次在內核中申請4K(一個頁的大小來存放fd),儘可能控制在4K之內

epoll仍是poll的一種優化,返回後不須要對全部的fd進行遍歷,在內核中維持了fd的列表。select和poll是將這個內核列表維持在用戶態,而後傳遞到內核中。可是隻有在2.6的內核才支持。

epoll更適合於處理大量的fd ,且活躍fd不是不少的狀況,畢竟fd較多仍是一個串行的操做

 

epoll哪些觸發模式,有啥區別?(必須很是詳盡的解釋水平觸發和邊緣觸發的區別,以及邊緣觸發在編程中要作哪些更多的確認)

epoll能夠同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變爲就緒狀態,它只說一遍,若是咱們沒有采起行動,那麼它將不會再次告知,這種方式稱爲邊緣觸發),理論上邊緣觸發的性能要更高一些,可是代碼實現至關複雜。

epoll一樣只告知那些就緒的文件描述符,並且當咱們調用epoll_wait()得到就緒文件描述符時,返回的不是實際的描述符,而是一個表明就緒描述符數量的值,你只須要去epoll指定的一個數組中依次取得相應數量的文件描述符便可,這裏也使用了內存映射(mmap)技術,這樣便完全省掉了這些文件描述符在系統調用時複製的開銷。

另外一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用必定的方法後,內核纔對全部監視的文件描述符進行掃描,而epoll事先經過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用相似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便獲得通知。

 

大規模鏈接上來,併發模型怎麼設計

tcp結束鏈接怎麼握手,time_wait狀態是什麼,爲何會有time_wait狀態?哪一方會有time_wait狀態,如何避免time_wait狀態佔用資源(必須回答的詳細)

tcp頭多少字節?哪些字段?(必問)

頭20字節,選項12字節

什麼是滑動窗口(必問)

滑動窗口(Sliding window )是一種流量控制技術。滑動窗口協議是用來改善吞吐量的一種技術,即允許發送方在接收任何應答以前傳送附加的包。接收方告訴發送方在某一時刻能送多少包(稱窗口尺寸)。TCP中採用滑動窗口來進行傳輸控制,滑動窗口的大小意味着接收方還有多大的緩衝區能夠用於接收數據。發送方能夠經過滑動窗口的大小來肯定應該發送多少字節的數據。當滑動窗口爲0時,發送方通常不能再發送數據報,但有兩種狀況除外,一種狀況是能夠發送緊急數據,例如,容許用戶終止在遠端機上的運行進程。另外一種狀況是發送方能夠發送一個1字節的數據報來通知接收方從新聲明它但願接收的下一字節及發送方的滑動窗口大小。滑動窗口協議的基本原理就是在任意時刻,發送方都維持了一個連續的容許發送的幀的序號,稱爲發送窗口;同時,接收方也維持了一個連續的容許接收的幀的序號,稱爲接收窗口。發送窗口和接收窗口的序號的上下界不必定要同樣,甚至大小也能夠不一樣。不一樣的滑動窗口協議窗口大小通常不一樣。發送方窗口內的序列號表明了那些已經被髮送,可是尚未被確認的幀,或者是那些能夠被髮送的幀。

connect會阻塞,怎麼解決?(必考必問)

最一般的方法最有效的是加定時器;也能夠採用非阻塞模式。

設置非阻塞,返回以後用select檢測狀態)

若是select返回可讀,結果只讀到0字節,什麼狀況?

某個套接字集合中沒有準備好,可能會select內存用FD_CLR清該位爲0;

keepalive是什麼東東?如何使用?

設置Keepalive參數,檢測已中斷的客戶鏈接

·        Determine how long to wait beforeprobing the connection. On most platforms the default is 2 hours.

·       Determine how long to wait beforeretrying the probe.

·       Determine how many times to probe theconnection.

 

列舉你所知道的tcp選項,並說明其做用。

1.窗口擴大因子TCP Window Scale Option (WSopt)

TCP窗口縮放選項是用來增長TCP接收窗口的大小而超過65536字節。

2.SACK選擇確認選項

最大報文段長度(M S S)表示T C P傳往另外一端的最大塊數據的長度。當創建一個鏈接時,每一方都有用於通告它指望接收的 M S S選項(M S S選項只能出如今S Y N報文段中)。經過MSS,應用數據被分割成TCP認爲最適合發送的數據塊,由TCP傳遞給IP的信息單位稱爲報文段或段(segment)。

TCP通訊時,若是發送序列中間某個數據包丟失,TCP會經過重傳最後確認的包開始的後續包,這樣原先已經正確傳輸的包也可能重複發送,急劇下降了TCP性能。爲改善這種狀況,發展出SACK(SelectiveAcknowledgment, 選擇性確認)技術,使TCP只從新發送丟失的包,不用發送後續全部的包,並且提供相應機制使接收方能告訴發送方哪些數據丟失,哪些數據重發了,哪些數據已經提早收到等。

3.MSS:Maxitum Segment Size 最大分段大小

socket什麼狀況下可讀?

a.The number of bytes of data in the socket receive buffer is greater than or 
     equal to the current size of the low-water mark forthe socket receive buffer.
     A read operation on the socket will not block and willreturn a value greater than 0
b.  The read half of the connections is closed (i.e., A TCPconnection that has received a FIN).
     A read operation on the socket will not block and willreturn 0 (i.e., EOF)
c. The socket is a listening socket and the number of completed connection isnonzero.
    An accept on the listening socket will normally not block,although we will describe a   
d. A socket error is pending. A read operation on the socket will not block andwill return
    an error (-1) with errno set to the specific error condition

db:
mysql,會考sql語言,服務器數據庫大規模數據怎麼設計,db各類性能指標

最後:補充一個最最重要,最最坑爹,最最有難度的一個題目:一個每秒百萬級訪問量的互聯網服務器,每一個訪問都有數據計算和I/O操做,若是讓你設計,你怎麼設計?

1)tcp三次握手的過程,accept發生在三次握手哪一個階段?

三次握手以後
2)Tcp流, udp的數據報,之間有什麼區別,爲何TCP要叫作數據流?
流無邊界,數據報有邊界.TCP是先進先出的,而且可靠.

3)const的含義及實現機制,好比:const int i,是怎麼作到i只可讀的?
編譯器相關,優化可能讓其直接轉爲一常量代入. const用來講明所定義的變量是隻讀的。

  這些在編譯期間完成,編譯器可能使用常數直接替換掉對此變量的引用。



4) valitale的含義。

volatile吧,告訴編譯器此處必須得從地址去取,不得做相關優化。千萬注意,這裏與硬件cache可不是一回事。
5)OFFSETOF(s, m)的宏定義,s是結構類型,m是s的成員,求m在s中的偏移量。

#define OFFSETOF(s, m) ({s s1;(void*)(&s1)-(void*)(&s1->m);})/*gcc*/


6)100億個數,求最大的1萬個數,並說出算法的時間複雜度。
建一個堆,先把最開始的1萬個數放進去。之後每進一個,都把最小的趕出來。

7)設計一個洗牌的算法,並說出算法的時間複雜度。

產生2*54+rand()%2次交換,全部序列已經很接近平均分佈(只要rand()知足均分),而且比較快。不然會是複雜度比較高的算法。我統計過。
 socket在什麼狀況下可讀?
擁塞控制是把總體當作一個處理對象的,流量控制是對單個的。感知的手段應該很多,好比在TCP協議裏,TCP報文的重傳自己就能夠做爲擁塞的依據。依據這樣的原理,應該能夠設計出不少手段。
--------------
1)三次握手以後
2)流無邊界,數據報有邊界.TCP是先進先出的,而且可靠.
3)編譯器相關,優化可能讓其直接轉爲一常量代入.
4)volatile吧,告訴編譯器此處必須得從地址去取,不得做相關優化。千萬注意,這裏與硬件cache可不是一回事。
5)#define OFFSETOF(s, m) ({s s1;(void*)(&s1)-(void*)(&s1->m);})/*gcc*/
6)建一個堆,先把最開始的1萬個數放進去。之後每進一個,都把最小的趕出來。
7)產生2*54+rand()%2次交換,全部序列已經很接近平均分佈(只要rand()知足均分),而且比較快。不然會是複雜度比較高的算法。我統計過。
不知道想問什麼。
9)擁塞控制是把總體當作一個處理對象的,流量控制是對單個的。感知的手段應該很多,好比在TCP協議裏,TCP報文的重傳自己就能夠做爲擁塞的依據。依據這樣的原理,應該能夠設計出不少手段。

9)流量控制與擁塞控制的區別,節點計算機怎樣感知網絡擁塞了?

 

 

前段時間專心面過騰訊,通過了N輪的技術面,結果仍是掛了,但沒掛在技術面,比較欣慰,回來以後寫一點總結,以供有夢想進入騰訊作後臺服務器開發的同窗參考,本文章爲胡成精心總結,胡成原創,copy和轉載請通知。ps:()以內的文字由做者點評,非面試題文字。

linux和os:

netstat tcpdump ipcs ipcrm (若是這四個命令沒據說過或者不能熟練使用,基本上能夠回家,經過的機率較小^_^ ,這四個命令的熟練掌握程度基本上能體現面試者實際開發和調試程序的經驗)

cpu 內存硬盤等等與系統性能調試相關的命令必須熟練掌握,設置修改權限 tcp網絡狀態查看各進程狀態抓包相關等相關命令必須熟練掌握

awk sed需掌握

共享內存的使用實現原理(必考必問,而後共享內存段被映射進進程空間以後,存在於進程空間的什麼位置?共享內存段最大限制是多少?)

c++進程內存空間分佈(注意各部分的內存地址誰高誰低,注意棧從高道低分配,堆從低到高分配)

ELF是什麼?其大小與程序中全局變量的是否初始化有什麼關係(注意.bss段)

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

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

gdb調試相關的經驗,會被問到

如何定位內存泄露?

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

32位系統一個進程最多多少堆內存

多線程和多進程的區別(重點面試官最最關心的一個問題,必須從cpu調度,上下文切換,數據共享,多核cup利用率,資源佔用,等等各方面回答,而後有一個問題必須會被問到:哪些東西是一個線程私有的?答案中必須包含寄存器,不然悲催)

寫一個c程序辨別系統是64位 or32位

寫一個c程序辨別系統是大端or小端字節序

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

i++是否原子操做?並解釋爲何???????

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

列舉說明linux系統的各種異步機制

exit() _exit()的區別?

如何實現守護進程?

linux的內存管理機制是什麼?

linux的任務調度機制是什麼?

標準庫函數和系統調用的區別?

補充一個坑爹坑爹坑爹坑爹的問題:系統如何將一個信號通知到進程?(這一題哥沒有答出來)

c語言:

宏定義和展開(必須精通)

位操做(必須精通)

指針操做和計算(必須精通)

內存分配(必須精通)

sizeof必考

各種庫函數必須很是熟練的實現

哪些庫函數屬於高危函數,爲何?(strcpy等等)

c++:

一個String類的完整實現必須很快速寫出來(注意:賦值構造,operator=是關鍵)

虛函數的做用和實現原理(必問必考,實現原理必須很熟)

sizeof一個類求大小(注意成員變量,函數,虛函數,繼承等等對大小的影響)

指針和引用的區別(通常都會問到)

多重類構造和析構的順序

stl各容器的實現原理(必考)

extern c 是幹啥的,(必須將編譯器的函數名修飾的機制解答的很透徹)

volatile是幹啥用的,(必須將cpu的寄存器緩存機制回答的很透徹)

static const等等的用法,(能說出越多越好)

數據結構或者算法:

《離散數學》範圍內的一切問題皆由可能被深刻問到(這個最坑爹,最重要,最體現功底,最能加分,特別是各種樹結構的實現和應用)

各種排序:大根堆的實現,快排(如何避免最糟糕的狀態?),bitmap的運用等等

hash,任何一個技術面試官必問(例如爲何通常hashtable的桶數會取一個素數?如何有效避免hash結果值的碰撞)

網絡編程:

tcp與udp的區別(必問)

udp調用connect有什麼做用?

tcp鏈接中時序圖,狀態圖,必須很是很是熟練

socket服務端的實現,select和epoll的區別(必問)

epoll哪些觸發模式,有啥區別?(必須很是詳盡的解釋水平觸發和邊緣觸發的區別,以及邊緣觸發在編程中要作哪些更多的確認)

大規模鏈接上來,併發模型怎麼設計

 

tcp結束鏈接怎麼握手,time_wait狀態是什麼,爲何會有time_wait狀態?哪一方會有time_wait狀態,如何避免time_wait狀態佔用資源(必須回答的詳細)

·       TIME_WAIT:表示收到了對方的FIN報文,併發送出了ACK報文。 TIME_WAIT狀態下的TCP鏈接會等待2*MSL(Max Segment Lifetime,最大分段生存期,指一個TCP報文在Internet上的最長生存時間。每一個具體的TCP協議實現都必須選擇一個肯定的MSL值,RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒,Linux能夠cat /proc/sys/net/ipv4/tcp_fin_timeout看到本機的這個值),而後便可回到CLOSED 可用狀態了。若是FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,能夠直接進入到TIME_WAIT狀態,而無須通過FIN_WAIT_2狀態。

  • 若是使用了nginx代理,那麼系統TIME_WAIT的數量會變得比較多,這是因爲nginx代理使用了短連接的方式和後端交互的緣由,使得nginx和後端的ESTABLISHED變得不多而TIME_WAIT不少。這不但發生在安裝nginx的代理服務器上,並且也會使後端的app服務器上有大量的TIME_WAIT。查閱TIME_WAIT資料,發現這個狀態不少也沒什麼大問題,但可能由於它佔用了系統過多的端口,致使後續的請求沒法獲取端口而形成障礙。

    雖然TIME_WAIT會形成一些問題,可是要徹底槍斃掉它也是不正當的,雖然看起來這麼作沒什麼錯。具體可看這篇文檔:

    http://hi.baidu.com/tim_bi/blog/item/35b005d784ca91d5a044df1d.html

    因此目前看來最好的辦法是讓每一個TIME_WAIT早點過時。

    在linux上能夠這麼配置:

    #讓TIME_WAIT狀態能夠重用,這樣即便TIME_WAIT佔滿了全部端口,也不會拒絕新的請求形成障礙
    echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
    #讓TIME_WAIT儘快回收,我也不知是多久,觀察大概是一秒鐘
    echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle

    不少文檔都會建議兩個參數都配置上,可是我發現只用修改tcp_tw_recycle就能夠解決問題的了,TIME_WAIT重用TCP協議自己就是不建議打開的。

    不能重用端口可能會形成系統的某些服務沒法啓動,好比要重啓一個系統監控的軟件,它用了40000端口,而這個端口在軟件重啓過程當中恰好被使用了,就可能會重啓失敗的。linux默認考慮到了這個問題,有這麼個設定:

    #查看系統本地可用端口極限值
    cat /proc/sys/net/ipv4/ip_local_port_range

    用這條命令會返回兩個數字,默認是:32768 61000,說明這臺機器本地能向外鏈接61000-32768=28232個鏈接,注意是本地向外鏈接,不是這臺機器的全部鏈接,不會影響這臺機器的80端口的對外鏈接數。但這個數字會影響到代理服務器(nginx)對app服務器的最大鏈接數,由於nginx對app是用的異步傳輸,因此這個環節的鏈接速度很快,因此堆積的鏈接就不多。假如nginx對app服務器之間的帶寬出了問題或是app服務器有問題,那麼可能使鏈接堆積起來,這時能夠經過設定nginx的代理超時時間,來使鏈接儘快釋放掉,通常來講極少能用到28232個鏈接。

    由於有軟件使用了40000端口監聽,經常出錯的話,能夠經過設定ip_local_port_range的最小值來解決:

    echo "40001 61000" > /proc/sys/net/ipv4/ip_local_port_range

    可是這麼作很顯然把系統可用端口數減小了,這時能夠把ip_local_port_range的最大值往上調,可是好習慣是使用不超過32768的端口來偵聽服務,另外也沒必要要去修改ip_local_port_range數值成1024 65535之類的,意義不大。

    由於使用了nginx代理,在windows下也會形成大量TIME_WAIT,固然windows也能夠調整:

    在註冊表(regedit)的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters上添加一個DWORD類型的值TcpTimedWaitDelay,值就是秒數,便可。

    windows默認是重用TIME_WAIT,我如今還不知道怎麼改爲不重用的,本地端口也沒查到是什麼值,但這些都關係不大,均可以按系統默認運做。
  • ------------------------------------------------------------------------------------------------------------------------
  • TIME_WAIT狀態
  • 根據TCP協議,主動發起關閉的一方,會進入TIME_WAIT狀態,持續2*MSL(Max Segment Lifetime),缺省爲240秒,在這個post中簡潔的介紹了爲何須要這個狀態。
  • 值得一說的是,對於基於TCP的HTTP協議,關閉TCP鏈接的是Server端,這樣,Server端會進入TIME_WAIT狀態,可想而知,對於訪問量大的Web Server,會存在大量的TIME_WAIT狀態,假如server一秒鐘接收1000個請求,那麼就會積壓240*1000=240,000個TIME_WAIT的記錄,維護這些狀態給Server帶來負擔。固然現代操做系統都會用快速的查找算法來管理這些TIME_WAIT,因此對於新的TCP鏈接請求,判斷是否hit中一個TIME_WAIT不會太費時間,可是有這麼多狀態要維護老是很差。
  • HTTP協議1.1版規定default行爲是Keep-Alive,也就是會重用TCP鏈接傳輸多個request/response,一個主要緣由就是發現了這個問題。還有一個方法減緩TIME_WAIT壓力就是把系統的2*MSL時間減小,由於240秒的時間實在是忒長了點,對於Windows,修改註冊表,在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一個DWORD類型的值TcpTimedWaitDelay,通常認爲不要少於60,否則可能會有麻煩。
  • 對於大型的服務,一臺server搞不定,須要一個LB(Load Balancer)把流量分配到若干後端服務器上,若是這個LB是以NAT方式工做的話,可能會帶來問題。假如全部從LB到後端Server的IP包的source address都是同樣的(LB的對內地址),那麼LB到後端Server的TCP鏈接會受限制,由於頻繁的TCP鏈接創建和關閉,會在server上留下TIME_WAIT狀態,並且這些狀態對應的remote address都是LB的,LB的source port撐死也就60000多個(2^16=65536,1~1023是保留端口,還有一些其餘端口缺省也不會用),每一個LB上的端口一旦進入Server的TIME_WAIT黑名單,就有240秒不能再用來創建和Server的鏈接,這樣LB和Server最多也就能支持300個左右的鏈接。若是沒有LB,不會有這個問題,由於這樣server看到的remote address是internet上廣闊無垠的集合,對每一個address,60000多個port實在是夠用了。
  • 一開始我以爲用上LB會很大程度上限制TCP的鏈接數,可是實驗代表沒這回事,LB後面的一臺Windows Server 2003每秒處理請求數照樣達到了600個,難道TIME_WAIT狀態沒起做用?用Net Monitor和netstat觀察後發現,Server和LB的XXXX端口之間的鏈接進入TIME_WAIT狀態後,再來一個LB的XXXX端口的SYN包,Server照樣接收處理了,而是想像的那樣被drop掉了。翻書,從書堆裏面找出覆滿塵土的大學時代買的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中間提到一句,對於BSD-derived實現,只要SYN的sequence number比上一次關閉時的最大sequence number還要大,那麼TIME_WAIT狀態同樣接受這個SYN,難不成Windows也算BSD-derived?有了這點線索和關鍵字(BSD),找到這個post,在NT4.0的時候,仍是和BSD-derived不同的,不過Windows Server 2003已是NT5.2了,也許有點差異了。
  • 作個試驗,用Socket API編一個Client端,每次都Bind到本地一個端口好比2345,重複的創建TCP鏈接往一個Server發送Keep-Alive=false的HTTP請求,Windows的實現讓sequence number不斷的增加,因此雖然Server對於Client的2345端口鏈接保持TIME_WAIT狀態,可是老是可以接受新的請求,不會拒絕。那若是SYN的Sequence Number變小會怎麼樣呢?一樣用Socket API,不過此次用Raw IP,發送一個小sequence number的SYN包過去,Net Monitor裏面看到,這個SYN被Server接收後如泥牛如海,一點反應沒有,被drop掉了。
  • 按照書上的說法,BSD-derived和Windows Server 2003的作法有安全隱患,不過至少這樣至少不會出現TIME_WAIT阻止TCP請求的問題,固然,客戶端要配合,保證不一樣TCP鏈接的sequence number要上漲不要降低。
  • ----------------------------------------------------------------------------------------------------------------------------
  • Socket中的TIME_WAIT狀態
  • 在高併發短鏈接的server端,當server處理完client的請求後馬上closesocket此時會出現time_wait狀態而後若是client再併發2000個鏈接,此時部分鏈接就鏈接不上了,用linger強制關閉能夠解決此問題,可是linger會致使數據丟失,linger值爲0時是強制關閉,不管併發多少多能正常鏈接上,若是非0會發生部分鏈接不上的狀況!(可調用setsockopt設置套接字的linger延時標誌,同時將延時時間設置爲0。)
  • TCP/IP的RFC文檔。TIME_WAIT是TCP鏈接斷開時一定會出現的狀態。
    是沒法避免掉的,這是TCP協議實現的一部分。
    在WINDOWS下,能夠修改註冊表讓這個時間變短一些

  • time_wait的時間爲2msl,默認爲4min.
    你能夠經過改變這個變量:
    TcpTimedWaitDelay 
    把它縮短到30s
  • TCP要保證在全部可能的狀況下使得全部的數據都可以被投遞。當你關閉一個socket時,主動關閉一端的socket將進入TIME_WAIT狀態,而被動關閉一方則轉入CLOSED狀態,這的確可以保證全部的數據都被傳輸。當一個socket關閉的時候,是經過兩端互發信息的四次握手過程完成的,當一端調用close()時,就說明本端沒有數據再要發送了。這好似看來在握手完成之後,socket就都應該處於關閉CLOSED狀態了。但這有兩個問題,首先,咱們沒有任何機制保證最後的一個ACK可以正常傳輸,第二,網絡上仍然有可能有殘餘的數據包(wandering duplicates),咱們也必須可以正常處理。
    經過正確的狀態機,咱們知道雙方的關閉過程以下

  • 假設最後一個ACK丟失了,服務器會重發它發送的最後一個FIN,因此客戶端必須維持一個狀態信息,以便可以重發ACK;若是不維持這種狀態,客戶端在接收到FIN後將會響應一個RST,服務器端接收到RST後會認爲這是一個錯誤。若是TCP協議可以正常完成必要的操做而終止雙方的數據流傳輸,就必須徹底正確的傳輸四次握手的四個節,不能有任何的丟失。這就是爲何socket在關閉後,仍然處於 TIME_WAIT狀態,由於他要等待以便重發ACK。
  • 若是目前鏈接的通訊雙方都已經調用了close(),假定雙方都到達CLOSED狀態,而沒有TIME_WAIT狀態時,就會出現以下的狀況。如今有一個新的鏈接被創建起來,使用的IP地址與端口與先前的徹底相同,後創建的鏈接又稱做是原先鏈接的一個化身。還假定原先的鏈接中有數據報殘存於網絡之中,這樣新的鏈接收到的數據報中有多是先前鏈接的數據報。爲了防止這一點,TCP不容許從處於TIME_WAIT狀態的socket創建一個鏈接。處於TIME_WAIT狀態的socket在等待兩倍的MSL時間之後(之因此是兩倍的MSL,是因爲MSL是一個數據報在網絡中單向發出到認定丟失的時間,一個數據報有可能在發送圖中或是其響應過程當中成爲殘餘數據報,確認一個數據報及其響應的丟棄的須要兩倍的MSL),將會轉變爲CLOSED狀態。這就意味着,一個成功創建的鏈接,必然使得先前網絡中殘餘的數據報都丟失了。
  • 因爲TIME_WAIT狀態所帶來的相關問題,咱們能夠經過設置SO_LINGER標誌來避免socket進入TIME_WAIT狀態,這能夠經過發送RST而取代正常的TCP四次握手的終止方式。但這並非一個很好的主意,TIME_WAIT對於咱們來講每每是有利的。
  • 客戶端與服務器端創建TCP/IP鏈接後關閉SOCKET後,服務器端鏈接的端口
    狀態爲TIME_WAIT
  • 是否是全部執行主動關閉的socket都會進入TIME_WAIT狀態呢?
    有沒有什麼狀況使主動關閉的socket直接進入CLOSED狀態呢?
  • 主動關閉的一方在發送最後一個 ack 後
    就會進入 TIME_WAIT 狀態 停留2MSL(max segment lifetime)時間
    這個是TCP/IP必不可少的,也就是「解決」不了的。

    也就是TCP/IP設計者原本是這麼設計的
    主要有兩個緣由
    1。防止上一次鏈接中的包,迷路後從新出現,影響新鏈接
       (通過2MSL,上一次鏈接中全部的重複包都會消失)
    2。可靠的關閉TCP鏈接
       在主動關閉方發送的最後一個 ack(fin) ,有可能丟失,這時被動方會從新發
       fin, 若是這時主動方處於 CLOSED 狀態 ,就會響應 rst 而不是 ack。因此
       主動方要處於 TIME_WAIT 狀態,而不能是 CLOSED 。

    TIME_WAIT 並不會佔用很大資源的,除非受到攻擊。

    還有,若是一方 send 或 recv 超時,就會直接進入 CLOSED 狀態
  • socket-faq中的這一段講的也很好,摘錄以下:
    2.7. Please explain the TIME_WAIT state.

 

tcp頭多少字節?哪些字段?(必問)

什麼是滑動窗口(必問)

connect會阻塞,怎麼解決?(必考必問,提示:設置非阻塞,返回以後用select檢測狀態)

若是select返回可讀,結果只讀到0字節,什麼狀況?

keepalive 是什麼東東?如何使用?

 

keepalive

在TCP中有一個Keep-alive的機制能夠檢測死鏈接,原理很簡單,TCP會在空閒了必定時間後發送數據給對方:

1.若是主機可達,對方就會響應ACK應答,就認爲是存活的。

2.若是可達,但應用程序退出,對方就發RST應答,發送TCP撤消鏈接。

3.若是可達,但應用程序崩潰,對方就發FIN消息。

4.若是對方主機不響應ack, rst,繼續發送直到超時,就撤消鏈接。這個時間就是默認

的二個小時。

 

列舉你所知道的tcp選項,並說明其做用。

 

socket什麼狀況下可讀?

 

每次讀操做返回前都要檢查是否還有剩餘數據沒讀完,若是是的話保持數據有效標誌,不這樣設計的話會出現明顯的不一致,那就是數據在讀緩衝但沒有讀有效標誌。

固然也能夠設計成讓程序必須一次性取走全部數據,但這樣設計的接口不友好,win32中也確實存在這種類型的API

db:
mysql,會考sql語言,服務器數據庫大規模數據怎麼設計,db各類性能指標

最後:補充一個最最重要,最最坑爹,最最有難度的一個題目:一個每秒百萬級訪問量的互聯網服務器,每一個訪問都有數據計算和I/O操做,若是讓你設計,你怎麼設計?

 

 

shell下輸入「man 2read 」 你先看看。
ssize_t read(int fd, void *buf, size_t count);
意義:從文件描述符fd所指向的文件中讀取count個字節的數據到buf所指向的緩存中。
文件描述符是由無符號整數表示的句柄,進程使用它來標識打開的文件。
文件描述符0表明標準文件。
fd是這麼來的。
fd=(open或creat成功調用時的返回值)

 

 

於一個完整的程序,在內存中分佈狀況以下圖:

 

原文連接:http://blog.csdn.net/ibmfahsion/article/details/11992403?utm_source=tuicool

相關文章
相關標籤/搜索