中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Linux系統下fd分配的方法是什么

發布時間:2022-01-07 16:22:46 來源:億速云 閱讀:228 作者:柒染 欄目:系統運維

這篇文章將為大家詳細講解有關Linux系統下fd分配的方法是什么,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

最近幾天在公司里寫網絡通訊的代碼比較多,自然就會涉及到IO事件監測方法的問題。我驚奇的發現select輪訓的方法在那里居然還大行其道。我告訴他們現在無論在Linux系統下,還是windows系統下,select都應該被廢棄不用了,其原因是在兩個平臺上select的系統調用都有一個可以說是致命的坑。

在windows上面單個fd_set中容納的socket handle個數不能超過FD_SETSIZE(在win32 winsock2.h里其定義為64,以VS2010版本為準),并且fd_set結構使用一個數組來容納這些socket handle的,每次FD_SET宏都是向這個數組中放入一個socket handle,并且此過程中是限定了不能超過FD_SETSIZE,具體請自己查看winsock2.h中FD_SET宏的定義。

此處的問題是

若本身fd_set中的socket handle已經達到FD_SETSIZE個,那么后續的FD_SET操作實際上是沒有效果的,對應socket handle的IO事件將被遺漏!!!

而在Linux系統下面,該問題其實也是處在fd_set的結構和FD_SET宏上。此時fd_set結構是使用bit位序列來記錄每一個待檢測IO事件的fd。記錄的方式稍微復雜,如下

/usr/include/sys/select.h中

typedef long int __fd_mask;  #define __NFDBITS    (8 * sizeof (__fd_mask))  #define    __FDELT(d)    ((d) / __NFDBITS)   #define    __FDMASK(d)    ((__fd_mask) 1 << ((d) % __NFDBITS))   typedef struct    {      /* XPG4.2 requires this member name.  Otherwise avoid the name         from the global namespace.  */  #ifdef __USE_XOPEN      __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];  # define __FDS_BITS(set) ((set)->fds_bits)  #else      __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];  # define __FDS_BITS(set) ((set)->__fds_bits)  #endif    } fd_set;   #define    FD_SET(fd, fdsetp)    __FD_SET (fd, fdsetp)

/usr/include/bits/select.h中

1 # define __FD_SET(d, set)    (__FDS_BITS (set)[__FDELT (d)] |= __FDMASK (d))

可以看出,在上面的過程,實際上每個bit在fd_set的bit序列中的位置對應于fd的值。而fd_set結構中bit位個數是__FD_SETSIZE定義的,__FD_SETSIZE在/usr/include/bits/typesize.h(包含關系如下sys/socket.h -> bits/types.h -> bits/typesizes.h)中被定義為1024。

現在的問題是,當fd>=1024時,FD_SET宏實際上會引起內存寫越界。而實際上在man select中對已也有明確的說明,如下

NOTES

An fd_set is a fixed size buffer. Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to or
larger than FD_SETSIZE will result in undefined behavior. Moreover, POSIX requires fd to be a valid file descriptor.

這一點包括之前的我,是很多人沒有注意到的,并且云風大神有篇博文《一起 select 引起的崩潰》也描述了這個問題。

可以看出在Linux系統select也是不安全的,若想使用,得小心翼翼的確認fd是否達到1024,但這很難做到,不然還是老老實實的用poll或epoll吧。

扯得有點遠了,但也引出了本片文章要敘述的主題,就是Linux系統下fd值是怎么分配確定,大家都知道fd是int類型,但其值是怎么增長的,在下面的內容中我對此進行了一點分析,以2.6.30版本的kernel為例,歡迎拍磚。

首先得知道是哪個函數進行fd分配,對此我以pipe為例,它是分配fd的一個典型的syscall,在fs/pipe.c中定義了pipe和pipe2的syscall實現,如下

SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags)  {      int fd[2];      int error;       error = do_pipe_flags(fd, flags);      if (!error) {          if (copy_to_user(fildes, fd, sizeof(fd))) {              sys_close(fd[0]);              sys_close(fd[1]);              error = -EFAULT;          }      }      return error;  }   SYSCALL_DEFINE1(pipe, int __user *, fildes)  {      return sys_pipe2(fildes, 0);  }

