linux進(jìn)程

1 進(jìn)程介紹
  1.1 進(jìn)程和程序
  1.2 進(jìn)程層次結(jié)構(gòu)
  1.3 進(jìn)程狀態(tài)
2 進(jìn)程控制塊
  2.1 進(jìn)程狀態(tài)
  2.2 進(jìn)程標(biāo)志符
  2.3 進(jìn)程之間親屬關(guān)系
3 進(jìn)程創(chuàng)建與調(diào)度
  3.1 進(jìn)程創(chuàng)建
  3.2 進(jìn)程調(diào)度
4. 進(jìn)程間通信
  4.1 管道
    4.1.1 匿名管道pipe
    4.1.2 有名管道fifo
  4.2 信號(hào)
  4.3 消息隊(duì)列
  4.4 共享內(nèi)存
  4.5 信號(hào)量
  4.6 套接字
5 進(jìn)程相關(guān)命令
  5.1 ps/pstree
  5.2 kill/killall
  5.3 ipcs/ipcrm
6. 其他
  6.1 進(jìn)程/線程context switch消耗
7 參考

1 進(jìn)程介紹

1.1 進(jìn)程和程序

所謂進(jìn)程是由正文段(text)诲侮、用戶數(shù)據(jù)段(user segment)以及系統(tǒng)數(shù)據(jù)段(system segment)共同組成的一個(gè)執(zhí)行環(huán)境, 具體如下圖所示:

進(jìn)程的組成

1.2 進(jìn)程層次結(jié)構(gòu)

Linux所有進(jìn)程形成了一顆完整的進(jìn)程樹漾脂,Linux在啟動(dòng)時(shí)就創(chuàng)建一個(gè)稱為init的特殊進(jìn)程铡溪,顧名思義毅否,它是起始進(jìn)程矮固,是祖先,以后誕生的所有進(jìn)程都是它的后代——或是它的兒子氯质,或是它的孫子募舟。init進(jìn)程為每個(gè)終端(tty)創(chuàng)建一個(gè)新的管理進(jìn)程,這些進(jìn)程在終端上等待著用戶的登錄闻察。當(dāng)用戶正確登錄后拱礁,系統(tǒng)再為每一個(gè)用戶啟動(dòng)一個(gè)shell進(jìn)程,由shell進(jìn)程等待并接受用戶輸入的命令信息辕漂,下圖是一顆進(jìn)程樹示意圖:

進(jìn)程樹

通過 pstree 命令或者 ps -ejH 命令可以查看Linux進(jìn)程樹呢灶。
  如果子進(jìn)程所屬的父進(jìn)程結(jié)束了,這個(gè)子進(jìn)程就會(huì)變成"孤兒進(jìn)程"钮热,init進(jìn)程會(huì)負(fù)責(zé)收養(yǎng)孤兒進(jìn)程填抬。
  如果子進(jìn)程退出的信號(hào)父進(jìn)程沒有通過wait或者waitpid處理,那么子進(jìn)程會(huì)釋放基本所有占用的資源隧期,除了保留PCB(見后文)之外飒责,這樣的進(jìn)程會(huì)變成"僵尸進(jìn)程"

2 進(jìn)程控制塊

Linux進(jìn)程控制塊PCB描述了進(jìn)程的狀態(tài)、優(yōu)先級(jí)仆潮、地址空間以及可訪問的文件等等信息宏蛉,Linux上使用了一個(gè)task_struct結(jié)構(gòu)體描述了這些信息,這個(gè)結(jié)構(gòu)體包含的詳細(xì)信息如下:

  1. 狀態(tài)信息:描述進(jìn)程動(dòng)態(tài)的變化性置,如就緒態(tài)拾并,等待態(tài),僵死態(tài)等
  2. 鏈接信息: 描述進(jìn)程的親屬關(guān)系鹏浅,如祖父進(jìn)程嗅义,父進(jìn)程,養(yǎng)父進(jìn)程隐砸,子進(jìn)程之碗,兄進(jìn)程,孫進(jìn)程等
  3. 各種標(biāo)識(shí)符:用簡(jiǎn)單數(shù)字對(duì)進(jìn)程進(jìn)行標(biāo)識(shí)季希,如進(jìn)程標(biāo)識(shí)符褪那,用戶標(biāo)識(shí)符等
  4. 進(jìn)程間通信信息:描述多個(gè)進(jìn)程在同一任務(wù)上協(xié)作工作幽纷,如管道,消息隊(duì)列博敬,共享內(nèi)存友浸,套接字等
  5. 時(shí)間和定時(shí)器信息:描述進(jìn)程在生存周期內(nèi)使用CPU時(shí)間的統(tǒng)計(jì)、計(jì)費(fèi)等信息等
  6. 調(diào)度信息:描述進(jìn)程優(yōu)先級(jí)偏窝、調(diào)度策略等信息
  7. 文件系統(tǒng)信息:對(duì)進(jìn)程使用文件情況進(jìn)行記錄收恢,如文件描述符,系統(tǒng)打開文件表囚枪,用戶打開文件表等
  8. 虛擬內(nèi)存信息:描述每個(gè)進(jìn)程擁有的地址空間派诬,也就是進(jìn)程編譯連接后形成的空間
  9. 處理器環(huán)境信息:描述進(jìn)程的執(zhí)行環(huán)境(處理器的各種寄存器及堆棧等)

在進(jìn)程的整個(gè)生命周期中劳淆,系統(tǒng)(也就是內(nèi)核)總是通過PCB對(duì)進(jìn)程進(jìn)行控制的链沼;當(dāng)系統(tǒng)創(chuàng)建一個(gè)新的進(jìn)程時(shí),就為它建立一個(gè)PCB沛鸵;進(jìn)程結(jié)束時(shí)又收回其PCB括勺,進(jìn)程隨之也消亡。PCB是內(nèi)核中被頻繁讀寫的數(shù)據(jù)結(jié)構(gòu)曲掰,故應(yīng)常駐內(nèi)存疾捍。

2.1 進(jìn)程狀態(tài)

