unix環境高階程式設計(中)-程式篇

kinnylee發表於2018-10-16

目錄

前言

程式環境

程式控制

程式關係

訊號

執行緒

執行緒控制

高階IO

程式間通訊

網路間程式通訊:套接字

高階程式間通訊

前言

筆者將《unix環境高階程式設計》主要內容總結為三篇:檔案篇程式篇高階io和程式間通訊三大板塊。本文是unix環境高階程式設計系列文章第二篇:程式篇。該篇主要包括:

程式環境

介紹程式相關的基本概念和使用環境:程式執行前的準備工作,程式如何終止,程式執行相關的環境變數表,程式執行時的記憶體空間佈局,記憶體如何分配

程式控制

主要介紹程式控制符,程式如何建立,如何執行,如何終止,等待終止

程式關係

主要介紹程式之間的關係,包括:程式組,會話,控制終端。以及unix底層的資料結構如何建立他們之間的關係

訊號

主要介紹訊號的概念,如何設定訊號處理函式,收到訊號導致系統中斷的呼叫以及能自動重啟的呼叫。然後介紹如何傳送訊號,如何遮蔽訊號,以及導致的訊號阻塞

執行緒

主要介紹執行緒的概念,執行緒識別符號,執行緒如何建立,如何終止,等待終止狀態,設定自定義清理程式。然後對比了程式和執行緒相關概念和介面的對比。最後介紹執行緒的同步,包括:互斥量,讀寫鎖,條件變數

執行緒控制

主要介紹執行緒屬性,同步屬性:互斥量屬性,讀寫鎖屬性,條件變數屬性。然後介紹如何建立執行緒私有資料。最後介紹訊號和fork對執行緒的影響

守護程式

主要說明守護程式的特徵,常見的系統守護程式,以及守護程式的程式設計規則。然後介紹處理守護程式的通用日誌架構,最後介紹守護程式的一些慣例

一. 程式環境

1. 程式執行

程式執行從main函式開始,在這之前需要一些準備工作

  • 核心使用exec函式呼叫c程式
  • 執行c程式時,先呼叫一個特殊的啟動例程。
  • 可執行檔案將此啟動例程指定為程式的起始地址(gcc設定)
  • 啟動例程從核心取得命令列引數和環境變數
  • 上述工作準備就緒,開始執行main函式

2. 程式終止

2.1 正常終止

  • 從main返回
  • 呼叫exit:先執行一些清理工作(關閉io流等),然後進入核心
  • 呼叫_exit或_Exit:立即進入核心
  • 最後一個執行緒從其啟動例程返回
  • 最後一個執行緒呼叫pthread_exit

2.2 異常終止

  • 呼叫abort
  • 接到一個訊號並終止
  • 最後一個執行緒堆取消請求做出相應

2.3 終止處理程式

  • 終止處理程式由exit自動呼叫,無需手動呼叫
  • 註冊終止處理程式的方法:atexit,引數為函式地址
  • 註冊終止處理程式的最大數量:32
  • exit呼叫順序:與註冊順序相反,且不會去重,登記多次就呼叫多次

2.4 c程式啟動和終止流程圖

unix環境高階程式設計(中)-程式篇

  • 核心使用程式執行的唯一方法是:呼叫一個exec函式
  • 使用者函式可以直接呼叫_exit或者_Exit終止程式,此時直接進入核心,不會呼叫終止處理程式
  • 如果呼叫exit終止程式,它會先呼叫註冊的終止處理程式

3. 環境表

3.1 環境表記憶體佈局

每個程式都會接收到一張環境表。環境表是一個字元指標陣列,每個指標包含一個以null結束的c字串地址。全域性變數environ表示該地址

unix環境高階程式設計(中)-程式篇

3.2 環境變數設定

  • 獲取環境變數指定變數值的函式:getenv
  • 設定環境變數的函式:
    unix環境高階程式設計(中)-程式篇
    • putenv:引數為name=value的字串形式,name存在則先刪除
    • setenv:引數是否存在根據rewrite決定
    • unsetenv:刪除某個環境變數

