首先得知道是哪个函数进行fd分配,对此我以pipe为例,它是分配fd的一个典型的syscall,在fs/pipe.c中定义了pipe和pipe2的syscall实现,如下
1 SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags) 2 { 3 int fd[2]; 4 int error; 5 6 error = do_pipe_flags(fd, flags); 7 if (!error) { 8 if (copy_to_user(fildes, fd, sizeof(fd))) { 9 sys_close(fd[0]); 10 sys_close(fd[1]); 11 error = -EFAULT; 12 } 13 } 14 return error; 15 } 16 17 SYSCALL_DEFINE1(pipe, int __user *, fildes) 18 { 19 return sys_pipe2(fildes, 0); 20 } |
进一步分析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,实现也很简单,如下
1 int alloc_fd(unsigned start, unsigned flags) 2 { 3 struct files_struct *files = current->files; 4 unsigned int fd; 5 int error; 6 struct fdtable *fdt; 7 8 spin_lock(&files->file_lock); 9 repeat: 10 fdt = files_fdtable(files); 11 fd = start; 12 if (fd < files->next_fd) 13 fd = files->next_fd; 14 15 if (fd < fdt->max_fds) 16 fd = find_next_zero_bit(fdt->open_fds->fds_bits, 17 fdt->max_fds, fd); 18 19 error = expand_files(files, fd); 20 if (error < 0) 21 goto out; 22 23 /* 24 * If we needed to expand the fs array we 25 * might have blocked - try again. 26 */ 27 if (error) 28 goto repeat; 29 30 if (start <= files->next_fd) 31 files->next_fd = fd + 1; 32 33 FD_SET(fd, fdt->open_fds); 34 if (flags & O_CLOEXEC) 35 FD_SET(fd, fdt->close_on_exec); 36 else 37 FD_CLR(fd, fdt->close_on_exec); 38 error = fd; 39 #if 1 40 /* Sanity check */ 41 if (rcu_dereference(fdt->fd[fd]) != NULL) { 42 printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd); 43 rcu_assign_pointer(fdt->fd[fd], NULL); 44 } 45 #endif 46 47 out: 48 spin_unlock(&files->file_lock); 49 return error; 50 } |
在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吧!!