4

kqueueから期待される動作なのか、それとも私が間違っているのかわからないという問題が発生しています。

単一のソケットファイル記述子に対して、kqueueを使用して個別のイベントをインストールする必要があります。個別のイベントは、(sockfd、EVFILT_READ)と(sockfd、EVFILT_WRITE)のイベントです。ただし、後でkqueueからイベントをプルすると、kqueueから2つのイベントが取得されますが、両方のイベントに(event.filter&EVFILT_READ)と(event.filter&EVFILT_WRITE)が含まれています。

それらを分離する必要があります。そうしないと、どのイベントが実際に準備ができているか、読み取りまたは書き込みができるかわかりません。

これは、問題を説明するためにまとめたサンプルコードです。これは、各イベントで両方のフィルターフラグを取得する理由を理解するための単なるテストコードであることに注意してください。

int main(int argc, char ** argv) {
    //listening_sockfd = setup listening socket

    //setup kqueue and vars
    int kq = kqueue();
    int res = 0;
    int bufres = 0;
    struct timespec ts = {0};
    struct timespec * tsp = &ts;
    struct kevent ke2[2];

    //install read for listening socket
    struct kevent ke1;
    bzero(&ke1,sizeof(ke1));
    ke1.ident = listening_sockfd;
    ke1.flags = EV_ADD;
    ke1.filter = EVFILT_READ;
    res = kevent(kq,&ke1,1,NULL,0,tsp);

    while(1) {
        new_client:

        //wait for a client to connect
        res = kevent(kq,NULL,0,&ke1,1,NULL);
        if(res < 0) printf("%s\n", strerror(errno));

        if(res > 0) {
            //accept the client
            int clientfd = accept(skinfo.sockfd,NULL,NULL);

            //install read events for client
            bzero(&ke1,sizeof(ke1));
            ke1.ident = clientfd;
            ke1.flags = EV_ADD;
            ke1.filter = EVFILT_READ;
            res = kevent(kq,&ke1,1,NULL,0,tsp);
            if(res < 0) printf("%s\n",strerror(errno));

            while(1) {

                //wait for readable content from the client
                bzero(&ke1,sizeof(ke1));
                res = kevent(kq,NULL,0,&ke1,1,NULL);
                if(res < 0) printf("%s\n",strerror(errno));

                if(res > 0) {

                    //check for client disconnect
                    if(ke1.flags & EV_EOF) {
                        close(clientfd);
                        goto new_client;
                    }

                    //now install write events for client
                    bzero(&ke1,sizeof(ke1));
                    ke1.ident = clientfd;
                    ke1.flags = EV_ADD;
                    ke1.filter = EVFILT_WRITE;
                    res = kevent(kq,&ke1,1,NULL,0,tsp);
                    if(res < 0) printf("%s\n",strerror(errno));

                    //now wait for it to be writable - this will return
                    //immediately because the socket is writable.
                    res = kevent(kq,NULL,0,ke2,2,NULL);
                    if(res < 0) printf("%s\n",strerror(errno));

                    if(res >= 0) {
                        char buf[128];
                        printf("res: %i\n",res);

                        //we have two events from kqueue because I installed
                        //two (ident,filter) pairs.
                        int i = 0;
                        for(i; i<2; i++) {
                            printf("ident: %i\n",ke2[i].ident);
                            printf("filter[%i] %lu\n",i,ke2[i].filter);
                            printf("data: %lu\n",ke2[i].data);

                            //QUESTION: Why does EVFILT_READ && EVFILT_WRITE come
                            //back in the same event when I installed two seperate
                            //(ident,filter) pairs?
                            if(ke2[i].filter & EVFILT_READ) printf("EVFILT_READ\n");
                            if(ke2[i].filter & EVFILT_WRITE) printf("EVFILT_WRITE\n");

                            if(ke2[i].filter & EVFILT_READ) {
                                printf("readable!\n");
                                bufres = read(clientfd,buf,128);
                            }

                            if(ke2[i].filter & EVFILT_WRITE) {
                                printf("writable\n");

                                //shut off write events to stop infinite loop
                                //because the socket is writable
                                bzero(&ke1,sizeof(ke1));
                                ke1.ident = clientfd;
                                ke1.flags = EV_DELETE;
                                ke1.filter = EVFILT_WRITE;
                                res = kevent(kq,&ke1,1,NULL,0,tsp);

                                write(clientfd,buf,bufres);
                            }
                        }
                    }
                }
            }
        }
    }
}

私はこれを理解することができませんでした。別々のイベントをインストールしたときに、各kqueueイベントにEVFILT_READとEVFILT_WRITEの両方が含まれるのはなぜですか?

これが私に引き起こしている本当の問題は、これらのイベントは常にEVFILT_READを報告するため、読み取りが利用可能であると常に考えているコードの副作用がありますが、実際には存在しないため、read()の呼び出しは失敗し、その他の結果。このコードはこれらの結果を示していないことに注意してください。これは私が書いた問題のサンプルにすぎないので、これを理解して実際のコードを続行します。

何か案は?

PSprintfからの出力は次のとおりです。

res: 2
ident: 5
filter[0] 4294967295
data: 5
EVFILT_READ
EVFILT_WRITE
readable!
writable
ident: 5
filter[1] 4294967294
data: 146988
EVFILT_READ
EVFILT_WRITE
readable!
writable
4

2 に答える 2

8

AH!私はそれを理解したと思います。kqueueのドキュメントに記載されていないのは、event.filterにビットフラグが含まれていないということです。したがって、event.filter == EVFILT_READ、event.filter==EVFILT_WRITEを使用して読み取り/書き込みをチェックする必要があります。

于 2012-08-28T18:28:44.923 に答える
4

お気づきのとおり、これらはテスト対象のビットではありません。

OS Xのヘッダーを調べると、

#define EVFILT_READ             (-1)
#define EVFILT_WRITE            (-2)

-1はすべて1ビットであるためx & EVFILT_READ、ゼロ以外の場合はtrueになりx、0または1に等しくない場合はx & EVFILT_WRITEtrueになります。x

于 2014-03-21T11:31:11.007 に答える