[英]GCC on HP-UX, lots of poll(), pipe(), and file issues
我在構建“中間人”記錄器時遇到很多麻煩-目的是將其放置在/ usr / bin中項目上方的路徑上,並捕獲往返於應用程序的所有信息。 (由於某種原因,黑匣子第3方應用程序無法通過FTP傳輸。)一旦運行,中間人將派生,將stdout和stdin重定向到父級可以控制的管道,或從其控制父級的管道重定向,然后在/ usr / bin中執行程序。 (硬編碼;是的,我知道,我很糟糕。)
但是,一旦我運行poll(),事情就會變得很奇怪。 我丟失了日志文件的句柄,孩子對輸出管道的輪詢拋出錯誤,貓和狗開始同居,等等。
誰能對此有所啟發?
這是我目前擁有的...有問題的poll()標記有非縮進注釋,以便於定位。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>
#define MAX_STR_LEN 1024
static int directionFlag; /* 0 = input, 1 = output */
static int eofFlag;
/* Splits the next char from the stream inFile, with extra
information logged if directionFlag swaps */
void logChar(int inFilDes, int outFilDes, FILE *logFile, int direction)
{
char inChar = 0;
if(read(inFilDes, &inChar, sizeof(char)) > 0)
{
if(direction != directionFlag)
{
directionFlag = direction;
if(direction)
{
fprintf(logFile, "\nOUTPUT: ");
} else {
fprintf(logFile, "\nINPUT: ");
}
}
write(outFilDes, &inChar, sizeof(char));
fputc(inChar, stderr);
fputc(inChar, logFile);
} else {
eofFlag = 1;
}
return;
}
int main(int argc, char* argv[])
{
pid_t pid;
int childInPipe[2];
int childOutPipe[2];
eofFlag = 0;
/* [0] is input, [1] is output*/
if(pipe(childInPipe) < 0 || pipe(childOutPipe) < 0) {
fprintf(stderr,"Pipe error; aborting\n");
exit(1);
}
if((pid = fork()) == -1){
fprintf(stderr,"Fork error; aborting\n");
exit(1);
}
if(pid)
{
/*Parent process*/
int i;
int errcode;
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo=localtime(&rawtime);
struct pollfd pollArray[2] = {
{ .fd = 0, .events = POLLIN, .revents = 0 },
{ .fd = childOutPipe[0], .events = POLLIN, .revents = 0 }
};
/* Yet again, 0 = input, 1 = output */
nfds_t nfds = sizeof(struct pollfd[2]);
close(childInPipe[0]);
close(childOutPipe[1]);
/* We don't want to change around the streams for this one,
as we will be logging everything - and I do mean everything */
FILE *logFile;
if(!(logFile = fopen("/opt/middleman/logfile.txt", "a"))) {
fprintf(stderr, "fopen fail on /opt/middleman/logfile.txt\n");
exit(1);
}
fprintf(logFile, "Commandline: ");
for(i=0; i < argc; i++)
{
fprintf(logFile, "%s ", argv[i]);
}
fprintf(logFile, "\nTIMESTAMP: %s\n", asctime(timeinfo));
while(!eofFlag)
{
// RIGHT HERE is where things go to pot
errcode = poll(pollArray, nfds, 1);
// All following fprintf(logfile)s do nothing
if(errcode < 0) {
fprintf(stderr, "POLL returned with error %d!", errcode);
eofFlag = 1;
}
if((pollArray[0].revents && POLLERR) & errno != EAGAIN ) {
fprintf(stderr, "POLL on input has thrown an exception!\n");
fprintf(stderr, "ERRNO value: %d\n", errno);
fprintf(logFile, "POLL on input has thrown an exception!\n");
eofFlag = 1;
} else if(pollArray[0].revents && POLLIN) {
logChar(pollArray[0].fd, childInPipe[1], logFile, 0);
} else if((pollArray[1].revents && POLLERR) & errno != EAGAIN ) {
fprintf(stderr, "POLL on output has thrown an exception!\n");
fprintf(stderr, "ERRNO value: %d\n", errno);
fprintf(logFile, "POLL on output has thrown an exception!\n");
eofFlag = 1;
} else if(pollArray[1].revents && POLLIN) {
logChar(pollArray[1].fd, 1, logFile, 1);
}
}
fclose(logFile);
}
else
{
/*Child process; switch streams and execute application*/
int i;
int catcherr = 0;
char stmt[MAX_STR_LEN] = "/usr/bin/";
close(childInPipe[1]);
close(childOutPipe[0]);
strcat(stmt, argv[0]);
if(dup2(childInPipe[0],0) < 0) {
fprintf(stderr, "dup2 threw error %d on childInPipe[0] to stdin!\n", errno);
}
// close(childInPipe[0]);
if(dup2(childOutPipe[1],1) < 0)
{
fprintf(stderr, "dup2 threw error %d on childInPipe[1] to stdout!\n", errno);
}
/* Arguments need to be in a different format for execv */
char* args[argc+1];
for(i = 0; i < argc; i++)
{
args[i] = argv[i];
}
args[i] = (char *)0;
fprintf(stderr, "Child setup complete, executing %s\n", stmt);
fprintf(stdout, "Child setup complete, executing %s\n", stmt);
if(execv(stmt, args) == -1) {
fprintf(stderr, "execvP error!\n");
exit(1);
}
}
return 0;
}
修復之后,我嘗試通過此程序運行“橫幅”,這是我得到的輸出...
Commandline: banner testing
TIMESTAMP: Tue Jun 23 11:21:00 2009
日志文件具有以下內容:
Commandline: banner testing TIMESTAMP: Tue Jun 23 11:21:00 2009
ERRNO之所以為0的原因是因為poll()返回的值很好。 返回的是pollArray [1] .revents並返回錯誤,這意味着childOutPipe [0]被輪詢為有錯誤。 據我所知,logChar()從未被調用。
我將嘗試將poll()分為兩個不同的調用。
真正的問題:
struct pollfd pollArray[2] = {{0, POLLIN, 0}, {childOutPipe[0], POLLIN, 0}};
您正在對'struct pollfd'的順序和內容進行不必要的假設。 該標准只說它包含(至少)三個成員。 它沒有說明它們出現的順序。
標頭應定義pollfd結構,該結構至少應包括以下成員:
int fd The following descriptor being polled. short events The input event flags (see below). short revents The output event flags (see below).
由於您使用的是C99,因此請使用安全的初始化符號:
struct pollfd pollArray[2] =
{
{ .fd = 0, .events = POLLIN, .revents = 0 },
{ .fd = childOutPipe[0], .events = POLLIN, .revents = 0 },
};
您可以將<fcntl.h>
FILENO_STDIN
替換為標准輸入的0。
nfds_t nfds = sizeof(pollArray);
輪詢陣列的大小可能約為16(字節)-在大多數(但不是全部)機器(32位和64位)上。 您需要輪詢數組的尺寸(為2)。 這就是為什么所有的地獄都變得松散。 系統正在查看垃圾並感到困惑。
發表評論 :
若要查找在本地文件或函數中定義的數組的維數(而不是傳遞給函數的數組參數,也不要在另一個文件中定義的數組),請使用宏的變體:
#define DIM(x) (sizeof(x)/sizeof(*(x)))
這個名字使人聯想起在昏暗,遙遠的過去使用BASIC。 我見過的其他名稱是NELEMS
或ARRAY_SIZE
或DIMENSION
(回溯到Fortran IV),我敢肯定還有很多其他名稱。
發生的事情是,因為您沒有將nfds
設置為2,所以系統調用是在實際的struct pollfd
數組之后讀取數據,並試圖使不是struct pollfd
的東西成為首尾。 特別是,它可能正在寫入您所告知的內容,即struct pollfd
數組中一行的revents
字段,但實際空間為log FILE *
,因此已完全搞砸了。 對於其他局部變量也是如此。 換句話說,您有一個堆棧緩沖區溢出-aka堆棧溢出,這個名字應該很熟悉。 但這是因為您對其進行了編程。
固定:
nfds_t nfds = DIM(pollArray);
poll(pollArray, nfds, 1);
if (errcode < 0) {
poll()
的結果不會保存,並且永遠不會為變量errcode
分配值,但是您之后會立即檢查該值。 更正后的代碼可能顯示為:
errcode = poll(pollArray, nfds, 1);
if (errcode < 0)
{
fprintf(stderr, "POLL returned with error %d!\n", errcode);
eofFlag = 1;
}
請注意添加到錯誤消息的換行符-您需要它。 要么:
if (poll(pollArray, nfds, 1) < 0)
{
int errnum = errno;
fprintf(stderr, "POLL returned with error (%d: %s)\n",
errnum, strerror(errnum));
eofFlag = 1;
}
在第二種情況下,您可以將' #include <errno.h>
'添加到標題列表中。 保存errno
的值可防止通過函數調用更改它-但是,只有在函數(系統調用)失敗時,您才能可靠地測試errno
。 即使成功的函數調用也可能使errno
不為零。 (例如,在某些系統上,如果stderr
不在終端上,則I / O調用之后的errno
值為ENOTTY
,即使調用整體成功。)
關於可能是什么問題的一些先前想法; 我認為這里還有一些有用的信息。
我懷疑您的問題是 (已經檢查了Open Group的手冊頁,看來poll()
“損壞”了輪詢的描述符集,並且您必須在每個循環上重建它。 poll()
並沒有select()
遇到的問題。)這當然是相關的select()
系統調用的問題。
您的子代碼應該在應有的狀態下關閉所有文件描述符-您已注釋掉一個'close()`,而另一個則完全丟失。 當孩子完成將管道連接到標准輸入和輸出時,您不希望未復制的文件描述符仍然打開; 進程無法正確檢測到EOF。
類似的評論可能適用於家長。
另外,請注意,在子級的標准輸出上沒有出現任何內容之前,發送過程可能需要向子級發送多個數據包。 在極端情況下,考慮“ sort
”; 在生成任何輸出之前先讀取其所有數據。 因此,盡管我沒有完全了解它的功能,但我擔心方向轉換代碼。 就其本身而言,方向切換是無害的-當它開始與上次相反的方向寫入時,它只是寫入新的方向。
更嚴重的是,不要使用單字符讀寫。 讀取明智的大小緩沖區已滿。 合理的大小幾乎可以是256到8192之間的任何2的冪。 您可以自由選擇其他大小(管道緩沖區的大小可能是一個不錯的選擇)。 一次處理多個字符將大大提高性能。
我解決類似問題的方法是通過兩個過程進行監視,一個過程用於標准輸入,另一個過程用於標准輸出-或等效過程。 這意味着我根本不需要使用poll()
(或select()
)。 處理標准輸入的過程讀取並阻止等待更多信息; 當有東西到達時,它記錄下來並將其寫入孩子的標准輸入中。 對於處理標准輸出的過程類似。
如果需要,我可以挖掘出適用於管道的代碼(請參閱我的個人資料)。 我在一兩年前看過它(嗯,實際上是在2005年進行的最后一次編輯,盡管我是在2007年重新編譯的),但它仍處於工作狀態(它寫於1989年左右)。 我也有適用於套接字而不是管道的代碼。 他們需要適應您的需求; 它們是相當專業的(特別是管道版本,它知道客戶端-服務器數據庫協議,並嘗試處理完整的信息包)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.