“Linux程式設計”小結(程式間通訊)

onephone發表於2017-03-27

最近,藉著機會把《Linux程式設計(第4版)》這本書整本過了一遍。針對該書的主要知識要點, 自己做了總結和適當的補充,同時也標明瞭書中的一些錯誤

知識點

  1. /proc(P107)
    • Linux提供了一個特殊的檔案系統procfs,其以/proc目錄的形式展現。該目錄中包含了許多特殊檔案用來對驅動程式和核心資訊進行更高層次的訪問。
    • cat /proc/cpuinfo 檢視cpu資訊
    • cat /proc/sys/fs/file-max 檢視系統能同時使開啟的檔案總數, cat /proc/sys/fs/file-nr 可檢視具體的詳細資訊
    • cat /proc/net/sockstat 獲取網路套接字資訊
    • ls /proc/193/ 檢視程式資訊(PID為193), od -c /proc/193/cmdline 檢視啟動該程式的命令
    • 在許可權允許的範圍內也可對部分內容進行修改(如可開啟檔案總數、網路核心引數等),但該修改在系統重啟後便會失效
    • 原書109頁表述的“程式號在1~32000的數字,這是錯誤的!,具體值可通過cat /proc/sys/kernel/pid_max檢視,且就算在32位的系統下,PID的取值範圍應該是0~32767(short int型別), 0用於idle程式, 1用於init程式或systemd程式
  2. 檔案鎖定(P222)
    • 可以鎖定整個檔案,也可鎖定檔案的一部分
    • Linux通常會在/var/spool目錄下建立一個鎖檔案
    • 鎖檔案是建議鎖,不是強制鎖
    • 檔案中每個位元組在任意時刻只能擁有一種型別的鎖,即共享鎖(F_RDLCK,讀鎖)、獨佔鎖(F_WRLCK,寫鎖)或解鎖(F_UNLCK)
    • 檔案鎖的使用,很多時候相當於一個二進位制訊號量的使用
    • fcntl.h 中的open(), 也可通過unistd.h中的lockf()進行設定
    • 預防死鎖
  3. fcntl系統呼叫(P110)

    • 其對底層檔案描述符提供了很多的操作方式
    • 複製、獲取和設定檔案描述符標誌,獲取檔案狀態標誌,管理建議性檔案鎖
    • #include <fcntl.h>
  4. mmap函式(P111)

    • 即記憶體對映,其可以建立一段被兩個或多個程式讀寫的記憶體,允許程式間共享記憶體(Linux核心2.0版本開始包含該功能)
    • mmap建立一個指向一段記憶體區域的指標,該記憶體區域的指標與可以通過一個開啟的檔案描述符訪問的檔案的內容關聯。
    • # include <sys/mman.h>, mmap, msync, munmap
    • 這是一種記憶體共享機制,還有另外一種System V共享記憶體的方式!
  5. 資源和限制(P139)

    • sys/resource.h中包括對程式長度、執行優先順序和檔案資源等方面限制進行查詢和設定的函式
    • limits.h中定義了許多代表作業系統方面限制的顯示常量,如NAME_MAX, CHAR_BIT, CHAR_MAX, INT_MAX
    • 也可通過shell內建ulimit命令為某一特定shell中執行的程式設定限制
    • 通過/proc/etc/security/limits.conf等方式進行設定
    • nice可修改程式的執行優先順序
  6. dbm資料庫(P237)

    • dbm資料庫適合於儲存相對比較靜態的索引化資料,其也可以看做是一個索引化的檔案儲存系統
    • locateupdatedb 也使用到一個系統資料庫(但和dbm有些不同)
  7. 程式和訊號(P388)

    • UNIX中的定義“一個其中執行著一個或多個執行緒的地址空間和這些執行緒所需要的系統資源”
    • 程式是系統資源分配的最小單位,執行緒是系統排程的最小單位, 執行緒是程式中的一個執行路徑
    • 正在執行的程式由程式程式碼、資料、變數、開啟的檔案和環境組成
    • 程式被啟動後,系統會按順序選擇一個未被使用的數字作為其PID
    • 程式狀態程式碼(STAT),如s表示程式是會話期首程式 (P391)
    • 程式表就是一個資料結構,它會把載入到記憶體中所有程式的有關資訊儲存在一個表中 ps -ef
    • 程式根據nice值來決定其優先順序,預設為0, 隨著其不間斷的執行,其值會不斷變化(往往降低)
    • system() 是通過shell來啟動要執行的程式
    • exec系列函式,是通過替換程式映像來啟動新程式(把當前程式替換為一個新程式,原來的程式就不存在了),但PID, PPID和nice值和以前完全一樣。實質就是“執行中的程式開始執行exec呼叫中指定的可執行檔案中的程式碼” (P398)
    • sys/wait.h, sys/types.h, unistd.h, fork(), wait(), waitpid()
    • 殭屍程式:子程式雖然不再執行,但其仍然存在於系統中,因為它的退出碼還需要儲存,已備父程式今後的wait呼叫使用。同時它將成為一個死(defunct)程式或殭屍(zombie)程式
    • 核心轉儲檔案core,是程式在記憶體中的映象,其對除錯很有幫助
    • 新程式中應該使用sigaction函式(很好的支援訊號集),少用signal (P413)
    • SIGKILL是不可捕獲的
    • fork()函式的返回值可理解為指向當前程式的子程式連結串列(第一個號)
  8. POSIX執行緒(P416)

    • 準確定義:“執行緒是一個程式內部的一個控制序列”
    • 新的執行緒有自己的棧,但與它的建立者共享全域性變數、檔案描述符、訊號處理函式、當前目錄狀態
    • 執行緒庫使用“本地POSIX執行緒庫”(Native POSIX Thread Library, 即NPTL)
    • 可重入歷程(程式碼被呼叫多次仍能正常工作,呼叫可以來自不同的執行緒,也可以是某種形式的巢狀呼叫)
    • 編寫多執行緒程式時,需要動過定義_REENTRANT來高速編譯器我們需要可重入功能,且其定義必須位於#include之前
      • 它會對部分函式重新定義他們的可安全重入版本,如gethostbyname將變為gethostbyname_r
      • stdio.h中原來以巨集的形式實現的一些函式將變成可安全重入的函式
      • errno.h中定義的變數errno將成為一個函式呼叫,即以執行緒安全的方式來獲取真正的errno
      • 如編譯命令 cc -D_REENTRANT thread1.c -o thread1 -lpthread
    • pthread_creat, pthread_join, pthread_exit
    • 同步(P423)
      • 訊號量 (二進位制訊號量、計數訊號量), #include <semaphore.h>, sem_init, sem_wait(訊號量值-1), sem_post(訊號量值+1), sem_destory
      • 互斥量(互斥訪問),#include <pthread.h>, pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock, pthread_mutex_destroy
    • 執行緒的屬性
      • 脫離執行緒(detach thread),通過修改執行緒的屬性或呼叫pthread_detach的方式來建立
      • pthread_attr_init, pthread_attr_setdetachstate, pthread_attr_getdetachstate
      • 取消執行緒 pthread_cancel

