3.Linux應用編程——進程

進程的相關基本概念:
進程是一個獨立的可調度的任務
進程是一個抽象實體。當系統在執行某個程序時,分配和釋放各種資源。

操作系統特點:
多任務、多用戶、分時性

多任務的操作系統分爲:搶佔式、非搶佔式
搶佔式:多任務多用戶的操作系統具有絕對的控制權來控制每個任務可以使用CPU的時間。

進程和程序的區別:
程序是靜態的,它是一些保存在磁盤上的指令的有序集合,沒有任何執行的概念
進程是一個動態的概念,它是程序執行的過程,包括創建(通過函數接口創建)、調度(用戶不參與)、執行(CPU正在讀程序的內存空間)、消亡(釋放內存)。

進程是程序執行和資源管理的最小單位

進程包含的資源:代碼段(.text)、數據段(.data .bss .radata)、堆、棧、寄存器(pc)
注:代碼段存放的內容告訴cpu一系列的動作
分類
IO消耗型:某一類任務需要從磁盤或者是通過鼠標來進行輸入輸出操作
需要時常分配時間片,每次分配的時間片都比較短
處理器消耗型:通常情況下指的是維持系統正常運行的後臺任務
不需要時常分配時間片,每次分配的時間片都比較長

linux系統中的進程的類型
交互進程:和終端相關,可以前臺可以後臺。最大的生命週期是從執行開始到終端關閉
前臺進程可以輸入,如果想要殺後臺進程不能使用kill -2 進程號(kill -2 相當於ctrl c),可以使用信號9(kill -9)。
守護進程:和終端不相關,一定是後臺進程,最大生命週期是從執行開始到系統關閉。

pts/1:虛擬終端(跟終端相關,printf打印在終端上,若爲?則不相關)
注:第一行中左邊的13428是進程本身的PID,1是進程的父進程PID

查看進程狀態、;ps -aux或者是ps -axj
進程三態

進程因創建而就緒,因調度而執行;因時間片用完而重新就緒;
執行中因I/O請求而阻塞;
I/O完成而就緒
注意:阻塞以後不能直接執行,必須進入就緒狀態。

linux進程的運行狀態:

運行狀態(TASK_RUNNING)(R)
指正在被CPU運行或者就緒的狀態。這樣的進程被成爲runnning進程。運行態的進程可以分爲3種情況:內核運行態、用戶運行態、就緒態。

可中斷睡眠狀態(TASK_INTERRUPTIBLE)(S)
處於等待狀態中的進程,一旦被該進程等待的資源被釋放,那麼該進程就會進入運行狀態。

不可中斷睡眠狀態(TASK_UNINTERRUPTIBLE)(D)(應用層基本不用,在底層用於保護機制)
該狀態的進程只能用wake_up()函數喚醒。

暫停狀態(TASK_STOPPED)(T)
當進程收到信號SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU時就會進入暫停狀態。可向其發送SIGCONT信號讓進程轉換到可運行狀態。
注:暫停狀態一定是後臺。

僵死狀態(TASK_ZOMBIE)(Z)(子進程退出,父進程沒有回收資源,資源是使用內核中的task_struct描述)(儘量避免)
當進程已經終止運行,但是父進程還沒有詢問其狀態的情況。
孤兒進程:父進程退出,子進程沒有退出但是會被init收養,會由交互進程變成守護進程
注:

狀態號:
<:代表高優先級
N:低優先級
s:會話組長
注:會話指一個或多個進程組,進程組,包含一個或多個進程
+:前臺進程
l:多線程
NI:優先級(-20 ~ 19)-20最高 19最低

暫停進程 kill -19 進程號
使暫停進程繼續 kill -18 進程號
fg:將颳起的進程在後臺執行
bg:把後臺運行的進程放到前臺運行

進程的編程相關:
進程創建
pid_t fork(void);
返回值:
創建子進程1的fork將0返回給子進程,將創建子進程的PID返回給父進程。
出錯返回-1.
實踐1——瞭解fork函數的處理機制
程序:

#include <stdio.h>
int main(int argc, const char *argv[])
{
    fork();
    fork();
    printf("hello!\n");
    return 0;
}

輸出結果:
leo_walker@leo-walker:~/linux_app/process_1./a.outhello!leowalker@leowalker: /linuxapp/process1 hello!
hello!
hello!
總結:子進程從它被創建的fork後一句代碼開始運行,執行順序是隨機的。

