閱讀843 返回首頁    go 阿裏雲 go 技術社區[雲棲]


子進程及時知道父進程已經退出的最簡單方案

 

[精彩] 子進程及時知道父進程已經退出的最簡單方案?


https://www.chinaunix.net 作者:yuonunix  發表於:2003-10-31 10:14:14
發表評論】 【查看原文】 【C/C++討論區】【關閉

要父進程知道子進程退出,這太容易了,但是要子進程知道父進程退出,可有點麻煩。 

     父進程如果退出,子進程如何知道呢,最笨的方法,父子進程之間建立socket連接,然後建立心跳,沒隔1秒測試一把,當然太笨了,通過管道,可以嗎?如何做?有更加簡單的方法嗎? 

歡迎高手提出最簡單的辦法



 rc_hz 回複於:2003-08-11 11:52:01

肯定不能這樣實現,否則程序性能沒辦法保證


 yuonunix 回複於:2003-08-11 12:27:55

我目前想的方法,隻有用個管道,父進程每隔一秒向管道中寫,子進程接收,子進程一段時間從管道中讀不到字符,就認為父進程已經退出。但是這方法實在笨,想一想,又想不出好辦法。要不,在子進程中使用ps命令,這方法也很土呀。


 flw 回複於:2003-08-11 12:54:00

可以考慮在父進程退出時給子進程發送信號。SIGUSR1。


 fieryfox 回複於:2003-08-11 12:57:06

你的需求到底是什麼?父進程退出了,子進程也會退出,此時會收到SIGTERM信號。 
如果在子進程裏setpgrp,那可以用所有的IPC手段去通知子進程,flw的比較實用。


 yuonunix 回複於:2003-08-11 13:08:26

引用:原帖由 "flw"]可以考慮在父進程退出時給子進程發送信號。SIGUSR1。
 發表:

     這隻能處理父進程正常退出時的情況,如果父進程不是正常退出,而是遭遇了”kill -9 進程號“退出時,父進程就來不及用SIGUSR1通知子進程,就會退出,子進程照樣不知道父進程的狀態改變。因而這種方法好象沒有辦法實現“可靠地通知子進程”。


 fieryfox 回複於:2003-08-11 13:11:30

嗬嗬,kill -9誰也處理不了的,隻能認命。這是滿門抄斬的招法。


 qjlemon 回複於:2003-08-11 13:12:38

flw的辦法還是挺實用的。 
試試開個管道,子進程監視管道是否還開著。不需要實際的讀寫,隻是監視它的狀態 
或者父進程鎖一個文件,子進程如果"不“能得到鎖,說明父進程它老人家還健在。


 yuonunix 回複於:2003-08-11 13:15:48

引用:原帖由 "fieryfox" 發表:
你的需求到底是什麼?父進程退出了,子進程也會退出,此時會收到SIGTERM信號。 
如果在子進程裏setpgrp,那可以用所有的IPC手段去通知子進程,flw的比較實用。

      

哦,你的意思是如果父進程退出,子進程如果沒有setpgrp,就會收到SIGTERM信號,是嗎?我馬上嚐試一下。謝謝了!


 qjlemon 回複於:2003-08-11 13:33:29

引用:原帖由 "fieryfox" 發表:
你的需求到底是什麼?父進程退出了,子進程也會退出,此時會收到SIGTERM信號。 
如果在子進程裏setpgrp,那可以用所有的IPC手段去通知子進程,flw的比較實用。

     在BSD下試了一下似乎不中啊! 

static void     sig_term(int);

int
main(void)
{
        pid_t pid;

        if (signal(SIGTERM, sig_term) == SIG_ERR) {
                printf("can't catch SIGTERM/n");
                exit(1);
        }

        pid = fork();
        if (pid >; 0) {
                sleep(1);
                exit(0);
        }

        for ( ; ; )
                sleep(1);
}

static void
sig_term(int signo)
{
        printf("received signal %d/n", signo);
        return;
}



 qjlemon 回複於:2003-08-11 13:38:03

父進程退出了,子進程也會退出???


 qjlemon 回複於:2003-08-11 13:41:52




 yuonunix 回複於:2003-08-11 14:17:12

引用:原帖由 "yuonunix" 發表:
     

哦,你的意思是如果父進程退出,子進程如果沒有setpgrp,就會收到SIGTERM信號,是嗎?我馬上嚐試一下。謝謝了!

      

為了驗證這一點,我編了如下程序: 
#include <signal.h>; 
#include <sys/types.h>; 
#include <unistd.h>; 
#include <stdio.h>; 
int main(void) 



   register int pid; 
   if((pid = fork())<0) 
   { 
        printf("error/n"); 
   } 
  else if(pid!=0) 
       
  {  
    printf("begin sub progress/n"); 
    execl("/export/home/appsvr/tt/main1",NULL); 
  } 
  while(1) 
 { 
  }; 

}  

啟動父進程後,去查子進程信息 
ps -ef|grep main1去查,子進程的進程名稱已經不是main1了,請問怎樣去查子進程的信息。


 yuonunix 回複於:2003-08-11 14:23:50

引用:原帖由 "qjlemon"]
 發表:

      
父進程退出了,子進程也會退出,此時會收到SIGTERM信號。其意思是: 
父進程退出時,子進程會收到SIGTERM信號。(子進程應該能夠捕捉到SIGTERM信號) 

而qjlemon的例子是:父進程退出,然後父進程捕捉SIGTERM信號,是否和fieryfox的意思不符合呀


 yuxq 回複於:2003-08-11 14:34:59

good


 gadfly 回複於:2003-08-11 14:39:12

父進程退出的時候,不會自動發送sigterm信號的。 

qjlemon的例子是正確的。信號響應函數對子進程同樣有效


 yuonunix 回複於:2003-08-11 15:01:00

所以到現在為止,最簡單的方法還是:     

 試試開個管道,子進程監視管道是否還開著。不需要實際的讀寫,隻是監視它的狀態  
或者父進程鎖一個文件,子進程如果"不“能得到鎖,說明父進程它老人家還健在。 

即,要實現準確的判斷父進程狀態,沒有很好的方法,隻能這樣: 

子進程每隔一秒檢查一下鎖(或者管道),而無法得到通知羅。


 gadfly 回複於:2003-08-11 15:05:37

管道方式是不錯, 
不過不用不時的poll。 

一般網絡服務都用select監聽io,加到fd_set裏就好了。 

如果是其它的模式,用異步io,響應sigio信號應該是可以的。


 fieryfox 回複於:2003-08-11 15:45:15

faint,搞錯了。當console了。 
既然這樣,那也不需要什麼IPC,用getppid監控ppid的值,什麼時候為1 了,父進程就退出了。


 qjlemon 回複於:2003-08-11 15:51:18

試驗通過 :)     不過這就隻能循環測試了


 fieryfox 回複於:2003-08-11 15:57:58

