簡體   English   中英

HP-UX上的GCC,很多poll(),pipe()和文件問題

[英]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;
}


編輯6/23/09 12:20 PM

修復之后,我嘗試通過此程序運行“橫幅”,這是我得到的輸出...

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()分為兩個不同的調用。


好的,當我輪詢()時(即使在stdin上也不會返回錯誤消息),這使我無法寫入logFile。 另外,我發現while()循環運行了好幾次,然后輸出輪詢又返回並在管道上出現錯誤。 我越來越相信,poll()僅僅是一個失敗的原因。
在poll()之后,即使是成功的poll(),每次寫入logFile的嘗試都會失敗,並且errno設置為“錯誤的文件編號”。 這確實不應該發生。 老實說,我看不到它將如何影響我的文件句柄。
好吧,顯然我是個白痴。 謝謝你讓我挺直; 我假設nfds是字節大小,而不是數組大小。 這是固定的,瞧! 它不再殺死我的logFile句柄。

真正的問題:

第一個(但次要)問題

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。 我見過的其他名稱是NELEMSARRAY_SIZEDIMENSION (回溯到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 ,即使調用整體成功。)


以前的反省

關於可能是什么問題的一些先前想法; 我認為這里還有一些有用的信息。

我懷疑您的問題是poll() “損壞”了輪詢的描述符集,並且您必須在每個循環上重建它。 (已經檢查了Open Group的手冊頁,看來poll()並沒有select()遇到的問題。)這當然是相關的select()系統調用的問題。

您的子代碼應該在應有的狀態下關閉所有文件描述符-您已注釋掉一個'close()`,而另一個則完全丟失。 當孩子完成將管道連接到標准輸入和輸出時,您不希望未復制的文件描述符仍然打開; 進程無法正確檢測到EOF。

類似的評論可能適用於家長。

另外,請注意,在子級的標准輸出上沒有出現任何內容之前,發送過程可能需要向子級發送多個數據包。 在極端情況下,考慮“ sort ”; 在生成任何輸出之前先讀取其所有數據。 因此,盡管我沒有完全了解它的功能,但我擔心方向轉換代碼。 就其本身而言,方向切換是無害的-當它開始與上次相反的方向寫入時,它只是寫入新的方向。

更嚴重的是,不要使用單字符讀寫。 讀取明智的大小緩沖區已滿。 合理的大小幾乎可以是256到8192之間的任何2的冪。 您可以自由選擇其他大小(管道緩沖區的大小可能是一個不錯的選擇)。 一次處理多個字符將大大提高性能。


我解決類似問題的方法是通過兩個過程進行監視,一個過程用於標准輸入,另一個過程用於標准輸出-或等效過程。 這意味着我根本不需要使用poll() (或select() )。 處理標准輸入的過程讀取並阻止等待更多信息; 當有東西到達時,它記錄下來並將其寫入孩子的標准輸入中。 對於處理標准輸出的過程類似。

如果需要,我可以挖掘出適用於管道的代碼(請參閱我的個人資料)。 我在一兩年前看過它(嗯,實際上是在2005年進行的最后一次編輯,盡管我是在2007年重新編譯的),但它仍處於工作狀態(它寫於1989年左右)。 我也有適用於套接字而不是管道的代碼。 他們需要適應您的需求; 它們是相當專業的(特別是管道版本,它知道客戶端-服務器數據庫協議,並嘗試處理完整的信息包)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM