문제

나는 '중개자' 로거를 구축하는 데 많은 어려움을 겪고 있습니다. 의도는 /usr/bin의 항목 위 경로에 이를 배치하고 애플리케이션으로 오고가는 모든 것을 캡처하는 것입니다.(블랙박스 타사 앱이 어떤 이유로 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;
}


2009년 6월 23일 오후 12시 20분 수정

수정 후 이 프로그램을 통해 '배너'를 실행하려고 시도했는데, 내가 얻은 결과는 다음과 같습니다.

Child setup complete, executing /usr/bin/banner
POLL on output has thrown an exception!
ERRNO value: 0

로그 파일에는 다음이 포함됩니다.

Commandline: banner testing 
TIMESTAMP: Tue Jun 23 11:21:00 2009

ERRNO에 0이 있는 이유는 poll()이 정상적으로 반환되기 때문입니다.오류와 함께 돌아온 것은 pollArray[1].revents입니다. 이는 childOutPipe[0]가 오류가 있는 것으로 폴링되었음을 의미합니다.내가 아는 한 logChar()는 호출되지 않습니다.

나는 poll()을 두 개의 서로 다른 호출로 분할하려고 합니다.


좋습니다. 오류 메시지와 함께 반환되지 않는 stdin에서도 poll()을 수행하는 순간 logFile에 쓸 수 있는 기능이 종료됩니다.또한 출력 폴링이 파이프에 오류와 함께 다시 돌아오기 전에 while() 루프가 여러 번 실행된다는 것을 발견했습니다.나는 poll()이 단순히 잃어버린 원인이라는 것을 점점 더 확신하게 되었습니다.
errno가 "잘못된 파일 번호"로 설정된 경우 poll() 이후에 logFile에 쓰려는 모든 시도는 실패합니다. 심지어 성공적인 poll()도 마찬가지입니다.이런 일은 정말 일어나서는 안 됩니다.솔직히 그것이 내 파일 처리에 어떤 영향을 미칠지 알 수 없습니다.
좋아요, 그럼 제가 바보인 것 같군요.나를 바로잡아주셔서 감사합니다.나는 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 },
    };

표준 입력의 0을 다음으로 바꿀 수 있습니다. FILENO_STDIN ~에서 <fcntl.h>.

두 번째(주요) 문제

    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.특히, 그것은 아마도 당신이 말한 것을 쓰고 있을 것입니다. revents 행의 필드 struct pollfd 배열이지만 실제 공간은 로그입니다. FILE *, 그래서 완전히 망가졌습니다.다른 지역 변수에도 마찬가지입니다.즉, 스택 버퍼 오버플로(Stack Overflow라고도 함)가 있는 것입니다. 이 이름은 어렴풋이 익숙할 것입니다.하지만 당신이 프로그래밍했기 때문에 이런 일이 일어나고 있습니다.

고치다:

    nfds_t nfds = DIM(pollArray);

3차(중급) 문제

   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 0이 아닙니다.(예를 들어 일부 시스템에서는 다음과 같은 경우 stderr 터미널로 가지 않을 것입니다. errno I/O 호출 후 ENOTTY, 비록 호출이 전체적으로 성공했지만.)


이전 반추

문제가 무엇인지에 대한 사전 생각;내 생각엔 여기에 아직 유용한 정보가 있을 것 같아.

나는 당신의 문제가 다음과 같다고 생각합니다 poll() 폴링된 설명자 세트를 '손상'시키므로 각 루프에서 이를 다시 작성해야 합니다. (매뉴얼 페이지를 확인한 후 그룹 열기, 그것은 보인다 poll() 그런 문제는 없어 select() 겪고 있습니다.) 이것은 확실히 관련 문제입니다. select() 시스템 호출.

귀하의 하위 코드가 모든 파일 설명자를 닫아야 할 때 닫지 않습니다. 하나의 'close()'를 주석 처리했고 또 다른 하나가 완전히 누락되었습니다.하위 항목이 파이프를 표준 입력 및 출력에 연결하는 작업을 마쳤을 때, 복사되지 않은 파일 설명자가 계속 열려 있는 것을 원하지 않습니다.프로세스가 EOF를 제대로 감지할 수 없습니다.

유사한 의견이 상위 항목에도 적용될 수 있습니다.

또한 하위 항목의 표준 출력에 어떤 항목이 나타나기 전에 전송 프로세스에서 하위 항목에 여러 데이터 패킷을 보내야 할 수도 있습니다.극단적인 경우로 'sort';출력을 생성하기 전에 모든 데이터를 읽습니다. 방향 전환 코드가 걱정되기 때문에 그것이 수행하는 작업을 완전히 이해하지는 못했습니다. 방향 전환 자체는 무해합니다. 지난 번과 반대 방향으로 쓰기 시작하면 단순히 새로운 방향을 씁니다.

더 심각한 것은 단일 문자 읽기 및 쓰기를 사용하지 마십시오.적절한 크기의 버퍼가 가득 찼습니다.합리적인 크기는 256에서 8192 사이의 거의 모든 2의 거듭제곱일 수 있습니다.자유롭게 다른 크기를 선택할 수 있습니다(파이프 버퍼의 크기가 선택하기에 좋은 크기일 수 있습니다).한 번에 여러 문자를 처리하면 성능이 크게 향상됩니다.


비슷한 문제를 해결한 방법은 두 개의 프로세스를 모니터링하는 것입니다. 하나는 표준 입력용이고 다른 하나는 표준 출력용입니다.즉, 사용할 필요가 없습니다. poll() (또는 select()) 전혀.표준 입력을 처리하는 프로세스는 더 많은 정보를 기다리고 차단합니다.무언가가 도착하면 이를 기록하고 자식의 표준 입력에 씁니다.표준 출력을 처리하는 프로세스의 경우도 마찬가지입니다.

필요한 경우 파이프와 함께 작동하는 코드를 찾아낼 수 있습니다(내 프로필 참조).1~2년 전에 봤는데(흠;실제로 마지막 편집은 2005년에 이루어졌지만 2007년에 다시 컴파일했지만 여전히 작동 가능했습니다(1989년경에 작성되었습니다).파이프 대신 소켓에서 작동하는 코드도 있습니다.귀하의 요구 사항에 맞게 조정이 필요합니다.그들은 다소 전문화되었습니다(특히 파이프 버전은 클라이언트-서버 데이터베이스 프로토콜을 인식하고 완전한 정보 패킷을 처리하려고 시도합니다).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top