3.3 環境變數設定的底層實現

  • 環境變數表存放在程式儲存空間的頂部(棧之上),見下一小節的儲存空間佈局
  • 刪除一個環境變數很簡單,找到後刪除,後續指標依次往前移
  • 但是新增或者修改比較麻煩。因為它不能再向高地址(向上)擴充套件。同時也不能向下擴充套件到棧區。具體細節如下:
    • 修改現有的name:
      • 新value長度 < 舊的長度:覆蓋寫入
      • 新value長度 > 舊的長度:為value分配新空間,將指標指向該空間
    • 新增一個name:先為新name和value分配新空間
      • 第一次新增環境變數:先呼叫malloc為新的指標表分配空間,再將資料放到表尾
      • 不是第一次新增:呼叫realloc擴充套件空間

4. 儲存空間佈局

c程式由下面幾部分組成:

  • 正文段:cpu執行的機器指令部分。正文段有可被共享,只讀的特性。

  • 資料段(初始化資料段):包含程式中明確賦初始值的變數

  • 非初始化資料段(bss段):函式外申明的未初始化資料

  • 棧:區域性變數,函式呼叫所需資訊。每次呼叫時的返回地址等資訊都存放在棧。棧從高地址向低地址方向增長

  • 堆:動態儲存分配。位於非初始化資料段和棧之間

    使用size命令可以檢視各個部分的大小

unix環境高階程式設計(中)-程式篇

5. 儲存器分配

5.1 記憶體空間動態分配的函式

  • malloc:分配製度位元組數的儲存區,初始值不確定
  • calloc:指定數量,指定長度的物件分配空間,每一位初始化為0
  • realloc:更改以前分配的長度
    unix環境高階程式設計(中)-程式篇
  • 最終都呼叫sbrk核心函式,分配後不釋放會導致記憶體洩漏

5.2 其他替代的儲存器分配程式

分配器出錯難於追蹤,很多替代的分配器在分配或釋放時,會進行附加的操作,以便追蹤問題

  • libmalloc:
  • vmalloc:
  • 快速適配quick-fit: 改善了標準malloc的最佳適配或首次適配分配策略
  • alloca:在棧上分配空間,而不是堆上。

二. 程式控制

1. 基本概念

1.1 程式識別符號

  • 每個程式都有一個非負整數表示的唯一程式ID
  • id為0的程式通常是排程程式(交換程式,系統程式),是核心的一部分。
  • id為1的程式通常是init程式,是普通程式。以超級使用者執行。檔案為/sbin/init。負責在自舉核心後啟動unix系統。
  • id為2的程式通常為頁守護程式,負責支援虛擬儲存系統的分頁操作
  • 除了程式id,每個程式還有其他標識,呼叫getpid可以獲得

1.2 程式控制原語

  • 建立新程式:fork
  • 執行新程式:exec
  • 處理終止:exit
  • 等待終止:wait

2. 建立程式

2.1 fork函式

unix環境高階程式設計(中)-程式篇

  • 一個現有程式呼叫fork可以建立一個新程式,稱為子程式
  • fork函式呼叫一次,返回兩次:子程式返回0,父程式返回子程式id
  • 子程式是父程式的副本。正文段由父子共享,但是資料空間,堆,棧各自維護
  • 由於fork之後常常跟隨exec,現在很多實現並不是執行真正的複製,而是使用“寫時複製”技術(COW):父子共享訪問這些空間,且設為只讀,如果試圖修改,就只複製修改的部分
  • fork之後的執行順序是不確定的,取決於核心使用的排程演算法
  • fork的兩個應用場景:
    • 網路服務:父程式接收客戶端請求,請求來時fork出子程式處理,父程式繼續等待請求
    • shell:一個程式執行不同的程式

2.2 vfork函式

功能類似與fork,區別如下:

  • 區別一:vfork建立的子程式並不將父程式的地址空間完全複製到子程式中,子程式呼叫exec時,它在父程式的空間中執行,以提高效率(比前面說的COW效率更高)
  • 區別二:保證子程式先執行,呼叫exec或exit之後父程式才可能被排程