Linux上進(jìn)程狀態(tài)如下描述:

  • 就緒態(tài)(TASK_RUNNING):正在運(yùn)行或準(zhǔn)備運(yùn)行,處于這個(gè)狀態(tài)的所有進(jìn)程組成就緒隊(duì)列
  • 睡眠(或等待)態(tài):分為淺度睡眠態(tài)和深度睡眠態(tài)
  • 淺度睡眠態(tài)(TASK_INTERRUPTIBLE):進(jìn)程正在睡眠(被阻塞)栏妖,等待資源有效時(shí)被喚醒乱豆,不僅如此,也可以由其他進(jìn)程通過信號(hào) 或時(shí)鐘中斷喚醒
  • 深度睡眠態(tài)(TASK_UNINTERRUPTIBLE): 與前一個(gè)狀態(tài)類似吊趾,但其它進(jìn)程發(fā)來的信號(hào)和時(shí)鐘中斷并不能打斷它的熟睡宛裕,處于uninterruptible sleep狀態(tài)的進(jìn)程通常是在等待IO,比如磁盤IO论泛,網(wǎng)絡(luò)IO,其他外設(shè)IO,如果進(jìn)程正在等待的IO在較長(zhǎng)的時(shí)間內(nèi)都沒有響應(yīng)仿耽, 在ps命令看到的是處于-D狀態(tài)的進(jìn)程色徘,除非等待的事件響應(yīng),否則只能重啟系統(tǒng)才能干掉這個(gè)進(jìn)程
  • 暫停狀態(tài)(TASK_STOPPED):進(jìn)程暫停執(zhí)行坟瓢,比如勇边,當(dāng)進(jìn)程接收到如下信號(hào)后,進(jìn)入暫停狀態(tài):
    SIGSTOP-停止進(jìn)程執(zhí)行
    SIGTSTP-從終端發(fā)來信號(hào)停止進(jìn)程
    SIGTTIN-來自鍵盤的中斷
    SIGTTOU-后臺(tái)進(jìn)程請(qǐng)求輸出
  • 僵死狀態(tài)(TASK_ZOMBIE):進(jìn)程執(zhí)行結(jié)束但尚未消亡的一種狀態(tài)折联。此時(shí)粒褒,進(jìn)程已經(jīng)結(jié)束且釋放大部分資源,但尚未釋放其PCB
Linux進(jìn)程狀態(tài)

其中就緒狀態(tài)表示進(jìn)程已經(jīng)分配到除CPU以外的資源崭庸,等CPU調(diào)度它時(shí)就可以馬上執(zhí)行了怀浆。運(yùn)行狀態(tài)就是正在運(yùn)行了谊囚,獲得包括CPU在內(nèi)的所有資源。等待狀態(tài)表示因等待某個(gè)事件而沒有被執(zhí)行执赡,這時(shí)候不耗CPU時(shí)間镰踏,而這個(gè)時(shí)間有可能是等待IO、申請(qǐng)不到足夠的緩沖區(qū)或者在等待信號(hào)沙合。

2.2 進(jìn)程標(biāo)志符

每個(gè)進(jìn)程有進(jìn)程標(biāo)識(shí)符奠伪、用戶標(biāo)識(shí)符、組標(biāo)識(shí)符首懈。進(jìn)程標(biāo)識(shí)符PID是32位的無符號(hào)整數(shù)绊率,它被順序編號(hào):新創(chuàng)建進(jìn)程的PID通常是前一個(gè)進(jìn)程的PID加1。在Linux上允許的最大PID號(hào)是由變量pid_max來指定究履,可以在內(nèi)核編譯的配置界面里配置0x1000和0x8000兩種值滤否,即在4096以內(nèi)或是32768以內(nèi)。當(dāng)內(nèi)核在系統(tǒng)中創(chuàng)建進(jìn)程的PID大于這個(gè)值時(shí)最仑,就必須重新開始使用已閑置的PID號(hào),可以通過cat命令查看系統(tǒng)pid_max的值藐俺。

cat /proc/sys/kernel/pid_max
32768

另外,每個(gè)進(jìn)程都屬于某個(gè)用戶組泥彤。task_struct結(jié)構(gòu)中定義有用戶標(biāo)識(shí)符UID(User Identifier)和組標(biāo)識(shí)符GID(Group Identifier)欲芹。它們同樣是簡(jiǎn)單的數(shù)字,這兩種標(biāo)識(shí)符用于系統(tǒng)的安全控制吟吝。系統(tǒng)通過這兩種標(biāo)識(shí)符控制進(jìn)程對(duì)系統(tǒng)中文件和設(shè)備的訪問菱父。

2.3 進(jìn)程之間親屬關(guān)系

系統(tǒng)創(chuàng)建的進(jìn)程具有父/子關(guān)系。因?yàn)橐粋€(gè)進(jìn)程能創(chuàng)建幾個(gè)子進(jìn)程剑逃,而子進(jìn)程之間有兄弟關(guān)系浙宜。一個(gè)進(jìn)程可能有兩個(gè)父親,一個(gè)為親生父親炕贵,一個(gè)為養(yǎng)父梆奈。因?yàn)楦高M(jìn)程有可能在子進(jìn)程之前銷毀,就得給子進(jìn)程重新找個(gè)養(yǎng)父称开,但大多數(shù)情況下亩钟,生父和養(yǎng)父是相同的。進(jìn)程間親屬關(guān)系如下圖所示:

Linux進(jìn)程關(guān)系圖

3 進(jìn)程創(chuàng)建與調(diào)度

3.1 進(jìn)程創(chuàng)建

Linux首先通過fork()通過拷貝當(dāng)前進(jìn)程創(chuàng)建一個(gè)子進(jìn)程鳖轰。然后清酥,exec()函數(shù)負(fù)責(zé)讀取可執(zhí)行文件并將其載入進(jìn)程的地址空間開始運(yùn)行。
  新進(jìn)程是通過克隆父進(jìn)程(當(dāng)前進(jìn)程)而建立的蕴侣。fork() 和 clone()(用于線程)系統(tǒng)調(diào)用可用來建立新的進(jìn)程焰轻。當(dāng)這兩個(gè)系統(tǒng)調(diào)用結(jié)束時(shí),內(nèi)核在內(nèi)存中為新的進(jìn)程分配新的PCB昆雀,同時(shí)為新進(jìn)程要使用的堆棧分配物理頁辱志。Linux 還會(huì)為新進(jìn)程分配新的進(jìn)程標(biāo)識(shí)符蝠筑。然后,新的PCB地址保存在鏈表中揩懒,而父進(jìn)程的PCB內(nèi)容被復(fù)制到新進(jìn)程的 PCB中什乙。
  在克隆進(jìn)程時(shí),Linux 允許父子進(jìn)程共享相同的資源已球〕剂停可共享的資源包括文件、信號(hào)處理程序和進(jìn)程地址空間等智亮。當(dāng)某個(gè)資源被共享時(shí)忆某,該資源的引用計(jì)數(shù)值會(huì)增加 1,從而只有在兩個(gè)進(jìn)程均終止時(shí)阔蛉,內(nèi)核才會(huì)釋放這些資源弃舒。

