最近在重翻些FS的代码,file open的时候首先要获取unused fd, 例如如下:
/*
* Find an empty file descriptor entry, and mark it busy.
*/
int get_unused_fd(void)
{
struct files_struct * files = current->files;
int fd, error;
error = -EMFILE;
write_lock(&files->file_lock);
repeat:
fd = find_next_zero_bit(files->open_fds,
files->max_fdset,
files->next_fd);
........................
这里重点研究下find_next_zero_bit,这里是从open_fds中找到空隙的fd,翻开这块内部代码:
先温习下这里的几个重点asm命令(intel):
bsfl:
bsfl source, destination
从右向左(0->15,31位)在source中找到非0的位置,并保存在destination
scasl:
扫描EDI(隐含源)和EAX的匹配度,和repe,repne结合
比如 repe; scasl;
含义:如果EDI和EAX等值,继续loop, until到不匹配
static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
{
unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
int set = 0, bit = offset & 31, res;
if (bit) {
/*
* Look for zero in first byte
*/
__asm__(
//从offset开始的INT查找bit 1,
"bsfl %1,%0/n/t"
"jne 1f/n/t"
"movl $32, %0/n"
"1:"
: "=r" (set)
// 注意取反,目标是look zero,
: "r" (~(*p >> bit)));
if (set < (32 - bit))
return set + offset;
set = 32 - bit;
p++;
}
/*
* No zero yet, search remaining full bytes for a zero
*/
res = find_first_zero_bit (p, size - 32 * (p - (unsigned long *) addr));
return (offset + set + res);
}
/*
* Find-bit routines..
*/
static __inline__ int find_first_zero_bit(void * addr, unsigned size)
{
int d0, d1, d2;
int res;
if (!size)
return 0;
/* This looks at memory. Mark it volatile to tell gcc not to move it around */
__asm__ __volatile__(
// EAX赋值0xFFFFFFFF
"movl $-1,%%eax/n/t"
// EDX复制全0,xorl这种比movl要快
"xorl %%edx,%%edx/n/t"
// EDI也就是addr开始的源
"repe; scasl/n/t"
//如果没找到
"je 1f/n/t"
//找到,继续,首先把EAX的前部分设置0
"xorl -4(%%edi),%%eax/n/t"
"subl $4,%%edi/n/t"
//从EAX查找bit 1的位置,由于刚才xorl,这里的bit 1也就是我们希望的bit 0
"bsfl %%eax,%%edx/n"
// EDI-ADDR,得到偏移(单位是:字节)
"1:/tsubl %%ebx,%%edi/n/t"
//从字节换算成bit位
"shll $3,%%edi/n/t"
"addl %%edi,%%edx"
:"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2)
:"1" ((size + 31) >> 5), "2" (addr), "b" (addr));
return res;
}