麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 系統 > Unix > 正文

UNIX高級環境編程(10)進程控制(Process Control)- 競態條件,exec函數,解釋器文件和system函數

2024-06-28 13:21:27
字體:
來源:轉載
供稿:網友
UNIX高級環境編程(10)進程控制(PRocess Control)- 競態條件,exec函數,解釋器文件和system函數

本篇主要介紹一下幾個內容:

  • 競態條件(race condition)
  • exec系函數
  • 解釋器文件?

?

1 競態條件(Race Condition)

競態條件:當多個進程共同操作一個數據,并且結果依賴于各個進程的操作順序時,就會發生競態條件。

例如fork函數執行后,如果結果依賴于父子進程的執行順序,則會發生競態條件。

說到fork之后的父子進程的執行順序,我們可以通過下面的方式指定執行順序:

如果父進程等待子進程結束,則需要調用wait函數。

如果子進程等待父進程結束,則需要像下面這樣輪詢:

while (getppid() != 1)

? ? sleep(1);

輪詢的方式的缺點是非常浪費CPU時間。

?

如果希望避免競態條件和輪詢,則需要用到進程之間的信號機制,或者其他的ipC方式。

競態條件的例子:

Example:

#include "apue.h"

?

static void charatatime(char *);

?

int

main(void)

{

? ? pid_t ? pid;

?

? ? if ((pid = fork()) < 0) {

? ? ? ? err_sys("fork error");

? ? } else if (pid == 0) {

? ? ? ? charatatime("output from child/n");

? ? } else {

? ? ? ? charatatime("output from parent/n");

? ? }

? ? exit(0);

}

?

staticvoid

charatatime(char *str)

{

? ? char? ? *ptr;

? ??int ? ? c;

?

? ? setbuf(stdout, NULL); ? ? ? ? ? /* set unbuffered */

? ? for (ptr = str; (c = *ptr++) != 0; )

? ? ? ? putc(c, stdout);

}

輸出結果:

NewImage

我們可以發現,輸出結果并不一定,依賴于父子進程的執行順序,這里就發生了競態條件。

在例子中,我們設置了stdout得buffer為NULL,為了讓每一個字符的輸出都調用write,這樣可以盡可能多地發生進程間切換。

在下面的例子中,我們通過在父子進程間進行通信,來保證父進程先運行。

Example:

#include "apue.h"

?

static void charatatime(char *);

?

int

main(void)

{

? ? pid_t ? pid;

?

? ? TELL_WAIT();

?

? ? if ((pid = fork()) < 0) {

? ? ? ? err_sys("fork error");

? ? } else if (pid == 0) {

? ? ? ? WAIT_PARENT();? ? ? /* parent goes first */

? ? ? ? charatatime("output from child/n");

? ? } else {

? ? ? ? charatatime("output from parent/n");

? ? ? ? TELL_CHILD(pid);

? ? }

? ? exit(0);

}

static void

charatatime(char *str)

{

? ? char? ? *ptr;

? ? int ? ? c;

?

? ? setbuf(stdout, NULL); ? ? ? ? ? /* set unbuffered */

? ? for (ptr = str; (c = *ptr++) != 0; )

? ? ? ? putc(c, stdout);

}

執行結果:

NewImage

從結果可以看到,輸出是符合預期的。

所以進程間通信是解決競態條件的方式之一。

?

2 exec函數

fork函數的一個作用就是,創建出一個子進程,讓子進程執行exec函數,去執行另一個程序。

exec函數的作用就是用一個新的程序代替現在的進程,從新程序的main函數開始執行。

替換后,進程號不改變,被替換的內容包括文本段,數據段,堆和棧。

exec函數是一組函數,函數聲明如下:

NewImage

函數細節:

  • 前四個函數的參數pathname為文件路徑,后兩個函數的參數filename為文件名,最后一個為文件描述符。如果filename中又’/‘號,則認為是一個文件路徑,否則函數以環境變量為前綴對指定的文件進行搜索;
  • 如果execlp和execvp函數發現目標文件不是可執行文件,則會嘗試把它當做一個腳本調用/bin/sh去執行;
  • fexecve函數依賴調用者去保證文件的可執行,并且防止惡意用戶在時間差將目標可執行文件替換。
  • 函數名中的l代表list,v代表vector。l系函數的參數為命令行中傳入的參數(在參數列表中分別由arg0,arg1,arg2...表示),v系函數則需要將參數的指針放入一個數組中,將數組的地址傳入函數。
  • 環境變量列表的傳遞方式。函數名以e結尾的函數允許修改環境變量列表,函數的最后一個參數是一個指向一個指針數組的指針,數組中的指針指向環境變量的各個字符串。

