後臺開發知識點總結(一、Linux和OS)

  偶然在知乎上看到想要從事linux後臺開發需要的能力集錦,總結的挺全面的,鑑於自己貧弱的記憶力,還是在這裏總結一下供以後查看,順便檢驗一下自己。
  

1、 命令:netstat tcpdump ipcs ipcrm 這四個命令的熟練掌握程度基本上能體現實際開發和調試程序的經驗

  在《TCP/IP》協議一書中,經常使用到netstat和tcpdump這兩個命令,netstat常用於顯示各種網絡信息與自己的主機信息,例如路由表信息。tcpdump用於網絡數據包的截獲分析,例如三次握手,數據交換等等的顯示。這裏推薦一個更好用的工具wireshark,有比較好的交互界面,可以設置過濾信息等等,做爬蟲,分析網絡問題的利器。
  下面給出幾個簡單的例子,具體的使用可以參照linux的man命令或者鳥哥的私房菜。
  這裏寫圖片描述
- Proto :網絡的封包協議,主要分爲 TCP 與 UDP 封包;
- Recv-Q:非由用戶程序鏈接到此socket 的複製的總 bytes 數;
- Send-Q:非由進程主機傳送過來的 acknowledged 總 bytes 數;
- Local Address :本地端的IP:port 情況
- Foreign Address:進程主機的 IP:port 情況
- State:聯機狀態,主要有建立(ESTABLISED)及監聽(LISTEN);

  這裏寫圖片描述
  這裏顯示的信息從左至右分別是時間;源地址到目的地址;報文的flags,S是SYN,F是FIN,.是無標記;報文的序列號;下次期望的序列號;接收緩存的窗口大小。這些在《TCP/IP》卷一有詳細的論述。

  ipcs和ipcrm命令是用於顯示ipc信息和移除ipc消息對象的命令。這裏首先要對ipc有個大致概念。IPC是(interprocess communication)的簡稱,是運行在操作系統上的不同進程間通訊的方式。
  使用命令ipcs -a可以得到下面的信息,可以看到這裏顯示的方式有三種,下面介紹進程間通訊時再詳細講。

  • 共享內存
  • 信號量
  • 消息隊列
lijun0914:~/workspace $ ipcs -a

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages   

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

  這一條後面提到的tcp網絡狀態查看,抓包其實在上一條的命令中已經涵蓋了。設置修改權限的chmod感覺沒什麼可將的,就是修改文件的訪問權限,記住讀寫可執行的數值爲4,2,1,u,g,o爲用戶,用戶當前組,其他用戶,使用命令設置相應用戶的權限就可以了。看一下簡單的例子就明白了。

 chmod u+x file                   給file的屬主增加執行權限
 chmod 751 file                   給file的屬主分配讀、寫、執行(7)的權限,給file的所在組分配讀、執行(5)的權限,給其他用戶分配執行(1)的權限
 chmod u=rwx,g=rx,o=x file         上例的另一種形式
 chmod =r file                     爲所有用戶分配讀權限
 chmod 444 file                  同上例
 chmod a-wx,a+r   file           同上例
 chmod -R u+r directory            遞歸地給directory目錄下所有文件和子目錄的屬主分配讀的權限
 chmod 4755                        設置用ID,給屬主分配讀、寫和執行權限,給組和其他用戶分配讀、執行的權限。
  

觀察運行中的進程狀態可以使用靜態的ps和動態的top以及top的增強版htop。這些命令可以統計各個進程的CPU和內存MEM使用率。當然還有專門針對cpu,內存,io做監控的各個命令,mpstat,vmstat,iostat。
這裏寫圖片描述

htop

這裏會有一些進程優先級的概念,PRI越低越先被CPU執行,PRI(new)=PRI(old)+nice,人越不nice越愛搶嘛,很好記。
其中nice和renice可以設置優先級,區別是nice是在進程啓動前調整,renice可以在進程運行中動態調整。
這裏寫圖片描述

3、awk sed需掌握

  有時候你也許會想要提取多行的某列信息,或者想要對文本按某一規則進行處理,這時候你可能會選擇python或者shell編寫腳本,不過awk和sed可能會是更好的選擇,因爲需求經常一行就可以搞定。AWK是文本處理語言,常用來進行查詢,支持正則。sed是用程序進行文本編輯的流編輯器,只支持簡單的條件處理,使用label。sed同樣使用正則匹配,可以對文本進行修改,如果你對vim熟悉的話,sed上手會非常快,因爲他們有許多相似的命令,例如s/a/b/g 這樣的文本替換。
  關於工具使用沒有什麼特別的,注意awk是語言,可以使用if else等邏輯判斷,也可以使用system運行shell指令。
  這裏直接給三個鏈接。
  sed:http://coolshell.cn/articles/9070.html
  awk:http://www.delightpress.com.tw/bookRead/skns00004_read.pdf
http://wanggen.myweb.hinet.net/ach3/ach3.html?MywebPageId=2016161473995373791#sed_and_awk

4、共享內存的使用實現原理、然後共享內存段被映射進進程空間之後,存在於進程空間的什麼位置?共享內存段最大限制是多少?

  共享內存區是可用IPC形式裏面最快的。共享內存允許多個進程同時訪問同一內存區,進程會將內存區映射到自己的地址空間中。這樣進程間數據的傳遞不再涉及內核,減少了數據複製的動作。例如一個客戶從服務器讀的操作,使用管道消息隊列等形式的話,需要內核將數據複製到進程空間的服務器上,然後服務器寫到內核空間的IPC上。這樣一次讀取或者寫入需要將數據複製兩次。
  這裏寫圖片描述
  使用共享內存
  

  • 進程必須首先分配它
  • 隨後需要訪問這個共享內存塊的每一個進程都必須將這個共享內存綁定到自己的地址空間中
  • 當完成通信之後,所有進程都將脫離共享內存,並且由一個進程釋放該共享內存塊

/proc/sys/kernel/目錄下,記錄着共享內存的一些限制,如一個共享內存區的最大字節數shmmax,系統範圍內最大共享內存區標識符數shmmni等,可以手工對其調整,但不推薦這樣做。

這裏寫圖片描述
共享內存的使用,主要有以下幾個API:ftok()、shmget()、shmat()、shmdt()及shmctl()。

#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);

shmget():創建一個新的共享內存區,或者訪問一個已經存在的內存區。
shmat():創建或者打開後,通過shmat把它連接到調用進程的地址空間。
shmdt():斷開連接的內存區。當一個進程終止時,它所有鏈接的共享內存區都會自動斷掉,注意這個函數並不刪除共享內存區。
shmctl():提供對共享內存區的多種操作,例如刪除。

這裏需要提及一下mmap
  mmap系統調用並不是完全爲了用於共享內存而設計的。它本身提供了不同於一般對普通文件的訪問方式,進程可以像讀寫內存一樣對普通文件的操作。而Posix或系統V的共享內存IPC則純粹用於共享目的,當然mmap()實現共享內存也是其主要應用之一。
  mmap系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間後,進程可以像訪問普通內存一樣對文件進行訪問,不必再 調用read(),write()等操作。mmap並不分配空間, 只是將文件映射到調用進程的地址空間裏, 然後你就可以用memcpy等操作寫文件, 而不用write()了.寫完後用msync()同步一下, 你所寫的內容就保存到文件裏了. 不過這種方式沒辦法增加文件的長度, 因爲要映射的長度在調用mmap()的時候就決定了.

簡單說就是把一個文件的內容在內存裏面做一個映像,內存比磁盤快些。

PS:不是所有文件都可以內存映射。例如訪問終端或套接字的描述符,必須使用read和write或者其變體來訪問。

這裏不介紹shm_open這些POSIX操作了,更詳細的信息可以參考《unix網絡編程》(卷二)第12-14章

順便提一下區別:
Both methods are viable. mmap method is a little bit more restrictive then shmget, but easier to use. shmget is the old System V shared memory model and has the widest support. mmap/shm_open is the new POSIX way to do shared memory and is easier to use. If your OS permits the use of POSIX shared memory then I would suggest going with that.

Some hints:

  • If you create your children via fork then mmap with MAP_ANONYMOUS |
    MAP_SHARED is by far the easiest way - just one call.
  • If you start the processes independently, but can supply them with a
    shared memory name then shm_open (+ ftruncate) + mmap with MAP_SHARED
    is two/three calls. Requires librt on some OSes.
  • If your OS has /dev/shm/ then shm_open is equivalent to opening a
    file in /dev/shm/.

注意共享內存本身不提供同步技術,需要自己使用互斥或者信號量來保證同步。

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

這裏寫圖片描述
在《深入理解計算機系統》(第九章、虛擬存儲器)中對動態存儲器分配有比較詳細的講解。

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

