Linux文件系统(七)---系统调用之open操作(二) 之 get_unused_fd

Open系统调用


/* 2 */:这一步是需要找到一个没有使用的文件描述符fd

看一下sys_open函数中调用的这个函数get_unused_fd:


  1. 738 /* 
  2. 739  * Find an empty file descriptor entry, and mark it busy. 
  3. 740  */  
  4. 741 int get_unused_fd(void)  
  5. 742 {  
  6. 743         struct files_struct * files = current->files;  /* (1)获得当前进程的文件打开表,这个前面已经说过了 */  
  7. 744         int fd, error;  
  8. 745   
  9. 746         error = -EMFILE;  
  10. 747         write_lock(&files->file_lock);  
  11. 748   
  12. 749 repeat:  
  13. 750         fd = find_next_zero_bit(files->open_fds,      /* (2)在这个进程文件打开表中寻找还没有使用的fd */  
  14. 751                                 files->max_fdset,   
  15. 752                                 files->next_fd);  
  16. 753   
  17. 754         /* 
  18. 755          * N.B. For clone tasks sharing a files structure, this test 
  19. 756          * will limit the total number of files that can be opened. 
  20. 757          */  
  21. 758         if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)  /* 不能超过限制(考虑到fork进程情况,共享fd情况) */  
  22. 759                 goto out;  
  23. 760   
  24. 761         /* Do we need to expand the fdset array? */  
  25. 762         if (fd >= files->max_fdset) {    /* 如果当前进程的fd集合中最大fd比当前申请的fd小,那么需要扩大fdset,需要扩容:具体见http://blog.csdn.net/shanshanpt/article/details/38943731中files_struct结构体描述 */  
  26. 763                 error = expand_fdset(files, fd);  /* 扩容函数1 */  
  27. 764                 if (!error) {  
  28. 765                         error = -EMFILE;  
  29. 766                         goto repeat;  
  30. 767                 }  
  31. 768                 goto out;  
  32. 769         }  
  33. 770           
  34. 771         /*  
  35. 772          * Check whether we need to expand the fd array. 
  36. 773          */  
  37. 774         if (fd >= files->max_fds) {    /* 类似于上面,这里是需要扩大fd-array数组,具体见上面链接 */  
  38. 775                 error = expand_fd_array(files, fd);  /* 扩容函数2 */  
  39. 776                 if (!error) {  
  40. 777                         error = -EMFILE;  
  41. 778                         goto repeat;  
  42. 779                 }  
  43. 780                 goto out;  
  44. 781         }  
  45. 782   
  46. 783         FD_SET(fd, files->open_fds);     /* 将fd加入到打开文件描述符中 */  
  47. 784         FD_CLR(fd, files->close_on_exec);/* 从close-on-exec中清除 */  
  48. 785         files->next_fd = fd + 1;         /* 当前描述符是最大的fd,所有next就是fd+1 */  
  49. 786 #if 1  
  50. 787         /* Sanity check */  
  51. 788         if (files->fd[fd] != NULL) {  
  52. 789                 printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);  
  53. 790                 files->fd[fd] = NULL;  
  54. 791         }  
  55. 792 #endif  
  56. 793         error = fd;  
  57. 794   
  58. 795 out:  
  59. 796         write_unlock(&files->file_lock);  
  60. 797         return error;  
  61. 798 }  
  62. 799   


ATTENTION:注意上面的max_fdset和max_fds的区别,前者是当前可以容纳的最大的文件描述符的数量大小!后者是当前可以容纳的文件对象的数量大小!对于一个文件对象而言,可以存在多个文件描述符指向这一个文件对象!所以对于申请到的fd,要分别和这两个进行判断!


