Linux程式設計之程序
Linux下多工介紹
首先,簡單介紹下多工系統,任務,程序,執行緒分別是什麼?之間的區別是什麼?從巨集觀角度理解後再針對每一個仔細探究
-
什麼叫多工系統:多工系統指可以同一時間內執行多個應用程式,每個應用程式被稱作一個任務。
-
任務定義:任務是一個邏輯概念,指由一個軟體完成的任務,或者是一系列共同達到某一目的的操作。
-
程序定義:程序是指一個具有獨立功能的程式在某個資料集上的一次動態執行過程,它是系統進行資源分配和排程的最小單元。
-
執行緒定義:執行緒是程序內獨立的一條執行路線,是處理器排程的最小單元,也可以成為輕量級程序。
看了定義,還是不太理解,那就通俗的說一下它們的區別吧。
①通常一個任務是一個程式的一次執行,一個任務包含一個或多個完成獨立功能的子任務,這個獨立的子任務就是程序或執行緒。
②一個程序可以擁有多個執行緒,每個執行緒必須有一個父程序。
任務
任務是一個邏輯概念,指有一個軟體完成的任務,或者由一系列共同達到某一目的的操作。通常一個任務是一個程式的一次執行,一個任務包含一個或多個完成獨立功能的子任務,這個獨立的子任務就是一個程序或執行緒。任務、程序、執行緒之間的關係如圖
程序
程序的基本概念
程序是指一個具有獨立功能的程式在某個資料集上的一次動態執行的過程,是系統程序資源分配和排程的基本單元。一次任務的執行可以併發啟用多個程序,這些程序相互合作完成該任務的一個最終目標。 作業系統對程序的描述:PCB(程序控制塊)Linux下的程序描述——task_struct
程序具有併發性、動態性、互動性、獨立性、非同步性等主要特性
程序和程式之間的區別:程式是一段程式碼,是一些儲存在儲存器上的指令有序集合,沒有執行的概念;而程序是一個動態的概念,是程式執行的過程,包括動態建立、排程、消亡的整個過程,它是程式執行和資源管理的最小單位。
Linux下的程序結構
程序不但包括程式指令和資料,還包括程式計數器和處理器的所有暫存器及儲存臨時資料的程序堆疊,因此,正在執行的程序包括處理器當前的一切活動
核心將所有程序存放在雙向迴圈連結串列(程序連結串列)中,其中連結串列的頭是init_task描述符。連結串列的每一項都是型別為 task_struct,稱為程序描述符的結構,該結構包含了一個程序相關的所有資訊,定義在<include/linux/sched.h>檔案中。task_struct核心結構比較大,它能完整的描述一個程序,如程序的狀態、程序的基本資訊、程序識別符號、記憶體相關資訊、父程序相關資訊、與程序相關的終端資訊、當前工作目錄、開啟的檔案資訊、所接收的訊號資訊等。
task_struct結構體中最重要的兩個域:state(程序狀態)和pid(程序識別符號),下面就詳細說說這兩個
程序識別符號
Linux核心通過唯一的程序識別符號 PID 來標識每個程序(就和檔案描述符一樣)。PID存放在程序描述符的 pid 欄位中,新建立的 PID 通常是前一個程序的 PID 加1,不過PID的值有上限(最大值=PID_MAX_DEFAULT-1,通常為32767),讀者可以檢視/proc/sys/kernel/pid_max 來確定該系統的程序數上限。
當系統啟動後,核心通常作為某一個程序的代表。一個指向task_struct的巨集current用來記錄正在執行的程序。current經常作為程序描述符結構指標的形式出現在核心程式碼中,例如,current->pid 表示處理器正在執行的程序的PID。當系統需要檢視所有的程序時,則呼叫for_each_process()巨集,這將比系統搜尋陣列的速度要快的多。
檢視程序
程序的資訊可以通過/proc系統資料夾檢視
- 如:要獲取PID為1 的程序,我們需要檢視 /proc/1這個資料夾
- 大多數程序資訊同樣可以使用top和ps這些使用者級工具來獲取
#include<stio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
while(1)
{
sleep(1);
}
return 0;
}
- 通過系統呼叫獲取程序識別符號
在Linux中獲得當前程序號的(PID)和父程序號(PPID)的系統呼叫函式分別為 getpid() 和 getppid()。
下面演示在Linux中獲取程序pid
//getpid獲取當前程序ID
//getppid獲取父程序ID
//pid_t是C語言中使用者自定義型別
//在sys/types.h中定義
//程序識別符號演示
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("pid:%d\n",getpid());
printf("ppid:%d",getppid());
return 0;
}
程序狀態
Linux中的程序有以下幾種狀態:
● 執行狀態(TASK_RUNNING):程序當前正在執行,或者正在執行佇列中等待排程。
● 可中斷的阻塞狀態(TASK_INTERRUPTIBLE):程序處於阻塞(睡眠)狀態,正在等待某些事件發生或能夠佔用某些資源。處在這種狀態下的程序可以被訊號中斷。接收到訊號或被顯式的喚醒呼叫(如呼叫 wake_up 系列巨集:wake_up、wake_up_interruptible等)喚醒之後,程序將轉變為 TASK_RUNNING 狀態。
● 不可中斷的阻塞狀態(TASK_UNINTERRUPTIBLE):此程序狀態類似於可中斷的阻塞狀態(TASK_INTERRUPTIBLE),只是它不會處理訊號,把訊號傳遞到這種狀態下的程序不能改變它的狀態。在一些特定的情況下(程序必須等待,直到某些不能被中斷的事件發生),這種狀態是很有用的。只有在它所等待的事件發生時,程序才被顯示的喚醒呼叫喚醒。
● 可終止的阻塞狀態(TASK_KILLABLE):該狀態的執行機制類似於TASK_UNINTERRUPTIBLE,只不過處在該狀態下的程序可以響應致命訊號。它可以替代有效但可能無法終止的不可中斷的阻塞狀態(TASK_UNINTERRUPTIBLE),以及易於喚醒但安全性欠佳的可中斷的阻塞狀態TASK_INTERRUPTIBLE)。
● 暫停狀態(TASK_STOPPED):程序的執行被暫停,當程序收到 SIGSTOP、SIGSTP、SIGTTIN、SIGTTOU等訊號時,就會進入暫停狀態。
● 跟蹤狀態(TASK_TRACED):程序的執行被偵錯程式暫停。當一個程序被另一個監控時(如偵錯程式使用ptrace()系統呼叫監控測試程式),任何訊號都可以把這個程序置於跟蹤狀態。
● 殭屍狀態(EXIT_ZOMBIE):子程序先於父程序退出,它要保留退出原因在pcb中,因此退出後不會自動釋放所有資源,子程序退出後作業系統通知父程序說子程序退出了,需要去獲取原因,然後釋放子程序資源。假如父程序不管子程序的退出狀態,那麼這個子程序將進入僵死狀態,成為殭屍程序。
● 殭屍撤銷狀態(EXIT_DEAD):這是最終狀態,父程序呼叫 wait 函式族“收屍”後,程序徹底由系統刪除。
程序的建立、執行、終止
fork函式認識
- 執行man fork
- fork 有兩個返回值,若直線成功,在父程序中返回子程序的pid,在子程序中返回0
- 父子程序程式碼共享,資料各自開闢空間
#include<stdio.h>
#include<sys/typees>
#include<unistd.h>
int main()
{
int ret = fork();
printf("hello proc:%d,ret:%d\n",getpid(),ret);
sleep(1);
return 0;
}
- fork 之後通常要用if分流
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int ret = fork();
if (ret < 0)
{
perror("fork");
return 1;
}
else if(ret == 0)
{
printf("i am child:%d,ret:%d\n",getpid(),ret);
}
else
{
printf("i am father :%d,ret:%d\n",getpid(),ret);
}
sleep(1);
return 0;
}
殭屍程序
//zombie.c
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
int main()
{
int pid = fork();
if(pid < 0)
{
perror("fork error");
return -1;
}else ifpid == 0)
{
printf("this is child\n");
}else{
printf("this is parent\n");
}
while(1)
{
sleep(1);
}
return 0;
}
孤兒程序
父程序先於子程序退出,子程序將成為孤兒程序,當子程序成為孤兒程序時,Init程序將會回收,也就是說,父程序將變成init程序,init將負責釋放資源
//orphan.c
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
int main()
{
int pid = fork();
if(pid < 0)
{
return -1;
}else if(pid > 0)
{
printf("this is parent,%d\n",getpid());
}
printf("this is child,%d\n",getpid());
while(1)
{
sleep(1);
}
return 0;
}