ELF(Executable and Linking Format)是一種對象文件的格式,用於定義不同類型的對象文件(Object files)中都放了什麼東西、以及都以什麼樣的格式去放這些東西。它自最早在 System V 系統上出現後,被 xNIX 世界所廣泛接受,作爲缺省的二進制文件格式來使用。可以說,ELF是構成衆多xNIX系統的基礎之一。

ELF文件有三種類型:

  1. 可重定位的對象文件(Relocatable file) 由彙編器彙編生成的 .o 文件
  2. 可執行的對象文件(Executable file) 可執行應用程序
  3. 可被共享的對象文件(Shared object file) 動態庫文件,也即 .so 文件

在Unix下使用readelf命令來顯示可執行程序的信息,功能與objdump相似,但是顯示的更加具體。
下面是我做CSAPP的bomb實驗的可執行文件信息。

lijun0914:~/workspace/bomb $ readelf -all bomb
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400c90
  Start of program headers:          64 (bytes into file)
  Start of section headers:          18616 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 33

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       0000000000000030  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002c8  000002c8
       0000000000000300  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000004005c8  000005c8
       000000000000016d  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400736  00000736
       0000000000000040  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400778  00000778
       0000000000000060  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004007d8  000007d8
       0000000000000060  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400838  00000838
       0000000000000288  0000000000000018   A       5    12     8
  [11] .init             PROGBITS         0000000000400ac0  00000ac0
       000000000000000e  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400ad0  00000ad0
       00000000000001c0  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400c90  00000c90
       0000000000001614  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004022a4  000022a4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         00000000004022b0  000022b0
       00000000000004e5  0000000000000000   A       0     0     16
  [16] .eh_frame_hdr     PROGBITS         0000000000402798  00002798
       0000000000000104  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         00000000004028a0  000028a0
       0000000000000454  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000602df8  00002df8
       0000000000000008  0000000000000000  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000602e00  00002e00
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000602e08  00002e08
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000602e10  00002e10
       00000000000001d0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000602fe0  00002fe0
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000602fe8  00002fe8
       00000000000000f0  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         00000000006030e0  000030e0
       0000000000000660  0000000000000000  WA       0     0     32
  [25] .bss              NOBITS           0000000000603740  00003740
       00000000000006d0  0000000000000000  WA       0     0     32
  [26] .comment          PROGBITS         0000000000000000  00003740
       0000000000000053  0000000000000001  MS       0     0     1
  [27] .debug_aranges    PROGBITS         0000000000000000  00003793
       0000000000000030  0000000000000000           0     0     1
  [28] .debug_info       PROGBITS         0000000000000000  000037c3
       00000000000007a3  0000000000000000           0     0     1
  [29] .debug_abbrev     PROGBITS         0000000000000000  00003f66
       000000000000021f  0000000000000000           0     0     1
  [30] .debug_line       PROGBITS         0000000000000000  00004185
       0000000000000161  0000000000000000           0     0     1
  [31] .debug_str        PROGBITS         0000000000000000  000042e6
       00000000000002f3  0000000000000001  MS       0     0     1
  [32] .debug_loc        PROGBITS         0000000000000000  000045d9
       0000000000000188  0000000000000000           0     0     1
  [33] .shstrtab         STRTAB           0000000000000000  00004761
       0000000000000153  0000000000000000           0     0     1
  [34] .symtab           SYMTAB           0000000000000000  000051b8
       0000000000000eb8  0000000000000018          35    57     8
  [35] .strtab           STRTAB           0000000000000000  00006070
       00000000000006b6  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000002cf4 0x0000000000002cf4  R E    200000
  LOAD           0x0000000000002df8 0x0000000000602df8 0x0000000000602df8
                 0x0000000000000948 0x0000000000001018  RW     200000
  DYNAMIC        0x0000000000002e10 0x0000000000602e10 0x0000000000602e10
                 0x00000000000001d0 0x00000000000001d0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x0000000000002798 0x0000000000402798 0x0000000000402798
                 0x0000000000000104 0x0000000000000104  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
  GNU_RELRO      0x0000000000002df8 0x0000000000602df8 0x0000000000602df8
                 0x0000000000000208 0x0000000000000208  R      1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .jcr .dynamic .got 

Dynamic section at offset 0x2e10 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x400ac0
 0x000000000000000d (FINI)               0x4022a4
 0x0000000000000019 (INIT_ARRAY)         0x602df8
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x602e00
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x400298
 0x0000000000000005 (STRTAB)             0x4005c8
 0x0000000000000006 (SYMTAB)             0x4002c8
 0x000000000000000a (STRSZ)              365 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x602fe8
 0x0000000000000002 (PLTRELSZ)           648 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x400838
 0x0000000000000007 (RELA)               0x4007d8
 0x0000000000000008 (RELASZ)             96 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x400778
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x400736
 0x0000000000000000 (NULL)               0x0

Relocation section '.rela.dyn' at offset 0x7d8 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000602fe0  001000000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000603740  001d00000005 R_X86_64_COPY     0000000000603740 stdout + 0
000000603748  001e00000005 R_X86_64_COPY     0000000000603748 stdin + 0
000000603750  001f00000005 R_X86_64_COPY     0000000000603750 stderr + 0

