1. 程式人生 > 其它 >Linux多程序開發

Linux多程序開發

一、程序概述

程式是包含一系列資訊的檔案,這些資訊描述瞭如何在執行時建立一個程序

  • 二進位制格式標誌:核心利用此資訊來解釋檔案中的其他資訊。ELF可執行連結格式
  • 機器語言指令:對程式演算法進行編碼
  • 程式入口地址
  • 資料:變數的初始值 和 字面量值(如字串)
  • 符號表及重定位表:描述程式中函式和變數的位置及名稱。用於除錯和執行時的符號解析(動態連結)等。
  • 共享庫和動態連結資訊
  • 其他資訊:如,如何建立程序

程序是正在執行的程式的例項。在作業系統中,程序既是基本的分配單元,也是基本的執行單元。

從核心的角度看,程序分為使用者記憶體空間和核心資料結構。核心中記錄了:程序的標識號IDs,虛擬記憶體表、開啟檔案的描述符表、訊號傳遞及處理的有關資訊、程序資源使用及限制、當前工作目錄等。

併發和並行

並行(parallel):在同一時刻,有多條指令在多個處理器上同時執行

併發(concurrency):同一時刻只能有一條指令執行。一般是通過時間片輪轉的方式,達到巨集觀上具有多個程序同時執行的效果。

程序控制塊(PCB)

Linux核心的程序控制塊是task_struct 結構體

  • 程序id:pid_t, 範圍:0~32767。ppid 父程序id;pgid 程序組id

    

  • 程序的狀態:就緒,執行,阻塞,停止等
  • 程序切換時需要保護和恢復的cpu暫存器
  • 虛擬地址空間的資訊
  • 描述控制終端的資訊
  • 當前工作目錄
  • umask掩碼:使用者許可權
  • 檔案描述符表
  • 訊號表
  • 使用者id 和 組 id
  • 會話(Session)和程序組
  • 程序可以使用的資源上限(Resource Limit)

程序相關命令

檢視程序

  ps aux / ajx

  a:all的縮寫,顯示終端上的所有程序

  u:顯示程序的詳細資訊

  x:顯示沒有控制終端的程序

  j:顯示與作業控制相關的資訊

STAT引數意義

  • D:不可中斷
  • R:running
  • S :休眠
  • T :追蹤
  • Z :殭屍
  • X :死掉的程序
  • < :高優先順序
  • N :低優先順序
  • s :包含子程序
  • + :前臺程序

top:顯示實時程序動態;

 -d 指定顯示資訊更新的時間間隔,

在 top 命令 執行後,可以按以下按鍵對顯示的結果進行排序:

  ⚫ M 根據記憶體使用量排序

  ⚫ P 根據 CPU 佔有率排序

  ⚫ T 根據程序執行時間長短排序

  ⚫ U 根據使用者名稱來篩選程序

  ⚫ K 輸入指定的 PID 殺死程序

kill -9 pid :殺死程序

 

二、程序建立

#include <sys/types.h> #include <unistd.h>
pid_t fork(void);     函式的作用:用於建立子程序。     返回值:         fork()的返回值會返回兩次。一次是在父程序中,一次是在子程序中。         在父程序中返回建立的子程序的ID,         在子程序中返回0         如何區分父程序和子程序:通過fork的返回值。         在父程序中返回-1,表示建立子程序失敗,並且設定errno   失敗原因:程序號不足;系統記憶體不足
    父子程序之間的關係:     區別:         1.fork()函式的返回值不同             父程序中: >0 返回的子程序的ID             子程序中: =0         2.pcb中的一些資料             當前的程序的id pid             當前的程序的父程序的id ppid             訊號集
    共同點:         某些狀態下:子程序剛被創建出來,還沒有執行任何的寫資料的操作             - 使用者區的資料             - 檔案描述符表         父子程序對變數是不是共享的?         - 剛開始的時候,是一樣的,共享的。如果修改了資料,不共享了。         - 讀時共享(子程序被建立,兩個程序沒有做任何的寫的操作),寫時拷貝。    