進一步分析do_pipe_flags()實現,發現其使用get_unused_fd_flags(flags)來分配fd的,它是一個宏

#define get_unused_fd_flags(flags) alloc_fd(0, (flags)),位于include/linux/fs.h中

好了咱們找到了主角了,就是alloc_fd(),它就是內核章實際執行fd分配的函數。其位于fs/file.c,實現也很簡單,如下

int alloc_fd(unsigned start, unsigned flags)  {      struct files_struct *files = current->files;      unsigned int fd;      int error;      struct fdtable *fdt;       spin_lock(&files->file_lock);  repeat:      fdt = files_fdtable(files);     fd = start;      if (fd < files->next_fd)          fd = files->next_fd;       if (fd < fdt->max_fds)          fd = find_next_zero_bit(fdt->open_fds->fds_bits,                         fdt->max_fds, fd);       error = expand_files(files, fd);      if (error < 0)          goto out;       /*       * If we needed to expand the fs array we       * might have blocked - try again.       */      if (error)          goto repeat;       if (start <= files->next_fd)          files->next_fd = fd + 1;       FD_SET(fd, fdt->open_fds);      if (flags & O_CLOEXEC)          FD_SET(fd, fdt->close_on_exec);      else          FD_CLR(fd, fdt->close_on_exec);      error = fd;  #if 1      /* Sanity check */      if (rcu_dereference(fdt->fd[fd]) != NULL) {          printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);          rcu_assign_pointer(fdt->fd[fd], NULL);      }  #endif   out:      spin_unlock(&files->file_lock);      return error;  }

在pipe的系統調用中start值始終為0,而中間比較關鍵的expand_files()函數是根據所給的fd值,判斷是否需要對進程的打開文件表進行擴容,其函數頭注釋如下

/*   * Expand files.   * This function will expand the file structures, if the requested size exceeds   * the current capacity and there is room for expansion.   * Return <0 error code on error; 0 when nothing done; 1 when files were   * expanded and execution may have blocked.   * The files->file_lock should be held on entry, and will be held on exit.   */

此處對其實現就不做深究了,回到alloc_fd(),現在可以看出,其分配fd的原則是

每次優先分配fd值最小的空閑fd,當分配不成功,即返回EMFILE的錯誤碼,這表示當前進程中fd太多。

到此也印證了在公司寫的服務端程序(kernel是2.6.18)中,每次打印client鏈接對應的fd值得變化規律了,假如給一個新連接分配的fd值為8,那么其關閉之后,緊接著的新的鏈接分配到的fd也是8,再新的鏈接的fd值是逐漸加1的。

為此,我繼續找了一下socket對應fd分配方法,發現最終也是 alloc_fd(0, (flags),調用序列如下
socket(sys_call) -> sock_map_fd() -> sock_alloc_fd() -> get_unused_fd_flags()
open系統調用也是用get_unused_fd_flags(),這里就不列舉了。

現在想回頭說說開篇的select的問題。由于Linux系統fd的分配規則,實際上是已經保證每次的fd值盡量的小,一般非IO頻繁的系統,的確一個進程中fd值達到1024的概率比較小。因而對此到底是否該棄用select,還不能完全地做絕對的結論。如果設計的系統的確有其他措施保證fd值小于1024,那么用select無可厚非。

但在網絡通訊程序這種場合是絕不應該作此假設的,所以還是盡量的不用select吧!!

關于Linux系統下fd分配的方法是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

普定县| 武功县| 榆树市| 榆林市| 印江| 平利县| 台南县| 易门县| 六枝特区| 浏阳市| 西昌市| 舒城县| 湘潭市| 西乌珠穆沁旗| 延庆县| 石门县| 集安市| 华容县| 昌都县| 洪泽县| 宁南县| 丰宁| 平谷区| 都昌县| 修水县| 珠海市| 安宁市| 辛集市| 奈曼旗| 读书| 边坝县| 确山县| 大埔县| 安化县| 三明市| 长子县| 雅安市| 全南县| 博野县| 简阳市| 运城市|