實踐2——證明數據段和棧區的繼承
程序:

#include <stdio.h>
#include <unistd.h>
int x=100;
int main(int argc, const char *argv[])
{
    pid_t pid;
    int c=100;
    if((pid = fork())==-1)
    {
        perror("fail of fork");
        return -1;
    }
    else if(pid == 0)//子進程
    {
        c++;
        x++;
        printf("child c:%d\n",c);
        printf("child x:%d\n",x);
        printf("child c:%p\n",&c);
    }
    else//父進程
    {
        c++;
        x++;
        printf("parents c:%d\n",c);
        printf("parents x:%d\n",x);
        printf("parents c:%p\n",&c);
    }
    return 0;
}

結果:
parents c:101
parents x:101
parents c:0x7ffcc113ca08
leo_walker@leo-walker:~/linux_app/process_1$ child c:101
child x:101
child c:0x7ffcc113ca08

注:父子進程共享代碼段
數據區、堆、棧可能是繼承的,也可能是獨立的。具體要看代碼所處於的位置,若處於父子進程的判斷方式內部,則是獨立的,若是在其外部則是繼承的。
繼承(複製)出來的變量地址爲什麼一樣?
打印出來的地址是虛擬電子,物理地址實際上是不一樣的,虛擬地址一共32位,前10位是作爲下標來用的,中間10位下標找到頁,剩餘的12位做偏移,得到的就是物理地址空間.

實踐3——顯示父進程和子進程的fork的返回值、本身的進程號和其父進
程序:

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

int main(int argc, const char *argv[])
{
    pid_t pid;

    pid=fork();
    if(pid==-1)
    {

    }
    else if(pid==0)
    {
        printf("child pid:%d  PID:%d  PPID:%d\n",pid,getpid(),getppid());   
    }
    else
    {
        printf("father pid:%d  PID:%d  PPID:%d\n",pid,getpid(),getppid());  
    }
    while(1);
    return 0;
}

結果:
father pid:12175 PID:12174 PPID:2941
child pid:0 PID:12175 PPID:12174
注:getpid()獲得當前進程進程號(不要描述爲獲得子進程進程號)
getppid()獲得當前進程的父進程號

exec函數族:
exec函數族可以節省內存空間。
exec函數族要以空結尾。

主要搞懂下面這兩個函數就可以,殊途同歸;
execl:可調用其他可執行空間,調用的可執行程序會替換掉之前進程的進空間

execv:與execl其實用法是一樣的

因爲使用exec函數族會使調用函數之後的空間被清空,所以若是想繼續運行之後的代碼,可以使用一下函數來代替exec函數族來實現。
system(“ls -l 1.c”);
執行ls -l命令,再接上1.c

exit函數和_exit函數都可以提前退出進程,exit()退出時刷新緩存區,_exit()退出時不刷新緩存區。
return與exit的區別在return只能結束一個函數,而exit是結束一個進程。

殭屍進程的產生
如果子進程先退出,父進程還沒退出,那麼子進程必須等到父進程捕捉到子進程的退出狀態才能真正結束,否則這個子進程就會變成殭屍進程。
實踐4——殭屍進程的產生
程序:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
    pid_t pid;
    pid = fork();

    if(pid<0)
    {
        perror("fork");
        exit(1);
    }
    else if(pid==0)
    {
        exit(1);
    }
    else 
    {
        while(1);
    }
    return 0;
}

結果:
leo_wal+ 13347 62.0 0.0 4188 356 pts/0 R+ 19:28 0:01 ./a.out
leo_wal+ 13348 0.0 0.0 0 0 pts/0 Z+ 19:28 0:00 [a.out]

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
    int status;
    pid_t pid,ret_pid;
    pid = fork();
    if(pid<0)
    {
        perror("fork");
        exit(1);
    }
    else if(pid==0)
    {
        sleep(3);
        exit(1);
    }
    else 
    {
        sleep(5);
        ret_pid=wait(&status);
        printf("ret_pid:%d status:%d\n",ret_pid,status);
        while(1);
    }
    return 0;
}

結果:
子進程結束前
2941 13569 13569 2941 pts/0 13569 S+ 1000 0:00 ./a.out
13569 13570 13569 2941 pts/0 13569 S+ 1000 0:00 ./a.out
返回的ret_pid爲13570,也就是子進程的PID
子進程接收後,並且wait接收到status
2941 13569 13569 2941 pts/0 13569 R+ 1000 0:07 ./a.out

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章