GDB多程序除錯

使用 GDB 除錯的時候,GDB 預設只能跟蹤一個程序,可以在 fork 函式呼叫之前,通 過指令設定 GDB 除錯工具跟蹤父程序或者是跟蹤子程序,預設跟蹤父程序。 設定除錯父程序或者子程序:set follow-fork-mode [parent(預設)| child] 設定除錯模式:set detach-on-fork [on | off] 預設為 on,表示除錯當前程序的時候,其它的程序繼續執行,如果為 off,除錯當前進 程的時候,其它程序被 GDB 掛起。 檢視除錯的程序:info inferiors 切換當前除錯的程序:inferior id 使程序脫離 GDB 除錯:detach inferiors id

exec函式族

exec 函式族的作用是根據指定的檔名找到可執行檔案,並用它來取代呼叫程序的 內容,換句話說,就是在呼叫程序內部執行一個可執行檔案。

 例子

#include <unistd.h> int execl(const char *path, const char *arg, ...);     - 引數:         - path:需要指定的執行的檔案的路徑或者名稱             a.out /home/nowcoder/a.out 推薦使用絕對路徑             ./a.out hello world
        - arg:是執行可執行檔案所需要的引數列表             第一個引數一般沒有什麼作用,為了方便,一般寫的是執行的程式的名稱             從第二個引數開始往後,就是程式執行所需要的的引數列表。             引數最後需要以NULL結束(哨兵)
    - 返回值:         只有當呼叫失敗,才會有返回值,返回-1,並且設定errno         如果呼叫成功,沒有返回值。   int execlp(const char *file, const char *arg, ... );     - 會到環境變數中查詢指定的可執行檔案,如果找到了就執行,找不到就執行不成功。     - 引數:         - file:需要執行的可執行檔案的檔名             a.out             ps
        - arg:是執行可執行檔案所需要的引數列表             第一個引數一般沒有什麼作用,為了方便,一般寫的是執行的程式的名稱             從第二個引數開始往後,就是程式執行所需要的的引數列表。             引數最後需要以NULL結束(哨兵)
    - 返回值:         只有當呼叫失敗,才會有返回值,返回-1,並且設定errno         如果呼叫成功,沒有返回值。

    int execv(const char *path, char *const argv[]);     argv是需要的引數的一個字串陣列     char * argv[] = {"ps", "aux", NULL};     execv("/bin/ps", argv);
    int execve(const char *filename, char *const argv[], char *const envp[]);     char * envp[] = {"/home/nowcoder", "/home/bbb", "/home/aaa"};

三、程序退出、孤兒程序、殭屍程序

 孤兒程序(Orphan Process)

定義:父程序執行結束,子程序還在執行

核心就把孤兒程序的父程序設定為 init ,而 init 程序會迴圈地 wait() 它的已經退出的子程序。因此孤兒程序並不會有什麼危害。

殭屍程序(Zombie)

子程序核心區的pcb資訊需要父程序釋放,若父程序一直執行,未釋放該資訊,則會形成殭屍程序。

殭屍程序無法被kill -9 殺死

程序回收

父程序可以使用wait或waitpid得到它的退出狀態同時徹底清除掉這個程序。

區別在於,wait() 函式會阻塞, waitpid() 可以設定不阻塞,waitpid() 還可以指定等待哪個子程序結束。

一次wait或waitpid呼叫只能清理一個子程序,清理多個子程序應使用迴圈。

