進程基礎

學習筆記,小白可以相互學習,大佬看到能告訴咱理解不對的地方就好了。


1.進程相關概念

進程是一個獨立的可調讀的任務

進程是一個程序執行一次的過程

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


進程和程序的區別

程序是靜態的,它是保存在磁盤上的指令的有序集合,沒有任何執行的概念

進程是一動態的概念,它是程序執行的過程,包括創建,調度和消亡。

linux中的進程包含了1.2.3.,程序只有1.2.

進程 1.正文段 程序
1.2.3. 2.用戶數據段 1.2.
  3.系統數據段  
進程不僅僅包含了程序的指令和數據,而且包含了程序計數器值,CPU的所有寄存器值記憶存儲臨時數據的進程堆棧。


2.Linux下的進程

2.1結構

主要的進程標識:進程號,(getpid()得到進程號)  父進程號,(getppid()得到父進程號)

PID唯一地標識一個進程

2.2進程類型

交互類型:該類進程由shell控制和運行。交互進程可以在前臺或者後臺運行。
批處理進程:該類進程不屬於某個終端,他被提交到一個隊列中以便順序執行。

守護進程:該類進程在後臺運行,他一般在Linux啓動時開始運行,系統關閉才結束。

2.3進程運行狀態

運行態(R態):此時進程正在運行,或者準備運行。

等待態:此時程序在等待一個事件的發生或某種系統資源。

可中斷(S)

不可中斷(D)

停止態:此時進程被中止(T)。

死亡態:這是一個已終止的進程,但還在進程向量數組中佔有一個task_struct結構


3.調度進程

ps:查看系統中的進程(ps -elf查看優先級)

top:動態顯示系統中的進程

nice:按用戶指定的優先級運行進程(數字越小,優先級越高,例如nice -5 ./a.out)

renice:改變正在運行進程的優先級

kill:結束進程(包含後臺進程)。(kill - l查看kill的信號,9號信號和2號信號都是結束信號)

bg:將掛起的進程在後臺執行(ctrl + Z可以把一個進程掛起,bg + 作業號)

fg:把後臺運行的進程放到前臺運行(fg + 作業號)


4.函數

4.1fork()進程創建

頭文件 #include<sys/types.h>
#include<unistd.h>
函數原型 pid_t fork(void);
函數返回值 0:子進程
子進程PID:父進程
-1:出錯






fork()函數用於創建一個子進程,子進程是幾乎拷貝了父進程的全部內容。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
	printf("nihao\n");
	pid_t pid;
	if (0 > (pid = fork())) {
		perror("fork");
		return -1;
	}else if (pid == 0) {  //子進程
		printf("child: pid = %d, ppid = %d\n", getpid(), getppid());
	}else {  //父進程
//		sleep(1);
		printf("parent: child_pid = %d, pid = %d\n", pid, getpid());
	}
	printf("hello world!\n");

	return 0;
}

4.2 exec函數族

exec函數族提供了一種在進程中啓動另外一個程序執行的方法。他可以根據指定的文件名或者目錄名找到可執行文件,並用它來取代原調用進程數據段,代碼段和堆棧段。在執行完後,原調用進程的內容除了進程號,其他全被替換。

可執行文件可以是二進制文件,也可以是任何Linux下可執行的腳本文件。

頭文件 #include<unistd.h>
原型 int execv(const char *path,const *arg, argv[]);
  int execle(const char *path,const *arg,..., char *const envp[]);
  int execve(const char *path,char *const argv[],char *const envp );
  int execl(const char *path,const *arg, ...);
  int execlp(const char *file,const *arg, ...);
  int execvp(const char *file,char *const argv[]);
返回值 -1;出錯

p:系統會自動從環境變量“#PATH”所包含的路徑中進行查找。(p爲path,代表shell搜索路徑)

l: list便是逐個列舉的方式

v: vertor表示將所有參數構造成指針數組傳遞

e: 在envp[]中傳遞當前進程所使用的環境變量


例子:

execl:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;;
    }
    else if( pid == 0 )
    {
        if( 0 > execl("/bin/ls","ls","-l",NULL))//第一個參數是文件路徑
        //if( 0 > ("./a.out","./a.out",NULL))
        {
            perror("excel");
            return -1;
        }
    }
    else
    {
        printf("hello\n");
    }
    return 0;

}

execv:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    char *arg[] = {"ls","-l",NULL};
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;;
    }
    else if( pid == 0 )
    {
        if( 0 > execv("/bin/ls",arg))//就是把execl中命令的參數寫入了數組arg
        //if( 0 > ("./a.out","./a.out",NULL))
        {
            perror("excev");
            return -1;
        }
    }
    else
    {
        printf("hello\n");
    }
    return 0;

}