3. 終止程式

前面介紹了終止程式的8中情況。不管哪種方式,都有一些特性:

  • 最後都會執行核心中的同一段程式碼:為程式關閉所有開啟的檔案描述符,釋放使用的記憶體。
  • 都希望終止程式能夠通知父程式它是如何終止的:
    • 正常終止:程式將退出狀態作為引數傳給函式
    • 異常終止:核心產生一個指示其終止原因的終止狀態,_exit將終止狀態轉化為退出狀態
  • 父程式都能通過wait或waitpid取得終止狀態
  • 當一個程式中止時,核心就向其父程式傳送SIGCHLD訊號(非同步訊號)
  • 父程式可以選擇忽略或提供訊號處理程式
  • 如果父程式在子程式之前終止,子程式的父程式都變為init程式。(每個程式中止前都做檢查)

4. 等待中止

4.1 wait/waitpid函式

unix環境高階程式設計(中)-程式篇

4.1.1 呼叫wait的程式可能發生什麼情況:

  • 如果所有子程式都還在執行,則阻塞
  • 如果一個子程式已經終止,正等待父程式獲取終止狀態,則取得狀態立刻返回
  • 如果沒有任何子程式,則出錯返回
  • 如果程式由於收到SIGCHLD而呼叫wait,可能得到返回。任意時刻呼叫,可能會阻塞

4.1.2 區別

  • wait:使呼叫者阻塞
  • waitpid:選項可設定為阻塞或不阻塞,允許指定等待的子程式

4.1.3 引數

  • 不為空,則將狀態資訊儲存在引數中返回
  • 終止狀態巨集:存放在<sys/wait.h>
    • WIFEXITED:正常終止
    • WIFSIGNALED:異常終止
    • WIFSTOPPED:暫停子程式
    • WIFCONTINUED:暫停之後繼續

4.2 waitid函式

unix環境高階程式設計(中)-程式篇

  • 功能與waitpid相似,不過使用單獨的引數(idtype)表示要等待的子執行緒型別

4.3 wait3和wait4

unix環境高階程式設計(中)-程式篇

  • 功能比前面幾個wait函式多一項,與引數rusage有關
  • 要求返回終止程式及子程式使用的資源彙總,包括使用者cpu時間總量,系統cpu時間總量,頁面出錯次數,接收到的訊號次數。

5. 競爭條件

  • 多個程式企圖對共享資料進行某些處理,而最後的結果取決與允許的順序,則認為發生了競爭條件
  • 為了避免競爭條件,需要使用訊號或程式間通訊機制

6. 程式執行

6.1 exec說明

  • 程式呼叫exec以執行另一個程式
  • 呼叫exec時,該程式執行程式完全替換為新程式,新程式從main開始執行
  • 呼叫exec並不建立新的程式,所以前後程式id不變
  • exec用一個全新的程式替換當前程式的正文,資料,堆和棧

6.2 各函式說明

unix環境高階程式設計(中)-程式篇

  • 前四個取路徑名作為引數,後兩個取檔名做為引數
  • filename中包含/符號,則將其視為路徑名,否則就在PATH中搜尋
  • l代表list,v表示vector。l要求每個引數單獨傳入,v要求傳入引數陣列
  • 以e結尾的函式可以傳遞環境字串指標

7. 直譯器檔案

  • 在文字檔案第一行新增 #! pathname,比如 #! /bin/bash
  • 這種檔案由核心作為exec系統呼叫的一部分來完成
  • exec函式並不執行該檔案,而是第一行pathname指定的檔案

8. system函式

unix環境高階程式設計(中)-程式篇

9. 程式會計

  • 啟用該功能時,程式結束後會寫一個會計記錄:命令,cpu時間,啟動時間等
  • 存放位置:/var/account
  • 標頭檔案:<sys/acct.h>

三. 程式關係