樓主要在kill -9的情況下都行,隻能這樣了。如果不是-9,還可以用信號USR1一類的方法。


 yuonunix 回複於:2003-08-11 16:13:58

引用:原帖由 "fieryfox" 發表:
faint,搞錯了。當console了。 
既然這樣,那也不需要什麼IPC,用getppid監控ppid的值,什麼時候為1 了,父進程就退出了。

      

這個方法確實是所有方法中最簡單的方法,如果求簡單,就可以用這個了,如果求效率,可能就是如gadfly所言,用管道,然後select一把,所花的CPU應該是最小的了。


 yuonunix 回複於:2003-08-12 11:28:16

我再問個相關問題,如果C是B的子進程,B是A的子進程,C可以通過getppid知道 B 的狀態,B可以通過getppid知道A的狀態,那麼C怎樣可以知道A的狀態(A是否已經退出)呢?我現在想到的解決辦法是getppid 和 kill -USR1相結合, 

孫子直接獲取祖父進程的狀態,好像沒有比“getppid + kill -USR1”更簡單的方法,各位是否這樣認為?


 fieryfox 回複於:2003-08-12 11:46:29

你在做什麼應用,怎麼會有這樣的需求?


 yuonunix 回複於:2003-08-12 12:52:13

引用:原帖由 "gadfly" 發表:
父進程退出的時候,不會自動發送sigterm信號的。 

qjlemon的例子是正確的。信號響應函數對子進程同樣有效

      
你這句話隻對了一半,信號響應函數對子進程不是全有效的。而在上麵的例子裏,恰好是無效的。看完下麵的就會明白。 
 Signals set to the default action (SIG_DFL) in  the  calling 
     process  image are set to the default action in the new pro- 
     cess image (see signal(3C)). 
      Signals set to be ignored (SIG_IGN) by the calling  process 
     image  are  set to be ignored by the new process image. Sig- 
     nals set to be caught by the calling process image  are  set 
     to  the  default  action  in  the  new  process  image  (see 
     signal(3HEAD)).  After a successful call to any of the  exec 
     functions, alternate signal stacks are not preserved and the 
     SA_ONSTACK flag is cleared for all signals.


 qjlemon 回複於:2003-08-12 12:55:54

你自己試一下那段代碼就知道了。


 yuonunix 回複於:2003-08-12 12:57:27

引用:原帖由 "fieryfox"]你在做什麼應用,怎麼會有這樣的需求?
 發表:

      
我在做點Solaris上的研究。 :)


 qjlemon 回複於:2003-08-12 12:59:19

fork和exec是有重大區別的


 yuonunix 回複於:2003-08-12 13:31:43

引用:原帖由 "qjlemon"]fork和exec是有重大區別的
 發表:

     我嚐試了一下,發現如此注冊的信號,的確對父親進程和孩子進程都有效,不好意思,我混淆了exec、fork對信號的處理方式,謝謝qjlemon。


 qjlemon 回複於:2003-08-12 13:33:53

不要光看書 :)


 藍色鍵盤 回複於:2003-08-12 18:34:48

這個問題以前好像有人問過。 

偶目前采用的辦法是: 

在具有親緣關係的進程之間建立無名管道。這些進程通過這些管 

道傳遞簡單的和少量的數據(也可以傳遞描述字)。然後,親緣 

關係的進程都采用select來處理管道上發生的讀寫條件。此時, 

不論那個進程core了,或者有人kill  

-9了。對方的進程的select讀會成功返回,此時根據返回的數值 

便可判斷那個進程異常了,或者被他人幹掉了。 

這個方法偶一直用,覺得很好使,大家可以自己試試。


 jimyao 回複於:2003-08-13 16:04:35

對於父進程已經終止的所有進程,它們都有一個叫init的進程領養,作為它的的父進程,隻需要判斷,這些進程的父進程的ID是否是1就可以了。      

這個辦法也行。children輪訓自己的getppid()的結果,然後更具結果判斷是否父親被人幹掉! 
 :twisted:


 codan 回複於:2003-08-20 18:24:44

看了關於那篇關於子進程如何知道父進程退出的帖子寫了下麵的代碼(沒有出錯檢測)。  
下麵的代碼中的子進程將處於阻塞狀態,不用線程可能讓子進程能進行自己的工作嗎(如:子進程輸出不斷輸出一個字符'a',同時又觀察父進程狀態)?  


#include<stdio.h>; 

#include<unistd.h>; 
#include<stdlib.h>; 

int main() 

        pid_t pipes[2]; 
        int fork_result; 
        int read_res; 

        pipe(pipes);                                   /*建立管道*/ 

        fork_result=fork(); 
        if(fork_result==0){                        /*子進程*/ 
                close(pipes[1]);                     /*關閉管道寫*/ 
                read_res=read(pipes[0],0,1); 
                if(read_res==0) 
                        printf("Parent is closed/n"); 
        }else{                                           /*父進程5秒後退出*/ 
                sleep(5); 
                exit(EXIT_SECCESS); 
        } 
}



 gadfly 回複於:2003-08-21 21:18:38

子進程可以定時去讀呀。 

或者循環的時候,在某個特定的時候檢查。 

如果是監聽socket連接上的數據,也可以select來監聽。


 superchao 回複於:2003-10-30 18:44:05

首先子進程使用getppid可以知道父進程的PID, 
使用kill ( PID, 0)如果返回<0則說明父進程不在了! 
或者判斷getppid()返回的是否初始化進程的進程號1, 
如果是,則說明父進程不在了


 superchao 回複於:2003-10-30 19:30:13

還是使用kill ( 父進程號,0)判斷吧,


 flyingbear 回複於:2003-10-30 20:12:08

getppid 
如果父進程退出,則結果將 = 1.這個方案不知道有什麼漏洞沒


 qjlemon 回複於:2003-10-31 07:51:38

這個時候ppid=1是正確的,因為該進程的父進程確確實實就是1號進程。 
———一個進程的父進程並不是永遠不變的。


 hbczjzc 回複於:2003-10-31 09:55:44

第一部分--基本知識   
一.進程概念: 

進程定義了一計算的基本單位,是一個執行某一特定程序的實體.它擁有自己的地址空間,執行堆棧,文件描述符表等。 

二.創建進程: 

1.fork()函數原型 
#include <sys/types.h>; 
#include <unistd.h>; 
pid_t fork(viod); 

2.返回值 
子進程為0,父進程為子進程id,出錯為-1. 

3.fork()函數用法 

兩個用法: 
  一個父進程通過複製自己,使父子進程同時執行不同的代碼段.這對網絡服務進程是常見的:父進程等待客戶的服務請求.當這種請求到達時,父進程調用fork() 
函數,使子進程處理此請求.父進程則繼續等待下一個客戶服務請求. 
  每個進程要執行不同的程序,這會shell是常見的,在這種情況下,子進程在從fork()函數返回後立即調用exec()函數執行其他程序. 

用法: 
#include <sys/types.h>; 
#include <unistd.h>; 
main() 

