Linux 進程控制

相關概念

  • 程序: 編譯好的二進制文件, 存放在磁盤上(占用的是物理內存空間), 不占用系統資源(CPU, 內存, 打開的文件, 設備, 鎖...)
  • 進程: 抽象的概念, 與操作系統原理聯系緊密, 是活躍的程序, 占用系統資源, 在內存中執行.
    由原來的單道程序設計轉換成多道程序設計, 是CPU分成若干的時間碎片, 各個進程搶占資源去執行, 雖然程序有優先級高低, 但是只是搶占到的幾率高; 即使是多核, 也是需要時才會開啟其他核.
    處理速度:寄存器->cache->內存->硬盤->網絡->存儲介質
    其他概念: 串行和并發, CPU和MMU
  • 進程控制塊 PCB 在/usr/src/linux-headers-3.15.0-30/include/linux/sched.h文件中可以查看struct task_struct 結構體定義, 包含部分定義: 進程id, 進程狀態, 進程切換時需要保存和回復的CPU寄存器, 描述虛擬的地址空間信息, 當前工作目錄, 文件描述符表, 用戶id和組id, 會話和進程組, 資源上限(命令:ulimit -a 查看), umask掩碼...
  • 進程狀態: 初始態(創建時, 非常短暫), 就緒態(有執行資格, 但是沒執行權限, 需要搶占資源), 運行態(正在執行), 掛起態(沒有執行資格和執行權限), 終止態
    點我查看進程狀態圖

進程控制

程序默認的是單進程的; 下面介紹多進程的使用
創建進程函數: pid_t fork(void);(查看 Linux下使用命令:man 2 fork)
點擊查看描述圖
返回值: 失敗返回-1, 父進程返回子進程ID, 子進程返回0
查看當前進程 ps aux | grep "要查找的程序"; ps ajx

父子進程.png

剛fork完成時(父子進程之間) 1. 0-3G地址空間相同 2. PCB相同, 但是pid除外 3.其他內容由mmu映射到物理內存上, 讀時共享, 寫時復制

獲取pid 和 ppid 對應的函數是getpid(); getppid();

//wait 函數回收子進程
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
    pid_t pid;
    pid = fork();
    
    if(pid == -1) {
        perror("fork error");
        exit(-1);
    } else if(pid > 0) {
        printf("parent process\n");
    } else if(pid == 0) {
        printf("child process\n");
    }

    printf("mul process\n");
    return 0;
}

回收進程

進程有孤兒進程, 僵尸進程

  1. 孤兒進程 : 子進程活著, 父進程死了, linxu中的init進程會領養孤兒
  2. 僵尸進程 : 子進程死了, 父進程正在忙, 沒有去回收子進程
  3. 進程回收 : 代碼附在下邊

wait - 阻塞函數
pid_t wait(int* status);
調用一次回收一個子進程資源
返回值:

0: 回收的子進程的pid;
-1: 沒有子進程可以回收了;
status -- 傳出參數
獲取退出時候的返回值

  • 正常退出( return num; exit(num); exit(num);
    )
    WIFEXITED(status) > 0

獲取返回值: WEXITSTATUS(status)

  • 被信號殺死
    WIFSIGNALED(status) > 0

獲取殺死子進程的信號
WTERMSIG(status)

waitpid -- pid_t waitpid(pid_t pid, int *status, int options);
(可以設置非阻塞, 提高wait的效率)
pid: -1: 所有的子進程
0: 當前進程組的子進程

0(pid): 回收指定進程的pcb
-pid: 回收不在當前進程組的子進程

opttions: 阻塞: 0
非阻塞: WNOHANG 可以設置為非阻塞
可以有針對性的回收某一個子進程資源 ;需要注意的是即使是非阻塞, 也需要循環來回收子進程

//wait函數回收子進程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/wait.h>


int counter = 100;

int main(int argc, const char* argv[])
{
    int number = 5;

    int i;
    pid_t pid;

    for(i=0; i<number; ++i)
    {
        pid = fork();
        if(pid == 0)
            break;
    }

    if(i<number)
    {
        // 子進程
        counter += 100;
        printf("child pid = %d, ppid = %d\n", getpid(), getppid());
        printf("counter = %d\n\n", counter);
        //sleep(100);
        exit(9);
    }
    else if(i == number)
    {
        counter += 300;
        printf("parent pid = %d, ppid = %d\n", getpid(), getppid());
        printf("counter = %d\n\n", counter);
        sleep(1);

        // 回收子進程
        int status;
        pid_t wpid;
        while( (wpid=wait(&status)) != -1)
        {
            // 正常退出
            if(WIFEXITED(status))
            {
                printf("porcess exit by number: %d\n", WEXITSTATUS(status));
            }
            // 被信號殺死
            else if(WIFSIGNALED(status))
            {
                printf("process kill by signal: %d\n", WTERMSIG(status));
            }
            printf("child died pid = %d\n", wpid);
        }
    }
    

    return 10;
}