?這7個函數非常難記,了解函數名中得特別字母有助于記憶:

  • 字母p代表函數獲取一個filenam參數和環境變量來查找可執行文件;
  • 字母l代表函數獲取一個參數列表
  • 字母v代表函數獲取一個argv[]作為參數
  • 字母e代表函數獲取一個envp[]作為參數,取代環境變量列表,用戶可以修改環境變量然后傳遞給子進程

?exec函數小結:

NewImage

前面提到過,執行了exec函數后,進程的進程號不變。除了進程號,還有繼承而來的信息包括:

NewImage

exec函數替換程序之后,對于已經打開的文件描述符的處理,取決于flag close-on-exec。如果flag close-on-exec被打開,則exec替換程序后,打開的文件描述符會被關閉,負責這些文件描述會保持打開狀態,這種保持打開狀態的行為也是默認行為。

?real user ID和real group ID在exec函數后保持不變,但是effective user ID和effective group ID可以通過設置set-user-ID和set-group-ID標志位而決定是否改變。

一般實現時,7個exec函數,只有一個exec函數會被實現為系統調用。

7個exec函數之間的關系如圖所示:

NewImage

?

Example:

#include "apue.h"

#include <sys/wait.h>

?

char? ? *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };

?

int

main(void)

{

? ? pid_t ? pid;

?

? ? if ((pid = fork()) < 0) {

? ? ? ? err_sys("fork error");

? ? } elseif (pid == 0) {? /* specify pathname, specify environment */

? ? ? ? if (execle(“/*可執行文件所在路徑*//echoall", "echoall", "myarg1",

? ? ? ? ? ? ? ? "MY ARG2", (char *)0, env_init) < 0)

? ? ? ? ? ? err_sys("execle error");

? ? }

?

? ? if (waitpid(pid, NULL, 0) < 0)

? ? ? ? err_sys("wait error");

?

? ? if ((pid = fork()) < 0) {

? ? ? ??err_sys("fork error");

? ? } elseif (pid == 0) {? /* specify filename, inherit environment */

? ? ? ? if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)

? ? ? ? ? ? err_sys("execlp error");

? ? }

?

? ? exit(0);

}

?

3 解釋器文件(Interpreter Files)

所有現代UNIX系統都支持解釋器文件(interpreter files)。

解釋器文件開始一行的格式為:

#!pathname [optional-argument]

?例如,shell腳本的開始一行為:

?#!/bin/sh?

?要區分清楚解釋器文件和解釋器:

  • 解釋器文件:第一行以#!pathname XXX開始的文本文件
  • 解釋器:解釋器文件第一行#!pathname xxx中指定的xxx可執行文件

?需要注意的一點是:解釋器文件的第一行的長度是有限制的,長度計算包含了空格,’#!’和換行符。

Example:

#include "apue.h"

#include <sys/wait.h>

?

int

main(void)