1. 程式組

  • 每個程式除了有程式id外,還屬於一個程式組(一個或多個程式的集合)
  • 程式組與同一個作業相關聯,可以接收來自同一終端的各種訊號
  • 程式組有一個唯一的id,相關函式:getpgrp,getpgid
  • 每個程式組都可以有一個組長程式(程式組id=程式id)
  • 加入或建立一個新的程式組:setpgid,setsid
  • 一個程式只能為它或它自己設定程式組ID,子程式呼叫exec之後就不能改變它都程式組id

2. 會話

  • 會話是一個或多個程式組的集合
  • 建立會話:setsid

3. 控制終端

  • 一個會話可以有一個控制終端
  • 通常是登陸的終端裝置或偽終端裝置
  • 一個會話中的幾個程式組可以分為一個前臺程式組和一個或多個後臺程式組
    unix環境高階程式設計(中)-程式篇

4. 程式,程式組,會話,控制終端的實現

unix環境高階程式設計(中)-程式篇

  • 每個會話都分配一個session結構
    • s_count:程式組數。減為0時,可釋放該結構
    • s_leader:指向會話首程式指標,用proc結構表示
    • s_ttyvp:指向終端控制v-node的指標
    • s_ttyp:指向終端控制tty結構的指標
    • s_sid:會話id
  • 每個終端或偽終端裝置都分配一個tty結構
    • t_session:指向會話(互相指向)
    • t_pgrp:指向前臺程式組的pgrp結構。利用此傳送訊號到前臺程式組
    • t_termios:包含終端有關的資訊
    • t_winsize:包含終端視窗當前尺寸的winsize結構
  • 前臺程式組pgrp,包含特定程式組資訊
    • pg_id:程式組id
    • pg_session:指向此程式組所屬的session
    • pg_members:程式組成員列表指標
  • 每個程式proc
    • p_pid:程式id
    • p_pptr:指向父程式的指標
    • p_pgrp:指向所屬的程式組指標

四. 訊號

1. 訊號的概念

  • 訊號是軟體中斷,提供了一種處理非同步事件的方法
  • 每個訊號都有一個名字,以SIG開頭。在標頭檔案<signal.h>中定義為正整數的巨集
  • 產生訊號的事件對程式而言是隨機出現的,程式必須告訴核心呼叫什麼訊號處理函式或者忽略
  • 訊號產生的一些舉例
    • 硬體異常:如除0錯誤,無效記憶體引用
    • 程式呼叫kill(2):將訊號傳送給另一個程式或程式組,是否終止看訊號型別,以及是否捕獲該訊號
    • 程式呼叫kill(1):將訊號傳送給另一個程式,是否終止看訊號型別,以及是否捕獲該訊號
    • 檢測到某種軟體條件已經發生,傳送訊號通知其他程式
  • 一些常見的訊號
    • SIGABORT:異常終止
    • SIGALRM:超時
    • SIGBUS:硬體故障
    • SIGCHLD:子程式狀態改變
    • SIGEMT:硬體故障
    • SIGINT:終端中斷符
    • SIGIO:非同步IO
    • SIGKILL:終止
    • SIGPOLL:可輪詢事件
    • SIGSEGV:無效記憶體引用
  • 訊號的處理:
    • 執行一個程式時,通常所有訊號的狀態都是系統預設
    • 當呼叫exec時,將原先設定為要捕捉的訊號都修改為預設(訊號函式地址在新的程式可能無效)
    • shell中執行後臺程式時,會忽略中斷和退出訊號
    • fork建立子程式時,複製父程式的儲存映像,子程式會繼承父程式的訊號處理方式

2. signal函式

unix環境高階程式設計(中)-程式篇

  • 作用:設定訊號處理函式
  • 函式需要兩個引數,返回一個函式指標,該指標指向的函式無返回值,返回的函式需要一個整形引數
  • 第一個引數signo是整數,第二個引數是函式指標,該指標需要一個整形引數,無返回值

3. 中斷的系統呼叫

  • 程式執行低速的系統呼叫時,如果捕獲到訊號,系統呼叫被中斷不再繼續,返回出錯。以便喚醒被阻塞的系統呼叫
  • 代表必須顯示處理這種出錯
  • 為了幫助應用程式每次手動處理這個情況,引入了系統呼叫的自動重啟
  • 自動重啟的系統呼叫包括
    • ioctl
    • read
    • readv
    • write
    • writev
    • wait
    • waitpid

