c++禁止进程被结束_多进程任务实现
在調度系列文章的第一篇中我們提到調度是由好幾個模塊組成的,其中前面已經介紹了定時器,掃描模塊的相關實現知識點了,這里就繼續往后面的模塊介紹吧,那么接下來,我們就講下任務執行模塊吧。如果說任務調度系統系統,也許很多同學不知道是一個什么概念,但是如果說到crontab的話,大家可能就會恍然大悟了。任務調度系統其實就是一個比cron支持功能多點和復雜點的系統,但是他們本質上是一樣的,就是把任務管理起來,而其中調度系統和任務打交道的,也就是把任務調起來的模塊,就是任務執行模塊。而任務的存在方式也是有好多種的,用得比較多的幾種分別為可執行文件(主要指的是二進制文件命令行,當然,腳本也是通過命令行調起來,這里就不去糾結這個分類是否合理了),so以及腳本。由于文章篇幅的問題,我們把這幾種任務方式分成3篇博文來拆解,這次我們先來討論下怎樣調度二進制可執行文件構成的任務。
使用過命令行的同學都知道,我們使用命令行是一般是在shell中執行類似下面這樣的命令
/cmd/path/cmd_bin param1 param2 ... paramN但是問題來了,任務調度系統又不能和shell交互,要怎樣調起這樣的命令呢?linux為我們提供了一套可行的方式,多進程。至于怎樣實現多進程的方式,我們從簡單到復雜來講吧
system()函數
在c標準庫中有一個system()函數,在調用這個函數時,linux會產生一個子進程,然后由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令。命令執行完后子進程退出,隨即返回原調用的進程。這個函數的原型如下
int system(const char * string); 產生一個子進程并執行參數中的命令行。參數string即要執行的命令通過上面的分析可知,system()函數是完成能夠滿足我們的基本需求的,只要我們把要執行的命令拼裝成一個命令行,然后傳到system()函數即可。像下面例子所示
#include <stdlib.h> int main () {system("ls -lrt");return 0; }但是,由于system()在命令執行完之前是會阻塞主進程的,這對于多個任務同時執行,而且任務都需要執行很長時間,并且我們需要知道每個任務的執行結果的情況下,system()就不能很好地滿足我們的需求了。雖然不能直接使用,但是我們可以參考下system()函數是怎樣實現的。
//c_standard_lib/STDLIB/SYSTEM.C/* UNIX system calls */ int _Execl(const char *, const char *, ...); int _Fork(void); int _Wait (int *);int (system)(const char *s){ /* send text to system command line processor */if (s){ /* not just a test */int pid = _Fork();if (pid < 0); /* fork failed */else if (pid == 0){ /* continue here as child */_Execl("/bin/sh", "sh", "-c", s, NULL);exit(EXIT_FAILURE);}else /* continue here as parent */while (_Wait(NULL) != pid); /* wait for child */}return (-1);}從上面c標準庫的代碼中可以看到,在實現system()函數中使用到了3個其他系統函數,分別是fork(),execl()和wait()。既然如此,我們就順勢而為,看看能不能也使用這三個函數來實現一個滿足我們要求的做法。
自己動手,豐衣足食
上面我們知道了system()函數的用到了哪些系統調用,我們接下來就先熟悉下這些系統調用好了,原型如下
pid_t fork(void);fork()系統調用會通過復制一個現有進程來創建一個全新的進程。返回值:自進程中返回0,父進程返回進程id,出錯返回-1 int execl(const char *pathname, const char *arg, ... /* (char *) NULL */); int execlp(const char *file, const char *arg, ... /* (char *) NULL */); int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char * const envp[] */); int execv(const char *pathname, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[], char *const envp[]);在進程的創建上Unix采用了一個獨特的方法,它將進程創建與加載一個新進程映象分離。 當我們創建了一個進程之后,通常將子進程替換成新的進程映象,這可以用exec系列的函數來進行。 當然,exec系列的函數也可以將當前進程替換掉。path參數表示你要啟動程序的名稱包括路徑名 arg參數表示啟動程序所帶的參數,一般第一個參數為要執行命令名,不是帶路徑且arg必須以NULL結束 返回值:成功返回0,失敗返回-1 上述exec系列函數底層都是通過execve系統調用實現: int execve(const char *filename, char *const argv[],char *const envp[]);pid_t wait(int *wstatus); pid_t waitpid(pid_t pid, int *wstatus, int options);系統調用exit后,該進程并非馬上消失,而是留下一個叫僵尸進程的數據結構,僵尸進程是非常特使的一種,它放棄了幾乎所有的內存空間, 沒有任何可執行代碼,也不能別調度,僅僅在進程列表保留位置,而且不占用任何內存空間。wait()函數用于使父進程阻塞,直到一個子進程結束或者該進程接收到了一個指定的信號為止。 如果該父進程沒有子進程或者它的子進程已經結束,則wait()函數就會立即返回。waitpid()的作用和wait()一樣,但它并不一定要等待第一個終止的子進程(它可以通過pid指定需要等待終止的子進程),它還有若干options, 當options==WNOHANG時,相當于提供一個非阻塞版本的 wait()功能,也能支持作業控制。 實際上,wait()函數只是 waitpid()函數的一個特例,在Linux 內部實現 wait()函數時直接調用的就是waitpid()函數下面是一個簡單的應用例子,在實際的應用中,除了這3個api的應用之外,還需要做很多其他的事情,如注冊信號,寫日志,管道重定向等等,有興趣的同學可以看看apue或者《linux系統編程》等等經典著作,這里就不再啰嗦了。
#include <functional> #include <iostream> #include <sstream> #include <algorithm> #include <sys/wait.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <string.h>#include <agent/global.h> #include <agent/utility.h>int exec( const std::string& execName, std::vector<std::string>& params) {/*** Step 1. prepare argv*/std::vector<char*> argv;argv.resize(1024);argv[1023] = NULL;argv[0] = const_cast<char*> (execName.c_str());size_t N = params.size();N = (N > 1022) ? 1022 : N;int argc = 1;for (size_t i = 0; i < N; i++){argv[argc++] = const_cast<char*> (params.at(i).c_str());}argv[argc] = NULL;/*** Step 3. Fork and exec*/pid_t pid = fork();if (pid == 0){signal(SIGINT, SIG_IGN);// execexecvp(execName.c_str(), &argv[0]);_exit(-1); //不能用exit(),詳細原因參見apue}else if (pid > 0){/* parent process */int status = 0;pid_t nwait = waitpid(pid, &status, 0);while (nwait == -1 && errno == EINTR)nwait = waitpid(pid, &status, 0);if (nwait == -1){std::cout << "waitpid: " << strerror(errno) << std::endl;return -1;}// get exit value of child processint exit_value = 0;if (WIFEXITED(status)){// normally terminatedexit_value = WEXITSTATUS(status);}else if (WIFSIGNALED(status)){// killed or abortedexit_value = 0x80 | WTERMSIG(status);}elseexit_value = 0xf0;return exit_value;}return -1; }怎樣做到的
本來打算對上述api的實現剖析一番的,但是想到自己已經很就沒有去看這么底層的東西了,而且linux內核版本更新得很快,所以就放棄了,只能奉上幾個api在內核中的實現代碼,有興趣的同學自行學習好了
fork()內核實現?elixir.bootlin.comwaitpid內核實現?elixir.bootlin.comexecve內核代碼?elixir.bootlin.com總結
以上是生活随笔為你收集整理的c++禁止进程被结束_多进程任务实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中级软件测试笔试题100精讲_数字IC设
- 下一篇: wordpress外部调用到html_S