{

? ? pid_t ? pid;

?

? ? if ((pid = fork()) < 0) {

? ? ? ? err_sys("fork error");

? ? } else if (pid == 0) {? ? ? ? ? /* child */

? ? ? ? if (execl("/home/sar/bin/testinterp",

? ? ? ? ? ? ? ? ? “testinterp", "myarg1", "MY ARG2", (char *)0) < 0)

? ? ? ? ? ? err_sys("execl error");

? ? }

? ? if (waitpid(pid, NULL, 0) < 0)? /* parent */

? ? ? ? err_sys("waitpid error");

? ? exit(0);

}

輸出結果:

NewImage?

?輸出結果說明:

  • 程序的作用是輸出命令行中的每一個參數
  • 需要注意的是,第一個參數argv[0]是解釋器的據對路徑
  • 第二個參數是解釋器文件第一行的可選參數
  • 第三個參數是替換程序文件的路徑
  • 需要注意的是,參數’’testinterp”并沒有被輸出,因為內核認為第一個參數pathname包含更多的內容

?

4 system函數(system Function)

在程序執行一個命令字符串是很方便的。

例如:

system(“date > file");

?將日期重定向至file文件中。

函數聲明:

#include <stdlib.h>

int system(const char* cmdstring);

?函數細節:

  • 如果cmdstring是一個Null指針,則在system函數可以正常調用時返回非零值。這個特性可以用來檢查系統是否支持system函數。
  • 因為system函數是基于fork, exec和waitpid實現,所以system有三種返回值
    • 如果fork失敗或者waitpid返回錯誤并且不是EINTR,system函數返回-1;
    • 如果exec失敗,表明shell不能被執行,返回值和shell退出返回值(127)相同;
    • 如果fork,exec和waitpid都執行成功,并且system返回值是shell的終止狀態值,該值的形式由waitpid函數指定。

?system函數的一種實現,沒有處理信號的版本。

code

#include? ? <sys/wait.h>

#include? ? <errno.h>

#include? ? <unistd.h>

?

int

system(constchar *cmdstring) ? /* version without signal handling */

{

? ? pid_t ? pid;

? ? int ? ? status;

?

? ? if (cmdstring == NULL)

? ? ? ? return(1);? ? ? /* always a command processor with UNIX */

?

? ? if ((pid = fork()) < 0) {

? ? ? ? status = -1;? ? /* probably out of processes */

? ? } else if (pid == 0) {? ? ? ? ? ? ? /* child */

? ? ? ? execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);

? ? ? ? _exit(127); ? ? /* execl error */

? ? } else {? ? ? ? ? ? ? ? ? ? ? ? ? ? /* parent */

? ? ? ? while (waitpid(pid, &status, 0) < 0) {

? ? ? ? ? ? if (errno != EINTR) {

? ? ? ? ? ? ? ? status = -1; /* error other than EINTR from waitpid() */

? ? ? ? ? ? ? ? break;

? ? ? ? ? ??}

? ? ? ? }

? ? }

?

? ? return(status);

}

?代碼細節:

  • shell中的-c參數說明將后面的一個參數作為命令行輸入,而不是從標準輸入或者指定文件讀取;
  • 我們調用_exit而不是exit,防止子進程退出時,會將從父進程拷貝到的buffer打印。

使用system函數的好處是system函數為我們處理了所以的異常,并且提供了所有必須的信號處理。

Example

#include "apue.h"

#include <sys/wait.h>

?

int

main(void)

{

? ? int ? ? status;

?

? ? if ((status = system("date")) < 0)

? ? ? ? err_sys("system() error");

?

? ? pr_exit(status);

?

? ? if ((status = system("nosuchcommand")) < 0)

? ? ? ? err_sys("system() error");

?

? ? pr_exit(status);

?

? ? if ((status = system("who; exit 44")) < 0)

? ? ? ? err_sys("system() error");

?

? ? pr_exit(status);

?

? ? exit(0);

}

運行結果:

NewImage

?

?

參考資料:

《Advanced Programming in the UNIX Envinronment 3rd》


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲国产馆| 久久精品视频12 | 成人在线视频国产 | 日韩精品中文字幕在线播放 | 国产免费区 | 亚洲影院在线 | 国产青草视频在线观看 | 黄色一级片免费在线观看 | 成人免费av在线播放 | 黄色毛片观看 | 精品一区二区亚洲 | av在线收看 | 国产中出在线观看 | 欧美福利视频一区二区三区 | 久久精品视频69 | 天堂福利电影 | 一级免费黄色免费片 | 免费毛片观看 | 一级黄色大片在线观看 | 亚洲人成中文字幕在线观看 | 成人444kkkk在线观看 | 91午夜视频 | 91精品老司机 | 国产电影精品久久 | 模特三级在线观看 | 精品国产一区二区三区四区阿崩 | 久久思思爱 | 婷婷久久综合九色综合色多多蜜臀 | h视频在线免费观看 | 黄色网欧美 | 免费毛片免费看 | 日韩黄色片网站 | 中文字幕爱爱视频 | 成年人网站视频免费 | caoporn国产一区二区 | 国产无遮挡一区二区三区毛片日本 | 精品亚洲va在线va天堂资源站 | 国产老师做www爽爽爽视频 | 久久综合狠狠综合久久 | 国产91在线亚洲 | 国产免费让你躁在线视频 |