1. 程式人生 > >Linux 多工程式設計——程序的介紹

Linux 多工程式設計——程序的介紹

程序
我們平時寫的 C 語言程式碼,通過編譯器編譯,最終它會成為一個可執行程式,當這個可執行程式執行起來後(沒有結束之前),它就成為了一個程序。

程式是存放在儲存介質上的一個可執行檔案,而程序是程式執行的過程。程序的狀態是變化的,其包括程序的建立、排程和消亡。程式是靜態的,程序是動態的。

在 Linux 系統中,作業系統是通過程序去完成一個一個的任務,程序是管理事務的基本單元。程序擁有自己獨立的處理環境(如:當前需要用到哪些環境變數,程式執行的目錄在哪,當前是哪個使用者在執行此程式等)和系統資源(如:處理器 CPU 佔用率、儲存器、I/O裝置、資料、程式)。我們可以這麼理解,公司相當於作業系統,部門相當於程序,公司通過部門來管理(系統通過程序管理),對於各個部門,每個部門有各自的資源,如人員、電腦裝置、印表機等。

程序狀態
我們現在的電腦基本上都是多工,我們聊著 QQ 的時候,同時可以看著視訊,這裡相當於 QQ 和視訊兩個程式同時執行著(兩個程序)。早期的時候,電腦的 CPU 是單核的(單核理論上只執行操作一個任務),那它是如何做到多工的呢?這就涉及到程序的排程策略。現在給大家舉這麼一個例子,有 A,B,C 三個程序,在我們單 CPU 的情況下,每一個時刻只有一個程序在執行,如果 A 執行完,B 執行,B 執行完,C 執行,C 執行完,A 執行,而 CPU 的運算速度足夠快,A 兩次執行時間間隔足夠短,從巨集觀上就我們就看到 A,B,C 好像同時執行,這就是實現單 CPU 執行多個任務的核心原理,通過時間片輪詢排程策略實現多工(更多詳情,請看《Linux 程序排程淺析》)。

從上面的例子,我們可以得知,對於 A 程序而言,有時候在執行,有時候沒有執行,兩個狀態不一樣,所以,程序是有狀態的,同時,狀態是可以相互進行轉換的,從執行的狀態轉換為不執行的狀態,這裡,我們可以把程序執行的整個生命週期簡單劃分為三種狀態(實際上不指這三種狀態):就緒態、執行態、等待態。

就緒態:

程序已經具備執行的一切條件,正在等待分配 CPU 的處理時間。

執行態:

該程序正在佔用 CPU 執行。

等待態:

程序因不具備某些執行條件而暫時無法繼續執行的狀態。

這裡需要注意,就緒態和等待態都是不執行,但它們是有區別的,就緒態是指滿足條件,時間沒到,等待態是不滿足條件。

同樣的,程序的這三種狀態可以相互轉換:

為了讓大家更好地這三種狀態的轉換,給大家舉一個買火車票的例子。

Mike 匆忙地趕去火車站買火車票,太著急了,到了售票廳才發現忘記帶身份證,這時候,就算 Mike 排隊也沒用,因為 Mike 不具備買票的條件(沒帶身份證),這時候的 Mike 屬於等待態。

Mike 給它物件打電話,讓她把身份證帶過來,等會,身份證送到了,這時候,Mike 可以去排隊買票了,只是時間到,Mike 就可以買票了,這時,Mike 屬於就緒態。而這過程是由等待態轉換到就緒態。

等了 10 分鐘,終於到 Mike 了,Mike 開始買票,這時候, Mike 屬於執行態。而這過程是由就緒態轉換為執行態。

而在買票的過程中,Mike 的物件打電話給他,讓 Mike 也幫她買一張火車票,但是, Mike 沒有她物件的身份證,接著,Mike 繼續等他物件送身份證,這時候,Mike由執行態轉換為等待態。

假如是這麼一種情況,Mike 買火車票是給公司的同事買的(需要買 100 多張票),在買著票的過程中(執行態),後面還有很多人在排隊,後面排隊的人肯定不爽,這時售票員就說,20分鐘後,如果你還沒處理完,請你到後面排隊。結果,Mike 花了 20 分鐘還是沒有處理完,於是,乖乖地到後面重新排隊,這時候,Mike由執行態轉換為就緒態。

 

