Linux進程,linux進程查看
Linux進程
進程是Linux中交易管理的基本單元,所有的進程都擁有自己的獨立處理環境和系統資源,並且各進程之間不可以直接存取對方的資源,進程之間的交流需要通過特定的機制(IPC)。
在Linux系統的核心標頭檔中(/usr/src/kernels/核心版本/include/linux/sched.h)定義了進程式控制制塊(PCB)結構體struct task_struct來管理每個進程的資源。
進程資源分為兩個部分:核心空間進程資源、使用者空間進程資源
核心空間進程資源:PCB的相關資訊,控制塊本身、開啟的檔案表項、目前的目錄、當前終端資訊、線程基本資料、可訪問的記憶體空間、PID、PPID、UID、EID等。
使用者進程空間資源:通過成員mm_struct映射的記憶體空間,實際上就是進程的程式碼片段、資料區段、堆、棧、可訪問的共用庫記憶體空間。這些資源在進程退出的時候主動釋放。在進程運行時,可以通過查
看/proc/{pid}/maps檔案查看可以訪問的地址空間。
進程的狀態:就緒、運行、等待(可中斷、不可中斷)、停止、僵死
在/usr/src/kernels/核心版本/include/linux/sched.h標頭檔中定義了進程的狀態
/* * Task state bitmask. NOTE! These bits are also * encoded in fs/proc/array.c: get_task_state(). * * We have two separate sets of flags: task->state * is about runnability, while task->exit_state are * about the task exiting. Confusing, but this way * modifying one set can't modify the other one by * mistake. */#define TASK_RUNNING0#define TASK_INTERRUPTIBLE1#define TASK_UNINTERRUPTIBLE2#define __TASK_STOPPED4#define __TASK_TRACED8/* in tsk->exit_state */#define EXIT_ZOMBIE16#define EXIT_DEAD32/* in tsk->state again */#define TASK_DEAD64#define TASK_WAKEKILL128#define TASK_WAKING256#define TASK_PARKED512#define TASK_STATE_MAX1024
進程之間狀態的轉換圖如下:
進程的屬性
進程本身的屬性:PID(進程號)、PPID(父進程號)、PGID(進程組號)、SID(會話session ID)
調用getpid()函數可以擷取當前進程的PID,該函數在/usr/include/unistd.h標頭檔中聲明;
調用getppid()函數可以擷取當前進程父進程的PID,該函數在/usr/include/unistd.h標頭檔中聲明;
調用getpgid(_pid_t _pid)函數可以擷取指定進程_Pid的進程組號,該函數在/usr/include/unistd.h標頭檔中聲明;
調用getsid(_pid_t _pid)函數可以擷取指定進程_Pid的會話號SID(會話是一個或者多個進程的集合),該函數在/usr/include/unistd.h標頭檔中聲明;
進程的使用者屬性:RUID(真實使用者ID)、RGID(真是使用者組ID)、EUID(有效使用者ID)、EGID(有效使用者組ID)
調用getuid()函數可以擷取該進程的真實使用者RUID(執行此程式的使用者,建立該進程使用者的UID為此進程的RUID),該函數在/usr/include/unistd.h標頭檔中聲明;
調用geteuid()函數可以擷取有效使用者ID,該函數在/usr/include/unistd.h標頭檔中聲明;
調用getgid()擷取進程的使用者組ID(建立進程的使用者所在的組號),該函數在/usr/include/unistd.h標頭檔中聲明;
調用getegid()擷取有效進程使用者組ID,該函數在/usr/include/unistd.h標頭檔中聲明;
進程的管理
建立進程fork 和 vfork
在Linux環境下,建立進程的主要方法是調用fork函數。Linux下的所有進程都是由init進程(第一個進程PID=1)直接或者間接的建立,fork函數在/usr/include/unistd.h標頭檔中聲明;
若fork函數調用,那麼將產生兩個進程分支(父進程和子進程),在父進程中返回子進程的ID(大於0),在子進程中返回0;若調用失敗,在父進程中返回0;
下面是建立調用fork函數的程式
#include<stdio.h>#include<unistd.h>int main(){ pid_t pid; if( (pid=fork()) == -1 ) //call fork() and test if succeed { printf("fork error!"); return -1; } else if(pid == 0) //in sub thread { printf("This is sub thread.\n"); printf("in sub : My PID = %d\n",getpid()); printf("in sub : My PPID = %d\n",getppid()); } else //in father thread { printf("This is father thread.\n"); printf("in father : My PID = %d\n",getpid()); } return 0;}
子進程與父進程
子進程將複製父進程使用者空間的所有資訊(檔案緩衝區、程式碼片段、資料區段、BSS段、堆、棧、檔案描述符),其中複製的僅僅是檔案描述符,但是對於與檔案描述符關聯的核心檔案表項(struct file結構體)則是採用的是共用的方式。
下面的程式中,雖然子進程複製了父進程的檔案描述符fd,但是和父進程操作的是同一個檔案
#include <sys/types.h>#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <stdlib.h>int main(int argc,char *argv[]){ pid_t pid; int fd; int i=1; int status; char *ch1="hello"; char *ch2="world"; char *ch3="IN"; if((fd=open("test.txt",O_RDWR|O_CREAT,0644))==-1)//開啟一個檔案 { perror("parent open"); exit(EXIT_FAILURE); } if(write(fd,ch1,strlen(ch1))==-1)//父進程向檔案中寫入字串1 { perror("parent write"); exit(EXIT_FAILURE); } if((pid=fork())==-1)//建立一個子進程 { perror("fork"); exit(EXIT_FAILURE); } else if(pid==0) //在子進程中 { i=2; printf("in child\n"); printf("i=%d\n",i); if(write(fd,ch2,strlen(ch2))==-1)//子進程複製了父進程的fd,然後向檔案中寫入字串2 perror("child write"); return 0; } else { sleep(1);//休眠1秒鐘,等待子進程執行完畢 printf("in parent\n"); printf("i=%d\n",i); if(write(fd,ch3,strlen(ch3))==-1)//父進程寫入字串3 perror("parent,write"); wait(&status);//等待子進程結束 return 0; }}
使用vfork建立進程
使用fork建立進程會複製父進程使用者空間的大量資料,有些情境這樣做是極大的浪費,所以使用vfork建立新進程時並不複製父進程的地址空間,而是在必要的時候才申請新的儲存空間,vfork比fork可以極大程度的提高效能。vfork只在需要的時候覆制,一般採用和父進程共用所有資源的方式處理。
下面的程式說明了vfork建立的子進程和父進程共用資料區段和程式碼片段。
#include<unistd.h>#include<error.h>#include<sys/types.h>#include<stdio.h>#include<stdlib.h>int glob=6;int main(){ int var; pid_t pid; var=88; printf("in beginning:\tglob=%d\tvar=%d\n",glob,var); if((pid=vfork())<0) { perror("vfork"); exit(EXIT_FAILURE); } else if(pid==0) { printf("in child,modify the var:glob++,var++\n"); glob++; var++; printf("in child:\tglob=%d\tvar=%d\n",glob,var); _exit(0); } else { printf("in parent:\tglob=%d\tvar=%d\n",glob,var); return 0; }}
上述程式共用glob和var變數,如果把上述中的vfork改為fork結果將會不一樣。
結束進程
結束進程意味著要回收進程的資源,又可以分為回收進程使用者空間的資源和回收進程核心空間的資源
回收使用者進程空間的資源
進程在正常退出前都需要執行退出處理函數,重新整理流緩衝區等操作,然後釋放使用者進程空間的所有資源,而進程在核心中的資源PCB並不會立即釋放,僅僅調用退出函數而沒有回收核心資源PCB的進程是一個僵死進程。
顯示的調用exit或者_exit系統調用可以結束進程。調用exit將以反序的方式執行由on_exit函數和atexit函數註冊的清理函數,同時重新整理緩衝區,並把退出狀態status返回給父進程。_exti是直接退出,僅僅把退出狀態交給父進程,不會進行清理工作和重新整理緩衝區。
注意:exit和return的區別,exit退出當前進程,而return僅僅退出當前函數。
回收核心空間的資源
回收核心空間的資源不是由退出進程本身完成的,而是由該進程的父親進程完成的。調用wait函數的父進程將阻塞式的等待該進程的任意一個子進程結束後,回收該子進程的核心空間的資源,wait函數定義在/usr/include/sys/wait.h標頭檔中。還可以使用waitpid函數等待指定子進程結束,其定義在/usr/include/sys/Wait.h中
孤兒進程和僵死進程
孤兒進程:因為父進程退出而導致一個子進程被init進程收養的進程稱為孤兒進程,即孤兒進程的父進程改為init進程。
僵死進程:進程已經退出(使用者空間的資源已經被回收),但是其父親進程還沒有回收核心資源的進程稱為僵死進程,即該進程在核心空間的PCB沒有被釋放。
下面的程式示範了一個孤兒進程
#include<stdio.h>#include<stdlib.h>#include<unistd.h>int main(){ pid_t pid; if((pid=fork())==-1) perror("fork"); else if(pid==0) { printf("sub : pid=%d,ppid=%d\n",getpid(),getppid()); sleep(2); printf("sub : pid=%d,ppid=%d\n",getpid(),getppid()); } else { printf("father : pid=%d,ppid=%d\n",getpid(),getppid()); sleep(1); exit(0); }}
下面的程式示範了一個僵死進程
#include<stdio.h>#include<unistd.h>#include<stdlib.h>int main(){ pid_t pid; if((pid=fork())==-1) perror("fork"); else if(pid==0) { printf("child_pid pid=%d\n",getpid()); exit(0); } sleep(3); system("ps"); exit(0);}
程式的運行結果:
運行結果中的PID=10375的進程僵死,如果此時開啟/proc/10375/maps(該進程的使用者進程空間資訊),可以發現為空白,也就是使用者進程空間資源已經被回收。
守護進程
守護進程是一種在後台啟動並執行特殊進程,它脫離終端,從而可以避免守護進程被任何終端訊號打斷,它執行產生的資訊也不在任何終端顯示。守護進程周期性的執行某種任務或等待處理某些發生的事件,Linux的大多數伺服器就是使用守護進程實現的,比如web伺服器httpd等。
一般情況下,守護進程可以通過以下方式啟動:
(1)在系統啟動時啟動指令碼,通常放在/etc/rc.d目錄中;
(2)利用inet超級伺服器啟動,如telnet等;
(3)由cron命令定時啟動以及在終端用nohup命令啟動的進程也是守護進程。
建立一個守護進程需要遵循以下要點:
(1)屏蔽一個有關控制終端操作的訊號
(2)在後台運行。方法是在進程中建立子進程,並使父進程終止,使其在子進程中後台運行。
(3)脫離終端控制和進程組,這是因為一個進程屬於一個進程組,同進程組的進程共用一個控制終端,而一個進程關聯的控制終端和進程組通常是從父進程繼承下來的,因此子進程仍然受到父進程終端的影響。可以使用setsid()使子進程成為一個新的交談群組長和進程組長,與原來的進程組脫離關係。
(4)禁止進程重新開啟控制終端。進程已經成為一個無終端的交談群組長,但是仍然有許可權申請一個終端,只有交談群組長才能開啟終端。採用的辦法是再建立一個子進程,並讓父進程退出,該子進程就不再是交談群組長了,而且父進程也沒有終端控制。
(5)關閉開啟的檔案描述符。子進程會從父進程複製檔案描述符,這對守護進程沒有意義,反而會浪費系統資源,造成進程所佔的檔案系統無法卸載。
(6)改變當前工作目錄。進程活動的時候,進程工作目錄所在的檔案系統不能卸載,因此一般將守護進程的工作目錄設定為合適的目錄。
(7)重設檔案建立的掩碼。進程從父進程那裡繼承檔案建立掩碼umask,它可能修改守護進程所建立的檔案的儲存許可權,一般將檔案建立掩碼清除。
(8)處理SIGCHLD訊號(子進程退出訊號)。
怎查看linux上所有的進程
ps -aux 查看所有進程
top和uptime 可以查看你的系統運行狀態
如果你是想查看具體進程或者說服務的監聽連接埠等
你可以使用 netstat -an | grep ‘你的服務,或者連接埠’ 等
LINUX直接殺進程
kill -9 進程號
先 ps -ef | more 看進程情況
如果知道進程名稱 也可以用
ps -ef| grep 進程名稱
找到第二列 就是進程號 ,輸入 kill -9 進程號