4. 訊號術語

  • 訊號產生:引發訊號的事件發生時
  • 訊號來源:硬體異常,軟體條件,終端訊號,kill函式等
  • 訊號遞送:程式表中設定一個某種形式的標誌
  • 訊號未決:訊號產生與訊號遞送之間的時間間隔
  • 訊號阻塞:設定為阻塞時,將保持訊號未決狀態(傳遞被推遲,直到解除阻塞為止)。函式sigpending決定哪些訊號設定為阻塞並處於未決狀態。
  • 訊號遮蔽:程式的訊號遮蔽字,阻塞送到該程式的訊號集:sigprocmask可以檢視和更改訊號遮蔽字
  • 訊號集:sigset_t儲存

5. kill和raise

  • kill:將訊號發給程式或程式組
    • pid > 0: 訊號傳送給程式id為pid的程式
    • pid = 0:訊號傳送給與自己同處一個程式組的所有程式,而且有傳送訊號的許可權
    • pid < 0:訊號傳送給與其他程式組id等於pid絕對值,而且有傳送訊號的許可權。
    • pid = -1:傳送信給號有許可權傳送的所有程式
  • raise:允許程式向自身傳送訊號
  • raise(signo) = kill(getpid(), signo)

6. alarm和pause

alarm

  • alarm:設定定時器,定時器超時時,產生SIGALRM訊號。如果不忽略或不捕捉則停止呼叫該函式的程式
  • 引數:秒。
  • 說明:
    • 訊號由核心產生,由於程式排程的延遲,得到控制和處理還需一些時間
    • 一個程式只能有一個鬧鐘,第二次設定會覆蓋第一次,返回第一次剩餘時間。如果引數為0即取消鬧鐘

pause

  • 使呼叫程式掛起,直至捕捉到一個訊號
  • 只有執行了一個訊號處理程式並返回,pause返回-1

7. 訊號集

  • 概念:表示多個訊號的資料型別
  • 相關函式:
    unix環境高階程式設計(中)-程式篇

8. 訊號遮蔽字

  • 概念:規定了應該被阻塞不能傳遞給該程式的訊號集,以保護不被訊號中斷的程式碼
  • 作用域:僅為單執行緒定義的
  • 函式:
    unix環境高階程式設計(中)-程式篇
  • how引數:
    • SIG_BLOCK:當前訊號遮蔽字和set的並集。新增訊號遮蔽字
    • SIG_UNBLOCK:當前訊號遮蔽字和set補集的交集。解除訊號遮蔽字
    • SIG_SETMASK:訊號遮蔽字被set集合替代

9. sigspending

  • 作用:返回程式中被阻塞的訊號集
  • 原型:
    unix環境高階程式設計(中)-程式篇

10. sigaction

  • 作用:檢查或修改指定訊號相關聯的處理動作。替代早期的signal函式
  • 原型:
    unix環境高階程式設計(中)-程式篇
  • 引數:
    • act:若非空,為要修改的動作
    • oact:若非空,返回上一個動作

11. sigsuspend

  • 作用:恢復程式訊號遮蔽字,使其休眠
  • 原型:
    unix環境高階程式設計(中)-程式篇
  • 引數:sigmask
    • 將程式的訊號遮蔽字設定為由sigmask指定的值
  • 說明
    • 將程式的訊號遮蔽字設定為由sigmask指定的值,在捕捉到一個訊號或發生一個會終止該程式的訊號前,該程式被掛起。如果捕捉到訊號而且從函式返回,則suspend返回,且將訊號遮蔽字還原

12. abort

  • 作用:使異常程式中止
  • 說明:發生SIGABORT訊號給程式,程式不能忽略此訊號。訊號處理函式執行清理操作,具體由實現來決定,包括:清洗輸出流,刪除臨時檔案,關閉流等