execle:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc,char *argv[],char *envp[])//main函數的第三個參數
{
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;;
    }
    else if( pid == 0 )
    {
        if( 0 > execle("/usr/bin/env","env",NULL,envp))//env命令查看當前系統環境,which查看文件路徑,例如which 1.c
        {
            perror("excele");
            return -1;
        }
    }
    else
    {
        printf("hello\n");
    }
    return 0;

}

execve:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc,char *argv[],char *envp[])
{
    char *arg[] = {"env",NULL};
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;;
    }
    else if( pid == 0 )
    {
        if( 0 > execve("/usr/bin/env",arg,envp))
        {
            perror("exceve");
            return -1;
        }
    }
    else
    {
        printf("hello\n");
    }
    return 0;

}

execlp:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;;
    }
    else if( pid == 0 )
    {
        if( 0 > execlp("env","env",NULL))//第一個參數不用寫全具體路徑,只寫文件名,他會自動搜索
        {
            perror("excelp");
            return -1;
        }
    }
    else
    {
        printf("hello\n");
    }
    return 0;

}

execvp:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
    char *arg[] = {"env",NULL};
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;;
    }
    else if( pid == 0 )
    {
        if( 0 > execvp("env",arg))
        {
            perror("excevp");
            return -1;
        }
    }
    else
    {
        printf("hello\n");
    }
    return 0;

}


4.3exit和_exit

頭文件 exit: #include<stdlib.h>
  _exit: #includ<unistd.h>
原型 exit: void exit(int status)
  _exit: void _exit(int status)
函數傳入值 status是一個整型參數,可以利用這個參數
傳遞進程結束時的狀態。通常用0表示正常
結束:其他的數字表示出現了錯誤,進程非
正常結束。在實際編程中可用wait系統調用
接收子進程的返回值,進行相應處理。


exit和_exit的區別

_exit()函數作用最爲簡單,直接使進程終止運行,清除其使用的內存空間,並且銷燬其在內核的各種數據結構。

exit()會把文件緩衝區的內容寫回文件(就是刷新緩存)。

#include<stdlib.h>
#include<unistd.h>
int main()
{
	printf("hello\n");
	//exit(0);//只會直接打印hello
	_exit(0);//會打印hello world
	printf("world\n");
	return 0;
}


4.4 wait 和 waitpid

wait函數,調用該函數使進程阻塞,直到任意一個子進程結束或者是該進程接收到了一個信號爲止。如果該進程沒有子進程或者子進程已經結束,wait函數會立即返回。

waitpid函數,功能和wait函數類似。可以指定等待某個子進程結束以及等待的方式(非阻塞或者阻塞)。

wait函數
頭文件 #include<sys/types.h>
#include<sys/wait.h>
原型 pid_t wait(int *status)
參數 status是一個整型指針,指向的對象用來保存子
進程退出時的狀態。status若爲空則忽略子進程
退出時的狀態,不爲空則表示子進程退出時的
狀態。子進程結束的狀態可用特定的宏測定,
WIFEXITED(status)判斷子進程是否正常結束,
WEXIT(status)當子進程退出時,得到子進程的
退出值
返回值 成功:子進程的進程號
失敗:-1


waitpid函數
頭文件 #include<sys/types.h>
#include<sys/wait.h>
原型 pid_t waitpid(pid_t pid,int *status,int options)
參數pid pid>0,只等待進程ID等於pid的子進程,不管其他子進程是否已經結束退出了,
只要指定的子進程還沒結束,waitpid就會一直等下去。
  pid=-1,等待任意一個子進程結束,此時和wait的作用一樣
  pid=0,等待其組ID等於調用進程的組ID的任意一子進程
  pid<-1,等待其組ID等於pid的絕對值得任意一子進程
參數status 同wait
參數options WNOHANG:若由pid指定的子進程並不立即可用,則waitpid不阻塞,此時返回0
  WUNTRACED:若某實現支持作業控制,則由pid指定的任意一子進程狀態已
暫停,且其狀態自暫停以來還未報告過,則返回其狀態。
  0:同wait,阻塞父進程,等待子進程退出
返回值 正常:結束的子進程的進程號
使用WNOHANG,且沒有子進程結束時:0
  調用出粗:-1

#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;
    }
    else if( 0 == pid)
    {
        printf("child pid = %d\n",getpid());
        sleep(5);
        printf("child!\n");
    }
    else
    {
        printf("parent\n");
        pid = wait(NULL);
        if(0 > pid)
        {
            perror("wait");
            exit(-1);
        }
        printf("wait :pid = %d\n",pid);
    }
    return 0;
}