主要看上面的三个函数,第一个是寻找fd函数find_next_zero_bit:
这个函数的意义就是找到open_fds打开的文件描述符中第一个bit位=0的那一位,作为新的fd返回。(前面也说过,fd的管理是使用位管理的),反正是一堆位运算,慢慢看吧:

  1. 254 static inline unsigned long find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)  
  2. 255 {  
  3. 256         unsigned long *p = ((unsigned long *) addr) + (offset >> 5);  
  4. 257         unsigned long result = offset & ~31UL;  
  5. 258         unsigned long tmp;  
  6. 259   
  7. 260         if (offset >= size)  
  8. 261                 return size;  
  9. 262         size -= result;  
  10. 263         offset &= 31UL;  
  11. 264         if (offset) {  
  12. 265                 tmp = *(p++);  
  13. 266                 tmp |= ~0UL >> (32-offset);  
  14. 267                 if (size < 32)  
  15. 268                         goto found_first;  
  16. 269                 if (~tmp)  
  17. 270                         goto found_middle;  
  18. 271                 size -= 32;  
  19. 272                 result += 32;  
  20. 273         }  
  21. 274         while (size & ~31UL) {  
  22. 275                 if (~(tmp = *(p++)))  
  23. 276                         goto found_middle;  
  24. 277                 result += 32;  
  25. 278                 size -= 32;  
  26. 279         }  
  27. 280         if (!size)  
  28. 281                 return result;  
  29. 282         tmp = *p;  
  30. 283   
  31. 284 found_first:  
  32. 285         tmp |= ~0UL << size;  
  33. 286         if (tmp == ~0UL)        /* Are any bits zero? */  
  34. 287                 return result + size; /* Nope. */  
  35. 288 found_middle:  
  36. 289         return result + ffz(tmp);  
  37. 290 }  

扩大fdset集合函数:expand_fdset

  1. 162 /* 
  2. 163  * Expand the fdset in the files_struct.  Called with the files spinlock 
  3. 164  * held for write. 
  4. 165  */  
  5. 166 int expand_fdset(struct files_struct *files, int nr)  
  6. 167 {  
  7. 168         fd_set *new_openset = 0, *new_execset = 0;  
  8. 169         int error, nfds = 0;  
  9. 170   
  10. 171         error = -EMFILE;  
  11. 172         if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN)   /* 不能大于系统最大限制 */  
  12. 173                 goto out;  
  13. 174   
  14. 175         nfds = files->max_fdset;   /* 当前最大文件描述符 */  
  15. 176         write_unlock(&files->file_lock);  
  16. 177   
  17. 178         /* Expand to the max in easy steps,下面是一个 简单的扩展过程 */  
  18. 179         do {  
  19. 180                 if (nfds < (PAGE_SIZE * 8))  
  20. 181                         nfds = PAGE_SIZE * 8;  
  21. 182                 else {  
  22. 183                         nfds = nfds * 2;  
  23. 184                         if (nfds > NR_OPEN)  
  24. 185                                 nfds = NR_OPEN;  
  25. 186                 }  
  26. 187         } while (nfds <= nr);  
  27. 188   
  28. 189         error = -ENOMEM;  
  29. 190         new_openset = alloc_fdset(nfds);  /* 根据新的大小分配新的打开set集合 */  
  30. 191         new_execset = alloc_fdset(nfds);  /* 根据新的大小分配新的执行时候需要close的set集合 */  
  31. 192         write_lock(&files->file_lock);  
  32. 193         if (!new_openset || !new_execset)  
  33. 194                 goto out;  
  34. 195   
  35. 196         error = 0;  
  36. 197           
  37. 198         /* Copy the existing tables and install the new pointers:将老的数据拷贝到新的内存中来 */  
  38. 199         if (nfds > files->max_fdset) {  
  39. 200                 int i = files->max_fdset / (sizeof(unsigned long) * 8);  
  40. 201                 int count = (nfds - files->max_fdset) / 8;  
  41. 202                   
  42. 203                 /*  
  43. 204                  * Don't copy the entire array if the current fdset is 
  44. 205                  * not yet initialised.   
  45. 206                  */  
  46. 207                 if (i) {  
  47. 208                         memcpy (new_openset, files->open_fds, files->max_fdset/8);  
  48. 209                         memcpy (new_execset, files->close_on_exec, files->max_fdset/8);  
  49. 210                         memset (&new_openset->fds_bits[i], 0, count);  
  50. 211                         memset (&new_execset->fds_bits[i], 0, count);  
  51. 212                 }  
  52. 213                 /* 下面几步骤很重要,将新分配的挂载到files结构体中去 */  
  53. 214                 nfds = xchg(&files->max_fdset, nfds);  
  54. 215                 new_openset = xchg(&files->open_fds, new_openset);  
  55. 216                 new_execset = xchg(&files->close_on_exec, new_execset);  
  56. 217                 write_unlock(&files->file_lock);  
  57. 218                 free_fdset (new_openset, nfds);  
  58. 219                 free_fdset (new_execset, nfds);  
  59. 220                 write_lock(&files->file_lock);  
  60. 221                 return 0;  
  61. 222         }   
  62. 223         /* Somebody expanded the array while we slept ... */  
  63. 224   
  64. 225 out:  
  65. 226         write_unlock(&files->file_lock);  
  66. 227         if (new_openset)  
  67. 228                 free_fdset(new_openset, nfds);  
  68. 229         if (new_execset)  
  69. 230                 free_fdset(new_execset, nfds);  
  70. 231         write_lock(&files->file_lock);  
  71. 232         return error;  
  72. 233 }  