13. sleep

  • 作用:是呼叫程式被掛起
  • 原型:
    unix環境高階程式設計(中)-程式篇
  • 被喚醒的情況
    • 時間超過引數的時間
    • 程式捕獲到一個訊號,並從訊號處理函式返回

五. 執行緒

1. 執行緒的概念

  • 程式中獨自處理任務的一個控制單元
  • 執行緒包含的資訊:
    • 程式的所有資源,包括程式文字,程式全域性記憶體,堆記憶體,棧,檔案描述符
    • 自身的資訊:執行緒id,暫存器值,棧,排程優先順序和策略,訊號遮蔽字,errorno變數以及執行緒私有資料
  • 多執行緒的好處:
    • 每種事件型別分配單獨的執行緒,能簡化非同步事件程式碼。每個執行緒內部是同步的。
    • 要實現記憶體和檔案描述符的共享,使用多程式是很複雜的。多執行緒天生具備該功能
    • 任務並行,提高吞吐率
    • 介面互動程式,使用多執行緒可以改善響應時間
  • 說明:即使在單核cpu上也是可以很好的使用多執行緒,並不是只有多核cpu才能使用多執行緒

2. 執行緒標識

  • 執行緒id:執行緒的唯一標識
  • 表示:pthread_t資料型別,各個作業系統有不同的具體實現,linux下為無符號的長整形
  • 執行緒id比較:不能用簡單的數值比較,使用pthread_equal函式
  • 獲取ID:pthread_self函式

3. 執行緒建立

  • 函式:pthread_create
  • 原型:
    unix環境高階程式設計(中)-程式篇
  • 引數:
    • tidp:返回的建立執行緒的執行緒id
    • attr:執行緒屬性,設定為NULL表示使用預設執行緒屬性
    • start_rin:執行緒執行入口函式
    • arg:執行緒執行函式的引數,多個引數必須以結構體的方式傳入
  • 執行順序:建立時並不能保證哪個執行緒會先執行

4. 執行緒中止

4.1 執行緒中止的情況

  • 程式中任意一個執行緒呼叫exit,_exit或_Exit中的任意一個都會使整個程式中止
  • 單個執行緒可以通過以下方式退出,而不用結束整個程式
    • 執行緒從啟動例程中返回,返回值為執行緒退出碼
    • 執行緒被同一程式的其他執行緒取消:pthread_cancel
    • 呼叫pthread_exit函式,引數為返回值

4.2 獲取執行緒中止狀態

  • 原型:
    unix環境高階程式設計(中)-程式篇
  • 說明:呼叫該函式的執行緒將阻塞,直到第一個引數指定的執行緒中止
  • 引數:
    • thread:
    • rval_ptr:
      • 如果執行緒處理函式通過return返回,該值為return的值
      • 如果執行緒通過pthread_exit返回,該值為返回的值
      • 如果執行緒被取消,該值為PTHRREAD_CANCELED
      • 如果該值自己設定為NULL,表示不想獲取退出狀態

4.3 設定執行緒清理處理程式

unix環境高階程式設計(中)-程式篇

5. 程式原語和執行緒原語的對比

unix環境高階程式設計(中)-程式篇

6. 執行緒同步

6.1 互斥量

概述

  • 本質是一把鎖。訪問共享資源前加鎖,訪問完成後釋放鎖。
  • 加鎖後,其他執行緒想訪問將會被阻塞直到鎖被釋放
  • 鎖被釋放時,所有被阻塞執行緒將變成可執行狀態,但只有一個執行緒能搶到鎖,其他執行緒再次被阻塞

相關介面

  • 資料型別:pthread_mutex_t
    unix環境高階程式設計(中)-程式篇
  • 初始化:
    • 靜態分配的互斥量:置為常量PTHREAD_MUTEX_INITIALIZER
    • 動態分配的互斥量:pthread_mutex_init
  • 釋放:
    • pthread_mutex_destroy
  • 加鎖和釋放鎖
    unix環境高階程式設計(中)-程式篇
  • 注意事項:死鎖

6.2 讀寫鎖