//驗證父子進程是否文件共享
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, const char* argv[]) {
    int fd = open("temp", O_CREAT | O_RDWR, 0664);
    if(fd == -1) {
        perror("open error");
        exit(1);
    }

    pid_t pid = fork();
    if(pid == -1)  {
        perror("fork error");
        exit(1);
    }

    if(pid > 0) {
        char* p = "123123123";
        write(fd, p, strlen(p)+1);
    } else if(pid == 0) {
        // 睡1s保證父進程已經完成了文件的寫操作
        sleep(1);
        char buf[1024];
        lseek(fd, 0, SEEK_SET);
        int len = read(fd, buf, sizeof(buf));
        printf("%s\n", buf);
    }
    close(fd);
    return 0;
}
//子進程執行不同任務, 父進程負責回收子進程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    int i = 0;
    int number = 3;
    pid_t pid;

    for(i = 0; i<number; ++i)
    {
        pid = fork();
        if(pid == 0)
        {
            break;
        }
    }

    // 父進程
    if(i == number)
    {
        sleep(2);
        // 回收子進程
        pid_t wpid;
        int status;
        while( (wpid = waitpid(0, &status, WNOHANG)) != -1 )
        {
            if(wpid == 0)
            {
                continue;
            }
            printf("child pid = %d\n", wpid);
            if(WIFEXITED(status))
            {
                printf("return number: %d\n", WEXITSTATUS(status));
            }
            else if(WIFSIGNALED(status))
            {
                printf("exit by signal: %d\n", WTERMSIG(status));
            }
        }
    }
    else if(i == 0)
    {
        execl("/home/kevin/test/app", "app", NULL);
    }
    else if(i == 1)
    {
        execl("./error", "error", NULL);
    }
    else if(i == 2)
    {
        execlp("ps", "ps", "aux", NULL);
    }

    printf("over......\n");
    return 0;
}

//waitpid 來回收子進程, 注意非阻塞時也需要循環去回收
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    int num = 3;
    int i = 0;
    pid_t pid;

    for(i=0; i<num; ++i)
    {
        pid = fork();
        if(pid == 0)
        {
            break;
        }
    }

    if(i == 0)
    {
        execlp("ps", "ps", "aux", NULL);
        perror("execlp ps");
        exit(1);
    }
    else if(i == 1)
    {
        execl("/home/kevin/test/app", "app", NULL);
        perror("execl app");
        exit(1);
    }
    else if(i == 2)
    {
        execl("./error", "error", NULL);
        perror("execl error");
        exit(1);
    }
    else if(i == num)
    {
        // 回收
        int status;
        pid_t wpid;
        while( (wpid = waitpid(-1, &status, WNOHANG)) != -1 )
        {
            if(wpid == 0)
                continue;
            printf(" ----- child died pid = %d\n", wpid);
            if(WIFEXITED(status))
            {
                printf("return value %d\n", WEXITSTATUS(status));
            }
            else if(WIFSIGNALED(status))
            {
                printf("died by signal: %d\n", WTERMSIG(status));
            }
        }
    }
    return 0;
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 又來到了一個老生常談的問題,應用層軟件開發的程序員要不要了解和深入學習操作系統呢? 今天就這個問題開始,來談談操...
    tangsl閱讀 4,172評論 0 23
  • 進程創建 普通函數調用完成后,最多返回(return)一次,但fork/vfork會返回二次,一次返回給父進程,一...
    陳偉志閱讀 609評論 0 3
  • fork 函數生成子進程 輸出 創建成功。 父進程返回子進程的 pid=6437,子進程返回的pid=0。 父進程...
    謝小帥閱讀 306評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,933評論 18 139
  • 作用域 在JS中(非ES6),只有函數作用域,沒有塊作用域。例如,for循環,while等{}內部的變量其實是和外...
    DeeJay_Y閱讀 340評論 0 0