進程 · 全家桶

在這裏插入圖片描述

初見進程,先查一下戶口

①進程環境

別吃驚我爲什麼能有個圈圈的①,專用符號嘿嘿。

進程控制塊PCB:就是進程在操作系統中的“戶口”,具體實現是 task_struct數據結構:

1.狀態信息,例如這個進程處於可執行狀態,休眠,掛起等。

2.性質,由於unix有很多變種,進程有自己獨特的性質。

3.資源,資源的鏈接比如內存,還有資源的限制和權限等。

4.組織,例如按照家族關係建立起來的樹(父進程,子進程等)。

②進程狀態

運行狀態R(TASK_RUNNING)

可中斷睡眠狀態S(TASK_INTERRUPTIBLE)

不可中斷睡眠狀態D(TASK_UNINTERRUPTIBLE)

暫停狀態T(TASK_STOPPED或TASK_TRACED)

僵死狀態Z(EXIT_ZOMBIE)

退出狀態X(EXIT_DEAD)

以上兩部分,瞭解即可

③進程原語

3.1、fork
#include <unistd.h>

pid_t fork(void);

功能:子進程複製父進程中的0~3g空間和PCB,但ID號不同。

fork調用一次返回兩次
父進程中返回子進程id (就是大於0的意思)
子進程返回0
讀時共享寫時複製,可保高效

與之相關函數:

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

pid_t getpid(void); //獲取進程ID
pid_t getppid(void); //獲取父進程ID

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

uid_t getuid(void);//返回實際用戶ID
uid_t geteuid(void);//返回有效用戶ID

進程的產生方式:

進程的產生有多種方式,但是追本溯源是相通的。

(1)複製父進程的系統環境(放心,只要是你開的進程,肯定有父進程)
(2)在內核中建立進程結構
(3)將結構插入到進程列表,便於維護
(4)分配資源給該進程
(5)複製父進程的內存映射消息
(6)管理文件描述符和鏈接點
(7)通知父進程

下面是一張進程列表的圖,命令:pstree。
在這裏插入圖片描述

可以看到init是所有進程的父進程,其他進程都是有init進程直接或間接fork出來的。

3.2、exec族

爲什麼需要exec函數?

fork子進程是爲了執行新程序(fork創建了子進程後,子進程和父進程同時被OS調度執行,因此子進程可以單獨的執行一個程序,這個程序宏觀上將會和父進程程序同時進行)

可以直接在子進程的if中寫入新程序打代碼。但這樣不夠靈活,因爲我們只能把子進程程序的源代碼貼過來執行(必須知道源代碼,而且源代碼太長了也不好控制)
使用exec族函數運行新的可執行程序。exec族函數可以直接把一個編譯好的可執行程序直接加載運行。

有了exec族函數後,典型打父子進程程序是這樣的:子進程需要運行的程序被單獨編寫、單獨編譯鏈接成一個可執行程序(hello)。主進程爲父進程,fork創建了子進程後在子進程中exec來執行hello,達到父子進程分別做不同程序同時(宏觀上)運行的效果。

在我的印象中,我有一篇博客專門講解exec族,就那麼一找,還真有:exec族

代碼貼這兒,可以進那篇看更詳細,也可以在這裏看:

#include<unistd.h>

int execve(const char *path,char *const argv[],char *const envp[]);//這個是真正的系統調用
//以下的函數最後都是調用這個函數

int execl(const char *path,char *const argv,···);
int execlp(const char *file,char *const argv,···);
int execle(const char *path,char *const argv,···· ,char *const envp[]);
int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv,);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(void)
{
    pid_t pid;
    if((pid = fork()) < 0)
    {
        perror("fork");
        exit(1);
    }
    else if(pid == 0)
    {
        /* child */
        execl("/bin/ls", "ls", "-l", "-a",NULL);
    }
    else
    {
        printf("parent, child id = %d.\n",pid);
    }
    return 0;
}

3.3、wait/waitpid

這裏幾個概念:

殭屍進程:子進程退出,父進程沒有及時回收,子進程成爲殭屍進程
孤兒進程:父進程退出,而子進程沒有退出,子進程成爲孤兒進程
init進程:1號進程,負責收留孤兒進程,成爲他們的父進程

