1.首先是獲取當前程序的pid和ppid(parent pid)
#include<stdio.h> #include<unistd.h> int main() { printf("the pid of this program is %d\n",(int)getpid()); printf("the parent pid is %d\n",(int)getppid()); return 0; }
執行過程中發現,多次執行后pid一般會變化,而ppid一般不會變,
2.在程序中創建新進程可以有兩種方式,一種是直接通過system函數,該函數相當於創建一個子進程,並將函數內的參數傳遞給該子進程,等同於在命令行下執行該命令,若該shell無法執行,則返回值為127,其他錯誤則返回值-1,執行正確返回值0;
#include<stdlib.h> #include<stdio.h> int main() { int returnValue = system("ls -l"); printf("%d\n",returnValue); return 0; }
system函數執行的結果被返回到終端中輸出
還有一種方法就是通過fork()/exec()創建新進程,fork()函數創建一個父進程的拷貝給子進程,並在調用fork函數處繼續執行下去,創建出的子進程也繼續在該出進行,fork函數在父進程中的返回值的子進程的pid,而在子進程中的返回值的0,以此可以用來分辨當前程序是出於子進程還是父進程中
#include<stdio.h> #include<unistd.h> //#include<sys/type.h> int main() { pid_t childPid = fork(); if(childPid != 0) { printf("now we are in parent progress,pid=%d\n",(int)getpid()); printf("now we are in parent progress,childPid = %d\n",childPid); } else { printf("now we are in child progress,pid = %d\n",(int)getpid()); printf("now we are in child progress,parentPid = %d\n",(int)getppid()); } return 0; }
執行結果:
now we are in child progress,pid = 19991 now we are in child progress,parentPid = 19990 now we are in parent progress,pid=19990 now we are in parent progress,childPid = 19991
3.首先介紹下exec函數族,總共有六個函數
#include <unistd.h> int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);
這六個函數的第一個參數都是要執行的程序名,path代表需要詳細的路徑,file表示只需要傳入程序名,然后會自動在執行路徑中查找該程序名,const char *arg,...代表需要將參數依次傳入到exec函數中,而char *const argv[] 代表以NULL結尾的字符串數組,如{"ls","-l",NULL};而envp代表另外的環境變量數組,且必須以{"variable=value",...,NULL}形式傳入.如果exec函數執行正確,當前程序在exec執行后並不返回,否則要進行錯誤控制.代碼如下:
#include<unistd.h> #include<stdio.h> #include<errno.h> #include<string.h> #include<stdlib.h> int spawn(char *program,char **argv) { int childPid = fork(); if(childPid == -1) { fprintf(stderr,"error in fork:%s\n",strerror(errno)); return -1; } if(childPid != 0) { return childPid; } else { execvp(program,argv); fprintf(stderr,"error in exec function:%s\n",strerror(errno)); abort(); } } int main() { char *argList[]={"ls","-l","/root",NULL}; spawn("ls",argList); printf("done in main program\n"); return 0; }
但有一個問題就是,一般都是在main函數結束后才會輸出exec的執行結果,因此可以通過在父進程中wait函數(需要添加#include<sys/wait.h>頭文件)來等待子進程結束后再繼續執行父進程代碼.main函數如下所示,
int main() { int childStatus= 0; char *argList[]={"ls","-l","/root",NULL}; spawn("ls",argList); wait(&childStatus); if(WIFEXITED (childStatus)) { printf ("the child process exited normally, with exit code %d\n",WEXITSTATUS (childStatus)); } else { printf ("the child process exited abnormally\n"); } printf("done in main program\n"); return 0; }
還有waitpid函數后面跟特定的子進程pid,該函數可以用來等待特定的子進程結束后繼續運行父進程,
5.當父進程中wait了子進程的結束,子進程結束后就會消失並將推出值通過wait函數返回到父進程中,若父進程沒有wait子進程,則子進程的狀態就丟失了,該子進程就變成了一個僵屍進程,
僵屍進程就是進程已經退出了,但父進程卻並沒有將子進程清理干凈,因此可以在程序中定義一個sigaction函數,在響應函數中wait子進程結束.
4.nice命令能改變進程的nice值,nice值越大,優先級越高,一般的程序nice值為0,nice -n 10 sort input.txt > output.txt 命令對這個sort進程增加10的nice值,減少其優先級,renice命令可以直接更改nice值為指定值.只有root權限才能運用nice和renice更改nice值
5.signal函數
程序在運行過程中會收到各種各樣的信號,如SIGTERM,SIGKILL,SIGINT,SIGUSR1,SIGUSR2等,附常用的signal信號:
SIGHUP 終止進程 終端線路掛斷
SIGINT 終止進程 中斷進程
SIGQUIT 建立CORE文件終止進程,並且生成core文件
SIGILL 建立CORE文件 非法指令
SIGTRAP 建立CORE文件 跟蹤自陷
SIGBUS 建立CORE文件 總線錯誤
SIGSEGV 建立CORE文件 段非法錯誤
SIGFPE 建立CORE文件 浮點異常
SIGIOT 建立CORE文件 執行I/O自陷
SIGKILL 終止進程 殺死進程
SIGPIPE 終止進程 向一個沒有讀進程的管道寫數據
SIGALARM 終止進程 計時器到時
SIGTERM 終止進程 軟件終止信號
SIGSTOP 停止進程 非終端來的停止信號
SIGTSTP 停止進程 終端來的停止信號
SIGCONT 忽略信號 繼續執行一個停止的進程
SIGURG 忽略信號 I/O緊急信號
SIGIO 忽略信號 描述符上可以進行I/O
SIGCHLD 忽略信號 當子進程停止或退出時通知父進程
SIGTTOU 停止進程 后台進程寫終端
SIGTTIN 停止進程 后台進程讀終端
SIGXGPU 終止進程 CPU時限超時
SIGXFSZ 終止進程 文件長度過長
SIGWINCH 忽略信號 窗口大小發生變化
SIGPROF 終止進程 統計分布圖用計時器到時
SIGUSR1 終止進程 用戶定義信號1
SIGUSR2 終止進程 用戶定義信號2
SIGVTALRM 終止進程 虛擬計時器到時
Signal Value Action Comment
-------------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at tty
SIGTTIN 21,21,26 Stop tty input for background process
SIGTTOU 22,22,27 Stop tty output for background process
The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
程序在收到信號的時候會先停止當前運行的程序,然后轉去執行相應的signal響應函數,執行完響應函數后在返回到原來執行處(有些signal執行后程序直接退出,就不會返回),
#include<signal.h> #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> sig_atomic_t count1 = 0;//sig_atomic_t是signal頭文件中的宏定義,運算相比int更簡便 sig_atomic_t count2 = 0; void siguser1Handle(int signalNumber)//當收到SIGUSR1信號后的相應函數 { printf("receive signal SIGUSER1\n"); fflush(stdout);//stdout有緩沖區,只有fflush后才會即時輸出stdout中的字符串 count1++; } void siguser2Handle(int signalNumber) { printf("receive signal SIGUSER2\n"); fflush(stdout); count2++; } int main() { /*struct sigaction sa; memset(&sa,0,sizeof(sa)); sa.sa_handler = &siguser1Handle;//sigaction中的sa_handle有三種值,第一種是是SIG_DFL,代表使用該信號默認的響應函數,第二種是SIG_IGN,代表忽略該信號,第三種就是指向自定義的信號響應函數. sigaction (SIGUSR1, &sa, NULL); struct sigaction sb; memset(&sb,0,sizeof(sb)); sb.sa_handler = &siguser2Handle; sigaction (SIGUSR2, &sb, NULL);*/ if(signal(SIGUSR1,&siguser1Handle) == SIG_ERR)//接受SIGUSR1信號並相應 fprintf(stderr,"error in receive SIGUSR1 signal");//stderr沒有緩沖區,立馬就能輸出 if(signal(SIGUSR2,&siguser2Handle) == SIG_ERR)//接受SIGUSR2信號並相應 fprintf(stderr,"error in receive SIGUSR2 signal");//這四行和main函數內被注釋掉代碼的是一樣的結果,第一種更嚴謹些 for(int i = 0;i < 3;i++) { sleep(10); } printf("receive %d times SIGUSER1\n",count1); printf("receive %d times SIGUSER2\n",count2); return 0;
在程序為可以通過指令kill -USR1 pid為pid對應的進程發送SIGUSR1信號,
程序中可以通過kill函數為某個進程發送signal信號,如kill(pid,SIGUSR1),需要包含sys/types.h和signal.h兩個頭文件.
程序退出如果是用exit(number)的話,函數內的number參數應當是0-127范圍,因為如果通過signal來殺死進程的話,返回值是128+signal值
下面是給線程發送signal並執行signal函數的過程,步驟是先在主線程中定義好signal函數,然后給分線程發送signal.
#include<stdio.h> #include<signal.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<string.h> sig_atomic_t count=0; void siguser1Handle(int signalNumber) { count++; printf("receive %d times SIGUSR1\n",count); fflush(stdout); } void *thread_func(void *args) { while(1) { printf("thread \n"); fflush(stdout); sleep(5); } } int main() { pthread_t thread_id; struct sigaction sb; memset(&sb,0,sizeof(sb)); sb.sa_handler = &siguser1Handle; sigaction (SIGUSR1, &sb, NULL);//在主線程中定義好signal函數,然后在主線程中給分線程發送signal並執行signal handlers函數. pthread_create(&thread_id,NULL,thread_func,NULL); for(int i = 0;i < 3;i++) { pthread_kill(thread_id,SIGUSR1); sleep(3); } pthread_join(thread_id,NULL); pause(); return 0; }