pid_t pid; 

if((pid=fork())>;0) 

/*parent process*/ 

else if(pid==0) 

/*child process*/ 
exit(0); 

else 

printf("fork error/n"); 
eixt(0); 



4.進程終止 
   兩種可能 
--父進程先於子進程終止. 
所有子進程的父進程改變為init進程稱為進程由init進程領養. 

--子進程先於父進程終止. 
父進程調用wait()函數或者waitpid()函數可以得到子進程的進程id,進程的終止狀態,以及進程使用的cpu時間總量. 
  

  進程正常或者異常終止,係統內核就向其父進程發送SIGCHLD信號. 

5.進程終止調用的函數 

1.wait()函數 
等待子進程返回並修改狀態 
#include <sys/types.h>; 
#include <sys/wait.h>; 
pid_t wait(int *stat_loc); 
允許調用進程取得子進程的狀態信息。調用進程將會掛起直到其一個子進程終止. 
返回值為該子進程進程號,否則返回-1,同時stat_loc返回子進程的返回值. 
用法: 
#include <sys/type.h>; 
#include <sys/wait.h>; 
main() 

pid_t pid; 
if ((pid=fork())>;0) 

/*parent process*/ 
int child_status; 
wait(&child_status); 

else if(pid==0) 

/*child process*/ 
exit(0); 

else 

printf("fork error/n"); 
exit(0); 



2.waitpid()函數 
等待指定進程的子進程返回,並修改狀態. 
#include <sys/types.h>; 
#include <sys/wait.h>; 
pid_t waitpid(pid_t pid,int *stat_loc,int options); 
當pid等於-1,options等於0時,該函數等同於wait()函數。 

否則該函數的行為由參數pid和options決定。 
pid指定了父進程要求知道那些子進程的狀態,其中: 
=-1 要求知道任何一個子進程的返回狀態; 
>;0 要求知道進程號為pid的子進程的狀態; 
<-1 要求知道進程組號為pid的絕對值的子進程的狀態. 

options參數為以位方式表示的標誌,它是各個標誌以“或”運算組成的位圖,每個標誌以字節中某個位置1表示: 

WUNTRACED 報告任何未知但已停止運行的指定進程的子進程狀態,該進程的狀態自停止運行時起就沒有被報告過. 

WCONTINUED 報告任何繼續運行的指定進程的子進程狀態,該子進程的狀態自繼續運行起就沒有被報告過 

WNONANG 若調用本函數時,指定進程的子進程狀態,目前並不是立即有效的(即可被立即讀取的),調用進程不被掛起執行。 

WNOWAIT 保持那些將其狀態設置在stat_loc()函數的進程處於可等待狀態,該進程將直到下次被要求其返回狀態值. 


返回值為該子進程號,否則為-1,同時stat_loc()函數返回子進程的返回值 
用法: 


#include <sys/types.h>; 
#include <sys/wait.h>; 
main() 

pid_t pid; 
if ((pid=fork())>;0) 

/*parent process*/ 
int child_status; 
pid_t child_status; 
waitpid(child_pid,&child_status,0); 

else if (pid==0) 

/*child process*/ 
exit(0); 

else 

printf("fork error"); 
exit(0); 



exit()函數 
終止進程,並返回狀態。 
#include<stdlib.h>; 
viod eixt(int status); 
函數終止調用進程,exit()函數會關閉所有進程打開的描述符,向其父進程發送 SIGCHLD信號,並返回狀態,父進程可通過調用wait()或者waitpid()函數獲得其狀態 


第二部分--多進程80banner掃描器的具體實現 


一.思路: 

1.多ip處理模塊 
利用strtok()函數來分離ip 
strtok(ip,"."); 
for來循環ip 

2.usage提示模塊 
int usage(char *pro) 

printf("fork 80 banner scanner"); 
printf("usage:%s [startip] [stopip]/n",pro); 


3.掃描模塊 
viod scanip(char sip[20]) 

4.多進程及處理模塊 
fork() 


二.實現 

/******************************************************************** 
**fork() 80 banner scanner ver1.0 

*to complie: 
*user$gcc -o 80scaner 80scanner.c 

*to use: 
*user$./80scanner start_ip stop_ip (the best input c class ip otherwise *waste too many system resource ) 

*coded by nightcat 
*june 2003 

*********************************************************************/ 
/*所要的文件*/ 
#include <sys/types.h>; 
#include <sys/wait.h>; 
#include <stdio.h>; 
#include <string.h>; 
#include <sys/socket.h>; 
#include <netinet/in.h>; 
#include <errno.h>; 
#include <netdb.h>; 
#include <signal.h>; 

/*聲明函數*/ 
int usage(char *pro); 
void scanip(char *sip); 

int main(int argc,char *argv[]) 

/*聲明變量*/ 
int child_status; 
pid_t child_id; 
pid_t pid; 
char *pro; 
int count; 


char *start_ip,*stop_ip; 

char *chge; 
char scan_ip[20]; 

int ip1,ip2,ip3,ip4; 
int end1,end2,end3,end4; 