#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *wstatus);     功能:等待任意一個子程序結束,如果任意一個子程序結束了,次函式會回收子程序的資源。     引數:int *wstatus         程序退出時的狀態資訊,傳入的是一個int型別的地址,傳出引數。     返回值:         - 成功:返回被回收的子程序的id         - 失敗:-1 (所有的子程序都結束,呼叫函式失敗)
呼叫wait函式的程序會被掛起(阻塞),直到它的一個子程序退出或者收到一個不能被忽略的訊號時才被喚醒(相當於繼續往下執行) 如果沒有子程序了,函式立刻返回,返回-1;如果子程序都已經結束了,也會立即返回,返回-1. #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *wstatus, int options);     功能:回收指定程序號的子程序,可以設定是否阻塞。     引數:         - pid:             pid > 0 : 某個子程序的pid             pid = 0 : 回收當前程序組的所有子程序                 pid = -1 : 回收所有的子程序,相當於 wait()  (最常用)             pid < -1 : 某個程序組的組id的絕對值,回收指定程序組中的子程序         - options:設定阻塞或者非阻塞             0 : 阻塞             WNOHANG : 非阻塞         - 返回值:             > 0 : 返回子程序的id             = 0 : options=WNOHANG, 表示還有子程序或者             = -1 :錯誤,或者沒有子程序了

退出資訊相關巨集函式

四、程序間通訊

程序是獨立的資源分配單位,因此,程序不能直接訪問另一個程序的資源。而程序執行時有進行資訊互動與狀態傳遞的需求(IPC :Inter Precesses Communication)

目的:資料傳輸;通知事件;資源共享;程序控制(有些程序希望完全控制另一個程序的執行(如 Debug 程序),此時控制程序希望能夠攔截另一個程序的所有陷入和異常,並能夠及時知道它的狀態改變。)

管道

管道是一個在核心記憶體中維護的緩衝區。可以進行讀 / 寫操作。匿名管道沒有檔案實體,有名管道有檔案實體,但不儲存資料。可以按照操作檔案的方式操作管道。

管道是位元組流,順序傳遞,讀取順序與寫入順序一致

管道資料的傳遞是單向的,一端寫入,一端讀取;管道是半雙工的。

從管道讀資料是一次性的,無法使用lseek函式來隨機訪問資料。

讀管道:

  • 管道中有資料,read返回實際讀到的位元組數。
  • 管道中無資料:
    • 寫端被全部關閉,read返回0(相當於讀到檔案的末尾)
    • 寫端沒有完全關閉,read阻塞等待

寫管道:

  • 管道讀端全部被關閉,程序異常終止(程序收到SIGPIPE訊號)
  • 管道讀端沒有全部關閉:
    • 管道已滿,write阻塞
    • 管道沒有滿,write將資料寫入,並返回實際寫入的位元組數

匿名管道

只能在具有公共祖先的程序間使用。

pipefd【0】是讀端,1是寫端

有名管道

有名管道提供了一個路徑名與之關聯,以FIFO的檔案行使存在於檔案系統中。

記憶體對映

記憶體對映(Memory-mapped I/O)是將磁碟檔案的資料對映到記憶體,使用者通過修改 記憶體就能修改磁碟檔案。

#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);     - 功能:將一個檔案或者裝置的資料對映到記憶體中     - 引數:         - void *addr: NULL, 由核心指定         - length : 要對映的資料的長度,這個值不能為0。建議使用檔案的長度。                 獲取檔案的長度:stat lseek         - prot : 對申請的記憶體對映區的操作許可權             -PROT_EXEC :可執行的許可權             -PROT_READ :讀許可權             -PROT_WRITE :寫許可權             -PROT_NONE :沒有許可權             要操作對映記憶體,必須要有讀的許可權。             PROT_READ、PROT_READ|PROT_WRITE         - flags :             - MAP_SHARED : 對映區的資料會自動和磁碟檔案進行同步,程序間通訊,必須要設定這個選項             - MAP_PRIVATE :不同步,記憶體對映區的資料改變了,對原來的檔案不會修改,會重新建立一個新的檔案。(copy on write)         - fd: 需要對映的那個檔案的檔案描述符             - 通過open得到,open的是一個磁碟檔案             - 注意:檔案的大小不能為0,open指定的許可權不能和prot引數有衝突。                 prot: PROT_READ                open:只讀/讀寫                 prot: PROT_READ | PROT_WRITE   open:讀寫         - offset:偏移量,一般不用。必須指定的是4k的整數倍,0表示不偏移。     - 返回值:返回建立的記憶體的首地址         失敗返回MAP_FAILED,(void *) -1
int munmap(void *addr, size_t length);     - 功能:釋放記憶體對映     - 引數:         - addr : 要釋放的記憶體的首地址         - length : 要釋放的記憶體的大小,要和mmap函式中的length引數的值一樣。