3.2 進(jìn)程調(diào)度

CFS(完全公平調(diào)度器)是Linux內(nèi)核2.6.23版本開始采用的進(jìn)程調(diào)度器,它的基本原理是這樣的:設(shè)定一個(gè)調(diào)度周期(sched_latency_ns)馍忽,目標(biāo)是讓每個(gè)進(jìn)程在這個(gè)周期內(nèi)至少有機(jī)會(huì)運(yùn)行一次棒坏,換一種說法就是每個(gè)進(jìn)程等待CPU的時(shí)間最長(zhǎng)不超過這個(gè)調(diào)度周期;然后根據(jù)進(jìn)程的數(shù)量遭笋,大家平分這個(gè)調(diào)度周期內(nèi)的CPU使用權(quán),由于進(jìn)程的優(yōu)先級(jí)即nice值不同徒探,分割調(diào)度周期的時(shí)候要加權(quán)瓦呼;每個(gè)進(jìn)程的累計(jì)運(yùn)行時(shí)間保存在自己的vruntime字段里,哪個(gè)進(jìn)程的vruntime最小就獲得本輪運(yùn)行的權(quán)利测暗。
  新進(jìn)程的vruntime初值的設(shè)置與兩個(gè)參數(shù)有關(guān):

  • sched_child_runs_first:規(guī)定fork之后讓子進(jìn)程先于父進(jìn)程運(yùn)行;
  • sched_features的START_DEBIT位:規(guī)定新進(jìn)程的第一次運(yùn)行要有延遲央串。
      具體實(shí)現(xiàn)時(shí),CFS通過每個(gè)進(jìn)程的虛擬運(yùn)行時(shí)間(vruntime)來衡量哪個(gè)進(jìn)程最值得被調(diào)度碗啄。CFS中的就緒隊(duì)列是一棵以vruntime為鍵值的紅黑樹质和,虛擬時(shí)間越小的進(jìn)程越靠近整個(gè)紅黑樹的最左端。因此稚字,調(diào)度器每次選擇位于紅黑樹最左端的那個(gè)進(jìn)程饲宿,該進(jìn)程的vruntime最小。
      每個(gè)時(shí)鐘周期內(nèi)一個(gè)進(jìn)程的虛擬運(yùn)行時(shí)間是通過下面的方法計(jì)算的:
一次調(diào)度間隔的虛擬運(yùn)行時(shí)間=實(shí)際運(yùn)行時(shí)間*(NICE_0_WEIGHT/權(quán)重)

其中胆描,NICE_0_WEIGHT是nice為0時(shí)的權(quán)重瘫想。也就是說,nice值為0的進(jìn)程實(shí)際運(yùn)行時(shí)間和虛擬運(yùn)行時(shí)間相同昌讲。通過這個(gè)公式可以看到国夜,權(quán)重越大的進(jìn)程獲得的虛擬運(yùn)行時(shí)間越小,那么它將被調(diào)度器所調(diào)度的機(jī)會(huì)就越大短绸。

4. 進(jìn)程間通信

每個(gè)進(jìn)程各自有不同的用戶地址空間车吹,任何一個(gè)進(jìn)程的全局變量在另一個(gè)進(jìn)程中都看不到筹裕,所以進(jìn)程之間要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū)窄驹,進(jìn)程1把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū)饶碘,進(jìn)程2再?gòu)膬?nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機(jī)制稱為進(jìn)程間通信馒吴,如下圖所示:

進(jìn)程間通信

4.1 管道

4.1.1 匿名管道pipe

  • 匿名管道是半雙工的扎运,數(shù)據(jù)只能向一個(gè)方向流動(dòng);需要雙方通信時(shí)饮戳,需要建立起兩個(gè)管道
  • 只能用于父子進(jìn)程或者兄弟進(jìn)程之間(具有親緣關(guān)系的進(jìn)程);
  • 單獨(dú)構(gòu)成一種獨(dú)立的文件系統(tǒng):管道對(duì)于管道兩端的進(jìn)程而言豪治,就是一個(gè)文件,但它不是普通的文件扯罐,它不屬于某種文件系統(tǒng)负拟,而是自立門戶,單獨(dú)構(gòu)成一種文件系統(tǒng)歹河,并且只存在與內(nèi)存中掩浙。
  • 數(shù)據(jù)的讀出和寫入:一個(gè)進(jìn)程向管道中寫的內(nèi)容被管道另一端的進(jìn)程讀出。寫入的內(nèi)容每次都添加在管道緩沖區(qū)的末尾秸歧,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)厨姚。如果管道中沒有數(shù)據(jù),讀操縱將被阻塞键菱;如果管道buffer寫滿了谬墙,寫操縱將會(huì)被阻塞。

管道是基于文件描述符的通信方式经备。當(dāng)一個(gè)管道建立時(shí)拭抬,它會(huì)創(chuàng)建兩個(gè)文件描述符fd[0]和fd[1]。其中fd[0]固定用于讀管道侵蒙,而fd[1]固定用于寫管道,一般文件I/O的函數(shù)都可以用來操作管道造虎。下面是一個(gè)父子進(jìn)程通過匿名管道通信的代碼示例:

#include <unistd.h>
#include <sys/types.h>
main()
{
    int pipe_fd[2];
    pid_t pid;
    char r_buf[4];
    char** w_buf[256];
    int childexit=0;
    int i;
    int cmd;
    
    memset(r_buf,0,sizeof(r_buf));
    if(pipe(pipe_fd)<0)
    {
        printf("pipe create error\n");
        return -1;
    }
    if((pid=fork())==0)
    //子進(jìn)程:解析從管道中獲取的命令,并作相應(yīng)的處理
    {
        printf("\n");
        close(pipe_fd[1]);
        sleep(2);
        
        while(!childexit)
        {   
            read(pipe_fd[0],r_buf,4);
            cmd=atoi(r_buf);
            if(cmd==0)
            {
printf("child: receive command from parent over\n now child process exit\n");
                childexit=1;
            }
            
               else if(handle_cmd(cmd)!=0)
                return;
            sleep(1);
        }
        close(pipe_fd[0]);
        exit();
    }
    else if(pid>0)
    //parent: send commands to child
    {
    close(pipe_fd[0]);
    w_buf[0]="003";
    w_buf[1]="005";
    w_buf[2]="777";
    w_buf[3]="000";
    for(i=0;i<4;i++)
        write(pipe_fd[1],w_buf[i],4);
    close(pipe_fd[1]);
    }   
}
//下面是子進(jìn)程的命令處理函數(shù)(特定于應(yīng)用):
int handle_cmd(int cmd)
{
if((cmd<0)||(cmd>256))
//suppose child only support 256 commands
    {
    printf("child: invalid command \n");
    return -1;
    }
printf("child: the cmd from parent is %d\n", cmd);
return 0;
}

4.1.2 有名管道fifo

無名管道纷闺,由于沒有名字算凿,只能用于親緣關(guān)系的進(jìn)程間通信.。為了克服這個(gè)缺點(diǎn)急但,提出了有名管道(FIFO)澎媒。FIFO不同于無名管道之處在于它提供了一個(gè)路徑名與之關(guān)聯(lián),以FIFO的文件形式存在于文件系統(tǒng)中波桩,這樣戒努,即使與FIFO的創(chuàng)建進(jìn)程不存在親緣關(guān)系的進(jìn)程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信储玫,因此侍筛,通過FIFO不相關(guān)的進(jìn)程也能交換數(shù)據(jù)。有名管道的名字存在于文件系統(tǒng)中撒穷,內(nèi)容存放在內(nèi)存中匣椰。有名管道創(chuàng)建的API是:

有名管道創(chuàng)建

下面創(chuàng)建一個(gè)FIFO,并且寫入數(shù)據(jù):

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER "/tmp/fifoserver"
main(int argc,char** argv)
//參數(shù)為即將寫入的字節(jié)數(shù)
{
    int fd;
    char w_buf[4096*2];
    int real_wnum;
    memset(w_buf,0,4096*2);
    if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
        printf("cannot create fifoserver\n");
    if(fd==-1)
        if(errno==ENXIO)
            printf("open error; no reading process\n");
        
        fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
    //設(shè)置非阻塞標(biāo)志
    //fd=open(FIFO_SERVER,O_WRONLY,0);
    //設(shè)置阻塞標(biāo)志
    real_wnum=write(fd,w_buf,2048);
    if(real_wnum==-1)
    {
        if(errno==EAGAIN)
            printf("write to fifo error; try later\n");
    }
    else 
        printf("real write num is %d\n",real_wnum);
    real_wnum=write(fd,w_buf,5000);
    //5000用于測(cè)試寫入字節(jié)大于4096時(shí)的非原子性
    //real_wnum=write(fd,w_buf,4096);
    //4096用于測(cè)試寫入字節(jié)不大于4096時(shí)的原子性
    
    if(real_wnum==-1)
        if(errno==EAGAIN)
            printf("try later\n");
}

下面讀取這個(gè)FIFO寫入的數(shù)據(jù):

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER "/tmp/fifoserver"
main(int argc,char** argv)
{
    char r_buf[4096*2];
    int  fd;
    int  r_size;
    int  ret_size;
    r_size=atoi(argv[1]);
    printf("requred real read bytes %d\n",r_size);
    memset(r_buf,0,sizeof(r_buf));
    fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);
    //fd=open(FIFO_SERVER,O_RDONLY,0);
    //在此處可以把讀程序編譯成兩個(gè)不同版本:阻塞版本及非阻塞版本
    if(fd==-1)
    {
        printf("open %s for read error\n");
        exit(); 
    }
    while(1)
    {
        
        memset(r_buf,0,sizeof(r_buf));
        ret_size=read(fd,r_buf,r_size);
        if(ret_size==-1)
            if(errno==EAGAIN)
                printf("no data avlaible\n");
        printf("real read bytes %d\n",ret_size);
        sleep(1);
    }   
    pause();
    unlink(FIFO_SERVER);
}

4.2 信號(hào)

  • 信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制端礼,可以看作是異步通知禽笑,通知接收信號(hào)的進(jìn)程有哪些事情發(fā)生了。信號(hào)機(jī)制經(jīng)過POSIX實(shí)時(shí)擴(kuò)展后蛤奥,功能更加強(qiáng)大佳镜,除了基本通知功能外,還可以傳遞附加信息凡桥。
  • 如果該進(jìn)程當(dāng)前并未處于執(zhí)行狀態(tài)蟀伸,則該信號(hào)就有內(nèi)核保存起來,知道該進(jìn)程回復(fù)執(zhí)行并傳遞給它為止缅刽。
  • 如果一個(gè)信號(hào)被進(jìn)程設(shè)置為阻塞啊掏,則該信號(hào)的傳遞被延遲,直到其阻塞被取消是才被傳遞給進(jìn)程衰猛。

信號(hào)產(chǎn)生有兩個(gè)來源:硬件來源(比如我們按下了鍵盤或者其它硬件故障)迟蜜;軟件來源,最常用發(fā)送信號(hào)的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer以及sigqueue函數(shù)腕侄,軟件來源還包括一些非法運(yùn)算等操作小泉。

Linux系統(tǒng)中常用信號(hào):
(1)SIGHUP:用戶從終端注銷,所有已啟動(dòng)進(jìn)程都將收到該進(jìn)程冕杠。系統(tǒng)缺省狀態(tài)下對(duì)該信號(hào)的處理是終止進(jìn)程。
(2)SIGINT:程序終止信號(hào)酸茴。程序運(yùn)行過程中分预,按Ctrl+C鍵將產(chǎn)生該信號(hào)。
(3)SIGQUIT:程序退出信號(hào)薪捍。程序運(yùn)行過程中笼痹,按Ctrl+\鍵將產(chǎn)生該信號(hào)。
(4)SIGBUS和SIGSEGV:進(jìn)程訪問非法地址酪穿。
(5)SIGFPE:運(yùn)算中出現(xiàn)致命錯(cuò)誤凳干,如除零操作、數(shù)據(jù)溢出等被济。
(6)SIGKILL:用戶終止進(jìn)程執(zhí)行信號(hào)救赐。shell下執(zhí)行kill -9發(fā)送該信號(hào)。
(7)SIGTERM:結(jié)束進(jìn)程信號(hào)只磷。shell下執(zhí)行kill 進(jìn)程pid發(fā)送該信號(hào)经磅。
(8)SIGALRM:定時(shí)器信號(hào)泌绣。
(9)SIGCLD:子進(jìn)程退出信號(hào)。如果其父進(jìn)程沒有忽略該信號(hào)也沒有處理該信號(hào)预厌,則子進(jìn)程退出后將形成僵尸進(jìn)程阿迈。