概述

  • 也叫共享-獨佔鎖
  • 執行比互斥量更高的並行性
  • 三種狀態:
    • 讀模式加鎖狀態:可多個執行緒佔用
    • 寫模式加鎖狀態:僅一個執行緒佔用
    • 不加鎖狀態

相關介面

  • 資料型別:pthread_rwlock_t
  • 初始化和釋放:
    unix環境高階程式設計(中)-程式篇
  • 加鎖和解鎖:
    unix環境高階程式設計(中)-程式篇

6.3 條件變數

概述

  • 給多個執行緒提供了一個匯合的場所
  • 與互斥量一起使用時,執行執行緒以無競爭的方式等待特定條件發生
  • 條件變數本身由互斥量保護

相關介面

  • 資料型別:pthread_cond_t
  • 初始化:
    • 靜態分配的變數:PTHREAD_COND_INITIALIZER
    • 動態分配的變數:pthread_cond_init
      unix環境高階程式設計(中)-程式篇
  • 加鎖和釋放:
    unix環境高階程式設計(中)-程式篇
  • 喚醒等待條件的執行緒
    unix環境高階程式設計(中)-程式篇

六. 執行緒控制

1. 執行緒屬性

  • 資料結構:pthread_attr_t,結構體內容不可見
  • 屬性包括:
    • detashstate:執行緒的分離狀態屬性
    • guardsize:執行緒棧末尾的警戒緩衝區大小
    • stackaddr:執行緒棧最低地址
    • stacksize:執行緒棧大小
    • 併發度:控制著使用者執行緒可以對映的核心執行緒或程式數目
  • 其他屬性:不在pthread_attr_t中,影響呼叫pthread_cancel時的行為
    • 可取消狀態:pthread_setcancelstate
      • PTHREAD_CANCEL_ENABLE
        • PTHREAD_CANCEL_DISABLE
    • 可取消型別
  • 初始化與釋放:
    unix環境高階程式設計(中)-程式篇

2. 同步屬性

2.1 互斥量屬性

  • 資料結構:pthread_mutexattr_t
  • 初始化和釋放:
    unix環境高階程式設計(中)-程式篇
  • 屬性引數
    • 程式共享屬性
      • PTHREAD_PROCESS_PRIVATE:預設屬性,多個執行緒可訪問同一個同步物件
      • PTHREAD_PROCESS_SHARED:多個程式共享的記憶體區域分配的互斥量可以用於程式同步
    • 型別屬性
      • PTHREAD_MUTEX_NORMAL:正常屬性,不做特殊的錯誤檢查或死鎖檢查
      • PTHREAD_MUTEX_ERRORCHECK:提供錯誤檢查
      • PTHREAD_MUTEX_RECURSIVE:執行進行多次加鎖
      • PTHREAD_MUTEX_DEFAULT:請求預設語義,可以對映為其他型別
        unix環境高階程式設計(中)-程式篇

2.2 讀寫鎖屬性

  • 資料結構:pthread_rwlockattr_t
  • 初始化和釋放:
    unix環境高階程式設計(中)-程式篇
  • 屬性引數:
    • 程式共享屬性:同互斥量屬性

2.3 條件變數屬性

  • 資料結構:pthread_condattr_t
  • 初始化和釋放:
    unix環境高階程式設計(中)-程式篇
  • 屬性引數:
    • 程式共享屬性:同互斥量屬性

3. 執行緒安全

  • 執行緒安全:一個函式在同一時間可以被多個執行緒安全的呼叫。或者,一個函式對多個執行緒來說是可重入的。

4. 執行緒私有資料

4.1 執行緒私有資料的分配-建立鍵

  • 建立與該資料關聯的鍵,用於對執行緒私有資料對訪問權
    • 第二個引數:為該鍵關聯對解構函式,解構函式引數為地址
      unix環境高階程式設計(中)-程式篇
  • 該鍵可以被程式中對所有執行緒使用,但每個執行緒把這個鍵與不同的私有資料地址進行關聯
  • 執行緒可以為執行緒私有資料分配多個鍵
  • 安全的建立鍵:呼叫pthread_once函式,將建立鍵的函式作為引數傳入

