linux內核中聽過就能記住的概念

  打算給咱們部門弄個內部分享。發現你們對一些底層知識的認知停留在一句一句的,好比據說JVM使用-XX:-UseBiasedLocking取消偏向鎖能夠提升性能,由於它只適用於非多線程高併發應用。使用數字對象的緩存-XX:AutoBoxCacheMax=20000比默認緩存-128~127要提升性能。對於JVM和linux內核,操做系統沒有系統的概念,遇到實際問題每每沒有思路。因此個人內部分享,主要分爲linux部分,jvm部分和redis部分。這篇是linux篇。學習思路爲主,知識爲輔。我也是菜鳥一枚~~不過是個鑽石心的菜鳥,不怕別人知道我有多菜。java

  先說爲何我要去學習linux內核。我在上家公司負責整個公司的搜索引擎。有一次很熟練的在一臺虛擬機上新搭建了一套,壓測到8000,額,報了一個NIO異常,說是:too many open files。當時查了一下,那臺機器太破,和不少服務公用,內存快滿了。因此換了臺好點的機器就沒有這個問題了。可是句柄超限究竟是個什麼東西呢?先來看看linux內核的一些基本概念。linux

  大局觀嘛,先來看看unix的體系結構。redis

   簡單解釋一下:任何計算機系統都包含一個基本的程序集合,它控制計算機硬件資源,提供程序運行環境。稱爲操做系統。在這個集合裏,最重要的程序被稱爲內核,在系統啓動時被裝載。由於它相對較小,並且位於環境的核心。內核的接口被稱爲系統調用(system call)。公用函數庫構建在系統調用接口之上,也可以使用系統調用。shell是一個特殊的應用程序,爲運行其餘應用程序提供一個接口。shell

  一些操做系統容許全部的用戶程序直接與硬件部分進行交互,如MS-DOS。可是類Unix操做系統在胡勇應用程序前把與計算機物理組織相關的全部底層細節隱藏了。當程序想使用硬件資源時,必須向操做系統發出一個請求,內核對這個請求進行評估,若是容許使用這個資源,內核表明應用程序與相關的硬件部分進行交互。爲了實施這種機制,現代操做系統依靠特殊的硬件特性來禁止用戶程序直接與底層硬件部分打交道,或者直接訪問任意的物理地址。硬件爲CPU引入了至少兩種不一樣的執行模式:用戶程序的非特權模式和內核的特權模式。Unix把他們分別稱爲用戶態(User Mode)和內核態(Kernel Model)。編程

  咱們平時敲的一些linux命令,實際上都是對應的內核的C語言函數。好比cat xxx | grep 'x'。這裏面兩個命令用|鏈接起來,這個叫作「管道」。先用男孩紙慣用的職業一點的語言介紹一下:管道是一個普遍應用的進程間通訊手段。其做用是在具備親緣關係的進程之間傳遞消息,所謂有親緣關係,是指有同一個祖先。能夠是父子,兄弟或者祖孫等等。反正只要共同的祖先調用了pipe函數,打開的管道文件會在fork以後,被各個後代所共享。其本質是內核維護了一塊緩衝區與管道文件相關聯,對管道文件的操做,被內核轉換成對這塊緩衝區內存的操做。分爲匿名管道和命名管道。緩存

  這裏麪包含了一些概念。進程的概念你們都應該很清楚:程序的執行實例被稱爲進程。UNIX系統確保每一個進程都有一個惟一的數字表示符,稱爲進程ID(process ID),它是一個非負數。linux不少命令都會將其顯示出來。有3個用於進程控制的主要函數:fork,exec和waitpid。其中fork函數用來建立一個新進程,此進程是調用進程的一個副本,稱爲子進程。fork對父進程返回新的子進程的進程ID(一個非負整數),對子進程則返回0。由於fork建立一個新進程,因此說它被調用一次,但返回兩次。服務器

  一個進程內的全部線程共享同一地址空間,文件描述符,棧以及進程相關的屬性。由於它們能訪問同一存儲區,因此各線程在訪問共享數據時須要採起同步措施以免不一致性。說到這裏你們都應該多少有些概念了:爲何進程開銷大,線程涉及鎖。網絡

  匿名管道是一個未命名的,單向管道,經過父進程和一個子進程之間傳輸數據。只能實現本地機器上兩個進程之間的通訊,而不能實現跨網絡的通訊。經常使用的好比linux命令。數據結構

  命名管道是進程間單向或雙向管道,創建時指定一個名字,任何進程均可以經過該名字打開管道的另外一端,可跨網絡通訊。多線程

這是一個jvisualvm調試的截圖,藍框部分就至關於一個命名管道。

 

   好,如今來回答一個問題:用戶進程間通訊主要哪幾種方式?

  剛纔說的匿名管道和命名管道都算一種。除此以外,還有:信號,消息隊列,共享內存,信號量和套接字。不用頭疼,看到最後你極可能會有豁然開朗的感受,學的東西終於能夠串在一塊兒了。

  信號(signal):實際上是軟中斷信號的簡稱。用來通知進程發生了異步事件。在軟件層次上是對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一箇中斷請求是同樣的。信號是進程間通訊機制中惟一的異步通訊機制,一個進程沒必要經過任何操做來等待信號的到達。

  收到信號的進程對各類信號有不一樣的處理方法,主要是三類:

  1>相似中斷的處理程序,對於須要處理的信號,進程能夠指定處理函數,由該函數來處理。

  2>忽略某個信號,對該信號不作任何處理。

  3>對該信號的處理保留系統的默認值,這種缺省操做,對大部分的信號的缺省操做是讓進程終止。進程經過系統調用signal來指定進程對某個信號的處理行爲。

  下面是window的信號列表