Relocation section '.rela.plt' at offset 0x838 contains 27 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000603000  000100000007 R_X86_64_JUMP_SLO 0000000000000000 getenv + 0
000000603008  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __errno_location + 0
000000603010  000300000007 R_X86_64_JUMP_SLO 0000000000000000 strcpy + 0
000000603018  000400000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0
000000603020  000500000007 R_X86_64_JUMP_SLO 0000000000000000 write + 0
000000603028  000600000007 R_X86_64_JUMP_SLO 0000000000000000 __stack_chk_fail + 0
000000603030  000700000007 R_X86_64_JUMP_SLO 0000000000000000 alarm + 0
000000603038  000800000007 R_X86_64_JUMP_SLO 0000000000000000 close + 0
000000603040  000900000007 R_X86_64_JUMP_SLO 0000000000000000 read + 0
000000603048  000a00000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
000000603050  000b00000007 R_X86_64_JUMP_SLO 0000000000000000 fgets + 0
000000603058  000c00000007 R_X86_64_JUMP_SLO 0000000000000000 signal + 0
000000603060  000d00000007 R_X86_64_JUMP_SLO 0000000000000000 gethostbyname + 0
000000603068  000e00000007 R_X86_64_JUMP_SLO 0000000000000000 __memmove_chk + 0
000000603070  000f00000007 R_X86_64_JUMP_SLO 0000000000000000 __memcpy_chk + 0
000000603078  001100000007 R_X86_64_JUMP_SLO 0000000000000000 strtol + 0
000000603080  001200000007 R_X86_64_JUMP_SLO 0000000000000000 fflush + 0
000000603088  001300000007 R_X86_64_JUMP_SLO 0000000000000000 __isoc99_sscanf + 0
000000603090  001400000007 R_X86_64_JUMP_SLO 0000000000000000 __printf_chk + 0
000000603098  001500000007 R_X86_64_JUMP_SLO 0000000000000000 fopen + 0
0000006030a0  001600000007 R_X86_64_JUMP_SLO 0000000000000000 exit + 0
0000006030a8  001700000007 R_X86_64_JUMP_SLO 0000000000000000 connect + 0
0000006030b0  001800000007 R_X86_64_JUMP_SLO 0000000000000000 __fprintf_chk + 0
0000006030b8  001900000007 R_X86_64_JUMP_SLO 0000000000000000 sleep + 0
0000006030c0  001a00000007 R_X86_64_JUMP_SLO 0000000000000000 __ctype_b_loc + 0
0000006030c8  001b00000007 R_X86_64_JUMP_SLO 0000000000000000 __sprintf_chk + 0
0000006030d0  001c00000007 R_X86_64_JUMP_SLO 0000000000000000 socket + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table '.dynsym' contains 32 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcpy@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (3)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND alarm@GLIBC_2.2.5 (2)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close@GLIBC_2.2.5 (2)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.2.5 (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fgets@GLIBC_2.2.5 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND signal@GLIBC_2.2.5 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gethostbyname@GLIBC_2.2.5 (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memmove_chk@GLIBC_2.3.4 (4)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memcpy_chk@GLIBC_2.3.4 (4)
    16: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtol@GLIBC_2.2.5 (2)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fflush@GLIBC_2.2.5 (2)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __isoc99_sscanf@GLIBC_2.7 (5)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __printf_chk@GLIBC_2.3.4 (4)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fopen@GLIBC_2.2.5 (2)
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND connect@GLIBC_2.2.5 (2)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fprintf_chk@GLIBC_2.3.4 (4)
    25: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.2.5 (2)
    26: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_b_loc@GLIBC_2.3 (6)
    27: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __sprintf_chk@GLIBC_2.3.4 (4)
    28: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND socket@GLIBC_2.2.5 (2)
    29: 0000000000603740     8 OBJECT  GLOBAL DEFAULT   25 stdout@GLIBC_2.2.5 (2)
    30: 0000000000603748     8 OBJECT  GLOBAL DEFAULT   25 stdin@GLIBC_2.2.5 (2)
    31: 0000000000603750     8 OBJECT  GLOBAL DEFAULT   25 stderr@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 157 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400238     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000400254     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000400274     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000400298     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000004002c8     0 SECTION LOCAL  DEFAULT    5 
     6: 00000000004005c8     0 SECTION LOCAL  DEFAULT    6 
     7: 0000000000400736     0 SECTION LOCAL  DEFAULT    7 
     8: 0000000000400778     0 SECTION LOCAL  DEFAULT    8 
     9: 00000000004007d8     0 SECTION LOCAL  DEFAULT    9 
    10: 0000000000400838     0 SECTION LOCAL  DEFAULT   10 
    11: 0000000000400ac0     0 SECTION LOCAL  DEFAULT   11 
    12: 0000000000400ad0     0 SECTION LOCAL  DEFAULT   12 
    13: 0000000000400c90     0 SECTION LOCAL  DEFAULT   13 
    14: 00000000004022a4     0 SECTION LOCAL  DEFAULT   14 
    15: 00000000004022b0     0 SECTION LOCAL  DEFAULT   15 
    16: 0000000000402798     0 SECTION LOCAL  DEFAULT   16 
    17: 00000000004028a0     0 SECTION LOCAL  DEFAULT   17 
    18: 0000000000602df8     0 SECTION LOCAL  DEFAULT   18 
    19: 0000000000602e00     0 SECTION LOCAL  DEFAULT   19 
    20: 0000000000602e08     0 SECTION LOCAL  DEFAULT   20 
    21: 0000000000602e10     0 SECTION LOCAL  DEFAULT   21 
    22: 0000000000602fe0     0 SECTION LOCAL  DEFAULT   22 
    23: 0000000000602fe8     0 SECTION LOCAL  DEFAULT   23 
    24: 00000000006030e0     0 SECTION LOCAL  DEFAULT   24 
    25: 0000000000603740     0 SECTION LOCAL  DEFAULT   25 
    26: 0000000000000000     0 SECTION LOCAL  DEFAULT   26 
    27: 0000000000000000     0 SECTION LOCAL  DEFAULT   27 
    28: 0000000000000000     0 SECTION LOCAL  DEFAULT   28 
    29: 0000000000000000     0 SECTION LOCAL  DEFAULT   29 
    30: 0000000000000000     0 SECTION LOCAL  DEFAULT   30 
    31: 0000000000000000     0 SECTION LOCAL  DEFAULT   31 
    32: 0000000000000000     0 SECTION LOCAL  DEFAULT   32 
    33: 0000000000400cbc     0 FUNC    LOCAL  DEFAULT   13 call_gmon_start
    34: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    35: 0000000000602e08     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    36: 0000000000400ce0     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    37: 0000000000400d10     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    38: 0000000000400d50     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    39: 0000000000603758     1 OBJECT  LOCAL  DEFAULT   25 completed.6976
    40: 0000000000602e00     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    41: 0000000000400d70     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    42: 0000000000602df8     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    43: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS bomb.c
    44: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS phases.c
    45: 00000000004024b0    16 OBJECT  LOCAL  DEFAULT   15 array.3449
    46: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS support.c
    47: 00000000004012a0    86 FUNC    LOCAL  DEFAULT   13 sig_handler
    48: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS driverlib.c
    49: 000000000040168e   286 FUNC    LOCAL  DEFAULT   13 rio_readlineb
    50: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    51: 0000000000402cf0     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    52: 0000000000602e08     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    53: 0000000000602e00     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    54: 0000000000602e10     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    55: 0000000000602df8     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    56: 0000000000602fe8     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
    57: 00000000004022a0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    58: 00000000004013f9    65 FUNC    GLOBAL DEFAULT   13 skip
    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@@GLIBC_2.2.5
    60: 00000000004015c4   149 FUNC    GLOBAL DEFAULT   13 phase_defused
    61: 0000000000603190    24 OBJECT  GLOBAL DEFAULT   24 n31
    62: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@@GLIBC_2
    63: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    64: 0000000000603740     8 OBJECT  GLOBAL DEFAULT   25 stdout@@GLIBC_2.2.5
    65: 00000000006030e0     0 NOTYPE  WEAK   DEFAULT   24 data_start
    66: 0000000000603780  1600 OBJECT  GLOBAL DEFAULT   25 input_strings
    67: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strcpy@@GLIBC_2.2.5
    68: 0000000000603170    24 OBJECT  GLOBAL DEFAULT   24 n33
    69: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@@GLIBC_2.2.5
    70: 0000000000603748     8 OBJECT  GLOBAL DEFAULT   25 stdin@@GLIBC_2.2.5
    71: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND write@@GLIBC_2.2.5
    72: 0000000000603740     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    73: 0000000000603230    24 OBJECT  GLOBAL DEFAULT   24 n44
    74: 0000000000603290    24 OBJECT  GLOBAL DEFAULT   24 n46
    75: 0000000000603250    24 OBJECT  GLOBAL DEFAULT   24 n42
    76: 00000000006032b0    24 OBJECT  GLOBAL DEFAULT   24 n48
    77: 00000000004022a4     0 FUNC    GLOBAL DEFAULT   14 _fini
    78: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@@GLIBC_2
    79: 0000000000603760     4 OBJECT  GLOBAL DEFAULT   25 num_input_strings
    80: 0000000000401062   146 FUNC    GLOBAL DEFAULT   13 phase_5
    81: 00000000004013ba     2 FUNC    GLOBAL DEFAULT   13 initialize_bomb_solve
    82: 00000000004013bc    61 FUNC    GLOBAL DEFAULT   13 blank_line
    83: 00000000004017ac  2021 FUNC    GLOBAL DEFAULT   13 submitr
    84: 0000000000400f43   139 FUNC    GLOBAL DEFAULT   13 phase_3
    85: 0000000000400ee0    28 FUNC    GLOBAL DEFAULT   13 phase_1
    86: 00000000004012f6    37 FUNC    GLOBAL DEFAULT   13 invalid_phase
    87: 0000000000401fb8   469 FUNC    GLOBAL DEFAULT   13 init_driver
    88: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND alarm@@GLIBC_2.2.5
    89: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close@@GLIBC_2.2.5
    90: 00000000006032f0    16 OBJECT  GLOBAL DEFAULT   24 node3
    91: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND read@@GLIBC_2.2.5
    92: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    93: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fgets@@GLIBC_2.2.5
    94: 000000000040143a    34 FUNC    GLOBAL DEFAULT   13 explode_bomb
    95: 00000000006032d0    16 OBJECT  GLOBAL DEFAULT   24 node1
    96: 00000000006030e0     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    97: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND signal@@GLIBC_2.2.5
    98: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gethostbyname@@GLIBC_2.2.
    99: 0000000000603310    16 OBJECT  GLOBAL DEFAULT   24 node5
   100: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memmove_chk@@GLIBC_2.3.
   101: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __memcpy_chk@@GLIBC_2.3.4
   102: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
   103: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtol@@GLIBC_2.2.5
   104: 0000000000401204    62 FUNC    GLOBAL DEFAULT   13 fun7
   105: 00000000006030e8     0 OBJECT  GLOBAL HIDDEN    24 __dso_handle
   106: 00000000004022b0     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
   107: 0000000000603130    24 OBJECT  GLOBAL DEFAULT   24 n22
   108: 0000000000603340  1024 OBJECT  GLOBAL DEFAULT   24 host_table
   109: 0000000000400fce    62 FUNC    GLOBAL DEFAULT   13 func4
   110: 00000000006030f0    24 OBJECT  GLOBAL DEFAULT   24 n1
   111: 000000000040131b    29 FUNC    GLOBAL DEFAULT   13 string_length
   112: 0000000000402210   137 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
   113: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fflush@@GLIBC_2.2.5
   114: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __isoc99_sscanf@@GLIBC_2.
   115: 00000000006031b0    24 OBJECT  GLOBAL DEFAULT   24 n34
   116: 0000000000603150    24 OBJECT  GLOBAL DEFAULT   24 n32
   117: 0000000000603e10     0 NOTYPE  GLOBAL DEFAULT  ABS _end
   118: 0000000000400c90     0 FUNC    GLOBAL DEFAULT   13 _start
   119: 0000000000401242    81 FUNC    GLOBAL DEFAULT   13 secret_phase
   120: 0000000000603768     8 OBJECT  GLOBAL DEFAULT   25 infile
   121: 0000000000401660    46 FUNC    GLOBAL DEFAULT   13 sigalrm_handler
   122: 0000000000401f91    39 FUNC    GLOBAL DEFAULT   13 init_timeout
   123: 0000000000603740     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
   124: 0000000000400da0   311 FUNC    GLOBAL DEFAULT   13 main
   125: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __printf_chk@@GLIBC_2.3.4
   126: 0000000000603210    24 OBJECT  GLOBAL DEFAULT   24 n47
   127: 0000000000603270    24 OBJECT  GLOBAL DEFAULT   24 n43
   128: 00000000006031f0    24 OBJECT  GLOBAL DEFAULT   24 n41
   129: 000000000040149e   294 FUNC    GLOBAL DEFAULT   13 read_line
   130: 00000000006031d0    24 OBJECT  GLOBAL DEFAULT   24 n45
   131: 0000000000401338   106 FUNC    GLOBAL DEFAULT   13 strings_not_equal
   132: 000000000040100c    86 FUNC    GLOBAL DEFAULT   13 phase_4
   133: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fopen@@GLIBC_2.2.5
   134: 00000000004010f4   272 FUNC    GLOBAL DEFAULT   13 phase_6
   135: 0000000000603dc0    80 OBJECT  GLOBAL DEFAULT   25 scratch
   136: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
   137: 000000000040218d   119 FUNC    GLOBAL DEFAULT   13 driver_post
   138: 0000000000400efc    71 FUNC    GLOBAL DEFAULT   13 phase_2
   139: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@@GLIBC_2.2.5
   140: 000000000060375c     4 OBJECT  GLOBAL DEFAULT   25 bomb_id
   141: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND connect@@GLIBC_2.2.5
   142: 0000000000603740     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__
   143: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __fprintf_chk@@GLIBC_2.3.
   144: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
   145: 00000000006032e0    16 OBJECT  GLOBAL DEFAULT   24 node2
   146: 0000000000603300    16 OBJECT  GLOBAL DEFAULT   24 node4
   147: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@@GLIBC_2.2.5
   148: 0000000000603320    16 OBJECT  GLOBAL DEFAULT   24 node6
   149: 0000000000400ac0     0 FUNC    GLOBAL DEFAULT   11 _init
   150: 000000000040145c    66 FUNC    GLOBAL DEFAULT   13 read_six_numbers
   151: 0000000000603110    24 OBJECT  GLOBAL DEFAULT   24 n21
   152: 00000000004013a2    24 FUNC    GLOBAL DEFAULT   13 initialize_bomb
   153: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_b_loc@@GLIBC_2.3
   154: 0000000000603750     8 OBJECT  GLOBAL DEFAULT   25 stderr@@GLIBC_2.2.5
   155: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __sprintf_chk@@GLIBC_2.3.
   156: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND socket@@GLIBC_2.2.5

Histogram for `.gnu.hash' bucket list length (total of 3 buckets):
 Length  Number     % of total  Coverage
      0  1          ( 33.3%)
      1  1          ( 33.3%)     33.3%
      2  1          ( 33.3%)    100.0%

Version symbols section '.gnu.version' contains 32 entries:
 Addr: 0000000000400736  Offset: 0x000736  Link: 5 (.dynsym)
  000:   0 (*local*)       2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)
  004:   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   3 (GLIBC_2.4)     2 (GLIBC_2.2.5)
  008:   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)
  00c:   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   4 (GLIBC_2.3.4)   4 (GLIBC_2.3.4)
  010:   0 (*local*)       2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   5 (GLIBC_2.7)  
  014:   4 (GLIBC_2.3.4)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)
  018:   4 (GLIBC_2.3.4)   2 (GLIBC_2.2.5)   6 (GLIBC_2.3)     4 (GLIBC_2.3.4)
  01c:   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)

Version needs section '.gnu.version_r' contains 1 entries:
 Addr: 0x0000000000400778  Offset: 0x000778  Link: 6 (.dynstr)
  000000: Version: 1  File: libc.so.6  Cnt: 5
  0x0010:   Name: GLIBC_2.3  Flags: none  Version: 6
  0x0020:   Name: GLIBC_2.7  Flags: none  Version: 5
  0x0030:   Name: GLIBC_2.3.4  Flags: none  Version: 4
  0x0040:   Name: GLIBC_2.4  Flags: none  Version: 3
  0x0050:   Name: GLIBC_2.2.5  Flags: none  Version: 2

Displaying notes found at file offset 0x00000254 with length 0x00000020:
  Owner                 Data size       Description
  GNU                  0x00000010       NT_GNU_ABI_TAG (ABI version tag)
    OS: Linux, ABI: 2.6.24

Displaying notes found at file offset 0x00000274 with length 0x00000024:
  Owner                 Data size       Description
  GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 11c83ac9c51d3036cf2669235060f17e2cd0400b

.text section 裏裝載了可執行代碼;
.data section 裏面裝載了被初始化的數據;
.bss section 裏面裝載了未被初始化的數據;
.以 .rel 打頭的 sections 裏面裝載了重定位條目;
.symtab 或者 .dynsym section 裏面裝載了符號信息;
.strtab 或者 .dynstr section 裏面裝載了字符串信息;

注意bss中裝載了未初始化的數據,在目標文件中這個變量僅僅是一個佔位符,不佔用實際的磁盤空間。

7、使用過哪些進程間通訊機制,並詳細說明 主要介紹一下Linux下面的幾種進程通訊方式。

  1. 管道:管道的名字挺形象的,就一個一個先進先出的隊列,一個進程從一端讀,另一個進程從另一端寫,是一個環形緩衝區。管道有字節緩衝區,因此有大小限制。同時,管道分爲命名管道(FIFO)和匿名管道。只有父子之間的經常纔可以共享匿名管道,就是受fork限制,而命名管道可以在無親緣關係的進程間使用,使用mififo函數創建,指定pathname作爲路徑名。
#include<sys/types.h>
#include<sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

PS:創建後打開管道,必須讀或者寫,不能既讀又寫,屬於半雙工。
 2. 消息隊列:消息隊列就像一個信箱,有人投遞有人取。消息隊列具有內核持續性,一個進程往某個隊列寫入一些消息,終止後,另一個進程可以讀取。因此說是一個鏈表更爲合適。注意發送者可以設置優先級,優先級最高的最早消息總是位於隊列的頭部。
 3. 共享內存:共享內存是UNIX提供的進程通訊手段中最快的。前面已經介紹過了。注意一下需要自己提供同步的手段。
 4.信號:信號和信號量看起來很像。信號是指signal,用於向一個進程通知發生異步事件的機制,而信號量是一種同步手段,就是PV原語那些東西。信號的傳遞是通過修改信號所發到的進程的某一個位域完成的。只有一位,無法排隊。進程可以選擇執行默認行爲(如終止),執行一個信號處理函數或者忽略該信號。
 
簡單看一下unix常用的信號:
注意前面32個是傳統的unix信號,無法排隊,因此可能造成信號的丟失。而後面32是可靠信息,可靠的意思是信息可以排隊,信號不丟失。

lijun0914:~/workspace/bomb $ 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
16) SIGSTKFLT   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

這裏寫圖片描述

 5.套接字:socket,上面介紹的通訊手段限制了作用域,套接字編程應用則更爲廣泛,可用於不同機器之間的通訊。網絡的兩端都建立一個socket對象,然後通過socket對象進行數據的傳輸。《unix網絡編程卷一》對socket編程有詳細的介紹。

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

makefile是unix下,爲工程中各個目錄下文件制定編譯規則的工具。
陳皓的專欄講解的非常好。
http://blog.csdn.net/haoel/article/details/2886/

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

相信在Linux下調試C或者C++程序的基本都有gdb的調試經驗。
比較基礎的命令或者用法就是設置斷點,單步運行,查看變量,查看調用棧。
拿一個簡單的例子看一下:

#include <iostream>
#include <algorithm>
using namespace std;
void print(string s,int index){
    if(index==s.size()){
        cout<<s<<endl;
        return;
    }else{
        for(int i=0;i<10;++i){
            s[index]=i+'0';
            print(s,index+1);
        }
    }
}
int main(void){
    string s="000";
    print(s,0);
}
lijun0914:~/workspace $ gdb ./a.out 
//設置斷點,可使用行斷點,函數斷點,事件斷點,條件斷點
(gdb) break 10
Breakpoint 1 at 0x400c12: file 12from1Ton.cc, line 10.
//開始運行
(gdb) run
Starting program: /home/ubuntu/workspace/a.out 

Breakpoint 1, print (s="000", index=0) at 12from1Ton.cc:10
10                  s[index]=i+'0';
//顯示附近代碼
(gdb) list
5           if(index==s.size()){
6               cout<<s<<endl;
7               return;
8           }else{
9               for(int i=0;i<10;++i){
10                  s[index]=i+'0';
11                  print(s,index+1);
12              }
13          }
14      }
//顯示變量
(gdb) print s
$1 = "000"
//查看調用棧,即函數調用順序
(gdb) bt
#0  print (s="000", index=0) at 12from1Ton.cc:10
#1  0x0000000000400ce9 in main () at 12from1Ton.cc:17
//繼續從斷點處執行
(gdb) continue 
Continuing.

Breakpoint 1, print (s="000", index=1) at 12from1Ton.cc:10
10                  s[index]=i+'0';
//單步調試,以完整的語句爲單位往下執行
(gdb) next
11                  print(s,index+1);
(gdb) next

Breakpoint 1, print (s="000", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
//查看函數信息,注意這裏會顯示所有匹配到的函數,C++每一個函數都有自己的函數簽名
//在使用模板的時候,如果在模板函數裏打斷點,則只有一個實例生效,如果想要都打上斷點
//可以使用info functions命令,+break void print(std::string, int)即具體函數
(gdb) info functions print
All functions matching regular expression "print":

File ../stdio-common/printf_fphex.c:
int __printf_fphex(_IO_FILE *, const struct printf_info *, const void * const *);

File 12from1Ton.cc:
void print(std::string, int);
static void _GLOBAL__sub_I__Z5printSsi();

File argp-fmtstream.c:
ssize_t __argp_fmtstream_printf(struct argp_fmtstream *, const char *, ...);
.....

(gdb) continue 
Continuing.
000

Breakpoint 1, print (s="000", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
//使用step命令可以進入隱藏的函數調用中,也可以拿來進入C++的class
//例如使用stl的時候,它會一層層的追溯下去
(gdb) step
11                  print(s,index+1);
(gdb) step
print (s="001", index=3) at 12from1Ton.cc:5
5           if(index==s.size()){
//使用finish可以跳出調用棧的不停深入
//這裏的例子並不好,可以自己用stl嘗試一下,印象更深
(gdb) finish 
Run till exit from #0  print (s="001", index=3) at 12from1Ton.cc:7
0x0000000000400c56 in print (s="001", index=2) at 12from1Ton.cc:11
11                  print(s,index+1);
//另一個跳出調用的方法是使用臨時斷點
//注意下面的執行情況,臨時斷點只生效了一次就會被自動刪除
(gdb) tbreak 6
Temporary breakpoint 2 at 0x400be9: file 12from1Ton.cc, line 6.
(gdb) continue 
Continuing.

Breakpoint 1, print (s="001", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
(gdb) continue 
Continuing.

Temporary breakpoint 2, print (s="002", index=3) at 12from1Ton.cc:6
6               cout<<s<<endl;
(gdb) continue 
Continuing.
002

Breakpoint 1, print (s="002", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
(gdb) continue 
Continuing.
003

Breakpoint 1, print (s="003", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
(gdb) step
11                  print(s,index+1);
(gdb) step
print (s="005", index=3) at 12from1Ton.cc:5
5           if(index==s.size()){
//使用up也可以往高層走
(gdb) up
#1  0x0000000000400c56 in print (s="005", index=2) at 12from1Ton.cc:11
11                  print(s,index+1);
//使用斷點命令可以在斷點處指定一個命令序列,每次到達都執行此序列
//通常可以與condition一起使用
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>printf "the current value of index is %d\n",index
>continue
>end
(gdb) continue 
Continuing.
005

Breakpoint 1, print (s="005", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
the current value of index is 2
006

Breakpoint 1, print (s="006", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
the current value of index is 2
007
//condition可以跳過其他斷點,只在達到我們設定的條件時停下
(gdb) condition 1 s[0]=='2'
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/ubuntu/workspace/a.out 
......
194
195
196
197
198
199

Breakpoint 1, print (s="200", index=1) at 12from1Ton.cc:10
10                  s[index]=i+'0';
the current value of index is 1

Breakpoint 1, print (s="200", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
the current value of index is 2
200
//取消condition
(gdb) condition 1
Breakpoint 1 now unconditional.
(gdb) break 16
Note: breakpoint 4 also set at pc 0x400c98.
Breakpoint 5 at 0x400c98: file 12from1Ton.cc, line 16.
//清除斷點
(gdb) delete 1
(gdb) delete 2-3
No breakpoint number 2.
(gdb) delete 3-4
No breakpoint number 3.
(gdb) info breakpoints 
Num     Type           Disp Enb Address            What
5       breakpoint     keep y   0x0000000000400c98 in main() at 12from1Ton.cc:16
(gdb) delete 
Delete all breakpoints? (y or n) y
(gdb) quit

lijun0914:~/workspace $ gdb a.out
(gdb) break 16
Breakpoint 1 at 0x400c98: file 12from1Ton.cc, line 16.
(gdb) run
Starting program: /home/ubuntu/workspace/a.out 

Breakpoint 1, main () at 12from1Ton.cc:16
16          string s="000";
//使用watch可以設置觀察點,當變量或者表達式變化的時候運行停止
(gdb) watch s
Hardware watchpoint 2: s
(gdb) continue 
Continuing.
Hardware watchpoint 2: s

Old value = <error reading variable: Cannot access memory at address 0x64>
New value = "000"
0x00007ffff7b8ebda in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

(gdb) break 10
Breakpoint 3 at 0x400c12: file 12from1Ton.cc, line 10.
(gdb) continue 
Continuing.

Breakpoint 3, print (s="000", index=0) at 12from1Ton.cc:10
10                  s[index]=i+'0';
(gdb) print i
$1 = 0
//在運行中修改變量
(gdb) set var i=3
(gdb) continue 
Continuing.

Breakpoint 3, print (s="300", index=1) at 12from1Ton.cc:10
10                  s[index]=i+'0';
(gdb) break 5
Breakpoint 4 at 0x400bcd: file 12from1Ton.cc, line 5.
(gdb) continue 
Continuing.

Breakpoint 4, print (s="300", index=2) at 12from1Ton.cc:5
5           if(index==s.size()){
//從當前幀跳到任意一行
(gdb) jump 9
Continuing at 0x400c09.

Breakpoint 3, print (s="300", index=2) at 12from1Ton.cc:10
10                  s[index]=i+'0';
//反編譯,在查找崩潰問題時很有用
(gdb) disassemble print
Dump of assembler code for function print(std::string, int):
   0x0000000000400bbd <+0>:     push   %rbp
   0x0000000000400bbe <+1>:     mov    %rsp,%rbp
   0x0000000000400bc1 <+4>:     push   %rbx
   0x0000000000400bc2 <+5>:     sub    $0x38,%rsp
   0x0000000000400bc6 <+9>:     mov    %rdi,-0x38(%rbp)
   0x0000000000400bca <+13>:    mov    %esi,-0x3c(%rbp)
   0x0000000000400bcd <+16>:    mov    -0x3c(%rbp),%eax
   0x0000000000400bd0 <+19>:    movslq %eax,%rbx
   0x0000000000400bd3 <+22>:    mov    -0x38(%rbp),%rax
   0x0000000000400bd7 <+26>:    mov    %rax,%rdi
(gdb) set step 1
(gdb) step
0x00007ffff7b8d3e0 in std::string::operator[](unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

(gdb) disassemble $pc 
Dump of assembler code for function _ZNSsixEm:
   0x00007ffff7b8d3e0 <+0>:     push   %rbp
   0x00007ffff7b8d3e1 <+1>:     push   %rbx
   0x00007ffff7b8d3e2 <+2>:     mov    %rsi,%rbx
=> 0x00007ffff7b8d3e5 <+5>:     sub    $0x8,%rsp
   0x00007ffff7b8d3e9 <+9>:     mov    (%rdi),%rax
   0x00007ffff7b8d3ec <+12>:    mov    -0x8(%rax),%edx
   0x00007ffff7b8d3ef <+15>:    test   %edx,%edx
   0x00007ffff7b8d3f1 <+17>:    js     0x7ffff7b8d3ff <_ZNSsixEm+31>
   0x00007ffff7b8d3f3 <+19>:    mov    %rdi,%rbp
   0x00007ffff7b8d3f6 <+22>:    callq  0x7ffff7b2ecd0 <_ZNSs12_M_leak_hardEv@plt>
---Type <return> to continue, or q <return> to quit---
   0x00007ffff7b8d3fb <+27>:    mov    0x0(%rbp),%rax
   0x00007ffff7b8d3ff <+31>:    add    $0x8,%rsp
   0x00007ffff7b8d403 <+35>:    add    %rbx,%rax
   0x00007ffff7b8d406 <+38>:    pop    %rbx
   0x00007ffff7b8d407 <+39>:    pop    %rbp
   0x00007ffff7b8d408 <+40>:    retq   
End of assembler dump.
(gdb) nexti
0x00007ffff7b8d3e9 in std::string::operator[](unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

  這裏沒有使用call和return,因爲這個例子確實不合適,簡單來說call是調用函數,return是修改函數返回值。當然還有其他命令沒有詳細介紹,例如frame,info,where,不熟悉的google之。
  需要提一下的是程序出現分段錯誤,操作系統會生成一個coredump文件,使用gdb coredump core可以查看崩潰堆棧的信息。當然有時候會出現棧越界無法回溯,這裏給一個解決方法,通過一個地址根據鏈表結構回溯,具體見:https://zhuanlan.zhihu.com/p/20642841?refer=jilinxiaohuo

10、如何定位內存泄露?

  常見的內存問題有內存泄漏;內存的錯誤使用,例如無效的讀寫;緩衝區溢出等等。如果手頭有工具的話,我會優先選用可以做內存檢測的工具,例如purify和valgrind,這些是專用的內存調試器,通過插裝代碼等手段跟蹤內存。
  給給簡單的例子(取自《軟件調試實戰》):
  

#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[]){
    const int size = 100;
    int n,sum=0;
    int* A = (int*)malloc(sizeof(int)*size);

    for(n=size; n>0;n--)
        A[n] = n;
    for(n=0;n<size;n++)
        sum+=A[n];
    printf("sum=%d\n",sum);
    return 0;
}


lijun0914:~/workspace $ gcc -g testMem.cc
lijun0914:~/workspace $ valgrind --tool=memcheck --leak-check=yes ./a.out
==12784== Memcheck, a memory error detector
==12784== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==12784== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==12784== Command: ./a.out
==12784== 
//緩衝區溢出,無效的讀
==12784== Invalid write of size 4
==12784==    at 0x4005CB: main (testMem.cc:9)
==12784==  Address 0x51fc1d0 is 0 bytes after a block of size 400 alloc'd
==12784==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12784==    by 0x4005A6: main (testMem.cc:6)
==12784== 
==12784== Conditional jump or move depends on uninitialised value(s)
==12784==    at 0x4E8158E: vfprintf (vfprintf.c:1660)
==12784==    by 0x4E8B498: printf (printf.c:33)
==12784==    by 0x400616: main (testMem.cc:12)
==12784== 
//使用了未初始化的變量
==12784== Use of uninitialised value of size 8
==12784==    at 0x4E80A4B: _itoa_word (_itoa.c:179)
==12784==    by 0x4E846F6: vfprintf (vfprintf.c:1660)
==12784==    by 0x4E8B498: printf (printf.c:33)
==12784==    by 0x400616: main (testMem.cc:12)
==12784== 
==12784== Conditional jump or move depends on uninitialised value(s)
==12784==    at 0x4E80A55: _itoa_word (_itoa.c:179)
==12784==    by 0x4E846F6: vfprintf (vfprintf.c:1660)
==12784==    by 0x4E8B498: printf (printf.c:33)
==12784==    by 0x400616: main (testMem.cc:12)
==12784== 
==12784== Conditional jump or move depends on uninitialised value(s)
==12784==    at 0x4E84742: vfprintf (vfprintf.c:1660)
==12784==    by 0x4E8B498: printf (printf.c:33)
==12784==    by 0x400616: main (testMem.cc:12)
==12784== 
==12784== Conditional jump or move depends on uninitialised value(s)
==12784==    at 0x4E81659: vfprintf (vfprintf.c:1660)
==12784==    by 0x4E8B498: printf (printf.c:33)
==12784==    by 0x400616: main (testMem.cc:12)
==12784== 
==12784== Conditional jump or move depends on uninitialised value(s)
==12784==    at 0x4E816DC: vfprintf (vfprintf.c:1660)
==12784==    by 0x4E8B498: printf (printf.c:33)
==12784==    by 0x400616: main (testMem.cc:12)
==12784== 
sum=4950
==12784== 
//堆的檢測,未釋放內存
==12784== HEAP SUMMARY:
==12784==     in use at exit: 400 bytes in 1 blocks
==12784==   total heap usage: 1 allocs, 0 frees, 400 bytes allocated
==12784== 
==12784== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12784==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12784==    by 0x4005A6: main (testMem.cc:6)
==12784== 
==12784== LEAK SUMMARY:
==12784==    definitely lost: 400 bytes in 1 blocks
==12784==    indirectly lost: 0 bytes in 0 blocks
==12784==      possibly lost: 0 bytes in 0 blocks
==12784==    still reachable: 0 bytes in 0 blocks
==12784==         suppressed: 0 bytes in 0 blocks
==12784== 
==12784== For counts of detected and suppressed errors, rerun with: -v
==12784== Use --track-origins=yes to see where uninitialised values come from
==12784== ERROR SUMMARY: 14 errors from 8 contexts (suppressed: 0 from 0)

  如果手頭沒有工具,或者有定製的需要,可以自己實現代碼的插樁。例如重載malloc爲其添加一層封裝,記錄所有的分配和釋放,使用鏈表等結構記錄節點的增刪,最後遍歷一下。或者使用鏈接期墊片,動態鏈接會優先調用我們定義的同名函數。使用ld -wrap參數。
  當然也可以使用調試的手段,直接log記錄malloc和free。
  參考:windows下的hook方法:
  http://www.sxrczx.com/pages/impd.tencent.com/p29.html
  陳碩的插裝單元測試的方法:
  http://blog.csdn.net/Solstice/article/details/6423342

11、動態鏈接和靜態鏈接的區別

  參考《深入理解計算機系統》第七章,鏈接。
  靜態鏈接以一組可重定位目標文件爲輸入,文件由各種不同的代碼和數據節組成,通過符號解析和重定位生成一個完全鏈接的可以加載和運行的可執行文件。
  靜態鏈接有一些明顯的缺點,一是如果需要更新一個庫,需要重新編譯和鏈接庫文件。二是對於一些標準的函數,如果將這些代碼複製到每個程序運行的文本段中,會對存儲器的資源造成很大的浪費。
  共享庫就是爲解決靜態鏈接而生,共享庫是一個目標模塊。在運行時,可以加載到任意存儲器地址,並和一個在存儲器中的程序鏈接起來。這個過程稱爲動態鏈接。共享庫在unix下通常使用.so後綴,window下爲dll。
  共享庫使用兩種方式共享,一是一個庫只有一個so文件。所有引用該庫的執行程序共享這個文件的代碼和數據。二是一個共享庫的.text節的一個副本可以被不同的進程共享。
  注意在整個程序的鏈接過程中,鏈接器只是拷貝了一些重定位和符號信息。在程序加載(execve)時纔會解析so文件中代碼和數據的引用。

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

  32位就是4G的尋址空間,linux將其分爲兩部分,虛擬地址從0xC0000000到0xffffffff用於內核,爲系統空間。較低的3G字節爲用戶空間。理論上每個進程最多可以使用3G堆內存。而實際上一般限制到2G。
  而線程的棧空間大小在linux下可以使用ulimit -s查詢,我的環境下默認是8192字節。windows下一說默認1M,一說2M。

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

  區別的意思是優缺點吧。
  
  多線程:

  • 高效的內存共享,數據共享
  • 較輕的上下文切換開銷,不用切換地址空間,不用更改CR3寄存器,不用清空TLB。
  • 創建銷燬切換比較簡單

    多進程:

  • 更強的容錯性,不會一阻全阻,一個進程崩潰不會整個系統崩潰。

  • 更好的多核伸縮性,進程的使用將許多內核資源(如地址空間,頁表,打開的文件)隔離,在多核系統上的可伸縮性強於多線程程序

    在多核利用率上,多進程和多線程同樣可以提高多核利用率。
    其實對於創建和銷燬,上下文切換,其實在Linux系統下差別不大,Window下有較大差別。
    綜上,多進程和多線程的最主要的區別就在資源共享,隔離問題。如果工作使用的內存較大,使用多線程可以避免CPU cache的換入換出,影響性能。

    線程私有

  • ID,每個線程都有自己的ID作爲進程中唯一的表述。

  • 一組寄存器值,用來保存狀態
  • 各自的堆棧
  • 錯誤返回碼,防止錯誤還未被處理就被其他線程修改。
  • 信號屏蔽碼,每個線程感興趣的信號不同。
  • 優先級
  • 共享的:進程的代碼段,公有數據,進程打開的文件描述符,全局內存,進程的棧,堆內存等。

14、寫一個c程序辨別系統是64位 or 32位

理論上是不可以使用sizeof加指針判斷系統是32或者64位的。sizeof的定義與編譯器相關。

#include <stdio.h>

int main(void){
  printf("%d\n", __WORDSIZE);
  if (1==(1<<32))
    printf("32 bit\n");
  else
    printf("64 bit\n");
  return 0;
}

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

int is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

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

上面已經貼過了,這裏給各位lazy boy再粘一次。
這裏寫圖片描述

17、i++是否原子操作?並解釋爲什麼???????

  這個問題網上的解答千篇一律,並且具有誤導性。一般32位系統下,都會解釋說i++實際上拆分成3條彙編指令,讀,加,寫回。
  我這裏給個64位操作系統下的例子和反彙編結果。有興趣的同學可以運行一下這個程序,看看i會出來什麼奇怪的值。

#include <stdio.h>
#include <pthread.h>
int i = 0;
pthread_t thread[2];
void *thread1(){
  int num = 0;
  while(num<50){
    i++;
    printf("thread1: i = %d\n",i);
  }
  pthread_exit(NULL);
}
void *thread2(){
  int num = 0;
  while(num<50){
    i++;
    printf("thread2: i = %d\n",i);
  }
  pthread_exit(NULL);
}
int main(void){
  pthread_create(&thread[0],NULL,thread1,NULL);
  pthread_create(&thread[1],NULL,thread2,NULL);
  printf("the last number of i :%d\n",i);
  int test = 0;
  test++;
  return 0;
}
.....

000000000040069d <thread1>:
  40069d:       55                      push   %rbp
  40069e:       48 89 e5                mov    %rsp,%rbp
  4006a1:       48 83 ec 10             sub    $0x10,%rsp
  4006a5:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  4006ac:       eb 26                   jmp    4006d4 <thread1+0x37>
  4006ae:       8b 05 a0 09 20 00       mov    0x2009a0(%rip),%eax        # 601054 <i>
  4006b4:       83 c0 01                add    $0x1,%eax
  4006b7:       89 05 97 09 20 00       mov    %eax,0x200997(%rip)        # 601054 <i>
  4006bd:       8b 05 91 09 20 00       mov    0x200991(%rip),%eax        # 601054 <i>
  4006c3:       89 c6                   mov    %eax,%esi
  4006c5:       bf 14 08 40 00          mov    $0x400814,%edi
  4006ca:       b8 00 00 00 00          mov    $0x0,%eax
  4006cf:       e8 9c fe ff ff          callq  400570 <printf@plt>
  4006d4:       83 7d fc 31             cmpl   $0x31,-0x4(%rbp)
  4006d8:       7e d4                   jle    4006ae <thread1+0x11>
  4006da:       bf 00 00 00 00          mov    $0x0,%edi
  4006df:       e8 bc fe ff ff          callq  4005a0 <pthread_exit@plt>

000000000040072b <main>:
  40072b:       55                      push   %rbp
  40072c:       48 89 e5                mov    %rsp,%rbp
  40072f:       48 83 ec 10             sub    $0x10,%rsp
  400733:       b9 00 00 00 00          mov    $0x0,%ecx
  400738:       ba 9d 06 40 00          mov    $0x40069d,%edx
  40073d:       be 00 00 00 00          mov    $0x0,%esi
  400742:       bf 60 10 60 00          mov    $0x601060,%edi
  400747:       e8 14 fe ff ff          callq  400560 <pthread_create@plt>
  40074c:       b9 00 00 00 00          mov    $0x0,%ecx
  400751:       ba e4 06 40 00          mov    $0x4006e4,%edx
  400756:       be 00 00 00 00          mov    $0x0,%esi
  40075b:       bf 68 10 60 00          mov    $0x601068,%edi
  400760:       e8 fb fd ff ff          callq  400560 <pthread_create@plt>
  400765:       8b 05 e9 08 20 00       mov    0x2008e9(%rip),%eax        # 601054 <i>
  40076b:       89 c6                   mov    %eax,%esi
  40076d:       bf 36 08 40 00          mov    $0x400836,%edi
  400772:       b8 00 00 00 00          mov    $0x0,%eax
  400777:       e8 f4 fd ff ff          callq  400570 <printf@plt>
  40077c:       c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  400783:       83 45 fc 01             addl   $0x1,-0x4(%rbp)
  400787:       b8 00 00 00 00          mov    $0x0,%eax
  40078c:       c9                      leaveq 
  40078d:       c3                      retq   
  40078e:       66 90                   xchg   %ax,%ax

注意對於全局的i++,在線程裏的指令爲下面三步,這裏的三步都是可以被線程打斷的:

 mov    0x200959(%rip),%eax        # 601054 <i>
 add    $0x1,%eax
 mov    %eax,0x200950(%rip)        # 601054 <i>

對於局部的test++,對應指令爲:

addl   $0x1,-0x4(%rbp)

  難道根據網上說的理論,這裏是一條指令,就是原子的麼?未必。Imm()的意思是基地址+偏移量的存儲器(既內存)操作。這條指令中同時涉及了內存的讀寫。存在兩次內存訪問。哪怕是零此或者一次內存訪問,只有在數據對齊的情況下才是原子的,不然需要多次load操作。
  CPU和內存直接存在多道緩衝,在多核的情況下,無法保證數據一致性。即使在單核的情況下,也可能被DMA或者中斷干擾。注意,我強調的是原子操作的判定跟是不是一條彙編指令沒有必然聯繫
  另外,如果不存在資源的共享訪問,單論原子操作也是沒有意義的。

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

  前面介紹過linux系統的通訊機制,主要是指進程間通訊,其實通訊就是進程同步的手段。如果問進程間同步,見問題7,這裏要說的linux系統的同步機制是講線程間的同步。
  簡單總結一下。更多資料參考《unix環境高級編程》、《操作系統》。
  互斥量
  首先是最基礎的加鎖原語,互斥量。既確保同一時間只有一個線程訪問數據,通過在訪問共享資源前對互斥量加鎖,阻塞其他試圖再次加鎖的線程知道互斥鎖被釋放。互斥的具體實現有多種方法,例如開關中斷,使用原子的機器指令。
  讀寫鎖
  與互斥量類似,不過允許更高的並行性。讀寫鎖有三種狀態,讀模式的加鎖,寫模式的加鎖,不加鎖狀態。一次只有一個線程可以佔有寫模式的讀寫鎖,但是可以多個線程可以同時佔用讀模式的讀寫鎖。既讀模式下可以共享,寫模式下互斥。一般一個線程試圖以讀模式獲取鎖時,讀寫鎖通常會阻塞隨後的讀模式鎖請求
  條件變量
  互斥量是加鎖原語,條件變量屬於等待原語,用於等待某個條件成立後喚醒阻塞的線程。條件變量與互斥量一起使用,條件本身由互斥量保護。Java Object內置了條件變量wait(),notify(),notifyAll()。   
  pthread_cond_wait(),pthread_cond_signal(),pthread_cond_broadcast(Unix),從函數的命名就可以看出其大致作用。
  根據陳碩的總結,條件變量的正確使用方式:
  對於wait端:
  1.必須與mutex一起使用。
  2.在mutex已上鎖時才能調用wait()。
  3.把判斷布爾條件和wait()放到while循環中。
  第三個條件主要是爲了防止spurious wakeup,既虛假喚醒。因爲pthread_conf_wait能被除了pthread_cond_signal(),pthread_cond_broadcast外的其他信號喚醒。需要再wait後再次檢查,同時也是爲了避免錯過一次條件變量後永遠的等待下去。
  對於signal端:
  1.一定不要在mutex已經上鎖的情況下調用signal。
  2.在signal之前一般要修改布爾表達式。
  3.修改布爾表達式通常用mutex保護。
  4.注意區分signal和broadcast:“broadcast通常用於表明狀態變化,signal通常用於表示資源可用”。
  自旋鎖
  自旋鎖與互斥量類似,但它不是通過休眠使進程阻塞,二是在獲取鎖之前一直處於忙等。既一直佔用CPU資源直到鎖被釋放。
  屏障
  屏障主要用於多個線程之間的並行工作的協調。屏障允許每個線程等待,直到所有的合作線程都達到某個點,然後從該點繼續執行。
  信號量
  這個在《unix環境高級編程》中沒有提及,在《操作系統》中有論述。
  信號量可作用與進程間合作,以及多線程的同步。
  一個進程可以被迫在某一個位置停止,直到接收到某一個信號。爲了發信號,需要使用一個稱爲信號量的特殊變量,可以看做一個具有整數值得變量。其中只允許信號量取0和1的稱爲二元信號量。非二元信號量常稱爲計數信號量或一般信號量。
  一般在信號量上定義三個操作:
  1.一個信號量可以初始化成非負數。
  2.semWait操作使信號量減1。如果值變爲負數,則執行semWait的進程或線程被阻塞,否則繼續執行。
  3.semSignal操作使信號量加1。如果值<=0,則被semWait阻塞的進程被解除阻塞。
  信號量需要隊列保存阻塞在信號量上等待的進程。至於進程按什麼順序移除,最公平的是先進先出,採用此策略的爲強信號量。沒有規定順序的爲弱信號量。
  互斥量和二元信號量的主要區別在於互斥量的加鎖和解鎖必須由同一線程分別對應使用,信號量可以由一個線程釋放,另一個線程得到。至於用於互斥和用於同步的說法,十分牽強。
  陳碩關於信號量的建議是不用。
  因爲可用條件變量加互斥量完全代替,另外還需要擔心計數值需要和自己的數據長度常常保持一致的問題。
  死鎖
  死鎖大概已經被講爛了,我也不想再搬運了。堅持使用Scoped Locking,死鎖的時候好檢測。
  http://blog.csdn.net/joejames/article/details/37960873
  

19、列舉說明linux系統的各類異步機制

  老實講,我不知道這個問題是問什麼的。
  首先,同步異步只是一種概念問題。例如兩個任務A和B,同步是A等待B做完,注意同步並不是同時進行,而是需要協調合作的意思。異步就是A通知B執行後立即返回,自己做其他事情,等B完成後取結果。
  興許這個問題是指各類信號機制,或者是異步IO,proactor模式。我這裏就不細講了。對於這種問題,和問問題的人詳細探討,闡述清楚概念,讓他把握一下提問的藝術。

20、exit() _exit()的區別?

  exit()是進程終止時調用的函數。exit()會首先調用各終止處理程序(可以使用atexit()爲進程註冊終止處理程序,一個進程最多登記32個函數),然後關閉所有的打開流,flush輸出緩衝的數據。最後調用_exit()或者_Exit()。
  簡而言之,exit()比_exit()多了可以自己定義的處理函數以及對所有流的關閉。注意,大多Unix系統中,exit(3)是標準C庫中的一個函數,_exit(2)則是一個系統調用。

21、如何實現守護進程?

  守護進程是在後臺運行且不與任何控制終端管理的進程。Unix系統中有很多這樣的進程,使用命令ps -axj可以顯示此類進程。
  在編寫守護進程程序的時候需要遵守一些基本的規則和步驟。
  1.首先調用umask將文件模式創建屏蔽字設置爲一個已知值,通常爲0,。通過顯示的調用設置權限,防止繼承得來的文件模式拒絕了某些權限。
  2.調用fork,然後使父進程exit。這樣做使得:如果進程是作爲shell命令啓動的,父進程終止爲讓shell認爲該命令執行完畢,不用掛在終端輸入上。另外是爲了後續的setsid函數服務,因爲調用setsid的進程的先決條件是需要不是進程組組長。
  3.調用setsid創建一個新會話。調用成功會使得調用進程成爲新會話的首進程,並且成爲一個新進程組的組長,且調用進程沒有控制終端,如果先前有聯繫也會切斷。
  4.將當前工作目錄更改爲根目錄。防止繼承來的工作目錄在文件系統中無法卸載。
  5.關閉不再需要的文件描述符。
  6.忽略SIGHUP信號並在此fork。在此fork的原因是防止誤操作打開終端。只有會話首進程可以打開終端設備,再fork一次,把父進程退出,子進程繼續運行,確保了改進程不是首進程。而這裏必須忽略SIGHUP信號,因爲會話頭進程終止時,其會話的所有進程都會受到SIGHUP信號。注意,這一步是可選的,並不是標準配置
  7.某些守護進程打開dev/null使其某些文件描述符0、1和2。這樣,任何一個試圖讀標準輸入、寫標準輸出或標準錯誤的例程都不會產生任何效果。
  最後,因爲守護進程不應該有終端控制,所以在處理出錯消息的時候不能簡單的寫到標準錯誤上,好在已經有專門的syslogd進程提供了產生日誌消息的接口syslog(3)函數。改函數將消息發送至unix域數據報套接字/dev/log。在/etc/syslog.conf文件中可以配置不同種類消息送向何處。
  apue中的例子:
  

#include "apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>

void
daemonize(const char *cmd)
{
    int                  i, fd0, fd1, fd2;
    pid_t                pid;
    struct rlimit        rl;
    struct sigaction     sa;

    /*
    * Clear file creation mask.
    */
    umask(0);

    /*
    * Get maximum number of file descriptors.
    */
    if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
        err_quit("%s: can't get file limit", cmd);

    /*
    * Become a session leader to lose controlling TTY.
    */
    if((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if (pid != 0)    /* parent */
        exit(0);
    setsid();

    /*
    * Ensure future opens won't allocate controlling TTYs.
    */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if(sigaction(SIGHUP, &sa, NULL) < 0)
        err_quit("%s: can't ignore SIGHUP");
    if((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if( pid != 0 )    /* parent */
        exit(0);

    /*
    * Change the current working directory to the root so 
    * we won't prevent file system from being unmounted.
    */
    if(chdir("/") < 0)
        err_quit("%s: can't change directory to /");

    /*
    * Close all open file descriptors.
    */
    if(rl.rlim_max = RLIM_INFINITY)
        rl.rlim_max = 1024;
    for(i = 0; i < rl.rlim_max; i++)
        close(i);

    /*
    * Attach file descriptors 0, 1, and 2 to /dev/null.
    */    
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    /*
    * Initialize the log file.
    */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if(fd0 != 0 || fd1 != 1 || fd2 != 2)
    {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
}

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

  從進程虛擬內存和內核內存分配兩個方面簡單的說一下吧。這裏涉及的點太多,不一一細講。
  首先看內存分區的策略,是分頁還是分段。
  分頁是將主存劃分成各類大小固定的塊,分段是將用戶進程地址空間劃分成若干大小不同的段,每個段有自己完整的邏輯信息。這兩種塊都不需要連續的位於主存中,可以佔用主存的不同區域。
  再看是否使用虛擬內存。
  進程中的所有存儲器訪問都是邏輯地址,這些邏輯地址在運行時動態的轉化成物理地址。使用邏輯地址訪問,使得用戶可以突破主存大小的限制,使用分配在磁盤上的虛擬內存。
  Linux使用虛擬內存加分頁機制
  因爲頁表本身也需要存儲空間,如果只有一張頁表的話,對於4G內存,按主流的每頁4KB,需要4G/4KB=1M個頁,按每個也32B計算,需要32MB大小。爲了節省這部分空間,出現了多級頁表。04年之後使用的是四級頁面,書中多用3級頁面講解,但是思想是一致的。其虛擬地址到物理地址的轉化大致如下圖:
  這裏寫圖片描述
  即通過查找多級表項到最後一級,通過最後一級的地址加上偏移量得到最後的物理地址。
  頁分配
  Linux內核內存分配的基礎是用於用戶虛擬內存管理的頁分配機制。頁面分配使用夥伴系統。
  在這種分配方式下,內存從一個2的N次冪大的內存塊中分配。當內存塊比要分配的長度大兩倍以上,內存塊平均分裂成兩塊。選中其中一半,重複這個過程(檢查長度,滿足條件則分裂)直到內存塊剛好等於需要的長度。
  所有的塊信息保存在一個排序過的鏈表或者二叉樹中。當一個塊被釋放的時候與他的相鄰塊進行比較。如果他們都被釋放,就合併成一個大塊放進更大的一個塊列表 中。每當分配結束,分配器會從儘量小的塊重新開始分配,以避免產生不必要的碎片。
  對於一些小塊,Linux使用slab分配方案解決內部碎片問題。
  頁面替換算法
  用於處理必須讀取一個新頁時,應該替換主存中的哪一頁。Linux使用時鐘算法。其基本策略是把所有頁面保存在一個類似時鐘面的環形鏈表中。
  這裏寫圖片描述
  當發生缺頁中斷時,算法首先檢查錶針指向的頁面,如果它的R位是0就淘汰該頁面,並把新的頁面插入這個位置,然後把錶針前移一個位置;如果R位是1就清除R位並把錶針前移一個位置,重複這個過程直到找到了一個R位爲0的頁面爲止。
  當然還有其他頁面算法。
  

  • FIFO,先入先出
  • OPT,最佳,無法預測下次訪問距當前時間最長頁,無法實現
  • LRU,最近最少使用,需要給每個頁添加一個最後訪問的時間標籤,較難實現

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

  一般討論這個問題時分爲單核調度和多核調度。Linux的調度算法換過很多次了,再去照抄書本上的內容就有些不合時宜了。
  單核調度
  調度器就是爲了解決下一個要運行的線程是誰的問題,參考一些指標,例如優先級和使用時間。實時進程的調度就是挑選最高優先級的運行。根據優先級排隊FIFO,或者加上時間片。一般使用雙向鏈表實現。這裏主要討論普通進程的調度。
  最基礎的想法就是按時間片輪轉,大家分配一樣的時間例如10ms,然後輪流執行,這樣會造成交互性非常差,並且任務不分級。可以通過將線程定義爲實時線程解決,讓某個線程優先解決,但是這樣會造成CPU被佔滿,造成資源浪費。
  最新使用的是CFS算法,使用紅黑樹實現優先隊列。給每個調度隊列的線程一個vruntime變量,記錄線程的運行時間,每次都調度vruntime最小的線程。然後通過nice值作爲vruntime流逝的加權,nice大的進程時間流逝快,它佔用的時間片就少。
  多核調度
  多核調度,就是每個CPU有一個運行隊列,創建線程時加入到各自隊列中,然後對這個隊列執行單核的調度算法。
  當然還需要注意負載的均衡問題,Linux把不同線程組織到不同的sched_domain中,每個domain包含一組線程,各自有其對應算法。

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

  這裏寫圖片描述
  其實不少Linux下的C標準庫函數都是對系統調用的包裝,也有一些直接內聯的彙編。

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

前面講過信號了,爲什麼又問一遍?這個問題有什麼特殊的地方麼?
內核給進程發送信號,是在進程所在的進程表項的信號域設置對應的信號的位。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章