<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 分叉,第 1 部分:簡介 > 原文:[Forking, Part 1: Introduction](https://github.com/angrave/SystemProgramming/wiki/Forking%2C-Part-1%3A-Introduction) > 校驗:[飛龍](https://github.com/wizardforcel) > 自豪地采用[谷歌翻譯](https://translate.google.cn/) ## 一句警告 進程分叉是一個非常強大(非常危險)的工具。如果你陷入困境并造成一個分叉炸彈(本頁后面會有解釋),**你可能關閉整個系統**。為了減少這種情況,可以通過在命令行中鍵入`ulimit -u 40`將最大進程數限制為較小的數量,例如 40。請注意,此限制僅適用于用戶,這意味著如果您分叉了炸彈,那么您將無法殺死剛剛創建的所有進程,因為調用`killall`需要 shell 來 `fork()`...很諷刺對嗎?那么我們可以做些什么呢。一種解決方案是在另一個用戶(例如 root)之前生成另一個 shell 實例,然后從那里殺死進程。另一種方法是使用內置的`exec`命令來殺死所有用戶進程(小心你只有一次機會)。最后你可以重啟系統:) 在測試`fork()`代碼時,請確保您對所涉及的計算機具有 root 和/或物理訪問權限。如果您必須遠程處理`fork()`代碼,請記住`kill -9 -1`將在緊急情況下為您節省時間。 太長不看:如果您沒有為此做好準備,那么會**非常**危險。**警告過你了**。 ## 分叉介紹 ## `fork`做什么? 系統調用`fork`克隆當前進程以創建新進程。它通過復制現有進程的狀態創建一個新進程(子進程),但存在一些細微差別(下面討論)。子進程不是從`main`開始的。相反,它就像父進程一樣從`fork()`返回。 ## 什么是最簡單的`fork()`示例? 這是一個非常簡單的例子...... ```c printf("I'm printed once!\n"); fork(); // Now there are two processes running // and each process will print out the next line. printf("You see this line twice!\n"); ``` ## 為什么這個例子打印 42? 以下程序打印出 42 - 但`fork()`在`printf`之后!為什么? ```c #include <unistd.h> /*fork declared here*/ #include <stdio.h> /* printf declared here*/ int main() { int answer = 84 >> 1; printf("Answer: %d", answer); fork(); return 0; } ``` `printf`行僅執行一次,但是注意到打印內容沒有刷新到標準輸出(沒有打印換行符,我們沒有調用`fflush`或更改緩沖模式)。因此,輸出文本仍在進程內存中等待發送。執行`fork()`時,將復制整個進程內存,包括緩沖區。因此,子進程以非空輸出緩沖區開始,該緩沖區將在程序退出時刷新。 ## 如何編寫父子進程不同的代碼? 檢查`fork()`的返回值。返回值`-1` = 失敗; `0` = 在子進程中;正數 = 在父進程中(返回值是子進程 id)。這是一種記住哪種情況的方法: 子進程可以通過調用`getppid()`找到其父進程 - 被復制的原始進程 - 因此不需要來自`fork()`的任何其他返回信息。然而,父進程只能從`fork`的返回值中找出新子進程的 id: ```c pid_t id = fork(); if (id == -1) exit(1); // fork failed if (id > 0) { // I'm the original parent and // I just created a child process with id 'id' // Use waitpid to wait for the child to finish } else { // returned zero // I must be the newly made child process } ``` ## 什么是分叉炸彈? 當您嘗試創建無限數量的進程時,就是“分叉炸彈”。一個簡單的例子如下所示: ```c while (1) fork(); ``` 這通常會使系統幾乎停滯不前,因為它試圖將 CPU 時間和內存分配給準備運行的大量進程。注釋:系統管理員不喜歡分叉炸彈,并且可能對每個用戶可以擁有的進程數量設置上限,或者可能撤銷登錄權限,因為它會對其他用戶的程序產生干擾。您還可以使用`setrlimit()`限制創建的子進程數。 分叉炸彈不一定是惡意的 - 它們偶爾會因學生編碼錯誤而發生。 Angrave 認為,Matrix 三部曲,機器和人終于共同努力擊敗不斷復制的 Agent-Smith,是基于 AI 驅動的分叉炸彈的電影情節。 ## 等待和執行 ## 父進程如何等待子進程完成? 使用`waitpid`(或`wait`)。 ```c pid_t child_id = fork(); if (child_id == -1) { perror("fork"); exit(EXIT_FAILURE);} if (child_id > 0) { // We have a child! Get their exit code int status; waitpid( child_id, &status, 0 ); // code not shown to get exit status from child } else { // In child ... // start calculation exit(123); } ``` ## 我可以讓子進程執行另一個程序嗎? 是。在分叉后使用[`exec`](http://man7.org/linux/man-pages/man3/exec.3.html)函數之一。 `exec`函數集將進程映像替換為所調用的進程映像。這意味著`exec`調用后的任何代碼行都被更換。您希望子進程執行的任何其他工作,都應在`exec`調用之前完成。 [這篇維基百科文章](https://en.wikipedia.org/wiki/Exec_(system_call)#C_language_prototypes)幫助您理解了`exec`家族的名字。 命名方案可以像這樣縮寫: > 每個的基礎是`exec`(執行),后跟一個或多個字母: > > `e` - 指向環境變量的指針數組顯式傳遞給新的進程映像。 > > `l` - 命令行參數單獨傳遞(列表)到函數。 > > `p` - 使用`PATH`環境變量查找要執行的文件參數中指定的文件。 > > `v` - 命令行參數作為指針的數組(向量)傳遞給函數。 ```c #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char**argv) { pid_t child = fork(); if (child == -1) return EXIT_FAILURE; if (child) { /* I have a child! */ int status; waitpid(child , &status ,0); return EXIT_SUCCESS; } else { /* I am the child */ // Other versions of exec pass in arguments as arrays // Remember first arg is the program name // Last arg must be a char pointer to NULL execl("/bin/ls", "ls","-alh", (char *) NULL); // If we get to this line, something went wrong! perror("exec failed!"); } } ``` ## 執行另一個程序的更簡單方法 使用`system`。以下是如何使用它: ```c #include <unistd.h> #include <stdlib.h> int main(int argc, char**argv) { system("ls"); return 0; } ``` `system`調用將分叉,執行參數傳遞的命令,原始父進程將等待此操作完成。這也意味著`system`是一個阻塞調用:在`system`創建的進程退出之前,父進程無法繼續。這可能有用也可能沒用。此外,`system`實際上創建了一個 shell,然后給出了字符串,這比直接使用`exec`開銷更大。標準 shell 將使用`PATH`環境變量來搜索與命令匹配的文件名。對于許多簡單的執行某個命令的問題,使用系統通常就足夠了,但很快就會被更復雜或微妙的問題限制,它隱藏了`fork-exec-wait`模式的機制,所以我們鼓勵你學習和使用`fork`,`exec`和`waitpid`來代替。 ## 什么是最愚蠢的分叉示例? 一個稍微愚蠢的例子如下所示。它會打印什么?嘗試使用程序的多個參數。 ```c #include <unistd.h> #include <stdio.h> int main(int argc, char **argv) { pid_t id; int status; while (--argc && (id=fork())) { waitpid(id,&status,0); /* Wait for child*/ } printf("%d:%s\n", argc, argv[argc]); return 0; } ``` 驚人的并行表觀 - O(N) 睡眠排序是今天愚蠢的贏家。首次發表于 [4chan 2011](https://dis.4chan.org/read/prog/1295544154) 。這個糟糕但有趣的排序算法的一個版本如下所示。 ```c int main(int c, char **v) { while (--c > 1 && !fork()); int val = atoi(v[c]); sleep(val); printf("%d\n", val); return 0; } ``` 注意:由于系統調度程序的工作原理,算法實際上不是`O(N)`。雖然每個進程都有以`O(log(N))`運行的并行算法,但遺憾的是這不是其中之一。 ## 子進程與父進程有什么不同? 主要區別包括: * `getpid()`返回的進程 ID。 `getppid()`返回的父進程 ID。 * 當子進程完成時,通過信號 SIGCHLD 通知父進程,反之則不然。 * 子進程不會繼承待定信號或計時器警報。有關完整列表,請參見[`fork`手冊頁](http://man7.org/linux/man-pages/man2/fork.2.html)。 ## 子進程是否共享打開的文件句柄? 是!實際上,兩個進程都使用相同的底層內核文件描述符。例如,如果一個進程將隨機訪問位置倒回到文件的開頭,則兩個進程都會受到影響。 子進程和父進程分別應該`close`(或`fclose`)它們的文件描述符或文件句柄。 ## 怎樣才能找到更多? 閱讀手冊頁! * [`fork`](http://man7.org/linux/man-pages/man2/fork.2.html) * [`exec`](http://man7.org/linux/man-pages/man3/exec.3.html) * [`wait`](http://man7.org/linux/man-pages/man2/wait.2.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看