#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
    int status;
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;
    }
    else if( 0 == pid)
    {
        printf("child pid = %d\n",getpid());
        sleep(5);
        printf("child!\n");
        exit(5);
    }
    else
    {
        printf("parent\n");
        pid = wait(&status);//得到子進程的結束狀態,作用與前面一樣pid = waitpid(-1,NULL,0)
        if(0 > pid)
        {
            perror("wait");
            exit(-1);
        }
        printf("wait :pid = %d\n",pid);
        if(WIFEXITED(status))//判斷子進程是否正常結束
        {
            printf("child process exit normol\n");
            printf("child exit:%d\n",WEXITSTATUS(status));//當子進程正常退出時,得到子進程的退出值
        }
        else
        {
        
            printf("child process exit unnormol\n");
        }
    }
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
	int status;
	pid_t pid;
	if (0 > (pid = fork())) {
		perror("fork");
		exit(-1);
	}else if (pid == 0) { //子進程
		printf("child: pid = %d\n", getpid());
		sleep(5);
		printf("child\n");
		exit(0);
	}else { //父進程
		printf("parent\n");
		pid = waitpid(-1, NULL, WNOHANG);
		if (pid < 0) {
			perror("wait");
			exit(-1);
		}else if (pid > 0)
			printf("waitpid: pid = %d\n", pid);
		else 
			printf("No child process exit\n");
	}

	return 0;
}

5. 守護進程

守護進程,也就是通常所說的Daemon進程,是Linux中的後臺服務進程。守護進程常常在系統啓動時開始運行,在系統關閉時終止。

在Linux中,每一個系統與用戶進行交流的界面稱爲終端。從終端開始運行的進程都會依附於這個終端,當控制終端被關閉時,相應的進程也會被自動關閉,

但是守護進程可以突破這種限制。如果想讓某個進程不受終端變化的影響,必須把它變成守護進程。

ps -axj :查看系統中的守護進程

ps -ef | grep ./a.out(進程名) :可查看特定進程的信息,可用kill命令結束守護進程

Linux守護進程編寫步驟:

  1. 創建子進程,父進程退出(第一步完成,子進程就在形式上做到了與控制終端的脫離)
  2. 在子進程中創建新會話
  3. 改變當前目錄爲根目錄
  4. 重設文件權限掩碼
  5. 關閉文件描述符
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
#include<time.h>
void init_daemon()//創建守護進程
{
    /**1.創建子進程,父進程退出******************/
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        exit(-1);
    }
    else if(pid > 0)
    {
        exit(0);
    }
    /***2.在子進程中創建會話********************/
    if(0 > setsid())
    {
        perror("setsid");
        exit(-1);
    }

    /***3.改變當前目錄爲根目錄********************/
    if( 0 > chdir("/tmp"))
    {
        perror("chdir");
        exit(-1);
    }

    /****4.重設文件掩碼***********************/
    umask(0);

    /****5.關閉文件描述符**********************/
    int num = getdtablesize();
    int fd;
    for(fd = 0; fd < num; fd++)
    {
        close(fd);
    }
}



int main(int argc,char *argv[])
{
    init_daemon();
    /**創建一個文件,每隔一秒向其中寫入當前時間加行號****/

    int fd;
    if( 0 > (fd = open(argv[1],O_WRONLY | O_CREAT | O_APPEND,0666)))
    {
        perror("open");
        exit (-1);
    }
    write(fd,"hello world\n",sizeof("hello world"));

    int l = 0,ret;
    char a[100];
    time_t t;
    struct tm *tm;
    while(1)
    {
        char buf[100];
        time(&t);
        tm = localtime(&t);
        sprintf(buf,"%2d, %4d-%2d-%2d %2d:%2d:%2d\n",++l,tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);

        write(fd,buf,24);
        
        sleep(1);
    }
    close(fd);


  /*
    int l = 0;
    char buf[100];
    FILE *fp = fopen(argv[1],"ab+");
    if(NULL == fp)
    {
        perror("fopen");
        exit (-1);
    }

    while(NULL != fgets(buf,sizeof(buf),fp))
    {
        if(buf[strlen(buf)-1] == '\n')
        {
            l++;
        }
    }
    
    time_t t;
    struct tm *tm;
    while(1)
    {
        time(&t);
        tm = localtime(&t);
        sprintf(buf,"%2d, %4d-%2d-%2d %2d:%2d:%2d\n",++l,tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
        fputs(buf,fp);
        fflush(fp);
        sleep(1);
    }
    
    fclose(fp);
   */ 
    
    return 0;
}



發佈了31 篇原創文章 · 獲贊 21 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章