4.2 鍵與執行緒私有資料的關聯

unix環境高階程式設計(中)-程式篇

4.2 鍵與執行緒私有資料的取消

unix環境高階程式設計(中)-程式篇

5. 執行緒與訊號

  • 每個訊號有自己的訊號遮蔽字,但是訊號處理程式是共享的。意味著單個訊號修改了某個訊號相關的處理行為,其他執行緒必須共享這個行為
  • 設定執行緒訊號遮蔽字:pthread_sigmask
  • 等待訊號發生:sigwait
  • 發生訊號到執行緒:pthread_kill
  • linux執行緒是以獨立程式實現的。因此遇到訊號時行為與其他系統不同

6. 執行緒與fork

  • 執行緒呼叫fork時,為子程式建立整個程式地址空間的副本,繼承父程式的互斥量,讀寫鎖和條件變數的狀態
  • fork返回後,如果不是立馬呼叫exec,需要清理鎖狀態:pthread_atfork函式可以做到
  • 子程式內部只包含一個執行緒副本:父程式中呼叫fork函式的執行緒

7. 執行緒與io

  • pread和pwrite作為原子操作,可以解決併發執行緒對同一檔案進行讀寫操作對問題

七. 守護程式

1. 特徵

  • 守護程式沒有終端,在後臺執行的一種生存期較長的程式
  • 大多數都以超級使用者特權執行
  • 大多數守護程式的父程式是init程式

2. 系統守護程式

  • keventd守護程式: 在核心中執行計劃執行函式提供程式上下文
  • kapmd守護程式:為計算機高階電源管理提供支援
  • kswapd守護程式:頁面調出守護程式,將髒頁低速寫到磁碟以回收,用於支援虛擬子系統
  • bdflush和kupdated:將快取記憶體到資料沖洗到磁碟上
  • portmap:埠對映守護程式
  • syslogd:記錄日誌訊息
  • inetd,xinetd:監聽系統網路介面,接收網路服務請求
  • nfsd,lockd,rpciod:提供網路檔案系統的支援
  • crond:定時任務

3. 程式設計規則

  • 呼叫umask將檔案模式建立遮蔽字設定為0
  • 呼叫fork,使父程式退出
  • 呼叫setsid,建立新的會話,使得新程式:
    • 成為新會話的首程式
    • 成為一個新程式組的組長程式
    • 沒有控制終端
  • 將當前工作目錄更改為根目錄
  • 關閉不再需要的檔案描述符
  • 某些守護程式開啟/dev/null,使其具有檔案描述符0,1,2

4. 出錯記錄

4.1 守護程式日誌的來源

  • 核心例程呼叫log函式,任何一個使用者程式通過開啟然後讀/dev/klog裝置就可以讀取這些資訊
  • 大多數守護程式呼叫syslog(3)函式產生日誌訊息,這些訊息發生至/dev/log(udp網路程式設計方式傳送)
  • 在此主機上的使用者程式,或通過網路連線的其他主機的使用者程式可將日誌訊息發至UDP埠514

4.2 守護程式日誌處理

  • syslogd守護程式讀取這三種格式的日誌檔案。然後根據配置檔案/etc/syslog.conf,決定將不同型別的日誌送往何處

4.2 日誌

unix環境高階程式設計(中)-程式篇

5. 守護程式的慣例

  • 若守護程式使用鎖檔案(為了建立唯一守護程式),那麼該檔案通常放在/var/run/name.pid中
  • 若守護程式支援配置選項,配置檔案通常放在/etc/name.conf
  • 守護程式可以用命令列啟動,但通常是系統初始化指令碼之一(/etc/rc*或/etc/init.d/*啟動。要使守護程式重啟,可在/etc/inittab中為該守護程式新增_respawn記錄項
  • 配置檔案啟動後不再會被讀取,為避免修改配置檔案重啟,某些守護程式會捕捉SIGHUP訊號,然後重讀配置檔案

相關文章