程式間的通訊

  1. 管道 (P443)

    • popen呼叫
      • stdio.h中的FILE *popen(const char *command, const char *type), int pclose(FILE *stream)
      • popen呼叫執行的一個程式時,首先啟動Shell,即系統中的sh命令,再將command字串作為一個引數傳遞個它,即每個popen將多啟動兩個程式!
    • pipe呼叫

      • unistd.h中的int pipe(int pipefd[2]), int pipe2(int pipefd[2], int flags);
      • 寫到pipefd[1]中的所有資料都可以從pipefd[0]中讀回來
      • 如果在原先的程式中建立一個管道,然後再呼叫fork建立新的程式,即可通過管道在兩個程式之間傳遞資料!
    • dup呼叫

      • unistd.hint dup(int oldfd), int dup2(int oldfd, int newfd), int dup3(int oldfd, int newfd, int flags);
      • 可把管道用作標準輸入和標準輸出
    • 命名管道(有名管道)FIFO (P456)
      • 可在不相關的程式之間交換資料
      • named pipe 是一種特殊的檔案型別, 在檔案系統中以檔名的方式存在
      • mknod filename p 或者 mkfifo filename p 可建立一個FIFO檔案
      • 在程式中可使用#include <sys/types.h>, #include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode)或者int mknod(const char *path, mode_t mode, dev_t dev)
      • 同pipe建立的管道不同,FIFO以檔名的方式存在,在進行讀寫之前需要必須先open開啟
  2. 訊號量(P489)
    • 這個訊號量函式比執行緒中的訊號量函式更常用!
    • 用於管理對資源的訪問,它是一個特殊的變數,只能對它進行等待(wait)和傳送訊號(signal)操作
    • PV操作:P用於等待;V用於傳送訊號
    • 假設有一個訊號量sv, 則P(sv), V(sv)的意思? (P489)
    • #include <sys/sem.h>, semctl()用於直接控制訊號量的資訊, semget() 建立一個新訊號量或者獲取一個已有的訊號量, semop()用於改變訊號量的值
    • 訊號量都會對應到一個key_t(應用程式提供,有效地為訊號量命名)
  3. 共享記憶體(P496)
    • 用於程式之間高效的共享資料,允許兩個不相關的程式訪問同一個邏輯單元
    • #include <sys/shm.h>shmget建立或者獲取一個已有的,shmat(第一次建立共享記憶體段時,其不能被任何程式訪問,要想啟動對該共享記憶體的訪問,必須將其連線到一個程式的地址空間中,shmat成功則返回共享記憶體第一個位元組的指標), shmdt將共享記憶體從當前程式中分離
    • 共享記憶體會對應到一個key_t(應用程式提供,有效地為共享記憶體命名)
    • mmap也是實現記憶體共享的一種方式
  4. 套接字(P513)
    • 本地套接字的名字是Linux檔案系統中的檔名,一般放在/tmp/usr/tmp目錄中, AF_UNIX
      • 通過socket(AF_UNIX, SOCK_STREAM, 0)(UNIX檔案系統域)建立的本地套接字會在當前目錄下(預設)以檔案的形式存在,ls -lF socketname檢視
    • 網路套接字, AF_INET, AF_INET6
      • 流套接字 SOCK_STREAM, 資料包套接字 SOCK_DGRAM
    • 套接字的特性由3個屬性確定:域(domain)、型別(type)、協議(protocol), 地址格式隨域(又名協議族, protocol family)
    • 主機位元組序、網路位元組序:對於一個數字,高位(數字的位)在前(低地址空間)的是big endian, 低位在(低地址空間)前的是little endian, x86是little endian 記憶圖
    • 因特網守護程式 (xinetd/inetd):UNIX以超級伺服器的方式來提供多項網路服務,而特定的網路服務往往在需要的時候才會啟動!
    • setsockopt()可用來控制套接字的行為
    • select()系統呼叫允許程式同時在多個底層檔案描述符或套接字上等待輸入或處處活動的的到來;其可用於測試檔案描述符集,但每次都得遍歷全部的,通過其可同時處理多個客戶端而無需再依賴子程式了
    • IO多路複用機制:select, poll, epoll之間的區別
    • 關於同步、非同步與阻塞、非阻塞的理解
  5. 訊號
    • signal
  6. 檔案
    • Linux將一切事物看做檔案

原文連結: http://codeshold.me/2017/03/linux_programming.html

相關文章