進(jìn)程可以通過三種方式來響應(yīng)一個(gè)信號(hào):(1)忽略信號(hào),即對(duì)信號(hào)不做任何處理轧叽,其中苗沧,有兩個(gè)信號(hào)不能忽略:SIGKILL及SIGSTOP;(2)捕捉信號(hào)炭晒。定義信號(hào)處理函數(shù)待逞,當(dāng)信號(hào)發(fā)生時(shí),執(zhí)行相應(yīng)的處理函數(shù)腰埂;(3)執(zhí)行缺省操作飒焦,Linux對(duì)每種信號(hào)都規(guī)定了默認(rèn)操作。

下面是一個(gè)alarm信號(hào)使用例子:

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

static int alarm_fired = 0;

void ouch(int sig)
{
    alarm_fired = 1;
}

int main()
{
    //關(guān)聯(lián)信號(hào)處理函數(shù)
    signal(SIGALRM, ouch);
    //調(diào)用alarm函數(shù)屿笼,5秒后發(fā)送信號(hào)SIGALRM
    alarm(5);
    //掛起進(jìn)程
    pause();
    //接收到信號(hào)后牺荠,恢復(fù)正常執(zhí)行
    if(alarm_fired == 1)
        printf("Receive a signal %d\n", SIGALRM);
    exit(0);
}

注:如果父進(jìn)程在子進(jìn)程的信號(hào)到來之前沒有事情可做,我們可以用函數(shù)pause()來掛起父進(jìn)程驴一,直到父進(jìn)程接收到信號(hào)休雌。當(dāng)進(jìn)程接收到一個(gè)信號(hào)時(shí),預(yù)設(shè)好的信號(hào)處理函數(shù)將開始運(yùn)行肝断,程序也將恢復(fù)正常的執(zhí)行杈曲。這樣可以節(jié)省CPU的資源,因?yàn)榭梢员苊馐褂靡粋€(gè)循環(huán)來等待胸懈。

4.3 消息隊(duì)列

消息隊(duì)列是消息的鏈表担扑,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí)。在某個(gè)進(jìn)程往一個(gè)隊(duì)列寫入消息之前趣钱,并不需要另外某個(gè)進(jìn)程在該隊(duì)列上等待消息的到達(dá)涌献。這跟管道和FIFO是相反的,對(duì)后兩者來說首有,除非讀出者已存在燕垃,否則先有寫入者是沒有意義的。
  管道和FIFO都是隨進(jìn)程持續(xù)的井联,XSI IPC(消息隊(duì)列卜壕、信號(hào)量、共享內(nèi)存)都是隨內(nèi)核持續(xù)的烙常。當(dāng)一個(gè)管道或FIFO的最后一次關(guān)閉發(fā)生時(shí)轴捎,仍在該管道或FIFO上的數(shù)據(jù)將被丟棄。消息隊(duì)列,除非系統(tǒng)重啟或顯式刪除轮蜕,否則其一直存在昨悼。
 對(duì)于系統(tǒng)中的每個(gè)消息隊(duì)列,內(nèi)核維護(hù)一個(gè)定義在<sys/msg.h>頭文件中的信息結(jié)構(gòu)跃洛。

struct msqid_ds {
    struct ipc_perm msg_perm ; 
    struct msg*    msg_first ; //指向隊(duì)列中的第一個(gè)消息
    struct msg*    msg_last ; //指向隊(duì)列中的最后一個(gè)消息
    ……
} ;

下面是一個(gè)接收消息隊(duì)列數(shù)據(jù)的代碼:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>

struct msg_st
{
    long int msg_type;
    char text[BUFSIZ];
};

int main()
{
    int running = 1;
    int msgid = -1;
    struct msg_st data;
    long int msgtype = 0; //注意1

    //建立消息隊(duì)列
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(msgid == -1)
    {
        fprintf(stderr, "msgget failed with error: %d\n", errno);
        exit(EXIT_FAILURE);
    }
    //從隊(duì)列中獲取消息率触,直到遇到end消息為止
    while(running)
    {
        if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)
        {
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
            exit(EXIT_FAILURE);
        }
        printf("You wrote: %s\n",data.text);
        //遇到end結(jié)束
        if(strncmp(data.text, "end", 3) == 0)
            running = 0;
    }
    //刪除消息隊(duì)列
    if(msgctl(msgid, IPC_RMID, 0) == -1)
    {
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

下面是對(duì)消息隊(duì)列寫消息的例子:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>

#define MAX_TEXT 512
struct msg_st
{
    long int msg_type;
    char text[MAX_TEXT];
};

int main()
{
    int running = 1;
    struct msg_st data;
    char buffer[BUFSIZ];
    int msgid = -1;

    //建立消息隊(duì)列
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(msgid == -1)
    {
        fprintf(stderr, "msgget failed with error: %d\n", errno);
        exit(EXIT_FAILURE);
    }

    //向消息隊(duì)列中寫消息,直到寫入end
    while(running)
    {
        //輸入數(shù)據(jù)
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        data.msg_type = 1;    //注意2
        strcpy(data.text, buffer);
        //向隊(duì)列發(fā)送數(shù)據(jù)
        if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
        {
            fprintf(stderr, "msgsnd failed\n");
            exit(EXIT_FAILURE);
        }
        //輸入end結(jié)束輸入
        if(strncmp(buffer, "end", 3) == 0)
            running = 0;
        sleep(1);
    }
    exit(EXIT_SUCCESS);
}

4.4 共享內(nèi)存

采用共享內(nèi)存通信的一個(gè)顯而易見的好處是效率高汇竭,因?yàn)檫M(jìn)程可以直接讀寫內(nèi)存葱蝗,而不需要任何數(shù)據(jù)的拷貝。對(duì)于像管道和消息隊(duì)列等通信方式细燎,則需要在內(nèi)核和用戶空間進(jìn)行四次的數(shù)據(jù)拷貝两曼,而共享內(nèi)存則只拷貝兩次數(shù)據(jù):

  • 一次從輸入文件到共享內(nèi)存區(qū)
  • 另一次從共享內(nèi)存區(qū)到輸出文件。