linux也是用kill -l命令:

1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     17) SIGCHLD
18) SIGCONT     19) SIGSTOP     20) SIGTSTP     21) SIGTTIN
22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO
30) SIGPWR      31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1
36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4  39) SIGRTMIN+5
40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8  43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6  59) SIGRTMAX-5
60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2  63) SIGRTMAX-1
64) SIGRTMAX

  我在用gdb命令運行調試C語言程序的時候常常能夠看到這些信號量。

  再來看消息隊列。消息隊列提供了一種從一個進程向另外一個進程發送一個數據塊的方法。每一個數據塊都被認爲含有一個類型,接收進程能夠獨立的接收含有不一樣類型的數據結構。能夠經過發送消息來避免命名管道的同步和阻塞問題。可是消息隊列和命名管道同樣,每一個數據塊都有一個最大長度的限制。

  共享內存就是容許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種很是有效的方式。不一樣進程之間共享的內存一般安排爲同一段物理內存。進程能夠將同一段共享內存鏈接到他們本身的地址空間中,全部進程均可以訪問共享內存中的地址。

  信號量:爲了防止出現因多個程序同時訪問一個共享資源而引起的一系列問題,咱們須要一種方法,它能夠經過生成並使用令牌來受權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼須要獨佔式的執行。而信號量就能夠提供這樣的一種訪問機制。讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來協調對共享資源訪問的。

  套接字:這種通訊機制使得客戶端/服務器的開發工做既能夠在本地單機上進行,也能夠跨網絡進行。它的特性有三個屬性肯定:域(domain),類型(type)和協議(protocol)。簡單的說:源IP地址和目的IP地址以及源端口號和目的端口號的組合成爲套接字。

  下面介紹一下通訊過程,裏面涉及一些C語言的函數,不用怕,眼熟便可。若是你學習過nio,你會發現這些是很常接觸的。

  要想使不一樣主機的進程通訊,就必須使用套接字,套接字是用socket()函數建立,若是須要C/S模式,則須要把server的套接字與地址和端口綁定起來,使用bind(),當上述操做完成後,即可使用listen()來監聽這個端口,若是有其餘程序來connect,那麼server將會調用accept()來接受這個申請併爲其服務。client是調用connect()來創建與server之間的鏈接,這時會使用三次握手來創建一條數據連接。當鏈接被創建後,server與client即可以通訊了,通訊可使用read()/write(),send()/recv(),sendto()/recvfrom()等函數來實現,可是不一樣的函數做用和使用位置是不一樣的。當數據傳送完後,能夠調用close()來關閉server與client之間的連接。

  

  到此,本篇文章的主要內容就沒有了,基本就在介紹一個東西:linux內核的進程通訊。這是學習任何高級編程語言nio部分的基礎。下面引入一些輔助理解的概念。

  文件句柄:在文件I/O中,要從一個文件讀取數據,應用程序首先要調用操做系統函數並傳送文件名,並選一個到該文件的路徑來打開文件。該函數取回一個順序號,即文件句柄(file handle),該文件句柄對於打開的文件是惟一的識別依據。一個句柄就是你給一個文件,設備,套接字(socket)或者管道的一個名字,以便幫助你記住你證處理的名字,並隱藏某些緩存等的複雜性。說白了就是文件指針啦。

  文件描述符:內核利用文件描述符來訪問文件。打開現存文件或新建文件時,內核會返回一個文件描述符。讀寫文件也須要使用文件描述符來指定待讀寫的文件。文件描述符形式上是非負整數,實際上它是一個索引值,指向內核爲每個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者建立一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫每每會圍繞着文件描述符展開。可是文件描述符每每值適用於unix,linux這樣的操做系統。習慣上,標準輸入的文件描述符是0,標準輸出是1,標準錯誤是2.

`/letv/apps/jdk/bin/java -DappPort=4 $JAVA_OPTS -cp $PHOME/conf:$PHOME/lib/* com.letv.mms.transmission.http.VideoFullServerBootstrap $1 $3 > /dev/null 2>&1 &`

本身部署過java後臺程序的話,對上面的shell命令應該都能理解。 /dev/null 2>&1 這裏面的2就是文件描述符,這個是將錯誤輸出到文件。

  這兩個概念比較繞,不用過多區分,能夠當成一回事來理解。打開文件(open files)包括文件句柄但不只限於文件句柄,因爲lnux全部的事務都以文件的形式存在,要使用諸如共享內存,信號量,消息隊列,內存映射等都會打開文件,但這些不會佔用文件句柄。查看進程容許打開的最大文件句柄數的linux命令:ulimit -n 

 

  好了,今天的概念都介紹完了,回到最初的問題:too many open files。 當時的機器破,內存快滿了。因此搜索引擎走的是索引文件,有不少的IO操做,共享內存和內存映射這塊的文件確定是供不上的,報錯了。縈繞在心頭兩年的問題稍微有點認知了。

 

跑題時間:

  每當我打噴嚏的時候,我就在想究竟是誰在想我了。雖然明知道打噴嚏的緣由是剛進了一間有浮塵的屋子,或者是空中飄着的柳絮。ねえ、わたしのこと、おぼえてる?

相關文章
相關標籤/搜索