偶然在知乎上看到想要從事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文件有三種類型:
- 可重定位的對象文件(Relocatable file) 由彙編器彙編生成的 .o 文件
- 可執行的對象文件(Executable file) 可執行應用程序
- 可被共享的對象文件(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下面的幾種進程通訊方式。
- 管道:管道的名字挺形象的,就一個一個先進先出的隊列,一個進程從一端讀,另一個進程從另一端寫,是一個環形緩衝區。管道有字節緩衝區,因此有大小限制。同時,管道分爲命名管道(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、補充一個坑爹坑爹坑爹坑爹的問題:系統如何將一個信號通知到進程?
前面講過信號了,爲什麼又問一遍?這個問題有什麼特殊的地方麼?
內核給進程發送信號,是在進程所在的進程表項的信號域設置對應的信號的位。