1. 程式人生 > >我用select做多路復用踩到的坑

我用select做多路復用踩到的坑

多路復用 bits desc fin 數組越界 生成 cpp amp number

既然說是用select踩到的坑,那麽就先直接貼一段使用select的代碼上來瞅一下:
    bool SocketAction(int fd, const char* buf, size_t len, uint64_t milli_expire) {
        struct timeval tv;
        tv.tv_sec = milli_expire / 1000;
        tv.tv_usec = (milli_expire % 1000) * 1000;
        fd_set rd_set, wt_set;
        FD_ZERO(&rd_set);
        FD_ZERO(&wt_set);
        FD_SET(fd, &rd_set);
        FD_SET(fd, &wt_set);

        int ret = 0;
        while (true) {
            ret = select(fd+1, &rd_set, &wt_set, nullptr, &tv);
            if (ret < 0 && errno == EINTR) continue;
            break;
        }

        if(FD_ISSET(fd, &rd_set)) {
			char rd_buf[1024] = {0};
			ret = read(fd, rd_buf, sizeof(rd_buf));
			if(ret < 0)	return false;
			printf("%s\n", rd_buf);
		} else if(FD_ISSET(fd, &wt_set)) {
			ret = send(fd, buf, len, 0);
			if(ret < 0) return false;
		}

        return true;
    }

上面的代碼著實非常easy,僅僅是針對某一個socket fd進行讀寫操作,從邏輯上來說應該是沒有不論什麽問題的。然而這個在實際的使用過程中確實會出現故障。我們程序中封裝了socket的操作,如上代碼的方式使用了select。

在出現大量的socket連接時,會出現宕機現象,而且宕機生成的core文件的堆棧也莫名其妙的被破壞了。

遇到這個情況時。我直接被震驚了,由於細致檢查了代碼確實沒有發現不論什麽問題。為什麽每次都是連接數達到快1500的時候就出現宕機呢?

剛開始以為是其它的邏輯出了問題。於是細致檢查了其它的邏輯確實也沒有發現存在不論什麽問題,而且從堆棧被破壞的情況上分析,也懷疑是某個使用中出現了數組越界訪問引發的。但在整個邏輯中使用的都是stl裏的容器,沒有申請數組,出現讀寫越界著實無法理解。

此後僅僅能懷疑底層封裝的問題。在一步步嘗試凝視代碼的過程中。發現select使用的地方出現了問題。那麽問題到底在哪裏呢?

技術分享技術分享 int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);


技術分享 nfds is the highest-numbered file descriptor in any of the three sets, plus 1.

技術分享

在select的使用manual中,select的定義和nfds的說明如上所看到的。也僅僅是說明了select的nfds是最大的文件描寫敘述符+1。在實際的使用過程中也確實有這麽使用。好像也確實沒有問題。然而,不止這麽簡單。select的文件描寫敘述符的最大值實際是有一個潛在規則的。那就是select的最大文件描寫敘述符最大是1024。該值在centor os系統中。定義

在文件/usr/include/sys/select.h文件裏。例如以下:

技術分享 78 /* Maximum number of file descriptors in `fd_set‘. */
79 #define FD_SETSIZE __FD_SETSIZE

技術分享

而__FD_SETSIZE則定義在:/usr/include/bits/typesizes.h 中,例如以下:

技術分享 62 /* Number of descriptors that can fit in an `fd_set‘. */
63 #define __FD_SETSIZE 1024

技術分享

這下能夠理解了吧。在socket大於1024時。文件描寫敘述符自然就大於1024,則在使用select時訪問的大小就實際超越了系統中對於select的支持。因此出現讀寫越界,造成程序的宕機。

這次所踩的坑很隱晦,但也說明了一個問題。在使用系統接口時須要謹慎。盡管可能用法並沒有錯誤,但也要在使用的時候多註意其“潛規則”。

我用select做多路復用踩到的坑