學習筆記,小白可以相互學習,大佬看到能告訴咱理解不對的地方就好了。
1.進程相關概念
進程是一個獨立的可調讀的任務
進程是一個程序執行一次的過程
進程是程序執行和資源管理的最小單位
進程和程序的區別
程序是靜態的,它是保存在磁盤上的指令的有序集合,沒有任何執行的概念
進程是一動態的概念,它是程序執行的過程,包括創建,調度和消亡。
linux中的進程包含了1.2.3.,程序只有1.2.
進程 | 1.正文段 | 程序 |
1.2.3. | 2.用戶數據段 | 1.2. |
3.系統數據段 |
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;
}
wait函數,調用該函數使進程阻塞,直到任意一個子進程結束或者是該進程接收到了一個信號爲止。如果該進程沒有子進程或者子進程已經結束,wait函數會立即返回。
waitpid函數,功能和wait函數類似。可以指定等待某個子進程結束以及等待的方式(非阻塞或者阻塞)。
頭文件 | #include<sys/types.h> #include<sys/wait.h> |
原型 | pid_t wait(int *status) |
參數 | status是一個整型指針,指向的對象用來保存子 進程退出時的狀態。status若爲空則忽略子進程 退出時的狀態,不爲空則表示子進程退出時的 狀態。子進程結束的狀態可用特定的宏測定, WIFEXITED(status)判斷子進程是否正常結束, WEXIT(status)當子進程退出時,得到子進程的 退出值 |
返回值 | 成功:子進程的進程號 失敗:-1 |
頭文件 | #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守護進程編寫步驟:
- 創建子進程,父進程退出(第一步完成,子進程就在形式上做到了與控制終端的脫離)
- 在子進程中創建新會話
- 改變當前目錄爲根目錄
- 重設文件權限掩碼
- 關閉文件描述符
#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;
}