現(xiàn)代Linux有兩種共享內(nèi)存機(jī)制:

  • POSIX共享內(nèi)存(shm_open()玻驻、shm_unlink())
  • System V共享內(nèi)存(shmget()悼凑、shmat()、shmdt())

其中璧瞬,System V共享內(nèi)存歷史悠久户辫;而POSIX共享內(nèi)存機(jī)制接口更加方便易用,一般是結(jié)合內(nèi)存映射mmap使用嗤锉。

mmap和System V共享內(nèi)存的主要區(qū)別在于:

  • sysv shm是持久化的渔欢,除非被一個(gè)進(jìn)程明確的刪除,否則它始終存在于內(nèi)存里瘟忱,直到系統(tǒng)關(guān)機(jī)奥额;
  • mmap映射的內(nèi)存在不是持久化的,如果進(jìn)程關(guān)閉访诱,映射隨即失效垫挨,除非事先已經(jīng)映射到了一個(gè)文件上。

內(nèi)存映射機(jī)制mmap是POSIX標(biāo)準(zhǔn)的系統(tǒng)調(diào)用触菜,有匿名映射和文件映射兩種棒拂。

  • 匿名映射使用進(jìn)程的虛擬內(nèi)存空間,它和malloc(3)類似玫氢,實(shí)際上有些malloc實(shí)現(xiàn)會(huì)使用mmap匿名映射分配內(nèi)存,不過匿名映射不是POSIX標(biāo)準(zhǔn)中規(guī)定的谜诫。
  • 文件映射有MAP_PRIVATE和MAP_SHARED兩種漾峡。前者使用COW的方式,把文件映射到當(dāng)前的進(jìn)程空間喻旷,修改操作不會(huì)改動(dòng)源文件生逸。后者直接把文件映射到當(dāng)前的進(jìn)程空間,所有的修改會(huì)直接反應(yīng)到文件的page cache,然后由內(nèi)核自動(dòng)同步到映射文件上槽袄。

相比于IO函數(shù)調(diào)用烙无,基于文件的mmap的一大優(yōu)點(diǎn)是把文件映射到進(jìn)程的地址空間,避免了數(shù)據(jù)從用戶緩沖區(qū)到內(nèi)核page cache緩沖區(qū)的復(fù)制過程遍尺;當(dāng)然還有一個(gè)優(yōu)點(diǎn)就是不需要頻繁的read/write系統(tǒng)調(diào)用, 比較適合隨機(jī)讀取文件內(nèi)容的場(chǎng)景截酷。

由于接口易用,且可以方便的persist到文件乾戏,避免主機(jī)shutdown丟失數(shù)據(jù)的情況迂苛,所以在現(xiàn)代操作系統(tǒng)上一般偏向于使用mmap而不是傳統(tǒng)的System V的共享內(nèi)存機(jī)制。

建議僅把mmap用于需要大量?jī)?nèi)存數(shù)據(jù)操作的場(chǎng)景鼓择,而不用于IPC三幻。因?yàn)镮PC總是在多個(gè)進(jìn)程之間通信,而通信則涉及到同步問題呐能,如果自己手工在mmap之上實(shí)現(xiàn)同步念搬,容易滋生bug。推薦使用socket之類的機(jī)制做IPC摆出,基于socket的通信機(jī)制相對(duì)健全很多朗徊,有很多成熟的機(jī)制和模式,比如epoll, reactor等懊蒸。

下面是一個(gè)mmap共享內(nèi)存的范例:

#include<stdio.h>
#include<sys/mman.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>


#define ERR_EXIT(m)\
    do\
    {\
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)


int main()
{
    int fd = open("/home/jay/linux/test.txt",O_CREAT|O_WRONLY,0666); //1
    if(-1 == fd)
        ERR_EXIT("open");

    struct stat buf;
    fstat(fd,&buf);
    void* p = mmap(NULL,(int)buf.st_size,PROT_WRITE,MAP_SHARED,fd,0);

    if(MAP_FAILED == p)   //MAP_FAILED 是一個(gè)宏 等于 (void*)-1
        ERR_EXIT("mmap");

    strcpy(p,"hello world");

    if(-1 == munmap(p,buf.st_size))
        ERR_EXIT("munmp");

    return 0;
}

下面是一個(gè)system V api實(shí)現(xiàn)的內(nèi)存共享范例:

#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#define ERR_EXIT(m)\
    do\
    {\
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)


int main()
{
    key_t key=ftok("/tmp",12343);
    if(-1 == key)
        ERR_EXIT("ftok");

    int shmid = shmget(key,1024,IPC_CREAT|IPC_EXCL|0666);  //創(chuàng)建一個(gè)共享內(nèi)存
    if(-1 == shmid)
        ERR_EXIT("shmget");

    void* p = shmat(shmid,NULL,0);  
    if((void*)-1 == p)
        ERR_EXIT("shmat");

    strcpy(p,"11111111111");

    shmdt(p);    //只是斷開連接而已

    return 0;
}

4.5 信號(hào)量