有5種方式終止進程:
(1)main返回
(2)調用exit
(3)調用_exit
(4)調用abort(給自己發送異常終止信號)
(5)由一個信號終止
#include<sys/types.h>
#include<sys/wait.h>

pid_t wait(int *status);
//這裏的status爲一個整形指針,是該子進程的返回狀態。若該指針不爲空,則可以通過該指針獲取子進程退出時的狀態。

pid_t waitpid(pid_t pid,int *status,int options);
// pid是進程號
/*
	<-1 回收指定進程組內的任意子進程
	-1 回收任意子進程
	0 回收和當前waitpid調用一個組的所有子進程
	>0 回收指定ID的子進程
*/
//options:
/*
WNOHANG:強制回收,不阻塞。
WUNTRANCED:一般用上面那個
*/

來個聯繫方式吧,進程間通信

常用的通信方式有:管道、消息隊列、共享內存、文件空間映射。

管道:兩個進程間通信,最古老的通信方式了。

消息隊列:在內核中建立一個鏈表,發送方按照一定標識將數據發送到內核中,內核將其放入鏈表。
()接收方發送請求後,內核按照標識取出消息。
()消息隊列是一種完全異步的通信方式。

共享內存:共享內存是將內存中的一段地址,在多個進程間共享。多個進程通過掛載在自己名下的地址直接對共享內存進行操作。

文件空間映射:mmap函數用來將文件或設備空間映射到內存中,可以通過對映射後的內存空間操作來獲得與操作文件一致的效果。

這塊如果要展開的話,篇幅會很長,很長,所以我做了一個目錄表:
想要進程的聯繫方式?點這裏

進程間同步

進程間同步的方法主要有system信號量和進程間鎖,信號量我會在後面的文章再行整理,進程間鎖嘛,
進程間鎖

家庭關係如何?(進程間關係)

①進程組

一個或多個進程組成的集合,進程組的組ID是一個正整數。

//獲取當前進程組組ID

pid_t getpgid(pid_t pid);
pid_t getpgrp(void);

幾個概念:
組長進程:進程ID號等於組ID。
組長進程可以創建一個進程組,創建該進程組中的進程。
只要進程中有一個進程存在,進程組就存在,與組長進程是否終止無關。
進程組生存期:進程組創建到最後一個進程離開(終止或轉移到另一個進程組)

//一個進程可以爲自己或子進程設置進程組ID

int setpgid(pid_t pid,pid_t pgid);

//非root進程只能改變自己創建的子進程,或有權限操作的進程

②會話

pid_t setsid(void);

1、調用進程不能是進程組組長,該進程變成新會話的首進程。
2、該進程成爲一個新進程組的組長進程。
3、需要有root權限(ubunt不需要)
4、新會話丟棄原有控制終端,該會話沒有控制終端。
5、建立新會話時,先用fork,然後父進程終止,子進程調用 。

pid_t getsid(pid_t pid);

用於查看當前進程的會話ID

注意點:組長進程不能成爲新會話首進程,新會話首進程必定成爲組長進程。

來串僞代碼:

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
	pid_t pid;
	pid = fork();

	if(pid == 0)
	{
		//打印:
		getpid();//進程ID
		getpgid(0);//組ID
		getsid(0);//會話ID
		
		sleep(10);
		
		setsid();//子進程設爲會話組長
		//子進程非組長進程,故其成爲新會話首進程,且成爲組長進程。
		//該進程ID即爲會話進程ID
		
		//再打印一遍
		getpid();//進程ID
		getpgid(0);//組ID
		getsid(0);//會話ID
	}
}

守護者

守護進程

這篇我還想留着呢,所以就貼個鏈接吧,刪了怪可惜的。

程序、進程與線程的區分

這個問題老師問過我,當時我沒答上來。

1)進程是動態的,程序是靜態的。
(2)一個進程只能對應一個程序,而一個程序可以對應多個進程。

從操作系統方面來看,進程代表的是操作系統分配的內存、CPU時間片等資源的基本單位,是爲正在運行的程序準備的運行環境。

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