程序控制塊
對於作業系統而言,它需要控制很多程序,同時,每個程序都有不同的狀態,系統如何知道 A 執行完到 B 執行而不是 C?系統如何協調控制程序呢?

當我們執行一個程式使它成為一個程序時,系統會開闢一段記憶體空間存放與此程序相關的資料資訊,而這個資料資訊是通過結構體( task_struct,開啟 /usr/include/linux/sched.h 可以找到 task_struct 的定義 )來存放,我們把這個存放程序相關資料資訊的結構體稱為程序控制塊。作業系統就是通過這個程序控制塊來操作控制程序。更多詳情,請看《 Linux 程序管理》。

程序控制塊是作業系統中最重要的記錄型資料結構。程序控制塊記錄了用於描述程序進展情況及控制程序執行所需的全部資訊,它是程序存在的唯一標誌。程序控制塊裡有很多資訊,其中比較重要的是程序號,至於其他的一些資訊我們不在這詳細討論。

程序號
每個程序都由一個程序號來標識,其型別為 pid_t(無符號整型),程序號的範圍:0~32767。程序號總是唯一的,但程序號可以重用。當一個程序終止後,其程序號就可以再次使用。

系統允許一個程序建立新程序,新程序即為子程序,子程序還可以建立新的子程序,形成程序樹結構模型。整個 Linux 系統的所有程序也是一個樹形結構。樹根是系統自動構造的,即在核心態下執行的 0 號程序,它是所有程序的祖先。程序號為 0 的程序通常是排程程序,常被稱為交換程序( swapper )。由 0 號程序建立 1 號程序(核心態),1 號負責執行核心的部分初始化工作及進行系統配置,並建立若干個用於快取記憶體和虛擬主存管理的核心執行緒。隨後,1 號程序呼叫 execve() 執行可執行程式 init,並演變成使用者態 1 號程序,即 init 程序。

所以,在 Linux 下面所有的程序都由 init 程序直接或者間接建立。

接下來,再給大家介紹三個不同的程序號。

程序號(PID):

標識程序的一個非負整型數。

父程序號(PPID):

任何程序( 除 init 程序)都是由另一個程序建立,該程序稱為被建立程序的父程序,對應的程序號稱為父程序號(PPID)。如,A 程序建立了 B 程序,A 的程序號就是 B 程序的父程序號。

程序組號(PGID):

程序組是一個或多個程序的集合。他們之間相互關聯,程序組可以接收同一終端的各種訊號,關聯的程序有一個程序組號(PGID) 。這個過程有點類似於 QQ 群,組相當於 QQ 群,各個程序相當於各個好友,把各個好友都拉入這個 QQ 群裡,主要是方便管理,特別是通知某些事時,只要在群裡吼一聲,所有人都收到,簡單粗暴。但是,這個程序組號和 QQ 群號是有點區別的,預設的情況下,當前的程序號會當做當前的程序組號。

Linux 作業系統提供了三個獲得程序號的函式 getpid()、getppid()、getpgid()。

所需標頭檔案:

#include <sys/types.h>

#include <unistd.h>

pid_t getpid(void);

功能:

獲取本程序號(PID)

引數:

返回值:

本程序號

pid_t getppid(void);

功能:

獲取呼叫此函式的程序的父程序號(PPID)

引數:

返回值:

呼叫此函式的程序的父程序號(PPID)

pid_t getpgid(pid_t pid);

功能:

獲取程序組號(PGID)

引數:

pid:程序號

返回值:

引數為 0 時返回當前程序組號,否則返回引數指定的程序的程序組號

示例程式碼如下:


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
    pid_t pid, ppid, pgid;
 
    pid = getpid();
    printf("pid = %d\n", pid);
 
    ppid = getppid();
    printf("ppid = %d\n", ppid);
 
    pgid = getpgid(pid);
    printf("pgid = %d\n", pgid);
    
    return 0;
}

執行結果如下:

 

 

--------------------- 
作者:Mike__Jiang 
來源:CSDN 
原文:https://blog.csdn.net/tennysonsky/article/details/45873687 
版權宣告:本文為博主原創文章,轉載請附上博文連結!