常見問題

1.mmap什麼情況下會呼叫失敗?
- 第二個引數:length = 0
- 第三個引數:prot
- 只指定了寫許可權
- prot PROT_READ | PROT_WRITE
第5個引數fd 通過open函式時指定的 O_RDONLY / O_WRONLY

2.可以open的時候O_CREAT一個新檔案來建立對映區嗎?
- 可以的,但是建立的檔案的大小如果為0的話,肯定不行
- 可以對新的檔案進行擴充套件
- lseek()
- truncate()

3.mmap後關閉檔案描述符,對mmap對映有沒有影響?
int fd = open("XXX");
mmap(,,,,fd,0);
close(fd);
對映區還存在,建立對映區的fd被關閉,沒有任何影響。

4.對ptr越界操作會怎樣?
越界操作操作的是非法的記憶體 -> 段錯誤

訊號

訊號是事件發生時對程序的通知機制,也稱為軟體中斷,是一種非同步通訊的方式。(同步需要等待,非同步不需要等待)

特點:簡單;資訊少;滿足特定條件才傳送;優先順序較高

檢視訊號列表:kill -l   前31個為常規訊號,其餘為實時訊號

  • 2 SIGINT crtl + c時產生   終止程序
  • 3 SIGQUIT crtl + \時產生  終止程序
  • 9 SIGKILL  終止程序,無法被忽略,處理,阻塞
  • 11 SIGSEGV 無效的記憶體訪問(段錯誤) 終止程序併產生core檔案
  • 13 SIGPIPE 管道破裂,向沒有讀端的管道寫資料 終止程序
  • 17 SIGCHLD 子程序結束(收到SIGSTOP; SIGCONT)時傳送給父程序     忽略
  • 18 SIGCONT 如果程序已停止,則使其繼續執行  繼續/忽略
  • 19 SIGSTOP 停止程序,無法被忽略,處理,阻塞。  暫停程序

檢視訊號的詳細資訊: man 7 signal

訊號的物鍾預設處理動作

    • Term  終止程序
    • Ign        忽略
    • Core 終止程序,並生成core檔案
    • Stop        暫停
    • Cont        繼續執行被暫停的程序

訊號的狀態:產生;未決;遞達

sigkill 和 sigstop  無法被捕捉,阻塞或者忽略

#include <sys/types.h> #include <signal.h>
int kill(pid_t pid, int sig);     - 功能:給任何的程序或者程序組pid, 傳送任何的訊號 sig     - 引數:         - pid :             > 0 : 將訊號傳送給指定的程序             = 0 : 將訊號傳送給當前的程序組             = -1 : 將訊號傳送給每一個有許可權接收這個訊號的程序             < -1 : 這個pid=某個程序組的ID取反 (-12345)         - sig : 需要傳送的訊號的編號或者是巨集值,0表示不傳送任何訊號
    kill(getppid(), 9);     kill(getpid(), 9);     int raise(int sig);     - 功能:給當前程序傳送訊號     - 引數:         - sig : 要傳送的訊號     - 返回值:         - 成功 0         - 失敗 非0     kill(getpid(), sig);  