為了防止出現(xiàn)因多個(gè)程序同時(shí)訪問一個(gè)共享資源而引發(fā)的一系列問題荣倾,我們需要一種方法,它可以通過生成并使用令牌來授權(quán)骑丸,在任一時(shí)刻只能有一個(gè)執(zhí)行線程訪問代碼的臨界區(qū)域舌仍。臨界區(qū)域是指執(zhí)行數(shù)據(jù)更新的代碼需要獨(dú)占式地執(zhí)行。而信號(hào)量就可以提供這樣的一種訪問機(jī)制通危,讓一個(gè)臨界區(qū)同一時(shí)間只有一個(gè)線程在訪問它铸豁,也就是說信號(hào)量是用來調(diào)協(xié)進(jìn)程對(duì)共享資源的訪問的。
  信號(hào)量是一個(gè)特殊的變量菊碟,程序?qū)ζ湓L問都是原子操作节芥,且只允許對(duì)它進(jìn)行等待(即P(信號(hào)變量))和發(fā)送(即V(信號(hào)變量))信息操作。最簡(jiǎn)單的信號(hào)量是只能取0和1的變量逆害,這也是信號(hào)量最常見的一種形式头镊,叫做二進(jìn)制信號(hào)量。而可以取多個(gè)正整數(shù)的信號(hào)量被稱為通用信號(hào)量魄幕。這里主要討論二進(jìn)制信號(hào)量相艇。
  由于信號(hào)量只能進(jìn)行兩種操作等待和發(fā)送信號(hào),即P(sv)和V(sv),他們的行為是這樣的:

  • P(sv):如果sv的值大于零纯陨,就給它減1坛芽;如果它的值為零留储,就掛起該進(jìn)程的執(zhí)行
  • V(sv):如果有其他進(jìn)程因等待sv而被掛起,就讓它恢復(fù)運(yùn)行咙轩,如果沒有進(jìn)程因等待sv而掛起获讳,就給它加1.

下面是一個(gè)信號(hào)量實(shí)現(xiàn)臨界區(qū)訪問的示例代碼:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
};

static int sem_id = 0;

static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();

int main(int argc, char *argv[])
{
    char message = 'X';
    int i = 0;

    //創(chuàng)建信號(hào)量
    sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);

    if(argc > 1)
    {
        //程序第一次被調(diào)用,初始化信號(hào)量
        if(!set_semvalue())
        {
            fprintf(stderr, "Failed to initialize semaphore\n");
            exit(EXIT_FAILURE);
        }
        //設(shè)置要輸出到屏幕中的信息活喊,即其參數(shù)的第一個(gè)字符
        message = argv[1][0];
        sleep(2);
    }
    for(i = 0; i < 10; ++i)
    {
        //進(jìn)入臨界區(qū)
        if(!semaphore_p())
            exit(EXIT_FAILURE);
        //向屏幕中輸出數(shù)據(jù)
        printf("%c", message);
        //清理緩沖區(qū)丐膝,然后休眠隨機(jī)時(shí)間
        fflush(stdout);
        sleep(rand() % 3);
        //離開臨界區(qū)前再一次向屏幕輸出數(shù)據(jù)
        printf("%c", message);
        fflush(stdout);
        //離開臨界區(qū),休眠隨機(jī)時(shí)間后繼續(xù)循環(huán)
        if(!semaphore_v())
            exit(EXIT_FAILURE);
        sleep(rand() % 2);
    }

    sleep(10);
    printf("\n%d - finished\n", getpid());

    if(argc > 1)
    {
        //如果程序是第一次被調(diào)用胧弛,則在退出前刪除信號(hào)量
        sleep(3);
        del_semvalue();
    }
    exit(EXIT_SUCCESS);
}

static int set_semvalue()
{
    //用于初始化信號(hào)量尤误,在使用信號(hào)量前必須這樣做
    union semun sem_union;

    sem_union.val = 1;
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
        return 0;
    return 1;
}

static void del_semvalue()
{
    //刪除信號(hào)量
    union semun sem_union;

    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        fprintf(stderr, "Failed to delete semaphore\n");
}

static int semaphore_p()
{
    //對(duì)信號(hào)量做減1操作,即等待P(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;//P()
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr, "semaphore_p failed\n");
        return 0;
    }
    return 1;
}

static int semaphore_v()
{
    //這是一個(gè)釋放操作结缚,它使信號(hào)量變?yōu)榭捎盟鹞睿窗l(fā)送信號(hào)V(sv)
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;//V()
    sem_b.sem_flg = SEM_UNDO;
    if(semop(sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr, "semaphore_v failed\n");
        return 0;
    }
    return 1;
}

4.6 套接字

套接字(socket)是一種通信機(jī)制,憑借這種機(jī)制红竭,客戶/服務(wù)器(即要進(jìn)行通信的進(jìn)程)系統(tǒng)的開發(fā)工作既可以在本地單機(jī)上進(jìn)行尤勋,也可以跨網(wǎng)絡(luò)進(jìn)行。也就是說它可以讓不在同一臺(tái)計(jì)算機(jī)但通過網(wǎng)絡(luò)連接計(jì)算機(jī)上的進(jìn)程進(jìn)行通信茵宪。

socket通信流程

5 進(jìn)程相關(guān)命令

5.1 ps/pstree

ps命令參考前文 Linux內(nèi)存 這里還有額外補(bǔ)充一下關(guān)系進(jìn)程狀態(tài)的說明最冰。通過ps aux可以看到進(jìn)程的狀態(tài)。

O:進(jìn)程正在處理器運(yùn)行,這個(gè)狀態(tài)從來沒有見過.
S:休眠狀態(tài)(sleeping)
R:等待運(yùn)行(runable)R Running or runnable (on run queue) 進(jìn)程處于運(yùn)行或就緒狀態(tài)
I:空閑狀態(tài)(idle)
Z:僵尸狀態(tài)(zombie)
T:跟蹤狀態(tài)(Traced)
B:進(jìn)程正在等待更多的內(nèi)存頁
D: 不可中斷的深度睡眠稀火,一般由IO引起暖哨,同步IO在做讀或?qū)懖僮鲿r(shí),cpu不能做其它事情凰狞,只能等待篇裁,這時(shí)進(jìn)程處于這種狀態(tài),如果程序采用異步IO赡若,這種狀態(tài)應(yīng)該就很少見到了达布。

pstree命令將所有進(jìn)程以樹狀圖顯示,基本用法如下描述:

pstree [-a] [-c] [-h|-Hpid] [-l] [-n] [-p] [-u] [-G|-U] [pid|user]

下面是一個(gè)pstree命令的實(shí)例:

pstree
init─┬─acpid
     ├─agetty
     ├─bcron_start───sleep
     ├─bns_nginx───bns_nginx
     ├─casio-loader64───scribed───5*[{scribed}]
     ├─casio-loader64───casio-agent───5*[{casio-agent}]
     ├─casio-loader64───log_counter_col───3*[{log_counter_col}]

5.2 kill/killall

kill 的用法:

kill [信號(hào)代碼] 進(jìn)程ID(kill  -pid)
-s:指定發(fā)送的信號(hào)逾冬。 
-p:模擬發(fā)送信號(hào)黍聂。 
-l:指定信號(hào)的名稱列表。 
pid:要中止進(jìn)程的ID號(hào)身腻。 
Signal:表示信號(hào)产还。
注:信號(hào)代碼可以省略, 默認(rèn)發(fā)送的信號(hào)是 15) SIGTERM;我們常用的信號(hào)代碼是-9 嘀趟,表示強(qiáng)制終止雕沉;對(duì)于僵尸進(jìn)程,可以用kill -9 來強(qiáng)制終止退出去件;

killall命令使用進(jìn)程的名稱來殺死進(jìn)程坡椒,使用此指令可以殺死一組同名進(jìn)程。我們可以使用kill命令殺死指定進(jìn)程PID的進(jìn)程尤溜,如果要找到我們需要?dú)⑺赖倪M(jìn)程倔叼,我們還需要在之前使用ps等命令再配合grep來查找進(jìn)程,而killall把這兩個(gè)過程合二為一宫莱,是一個(gè)很好用的命令, 用法:

killall 參數(shù) 進(jìn)程名字
-e:對(duì)長(zhǎng)名稱進(jìn)行精確匹配丈攒;
-l:忽略大小寫的不同; 
-p:殺死進(jìn)程所屬的進(jìn)程組授霸;
-i:交互式殺死進(jìn)程巡验,殺死進(jìn)程前需要進(jìn)行確認(rèn); 
-l:打印所有已知信號(hào)列表碘耳;
-q:如果沒有進(jìn)程被殺死显设。則不輸出任何信息;
-r:使用正規(guī)表達(dá)式匹配要?dú)⑺赖倪M(jìn)程名稱辛辨; 
-s:用指定的進(jìn)程號(hào)代替默認(rèn)信號(hào)“SIGTERM”捕捂;
-u:殺死指定用戶的進(jìn)程。

5.3 ipcs/ipcrm

ipcs命令用于報(bào)告Linux中進(jìn)程間通信設(shè)施的狀態(tài)斗搞,顯示的信息包括消息列表指攒、共享內(nèi)存和信號(hào)量的信息。用法如下:

ipcs 選項(xiàng)
-a:顯示全部可顯示的信息僻焚; 
-q:顯示活動(dòng)的消息隊(duì)列信息允悦; 
-m:顯示活動(dòng)的共享內(nèi)存信息; 
-s:顯示活動(dòng)的信號(hào)量信息虑啤。

下面是一個(gè)ipcs命令輸出

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

ipcrm命令用來刪除一個(gè)或更多的消息隊(duì)列隙弛、信號(hào)量集或者共享內(nèi)存標(biāo)識(shí)「谰桑基本用法如下描述:

 ipcrm [-M shmkey] [-m shmid] [-Q msgkey] [-q msqid] [-S semkey] [-s semid] ...

-m SharedMemory id 刪除共享內(nèi)存標(biāo)識(shí) SharedMemoryID驶鹉。與 SharedMemoryID 有關(guān)聯(lián)的共享內(nèi)存段以及數(shù)據(jù)結(jié)構(gòu)都會(huì)在最后一次拆離操作后刪除。 
-M SharedMemoryKey 刪除用關(guān)鍵字 SharedMemoryKey 創(chuàng)建的共享內(nèi)存標(biāo)識(shí)铣墨。與其相關(guān)的共享內(nèi)存段和數(shù)據(jù)結(jié)構(gòu)段都將在最后一次拆離操作后刪除室埋。 
-q MessageID 刪除消息隊(duì)列標(biāo)識(shí) MessageID 和與其相關(guān)的消息隊(duì)列和數(shù)據(jù)結(jié)構(gòu)。 
-Q MessageKey 刪除由關(guān)鍵字 MessageKey 創(chuàng)建的消息隊(duì)列標(biāo)識(shí)和與其相關(guān)的消息隊(duì)列和數(shù)據(jù)結(jié)構(gòu)伊约。 
-s SemaphoreID 刪除信號(hào)量標(biāo)識(shí) SemaphoreID 和與其相關(guān)的信號(hào)量集及數(shù)據(jù)結(jié)構(gòu)姚淆。 
-S SemaphoreKey 刪除由關(guān)鍵字 SemaphoreKey 創(chuàng)建的信號(hào)標(biāo)識(shí)和與其相關(guān)的信號(hào)量集和數(shù)據(jù)結(jié)構(gòu)。

6. 其他

6.1 進(jìn)程/線程context switch消耗

線程和進(jìn)程在內(nèi)核的管理結(jié)構(gòu) task struct是一樣的屡律,切換的差別也只是是否刷新TLB腌逢,刷新TLB本身不費(fèi)時(shí),只是刷新后超埋,TLB miss會(huì)比較影響性能搏讶,但這不能算在“切換”時(shí)間內(nèi)佳鳖。真正的切換時(shí)間只有寄存器的保持到內(nèi)存中,并把將要執(zhí)行的線程/進(jìn)程的上下文從內(nèi)存拷貝到寄存器中媒惕,就完成了一次切換系吩,非常快妒蔚。

7. 參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末穿挨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肴盏,更是在濱河造成了極大的恐慌科盛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菜皂,死亡現(xiàn)場(chǎng)離奇詭異贞绵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)幌墓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門但壮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人常侣,你說我怎么就攤上這事蜡饵。” “怎么了胳施?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵溯祸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我舞肆,道長(zhǎng)焦辅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任椿胯,我火速辦了婚禮筷登,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哩盲。我一直安慰自己前方,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布廉油。 她就那樣靜靜地躺著惠险,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抒线。 梳的紋絲不亂的頭發(fā)上班巩,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音嘶炭,去河邊找鬼抱慌。 笑死逊桦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遥缕。 我是一名探鬼主播卫袒,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼单匣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宝穗,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤户秤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后逮矛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸡号,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年须鼎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鲸伴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晋控,死狀恐怖汞窗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赡译,我是刑警寧澤仲吏,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蝌焚,受9級(jí)特大地震影響裹唆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜只洒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一许帐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毕谴,春花似錦成畦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至忠寻,卻和暖如春惧浴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奕剃。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工衷旅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捐腿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓柿顶,卻偏偏與公主長(zhǎng)得像茄袖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嘁锯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容