/*輸入參數判斷*/ 
if(argc<3){ 
usage(argv[0]); 
exit(1); 


/*處理ip*/ 
start_ip=argv[1]; 
chge=strtok(start_ip,"."); 
ip1=atoi(chge); 
chge=strtok(NULL,"."); 
ip2=atoi(chge); 
chge=strtok(NULL,"."); 
ip3=atoi(chge); 
chge=strtok(NULL,"."); 
ip4=atoi(chge); 


stop_ip=argv[2]; 
chge=strtok(stop_ip,"."); 
end1=atoi(chge); 
chge=strtok(NULL,"."); 
end2=atoi(chge); 
chge=strtok(NULL,"."); 
end3=atoi(chge); 
chge=strtok(NULL,"."); 
end4=atoi(chge); 

/*循環掃描*/ 
for(count=ip4;count<=end4;count++){ 
sprintf(scan_ip,"%d.%d.%d.%d",ip1,ip2,ip3,count); 
/*產生進程*/ 
if((pid=fork())==0){ 
scanip(scan_ip); 
exit(0); 




/*用法函數*/ 
int  usage(char *pro){ 
printf("fork() 80 banner scanner/n"); 
printf("input c class ip"); 
printf("usage:%s [start_ip][stop_ip]/n",pro); 


/*掃描函數*/ 
void scanip(char sip[20]){ 
struct sockaddr_in addr; 
int s; 
char buffer[1024]; 
if((s=socket(AF_INET,SOCK_STREAM,0))<0){ 
perror("socket error/n"); 
exit(1); 

addr.sin_family=AF_INET; 
addr.sin_addr.s_addr=inet_addr(sip); 
addr.sin_port=htons(80); 
if(connect(s,(struct sockaddr *)&addr,sizeof(addr))<0){ 
perror("connect error"); 
exit(1); 

send(s,"HEAD / HTTP/1.0/n/n",17,0); 
recv(s,buffer,sizeof(buffer),0); 
printf("%s's http version:/n%s",sip,buffer); 
close(s); 
sleep(5); 
exit(0); 



三.總結: 


調試的問題: 

1.sprintf(scan_ip,"%d.%d.%d.%d",ip1,ip2,ip3,count);這條語句提示溢出,主要是scan_ip分配的內存空間不夠,由原來char *scan_ip 改為char scan_ip[20] 
問題解決 

2.send(s,"HEAD / HTTP/1.0/n/n",17,0);這條語句發送後沒有得到回歸的信息,隻要是HTTP/1.0少了兩個回車/n/n,加上就可以啦! 


其中新學的東西有: 
   1.ip的處理方法 
   2.多進程的使用方法


 hbczjzc 回複於:2003-10-31 10:05:09

1. Process Control 進程控製  
1.1 Creating new processes: fork() 創建新進程:fork函數  
1.1.1 What does fork() do? fork函數幹什麼?  
1.1.2 What's the difference between fork() and vfork()? fork函數 與 vfork函數的區別在哪裏?  
1.1.3 Why use _exit rather than exit in the child branch of a fork? 為何在一個fork的子進程分支中使用_exit函數而不使用exit函數?  
1.2 Environment variables 環境變量  
1.2.1 How can I get/set an environment variable from a program? 我怎樣在程序中獲得/設置環境變量?  
1.2.2 How can I read the whole environment? 我怎樣讀取整個環境變量表?  
1.3 How can I sleep for less than a second? 我怎樣睡眠小於一秒?  
1.4 How can I get a finer-grained version of alarm()? 我怎樣得到一個更細分時間單位的alarm函數版本(譯者注:希望alarm的時間小於一秒)?  
1.5 How can a parent and child process communicate? 父子進程如何通信?  
1.6 How do I get rid of zombie processes? 我怎樣去除僵死進程?  
1.6.1 What is a zombie? 何為僵死進程?  
1.6.2 How do I prevent them from occuring? 我怎樣避免它們的出現?  
1.7 How do I get my program to act like a daemon? 我怎樣使我的程序作為守護程序運行?  
1.8 How can I look at process in the system like ps does? 我怎樣象ps程序一樣審視係統的進程?  
1.9 Given a pid, how can I tell if it's a running program? 給定一個進程號(譯者注:pid: process ID),我怎樣知道它是個正在運行的程序?  
1.10 What's the return value of system/pclose/waitpid? system函數,pclose函數,waitpid函數 的返回值是什麼?  
1.11 How do I find out about a process' memory usage? 我怎樣找出一個進程的存儲器使用情況?  
1.12 Why do processes never decrease in size? 為什麼進程的大小不縮減?  
1.13 How do I change the name of my program (as seen by ****ps')? 我怎樣改變我程序的名字(即“ps”看到的名字)?  
1.14 How can I find a process' executable file? 我怎樣找到進程的相應可執行文件?  
1.14.1 So where do I put my configuration files then? 那麼,我把配置文件放在哪裏呢?  
1.15 Why doesn't my process get SIGHUP when its parent dies? 為何父進程死時,我的進程未得到SIGHUP信號?  
1.16 How can I kill all descendents of a process? 我怎樣殺死一個進程的所有派生進程?  

2. General File handling (including pipes and sockets) 一般文件操作(包括管道和套接字)  
2.1 How to manage multiple connections? 怎樣管理多個連接?  
2.1.1 How do I use select()? 我怎樣使用select()?  
2.1.2 How do I use poll()? 我怎樣使用poll() ?  
2.1.3 Can I use SysV IPC at the same time as select or poll? 我是否可以將SysV 進程間通信 (譯者注:IPC: Interprocess Communications) 與select或poll同  
時使用?  
2.2 How can I tell when the other end of a connection shuts down? 我怎麼知道連接的另一端已關閉? 
2.3 Best way to read directories? 讀目錄的最好方法?  
2.4 How can I find out if someone else has a file open? 我怎麼知道其他人已經打開一個文件?  
2.5 How do I ****lock' a file? 我怎樣鎖定一個文件?  
2.6 How do I find out if a file has been updated by another process? 我怎麼知道一個文件是否已被其他進程更新?  
2.7 How does the ****du' utility work? “du”工具程序是怎麼工作的?  
2.8 How do I find the size of a file? 我怎麼知道一個文件的大小?  
2.9 How do I expand ****~' in a filename like the shell does? 我怎樣象shell程序一樣將一個文件名中含有的“~”展開?  
2.10 What can I do with named pipes (FIFOs)? 我能用有名管道(FIFOs)(譯者注:FIFO: First In First Oout)幹什麼?  
2.10.1 What is a named pipe? 什麼是有名管道?  
2.10.2 How do I create a named pipe? 我怎樣創建一個有名管道?  
2.10.3 How do I use a named pipe? 我怎樣使用一個有名管道?  
2.10.4 Can I use a named pipe across NFS? 我能基於網絡文件係統(譯者注:NFS:Network File System)使用有名管道嗎?  
2.10.5 Can multiple processes write to the pipe simultaneously? 多個進程能否同時向這個管道寫執行寫操作?  
2.10.6 Using named pipes in applications 在應用程序中使用有名管道。  

3. Terminal I/O 終端輸入/輸出(I/O:input/output)  
3.1 How can I make my program not echo input? 我怎樣使我的程序不回射輸入?  
3.2 How can I read single characters from the terminal? 我怎樣從終端讀取單個字符?  
3.3 How can I check and see if a key was pressed? 我怎樣檢查是否一個鍵被摁下?  
3.4 How can I move the cursor around the screen? 我怎樣將光標在屏幕裏移動?  
3.5 What are pttys? pttys(pttys:Pseudo-teletypes)是什麼?  
3.6 How to handle a serial port or modem? 怎樣控製一個串行口和調製解調器(譯者注:modem: modulate-demodulate)  
3.6.1 Serial device names and types 串行設備和類型  
3.6.2 Setting up termios flags 設置termios的標誌位  
3.6.2.1 c_iflag  
3.6.2.2 c_oflag  
3.6.2.3 c_cflag  
3.6.2.4 c_lflag  
3.6.2.5 c_cc  

4. System Information 係統信息  
4.1 How can I tell how much memory my system has? 我怎樣知道我的係統有多少存儲器容量?  
4.2 How do I check a user's password? 我怎樣檢查一個用戶的口令?  
4.2.1 How do I get a user's password? 我怎樣得到一個用戶的口令?  
4.2.2 How do I get shadow passwords by uid? 我怎樣通過用戶號(譯者注:uid: User ID)得到陰影口令文件中的口令?  
4.2.3 How do I verify a user's password? 我怎樣核對一個用戶的口令?  

5. Miscellaneous programming 編程雜技  
5.1 How do I compare strings using wildcards? 我怎樣使用通配字符比較字符串?  
5.1.1 How do I compare strings using filename patterns? 我怎樣使用文件名通配模式比較字符串?  
5.1.2 How do I compare strings using regular expressions? 我怎樣使用正則表達式比較字符串?  
5.2 What's the best way to send mail from a program? 什麼是在程序中發送電子郵件的最好方法?  
5.2.1 The simple method: /bin/mail 簡單方法:/bin/mail  
5.2.2 Invoking the MTA directly: /usr/lib/sendmail 直接啟動郵件傳輸代理(譯者注:MTA: mail transfer agent):/usr/bin/sendmail  
5.2.2.1 Supplying the envelope explicitly 顯式提供收件人信息  
5.2.2.2 Allowing sendmail to deduce the recipients 允許sendmail程序根據郵件內容分析出收件人  

6. Use of tools 工具的使用  
6.1 How can I debug the children after a fork? 我怎樣調試fork函數產生的子進程?  
6.2 How to build library from other libraries? 怎樣通過其他庫文件建立新的庫文件?  
6.3 How to create shared libraries / dlls? 怎樣創建動態連接庫/dlls?  
6.4 Can I replace objects in a shared library? 我能更改一個動態連接庫裏的目標嗎?  
6.5 How can I generate a stack dump from within a running program? 我能在一個運行著的程序中生成堆棧映象嗎?  


1. 進程控製  
***********  

1.1 創建新進程:fork函數  
========================  

1.1.1 fork函數幹什麼?  
----------------------  

#include <sys/types.h>;  
#include <unistd.h>;  

pid_t fork(void);  

‘fork()’函數用於從已存在進程中創建一個新進程。新進程稱為子進程,而原進程稱為  
父進程。你可以通過檢查‘fork()’函數的返回值知道哪個是父進程,哪個是子進程。父  
進程得到的返回值是子進程的進程號,而子進程則返回0。以下這個範例程序說明它的基本  
功能:  

pid_t pid;  

switch (pid = fork())  
{  
case -1:  
/* 這裏pid為-1,fork函數失敗 */  
/* 一些可能的原因是 */  
/* 進程數或虛擬內存用盡 */  
perror("The fork failed!");  
break;  

case 0:  
/* pid為0,子進程 */  
/* 這裏,我們是孩子,要做什麼? */  
/* ... */  
/* 但是做完後, 我們需要做類似下麵: */  
_exit(0);  

default:  
/* pid大於0,為父進程得到的子進程號 */  
printf("Child's pid is %d/n",pid);  
}  

當然,有人可以用‘if() ... else ...’語句取代‘switch()’語句,但是上麵的形式是  
一個有用的慣用方法。  

知道子進程自父進程繼承什麼或未繼承什麼將有助於我們。下麵這個名單會因為  
不同Unix的實現而發生變化,所以或許準確性有了水份。請注意子進程得到的是  
這些東西的 *拷貝*,不是它們本身。  

由子進程自父進程繼承到:  

* 進程的資格(真實(real)/有效(effective)/已保存(saved) 用戶號(UIDs)和組號(GIDs))  

* 環境(environment)  

* 堆棧  

* 內存  

* 打開文件的描述符(注意對應的文件的位置由父子進程共享,這會引起含煳情況)  

* 執行時關閉(close-on-exec) 標誌 (譯者注:close-on-exec標誌可通過fnctl()對文件描  
述符設置,POSIX.1要求所有目錄流都必須在exec函數調用時關閉。更詳細說明,  
參見<<UNIX環境高級編程>;>; W. R. Stevens, 1993, 尤晉元等譯(以下簡稱<<高級編  
程>;>;, 3.13節和8.9節)  

* 信號(signal)控製設定  

* nice值 (譯者注:nice值由nice函數設定,該值表示進程的優先級,數值越小,優  
先級越高)  

* 進程調度類別(scheduler class) (譯者注:進程調度類別指進程在係統中被調度時所  
屬的類別,不同類別有不同優先級,根據進程調度類別和nice值,進程調度程序可計  
算出每個進程的全局優先級(Global process prority),優先級高的進程優先執行)  

* 進程組號  

* 對話期ID(Session ID) (譯者注:譯文取自<<高級編程>;>;,指:進程所屬的對話期  
(session)ID, 一個對話期包括一個或多個進程組, 更詳細說明參見<<高級編程>;>;  
9.5節)  

* 當前工作目錄  

* 根目錄 (譯者注:根目錄不一定是“/”,它可由chroot函數改變)  

* 文件方式創建屏蔽字(file mode creation mask (umask)) (譯者注:譯文取自<<高級編  
程>;>;,指:創建新文件的缺省屏蔽字)  

* 資源限製  

* 控製終端  

子進程所獨有:  

* 進程號  

* 不同的父進程號(譯者注:即子進程的父進程號與父進程的父進程號不同,父進  
程號可由getppid函數得到)  

* 自己的文件描述符和目錄流的拷貝(譯者注:目錄流由opendir函數創建,因其為  
順序讀取,顧稱“目錄流”)  

* 子進程不繼承父進程的進程,正文(text),數據和其它鎖定內存(memory locks)  
(譯者注:鎖定內存指被鎖定的虛擬內存頁,鎖定後,不允許內核將其在必要時  
換出(page out),詳細說明參見<<The GNU C Library Reference Manual>;>; 2.2版,  
1999, 3.4.2節)  

* 在tms結構中的係統時間(譯者注:tms結構可由times函數獲得,它保存四個數據  
用於記錄進程使用中央處理器(CPU:Central Processing Unit)的時間,包括:用戶時  
間,係統時間,用戶各子進程合計時間,係統各子進程合計時間)  

* 資源使用(resource utilizations)設定為0  

* 阻塞信號集初始化為空集(譯者注:原文此處不明確,譯文根據fork函數手冊頁  
稍做修改)  

* 不繼承由timer_create函數創建的計時器  

* 不繼承異步輸入和輸出  

1.1.2 fork函數 與 vfork函數的區別在哪裏裏?  
-------------------------------------------  

有些係統有一個係統調用‘vfork()’,它最初被設計成‘fork()’的較少額外支出  
(lower-overhead)版本。因為‘fork()’包括拷貝整個進程的地址空間,所以非常  
“昂貴”,這個‘vfork()’函數因此被引入。(在3.0BSD中)(譯者注:BSD:  
Berkeley Software Distribution)  

*但是*,自從‘vfork()’被引入,‘fork()’的實現方法得到了很大改善,最值得  
注意的是“寫操作時拷貝”(copy-on-write)的引入,它是通過允許父子進程可訪問  
相同物理內存從而偽裝(fake)了對進程地址空間的真實拷貝,直到有進程改變內  
存中數據時才拷貝。這個提高很大程度上抹殺了需要‘vfork()’的理由;事實上,  
一大部份係統完全喪失了‘vfork()’的原始功能。但為了兼容,它們仍然提供  
‘vfork()’函數調用,但它隻是簡單地調用‘fork()’,而不試圖模擬所有‘vfork()’  
的語義(semantics, 譯文取自<<高級編程>;>;,指定義的內容和做法)。  

結論是,試圖使用任何‘fork()’和‘vfork()’的不同點是*很*不明智的。事實上,  
可能使用‘vfork()’根本就是不明智的,除非你確切知道你想*幹什麼*。  

兩者的基本區別在於當使用‘vfork()’創建新進程時,父進程將被暫時阻塞,而  
子進程則可以借用父進程的地址空間。這個奇特狀態將持續直到子進程要麼退  
出,要麼調用‘execve()’,至此父進程才繼續執行。  

這意味著一個由‘vfork()’創建的子進程必須小心以免出乎意料地改變父進程的  
變量。特別的,子進程必須不從包含‘vfork()’調用的函數返回,而且必須不調  
用‘exit()’(如果它需要退出,它需要使用‘_exit()’;事實上,對於使用正常  
‘fork()’創建的子進程這也是正確的)(譯者注:參見1.1.3)  

1.1.3 為何在一個fork的子進程分支中使用_exit函數而不使用exit函數?  
-----------------------------------------------------------------  

‘exit()’與‘_exit()’有不少區別在使用‘fork()’,特別是‘vfork()’時變得很  
突出。  

‘exit()’與‘_exit()’的基本區別在於前一個調用實施與調用庫裏用戶狀態結構  
(user-mode constructs)有關的清除工作(clean-up),而且調用用戶自定義的清除程序  
(譯者注:自定義清除程序由atexit函數定義,可定義多次,並以倒序執行),相對  
應,後一個函數隻為進程實施內核清除工作。  

在由‘fork()’創建的子進程分支裏,正常情況下使用‘exit()’是不正確的,這是  
因為使用它會導致標準輸入輸出(譯者注:stdio: Standard Input Output)的緩衝區被  
清空兩次,而且臨時文件被出乎意料的刪除(譯者注:臨時文件由tmpfile函數創建  
在係統臨時目錄下,文件名由係統隨機生成)。在C++程序中情況會更糟,因為靜  
態目標(static objects)的析構函數(destructors)可以被錯誤地執行。(還有一些特殊情  
況,比如守護程序,它們的*父進程*需要調用‘_exit()’而不是子進程;適用於絕  
大多數情況的基本規則是,‘exit()’在每一次進入‘main’函數後隻調用一次。)  

在由‘vfork()’創建的子進程分支裏,‘exit()’的使用將更加危險,因為它將影響  
*父*進程的狀態。  

1.2 環境變量  
============  

1.2.1 如何從程序中獲得/設置環境變量?  
--------------------------------------  
獲得一個環境變量可以通過調用‘getenv()’函數完成。  

#include <stdlib.h>;  

char *getenv(const char *name);  

設置一個環境變量可以通過調用‘putenv()’函數完成。  

#include <stdlib.h>;  

int putenv(char *string);  

變量string應該遵守"name=value"的格式。已經傳遞給putenv函數的字符串*不*能夠被  
釋放或變成無效,因為一個指向它的指針將由‘putenv()’保存。這意味著它必須是  
在靜態數據區中或是從堆(heap)分配的。如果這個環境變量被另一個‘putenv()’的  
調用重新定義或刪除,上述字符串可以被釋放。  

/* 譯者增加:  

因為putenv()有這樣的局限,在使用中經常會導致一些錯  
誤,GNU libc 中還包括了兩個BSD風格的函數:  
#include <stdlib.h>;  
int setenv(const char *name, const char *value, int replace);  
void unsetenv(const char *name);  

setenv()/unsetenv()函數可以完成所有putenv()能做的事。setenv() 可以不受指針  
限製地向環境變量中添加新值,但傳入參數不能為空(NULL)。當replace為0時,如  
果環境變量中已經有了name項,函數什麼也不做(保留原項),否則原項被覆蓋。  
unsetenv()是用來把name項從環境變量中刪除。注意:這兩個函數隻存在在BSD和GNU  
庫中,其他如SunOS係統中不包括它們,因此將會帶來一些兼容問題。我們可以用  
getenv()/putenv()來實現:  

int setenv(const char *name, const char *value, int replace)  
{  
char *envstr;  

if (name == NULL || value == NULL)  
return 1;  
if (getenv(name) !=NULL)  
{  
envstr = (char *) malloc(strlen(name) + strlen(value) + 2);  
sprintf (envstr, "%s=%s", name, value);  
if (putenv(envstr));  
return 1;  
}  
return 0;  
}  
*/  

記住環境變量是被繼承的;每一個進程有一個不同的環境變量表拷貝(譯者注:  
從core文件中我們可以看出這一點)。結果是,你不能從一個其他進程改變當前  
進程的環境變量,比如shell進程。  

假設你想得到環境變量‘TERM’的值,你需要使用下麵的程序:  

char *envvar;  

envvar=getenv("TERM");  

printf("The value for the environment variable TERM is ");  
if(envvar)  
{  
printf("%s/n",envvar);  
}  
else  
{  
printf("not set./n");  
}  

現在假設你想創建一個新的環境變量,變量名為‘MYVAR’,值為‘MYVAL’。  
以下是你將怎樣做:  

static char envbuf[256];  

sprintf(envbuf,"MYVAR=%s","MYVAL");  

if(putenv(envbuf))  
{  
printf("Sorry, putenv() couldn't find the memory for %s/n",envbuf);  
/* Might exit() or something here if you can't live without it */  
}  

1.2.2 我怎樣讀取整個環境變量表?  
--------------------------------  

如果你不知道確切你想要的環境變量的名字,那麼‘getenv()’函數不是很有用。  
在這種情況下,你必須更深入了解環境變量表的存儲方式。  

全局變量,‘char **envrion’,包含指向環境字符串指針數組的指針,每一個字  
符串的形式為‘“NAME=value”’(譯者注:和putenv()中的“string”的格式相同)。  
這個數組以一個‘空’(NULL)指針標記結束。這裏是一個打印當前環境變量列表  
的小程序(類似‘printenv’)。  

#include <stdio.h>;  

extern char **environ;  

int main()  
{  
char **ep = environ;  
char *p;  
while ((p = *ep++))  
printf("%s/n", p);  
return 0;  
}  

一般情況下,‘envrion’變量作為可選的第三個參數傳遞給‘main()’;就是說,  
上麵的程序可以寫成:  

#include <stdio.h>;  

int main(int argc, char **argv, char **envp)  
{  
char *p;  
while ((p = *envp++))  
printf("%s/n", p);  
return 0;  
}  

雖然這種方法被廣泛的操縱係統所支持(譯者注:包括DOS),這種方法事實上並  
沒有被POSIX(譯者注:POSIX: Portable Operating System Interace)標準所定義。(一  
般的,它也比較沒用)  

1.3 我怎樣睡眠小於一秒?  
========================  

在所有Unix中都有的‘sleep()’函數隻允許以秒計算的時間間隔。如果你想要更  
細化,那麼你需要尋找替換方法:  

* 許多係統有一個‘usleep()’函數  

* 你可以使用‘select()’或‘poll()’,並設置成無文件描述符並試驗;一個普  
遍技巧是基於其中一個函數寫一個‘usleep()’函數。(參見comp.unix.questions  
FAQ 的一些例子)  

* 如果你的係統有itimers(很多是有的)(譯者注:setitimer和getitimer是兩個操作  
itimers的函數,使用“man setitimer”確認你的係統支持),你可以用它們自己攛一  
個‘usleep()’。(參見BSD源程序的‘usleep()’以便知道怎樣做)  

* 如果你有POSIX實時(realtime)支持,那會有一個‘nanosleep()’函數。  

眾觀以上方法,‘select()’可能是移植性最好的(直截了當說,它經常比  
‘usleep()’或基於itimer的方法更有效)。但是,在睡眠中捕獲信號的做法會有  
所不同;基於不同應用,這可以成為或不成為一個問題。  

無論你選擇哪條路,意識到你將受到係統計時器分辨率的限製是很重要的(一  
些係統允許設置非常短的時間間隔,而其他的係統有一個分辨率,比如說10毫  
秒,而且總是將所有設置時間取整到那個值)。而且,關於‘sleep()’,你設置  
的延遲隻是最小值(譯者注:實際延遲的最小值);經過這段時間的延遲,會有  
一個中間時間間隔直到你的進程重新被調度到。  

1.4 我怎樣得到一個更細分時間單位的alarm函數版本?  
==================================================  

當今Unix係統傾向於使用‘setitimer()’函數實現鬧鍾,它比簡單的‘alarm()’函  
數具有更高的分辨率和更多的選擇項。一個使用者一般需要首先假設‘alarm()’  
和‘setitimer(ITIMER_REAL)’可能是相同的底層計時器,而且假設同時使用兩  
種方法會造成混亂。  

Itimers可被用於實現一次性或重複信號;而且一般有3種不同的計時器可以用:  

****ITIMER_REAL'  
計數真實(掛鍾)時間,然後發送‘SIGALRM’信號  

****ITIMER_VIRTUAL'  
計數進程虛擬(用戶中央處理器)時間,然後發送‘SIGVTALRM’信號  

****ITIMER_PROF'  
計數用戶和係統中央處理器時間,然後發送‘SIGPROF’信號;它供解釋器  
用來進行梗概處理(profiling)  

然而itimers不是許多標準的一部份,盡管它自從4.2BSD就被提供。POSIX實時標  
準的擴充定義了類似但不同的函數。  

1.5 父子進程如何通信?  
======================  

一對父子進程可以通過正常的進程間通信的辦法(管道,套接字,消息隊列,共  
享內存)進行通信,但也可以通過利用它們作為父子進程的相互關係而具有的一  
些特殊方法。  

一個最顯然的方法是父進程可以得到子進程的退出狀態。  

因為子進程從它的父進程繼承文件描述符,所以父進程可以打開一個管道的兩端,  
然後fork,然後父進程關閉管道這一端,子進程關閉管道另一端。這正是你從你的  
進程調用‘popen()’函數運行另一個程序所發生的情況,也就是說你可以向  
‘popen()’返回的文件描述符進行寫操作而子進程將其當作自己的標準輸入,或  
者你可以讀取這個文件描述符來看子進程向標準輸出寫了什麼。(‘popen()’函數  
的mode參數定義你的意圖(譯者注:mode=“r”為讀,mode=“w”為寫);如果你  
想讀寫都做,那麼你可以並不困難地用管道自己做到)  

而且,子進程繼承由父進程用mmap函數映射的匿名共享內存段(或者通過映射特  
殊文件‘/dev/zero’);這些共享內存段不能從無關的進程訪問。  

1.6 我怎樣去除僵死進程?  
========================  

1.6.1 何為僵死進程?  
--------------------  

當一個程序創建的子進程比父進程提前結束,內核仍然保存一些它的信息以便父  
進程會需要它 - 比如,父進程可能需要檢查子進程的退出狀態。為了得到這些信  
息,父進程調用‘wait()’;當這個調用發生,內核可以丟棄這些信息。  

在子進程終止後到父進程調用‘wait()’前的時間裏,子進程被稱為‘僵死進程’  
(‘zombie’)。(如果你用‘ps’,這個子進程會有一個‘Z’出現在它的狀態區  
裏指出這點。)即使它沒有在執行,它仍然占據進程表裏一個位置。(它不消耗其  
它資源,但是有些工具程序會顯示錯誤的數字,比如中央處理器的使用;這是  
因為為節約空間進程表的某些部份與會計數據(accounting info)是共用(overlaid)的。)  

這並不好,因為進程表對於進程數有固定的上限,係統會用光它們。即使係統沒  
有用光 ,每一個用戶可以同時執行的進程數有限製,它總是小於係統的限製。  
順便說一下,這也正是你需要總是 檢查‘fork()’是否失敗的一個原因。  

如果父進程未調用wait函數而終止,子進程將被‘init’進程收管,它將控製子進  
程退出後必須的清除工作。(‘init’是一個特殊的係統程序,進程號為1 - 它實際  
上是係統啟動後運行的第一個程序),  

1.6.2 我怎樣避免它們的出現?  
----------------------------  

你需要卻認父進程為每個子進程的終止調用‘wait()’(或者‘waitpid()’,  
‘wait3()’,等等); 或者,在某些係統上,你可以指令係統你對子進程的退出狀  
態沒有興趣。(譯者注:在SysV係統上,可以調用signal函數,設置SIGCLD信號為  
SIG_IGN,係統將不產生僵死進程, 詳細說明參見<<高級編程>;>;10.7節)  

另一種方法是*兩次*‘fork()’,而且使緊跟的子進程直接退出,這樣造成孫子進  
程變成孤兒進程(orphaned),從而init進程將負責清除它。欲獲得做這個的程序,參  
看範例章節的函數‘fork2()’。  

為了忽略子進程狀態,你需要做下麵的步驟(查詢你的係統手冊頁以知道這是否正  
常工作):  

struct sigaction sa;  
sa.sa_handler = SIG_IGN;  
#ifdef SA_NOCLDWAIT  
sa.sa_flags = SA_NOCLDWAIT;  
#else  
sa.sa_flags = 0;  
#endif  
sigemptyset(&sa.sa_mask);  
sigaction(SIGCHLD, &sa, NULL);  

如果這是成功的,那麼‘wait()’函數集將不再正常工作;如果它們中任何一個被  
調用,它們將等待直到*所有*子進程已經退出,然後返回失敗,並且  
‘errno==ECHILD’。  

另一個技巧是捕獲SIGCHLD信號,然後使信號處理程序調用‘waitpid()’或  
‘wait3()’。參見範例章節的完整程序。  

1.7 我怎樣使我的程序作為守護程序運行?  
======================================  

一個“守護程序”進程通常被定義為一個後台進程,而且它不屬於任何一個終端  
會話,(terminal session)。許多係統服務由守護程序實施;如網絡服務,打印等。  

簡單地在後台啟動一個程序並非足夠是這些長時間運行的程序;那種方法沒有正  
確地將進程從啟動它的終端脫離(detach)。而且,啟動守護程序的普遍接受的的方  
法是簡單地手工執行或從rc腳本程序執行(譯者注:rc:runcom);並希望這個守護  
程序將其*自身*安置到後台。  

這裏是成為守護程序的步驟:  

1. 調用‘fork()’以便父進程可以退出,這樣就將控製權歸還給運行你程序的  
命令行或shell程序。需要這一步以便保證新進程不是一個進程組頭領進程(process  
group leader)。下一步,‘setsid()’,會因為你是進程組頭領進程而失敗。  

2. 調用‘setsid()’ 以便成為一個進程組和會話組的頭領進程。由於一個控製終端  
與一個會話相關聯,而且這個新會話還沒有獲得一個控製終端,我們的進程沒  
有控製終端,這對於守護程序來說是一件好事。  

3. 再次調用‘fork()’所以父進程(會話組頭領進程)可以退出。這意味著我們,一  
個非會話組頭領進程永遠不能重新獲得控製終端。  

4. 調用‘chdir("/")’確認我們的進程不保持任何目錄於使用狀態。不做這個會導  
致係統管理員不能卸裝(umount)一個文件係統,因為它是我們的當前工作目錄。  

[類似的,我們可以改變當前目錄至對於守護程序運行重要的文件所在目錄]  

5. 調用‘umask(0)’以便我們擁有對於我們寫的任何東西的完全控製。我們不知  
道我們繼承了什麼樣的umask。  

[這一步是可選的](譯者注:這裏指步驟5,因為守護程序不一定需要寫文件)  

6. 調用‘close()’關閉文件描述符0,1和2。這樣我們釋放了從父進程繼承的標  
準輸入,標準輸出,和標準錯誤輸出。我們沒辦法知道這些文描述符符可能  
已經被重定向去哪裏。注意到許多守護程序使用‘sysconf()’來確認  
‘_SC_OPEN_MAX’的限製。‘_SC_OPEN_MAX’告訴你每個進程能夠打  
開的最多文件數。然後使用一個循環,守護程序可以關閉所有可能的文件描  
述符。你必須決定你需要做這個或不做。如果你認為有可能有打開的文件描  
述符,你需要關閉它們,因為係統有一個同時打開文件數的限製。  

7. 為標準輸入,標準輸出和標準錯誤輸出建立新的文件描述符。即使你不打算  
使用它們,打開著它們不失為一個好主意。準確操作這些描述符是基於各自  
愛好;比如說,如果你有一個日誌文件,你可能希望把它作為標準輸出和標  
準錯誤輸出打開,而把‘/dev/null’作為標準輸入打開;作為替代方法,你可  
以將‘/dev/console’作為標準錯誤輸出和/或標準輸出打開,而‘/dev/null’作  
為標準輸入,或者任何其它對你的守護程序有意義的結合方法。(譯者注:一  
般使用dup2函數原子化關閉和複製文件描述符,參見<<高級編程>;>;3.12節)  

如果你的守護程序是被‘inetd’啟動的,幾乎所有這些步驟都不需要(或不建議  
采用)。在那種情況下,標準輸入,標準輸出和標準錯誤輸出都為你指定為網絡  
連接,而且‘fork()’的調用和會話的操縱不應做(以免使‘inetd’造成混亂)。隻  
有‘chdir()’和‘umask()’這兩步保持有用。  

1.8 我怎樣象ps程序一樣審視係統的進程?  
=======================================  

你真的不該想做這個。  

到目前為止,移植性最好的是調用‘popen(pscmd,"r")’並處理它的輸出。(pscmd  
應當是類似SysV係統上的‘“ps -ef”’,BSD係統有很多可能的顯示選項:選  
擇一個。)  

在範例章節有這個問題的兩個完整解決方法;一個適用於SunOS 4,它需要root權  
限執行並使用‘kvm_*’例程從內核數據結果讀取信息;另一種適用於SVR4係統  
(包括Sun OS 5),它使用‘/proc’文件係統。  

在具有SVR4.2風格‘/proc’的係統上更簡單;隻要對於每一個感興趣的進程號從  
文件‘/proc/進程號/psinfo’讀取一個psinfo_t結構。但是,這種可能是最清晰的方  
法也許又是最不得到很好支持的方法。(在FreeBSD的‘/proc’上,你從  
‘/proc/進程號/status’讀取一個半未提供文檔說明(semi-undocumented)的可打印字  
符串;Linux有一些與其類似的東西)  

1.9 給定一個進程號,我怎樣知道它是個正在運行的程序?  
=====================================================  

使用‘kill()’函數,而已0作為信號代碼(signal number)。  

從這個函數返回有四種可能的結果:  

* ‘kill()’返回0  

- 這意味著一個給定此進程號的進程退出,係統允許你向它發送信號。該進  
程是否可以是僵死進程與不同係統有關。  

* ‘kill()’返回-1,‘errno == ESRCH’  

- 要麼不存在給定進程號的進程,要麼增強的安全機製導致係統否認它的存  
在。(在一些係統上,這個進程有可能是僵死進程。)  

* ‘kill()’返回-1,‘errno == EPERM’  

- 係統不允許你殺死(kill)這個特定進程。這意味著要麼進程存在(它又可能是  
僵死進程),要麼嚴格的增強安全機製起作用(比如你的進程不允許發送信號  
給*任何人*)。  

* ‘kill()’返回-1,伴以其它‘errno’值  

- 你有麻煩了!  

用的最多的技巧是認為調用“成功”或伴以‘EPERM’的“失敗”意味著進程存  
在,而其它錯誤意味著它不存在。  

如果你特別為提供‘/proc’文件係統的係統(或所有類似係統)寫程序,一個替換  
方法存在:檢查‘proc/進程號’是否存在是可行的。  

1.10 system函數,pclose函數,waitpid函數 的返回值是什麼?  
==========================================================  

‘system()’,‘pclose()’或者‘waitpid()’的返回值不象是我進程的退出值(exit  
value)(譯者注:退出值指調用exit() 或_exit()時給的參數)... 或者退出值左移了8  
位...這是怎麼搞的?  

手冊頁是對的,你也是對的! 如果查閱手冊頁的‘waitpid()’你會發現進程的返回  
值被編碼了。正常情況下,進程的返回值在高16位,而餘下的位用來作其它事。  
如果你希望可移植,

最後更新:2017-04-02 06:51:26

  上一篇:go STL中的hash_map
  下一篇:go magento -- 修正1.4.1.1和1.4.1.1裏Newsletter的bug