void abort(void);     - 功能: 傳送SIGABRT訊號給當前的程序,殺死當前程序     kill(getpid(), SIGABRT);   #include <unistd.h> unsigned int alarm(unsigned int seconds);     - 功能:設定定時器(鬧鐘)。函式呼叫,開始倒計時,當倒計時為0的時候,             函式會給當前的程序傳送一個訊號:SIGALARM     - 引數:         seconds: 倒計時的時長,單位:秒。如果引數為0,定時器無效(不進行倒計時,不發訊號)。                 取消一個定時器,通過alarm(0)。     - 返回值:         - 之前沒有定時器,返回0         - 之前有定時器,返回之前的定時器剩餘的時間
- SIGALARM :預設終止當前的程序,每一個程序都有且只有唯一的一個定時器。     alarm(10);  -> 返回0     過了1秒     alarm(5);   -> 返回9
alarm(100) -> 該函式是不阻塞的   #include <sys/time.h> int setitimer(int which, const struct itimerval *new_value,                     struct itimerval *old_value);
    - 功能:設定定時器(鬧鐘)。可以替代alarm函式。精度微妙us,可以實現週期性定時     - 引數:         - which : 定時器以什麼時間計時             ITIMER_REAL: 真實時間,時間到達,傳送 SIGALRM   常用             ITIMER_VIRTUAL: 使用者時間,時間到達,傳送 SIGVTALRM             ITIMER_PROF: 以該程序在使用者態和核心態下所消耗的時間來計算,時間到達,傳送 SIGPROF
        - new_value: 設定定時器的屬性                     struct itimerval {      // 定時器的結構體             struct timeval it_interval;  // 每個階段的時間,間隔時間             struct timeval it_value;     // 延遲多長時間執行定時器             };
            struct timeval {        // 時間的結構體                 time_t      tv_sec;     //  秒數                     suseconds_t tv_usec;    //  微秒                 };
        過10秒後,每個2秒定時一次                 - old_value :記錄上一次的定時的時間引數,一般不使用,指定NULL         - 返回值:         成功 0         失敗 -1 並設定錯誤號  

訊號捕捉函式

#include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);     - 功能:設定某個訊號的捕捉行為     - 引數:         - signum: 要捕捉的訊號         - handler: 捕捉到訊號要如何處理             - SIG_IGN : 忽略訊號             - SIG_DFL : 使用訊號預設的行為             - 回撥函式 :  這個函式是核心呼叫,程式設計師只負責寫,捕捉到訊號後如何去處理訊號。             回撥函式:                 - 需要程式設計師實現,提前準備好的,函式的型別根據實際需求,看函式指標的定義                 - 不是程式設計師呼叫,而是當訊號產生,由核心呼叫                 - 函式指標是實現回撥的手段,函式實現之後,將函式名放到函式指標的位置就可以了。
    - 返回值:         成功,返回上一次註冊的訊號處理函式的地址。第一次呼叫返回NULL         失敗,返回SIG_ERR,設定錯誤號         SIGKILL SIGSTOP不能被捕捉,不能被忽略。   #include <signal.h> int sigaction(int signum, const struct sigaction *act,                         struct sigaction *oldact);
    - 功能:檢查或者改變訊號的處理。訊號捕捉     - 引數:         - signum : 需要捕捉的訊號的編號或者巨集值(訊號的名稱)         - act :捕捉到訊號之後的處理動作         - oldact : 上一次對訊號捕捉相關的設定,一般不使用,傳遞NULL     - 返回值:         成功 0         失敗 -1
    struct sigaction {     // 函式指標,指向的函式就是訊號捕捉到之後的處理函式     void     (*sa_handler)(int);     // 不常用     void     (*sa_sigaction)(int, siginfo_t *, void *);     // 臨時阻塞訊號集,在訊號捕捉函式執行過程中,臨時阻塞某些訊號。     sigset_t   sa_mask;     // 使用哪一個訊號處理對捕捉到的訊號進行處理     // 這個值可以是0,表示使用sa_handler,也可以是SA_SIGINFO表示使用sa_sigaction     int        sa_flags;     // 被廢棄掉了     void     (*sa_restorer)(void); };  

訊號集

在 PCB 中有兩個非常重要的訊號集(sigset_t,64位)。一個稱之為 “阻塞訊號集” ,另一個稱之為 “未決訊號集” 。這兩個訊號集都是核心使用點陣圖機制來實現的。 作業系統不允許我們直接對這兩個訊號集進行位操作。而需自定義另外一個集合,藉助訊號集操作函式 來對 PCB 中的這兩個訊號集進行修改。

empty:設定為0

fill:設定為1

add:新增

del:刪除

ismember:判斷是否是成員

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);     - 功能:將自定義訊號集中的資料設定到核心中(設定阻塞,解除阻塞,替換)     - 引數:         - how : 如何對核心阻塞訊號集進行處理             SIG_BLOCK: 將使用者設定的阻塞訊號集新增到核心中,核心中原來的資料不變                 假設核心中預設的阻塞訊號集是mask, mask | set             SIG_UNBLOCK: 根據使用者設定的資料,對核心中的資料進行解除阻塞                 mask &= ~set             SIG_SETMASK:覆蓋核心中原來的值                 - set :已經初始化好的使用者自定義的訊號集         - oldset : 儲存設定之前的核心中的阻塞訊號集的狀態,可以是 NULL     - 返回值:         成功:0         失敗:-1             設定錯誤號:EFAULT、EINVAL
int sigpending(sigset_t *set);     - 功能:獲取核心中的未決訊號集     - 引數:set,傳出引數,儲存的是核心中的未決訊號集中的資訊。  

共享記憶體

在使用者記憶體空間中,建立共享區,允許多個程序共享,不需要核心參與;因此速度較快。

◼ 呼叫 shmget() 建立一個新共享記憶體段或取得一個既有共享記憶體段的識別符號(即由其 他程序建立的共享記憶體段)。這個呼叫將返回後續呼叫中需要用到的共享記憶體識別符號。

◼ 使用 shmat() 來附上共享記憶體段,即使該段成為呼叫程序的虛擬記憶體的一部分。

◼ 此刻在程式中可以像對待其他可用記憶體那樣對待這個共享記憶體段。為引用這塊共享記憶體, 程式需要使用由 shmat() 呼叫返回的 addr 值,它是一個指向程序的虛擬地址空間 中該共享記憶體段的起點的指標。

◼ 呼叫 shmdt() 來分離共享記憶體段。在這個呼叫之後,程序就無法再引用這塊共享記憶體 了。這一步是可選的,並且在程序終止時會自動完成這一步。

◼ 呼叫 shmctl() 來刪除共享記憶體段。只有噹噹前所有附加記憶體段的程序都與之分離之 後記憶體段才會銷燬。只有一個程序需要執行這一步。

共享記憶體相關的函式 #include <sys/ipc.h> #include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);     - 功能:建立一個新的共享記憶體段,或者獲取一個既有的共享記憶體段的標識。         新建立的記憶體段中的資料都會被初始化為0     - 引數:         - key : key_t型別是一個整形,通過這個找到或者建立一個共享記憶體。                 一般使用16進製表示,非0值         - size: 共享記憶體的大小         - shmflg: 屬性             - 訪問許可權             - 附加屬性:建立/判斷共享記憶體是不是存在                 - 建立:IPC_CREAT                 - 判斷共享記憶體是否存在: IPC_EXCL , 需要和IPC_CREAT一起使用                     IPC_CREAT | IPC_EXCL | 0664         - 返回值:             失敗:-1 並設定錯誤號             成功:>0 返回共享記憶體的引用的ID,後面操作共享記憶體都是通過這個值。

void *shmat(int shmid, const void *shmaddr, int shmflg);     - 功能:和當前的程序進行關聯     - 引數:         - shmid : 共享記憶體的標識(ID),由shmget返回值獲取         - shmaddr: 申請的共享記憶體的起始地址,指定NULL,核心指定         - shmflg : 對共享記憶體的操作             - 讀 : SHM_RDONLY, 必須要有讀許可權             - 讀寫: 0     - 返回值:         成功:返回共享記憶體的首(起始)地址。  失敗(void *) -1

int shmdt(const void *shmaddr);     - 功能:解除當前程序和共享記憶體的關聯     - 引數:         shmaddr:共享記憶體的首地址     - 返回值:成功 0, 失敗 -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);     - 功能:對共享記憶體進行操作。刪除共享記憶體,共享記憶體要刪除才會消失,建立共享記憶體的進行被銷燬了對共享記憶體是沒有任何影響。     - 引數:         - shmid: 共享記憶體的ID         - cmd : 要做的操作             - IPC_STAT : 獲取共享記憶體的當前的狀態             - IPC_SET : 設定共享記憶體的狀態             - IPC_RMID: 標記共享記憶體被銷燬         - buf:需要設定或者獲取的共享記憶體的屬性資訊             - IPC_STAT : buf儲存資料             - IPC_SET : buf中需要初始化資料,設定到核心中             - IPC_RMID : 沒有用,NULL
key_t ftok(const char *pathname, int proj_id);     - 功能:根據指定的路徑名,和int值,生成一個共享記憶體的key     - 引數:         - pathname:指定一個存在的路徑             /home/nowcoder/Linux/a.txt             /         - proj_id: int型別的值,但是這系統呼叫只會使用其中的1個位元組                    範圍 : 0-255  一般指定一個字元 'a'

問題1:作業系統如何知道一塊共享記憶體被多少個程序關聯?     - 共享記憶體維護了一個結構體struct shmid_ds 這個結構體中有一個成員 shm_nattch     - shm_nattach 記錄了關聯的程序個數
問題2:可不可以對共享記憶體進行多次刪除 shmctl     - 可以的     - 因為shmctl 標記刪除共享記憶體,不是直接刪除     - 什麼時候真正刪除呢?         當和共享記憶體關聯的程序數為0的時候,就真正被刪除     - 當共享記憶體的key為0的時候,表示共享記憶體被標記刪除了         如果一個程序和共享記憶體取消關聯,那麼這個程序就不能繼續操作這個共享記憶體。也不能進行關聯。
    共享記憶體和記憶體對映的區別     1.共享記憶體可以直接建立,記憶體對映需要磁碟檔案(匿名對映除外)     2.共享記憶體效果更高     3.記憶體         所有的程序操作的是同一塊共享記憶體。         記憶體對映,每個程序在自己的虛擬地址空間中有一個獨立的記憶體。     4.資料安全         - 程序突然退出             共享記憶體還存在             記憶體對映區消失         - 執行程序的電腦宕機,宕機了             資料存在在共享記憶體中,沒有了             記憶體對映區的資料 ,由於磁碟檔案中的資料還在,所以記憶體對映區的資料還存在。
    5.生命週期         - 記憶體對映區:程序退出,記憶體對映區銷燬         - 共享記憶體:程序退出,共享記憶體還在,標記刪除(所有的關聯的程序數為0),或者關機             如果一個程序退出,會自動和共享記憶體進行取消關聯。   共享記憶體操作命令

五、守護程序

控制終端

在UNIX系統中,使用者通過中斷系統登入後得到一個shell程序,這個終端就是shell程序的控制終端,該資訊儲存在PCB中。

預設情況下,標準輸入、標準輸出、標準錯誤都指向控制終端。

程序組

程序組是一組相關程序的集合, 會話是一組相關程序組的集合。程序組和會話是為支援 shell 作業控制而定義的抽象概念,使用者通過 shell 能夠互動式地在前臺或後臺執行命令。

進行組由一個或多個共享同一程序組識別符號(PGID)的程序組成。一個程序組擁有一 個程序組首程序,該程序是建立該組的程序,其程序 ID 為該程序組的 ID,新程序 會繼承其父程序所屬的程序組 ID。

會話

一個會話中的所有程序共享單個控制終端。控制終端會在會話首程序首次開啟一個終 端裝置時被建立。一個終端最多可能會成為一個會話的控制終端。

當控制終端的連線建立起來之後,會話首程序會成為該終端的控制程序

守護程序

守護程序(Daemon Process),也就是通常說的 Daemon 程序(精靈程序),是 Linux 中的後臺服務程序。它是一個生存期較長的程序,通常獨立於控制終端並且周 期性地執行某種任務或等待處理某些發生的事件。一般採用以 d 結尾的名字。

特點:生命週期長,一直執行到系統關閉;在後臺執行且不擁有控制終端。

建立步驟