再看一下具体的alloc_fdset函数:

  1. 128 /* 
  2. 129  * Allocate an fdset array, using kmalloc or vmalloc. 
  3. 130  * Note: the array isn't cleared at allocation time. 
  4. 131  */  
  5. 132 fd_set * alloc_fdset(int num)  
  6. 133 {  
  7. 134         fd_set *new_fdset;  
  8. 135         int size = num / 8;  
  9. 136   
  10. 137         if (size <= PAGE_SIZE)  
  11. 138                 new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);  
  12. 139         else  
  13. 140                 new_fdset = (fd_set *) vmalloc(size);  
  14. 141         return new_fdset;  
  15. 142 }  

回到上面,看一下扩大fd数组的函数expand_fd_array:

  1.  52 /* 
  2.  53  * Expand the fd array in the files_struct.  Called with the files 
  3.  54  * spinlock held for write. 
  4.  55  */  
  5.  56   
  6.  57 int expand_fd_array(struct files_struct *files, int nr)  
  7.  58 {  
  8.  59         struct file **new_fds;  
  9.  60         int error, nfds;  
  10.  61   
  11.  62           
  12.  63         error = -EMFILE;  
  13.  64         if (files->max_fds >= NR_OPEN || nr >= NR_OPEN)   /* 不能超过最大系统限制 */  
  14.  65                 goto out;  
  15.  66   
  16.  67         nfds = files->max_fds;     /* 当前进程中最大的fd */  
  17.  68         write_unlock(&files->file_lock);  
  18.  69   
  19.  70         /*  
  20.  71          * Expand to the max in easy steps, and keep expanding it until 
  21.  72          * we have enough for the requested fd array size.  
  22.  73          */  
  23.  74         /* 简单的扩展策略 */  
  24.  75         do {  
  25.  76 #if NR_OPEN_DEFAULT < 256  
  26.  77                 if (nfds < 256)  
  27.  78                         nfds = 256;  
  28.  79                 else   
  29.  80 #endif  
  30.  81                 if (nfds < (PAGE_SIZE / sizeof(struct file *)))  
  31.  82                         nfds = PAGE_SIZE / sizeof(struct file *);  
  32.  83                 else {  
  33.  84                         nfds = nfds * 2;  
  34.  85                         if (nfds > NR_OPEN)  
  35.  86                                 nfds = NR_OPEN;  
  36.  87                 }  
  37.  88         } while (nfds <= nr);  
  38.  89   
  39.  90         error = -ENOMEM;  
  40.  91         new_fds = alloc_fd_array(nfds);    /* 分配新的fd_array数组 */  
  41.  92         write_lock(&files->file_lock);  
  42.  93         if (!new_fds)  
  43.  94                 goto out;  
  44.  95   
  45.  96         /* Copy the existing array and install the new pointer */  
  46.  97   
  47.  98         if (nfds > files->max_fds) {  
  48.  99                 struct file **old_fds;  
  49. 100                 int i;  
  50. 101                 /* 将当前进行的文件数组指针指向新申请的fd数组! */  
  51. 102                 old_fds = xchg(&files->fd, new_fds);  
  52. 103                 i = xchg(&files->max_fds, nfds);  
  53. 104   
  54. 105                 /* Don't copy/clear the array if we are creating a new 
  55. 106                    fd array for fork() */  
  56. 107                 if (i) {   /* 下面将老的数据拷贝过去 */  
  57. 108                         memcpy(new_fds, old_fds, i * sizeof(struct file *));  
  58. 109                         /* clear the remainder of the array */  
  59. 110                         memset(&new_fds[i], 0,  
  60. 111                                (nfds-i) * sizeof(struct file *));   
  61. 112   
  62. 113                         write_unlock(&files->file_lock);  
  63. 114                         free_fd_array(old_fds, i);  
  64. 115                         write_lock(&files->file_lock);  
  65. 116                 }  
  66. 117         } else {  
  67. 118                 /* Somebody expanded the array while we slept ... */  
  68. 119                 write_unlock(&files->file_lock);  
  69. 120                 free_fd_array(new_fds, nfds);  
  70. 121                 write_lock(&files->file_lock);  
  71. 122         }  
  72. 123         error = 0;  
  73. 124 out:  
  74. 125         return error;  
  75. 126 }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值