Linux2.4.16_select说明

1. Linux2.4.16_sys_select源码简单介绍:

select 按照位图的方式来标记文件描述符,计算和转换起来不是太容易;

但是在转换的细节之处,还是值得看看的.

asmlinkage long

sys32_select (int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp32)
{
fd_set_bits fds;
char *bits;
long timeout;
int ret, size;


timeout = MAX_SCHEDULE_TIMEOUT;
if (tvp32) {
time_t sec, usec;
ret = -EFAULT;
if (get_user(sec, &tvp32->tv_sec) || get_user(usec, &tvp32->tv_usec))
goto out_nofds;


ret = -EINVAL;
if (sec < 0 || usec < 0)
goto out_nofds;


/// linux MAX_SELECT_SECONDS :linux中能够支持的最大超时时间是有限的,如果超过最大超时时间,会永久阻塞(直到有事件触发)

linux kernels version 2.6.24.4起,最大支持的超时时间是(MAX-999ull)/HZ;其中 HZ = 10000;MAX = (1<<31) - 1

正因为这个原因,在timeout初始化时 即赋值MAX_SELECT_SECONDS

if ((unsigned long) sec < MAX_SELECT_SECONDS) {
timeout = ROUND_UP_TIME(usec, 1000000/HZ);
timeout += sec * (unsigned long) HZ;
}
}


ret = -EINVAL;
if (n < 0)

goto out_nofds;


/// 限定支持的socket fd 的最大数

if (n > current->files->max_fdset)
n = current->files->max_fdset;


/*
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
* since we used fdset we need to allocate memory in units of
* long-words.
*/
ret = -ENOMEM;
size = FDS_BYTES(n);
bits = kmalloc(6 * size, GFP_KERNEL);
if (!bits)

goto out_nofds;


fds.in      = (unsigned long *)  bits;
fds.out     = (unsigned long *) (bits +   size);
fds.ex      = (unsigned long *) (bits + 2*size);
fds.res_in  = (unsigned long *) (bits + 3*size);
fds.res_out = (unsigned long *) (bits + 4*size);
fds.res_ex  = (unsigned long *) (bits + 5*size);

将 inp 、outp、exp拷贝到 bits的内存块中
if ((ret = get_fd_set(n, inp, fds.in)) ||
   (ret = get_fd_set(n, outp, fds.out)) ||
   (ret = get_fd_set(n, exp, fds.ex)))
goto out;
zero_fd_set(n, fds.res_in);
zero_fd_set(n, fds.res_out);
zero_fd_set(n, fds.res_ex);


ret = do_select(n, &fds, &timeout);


/// 返回剩余时间给调用者

if (tvp32 && !(current->personality & STICKY_TIMEOUTS)) {
time_t sec = 0, usec = 0;
if (timeout) {
sec = timeout / HZ;
usec = timeout % HZ;
usec *= (1000000/HZ);
}
if (put_user(sec, &tvp32->tv_sec) || put_user(usec, &tvp32->tv_usec)) {
ret = -EFAULT;
goto out;
}
}


if (ret < 0)

goto out;

/// signal_pending 检测任务是否发生中断(前提是任务是要可中断的,当前任务可中断设置见do_select)

/// 检测的前提条件是do_select观察的socket fd没有期望的事件发生,即ret = 0

if (!ret) {  
ret = -ERESTARTNOHAND;
if (signal_pending(current))
goto out; /// 如果 发生过中断,则返回 ERESTARTNOHAND错误;告诉调用者sys_select因为中断而返回
ret = 0;
}

set_fd_set(n, inp, fds.res_in);
set_fd_set(n, outp, fds.res_out);
set_fd_set(n, exp, fds.res_ex);

out:
kfree(bits);
out_nofds:
return ret;

}

/// do_select中覆盖了select的具体调用过程

int do_select(int n, fd_set_bits *fds, long *timeout)
{
poll_table table, *wait;
int retval, i, off;
long __timeout = *timeout;


  read_lock(&current->files->file_lock);
retval = max_select_fd(n, fds);
read_unlock(&current->files->file_lock);


if (retval < 0)
return retval;
n = retval;


poll_initwait(&table);
wait = &table;
if (!__timeout)
wait = NULL;
retval = 0;
for (;;)
{
/// 设置可中断
set_current_state(TASK_INTERRUPTIBLE);
/// 一个个的遍历 socket fd ,在这里知道 为什么 select的效率 不会太高了吧
for (i = 0 ; i < n; i++)
{
unsigned long bit = BIT(i);
unsigned long mask;
struct file *file;

off = i / __NFDBITS;
if (!(bit & BITS(fds, off)))
continue;
file = fget(i);
mask = POLLNVAL;
if (file)
{
mask = DEFAULT_POLLMASK;
if (file->f_op && file->f_op->poll)
mask = file->f_op->poll(file, wait);
fput(file);
}

/// 检查当前遍历的socket fd 的文件系统 是否满足期望
if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) 
{
SET(bit, __RES_IN(fds,off));
retval++;
wait = NULL;
}
if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off)))
{
SET(bit, __RES_OUT(fds,off));
retval++;
wait = NULL;
}
if ((mask & POLLEX_SET) && ISSET(bit, __EX(fds,off))) 
{
SET(bit, __RES_EX(fds,off));
retval++;
wait = NULL;
}
}

wait = NULL;

/// 很明显,前两个条件是要么socket fd有期望的事件发生,要么超时就返回;

/// 第三个条件 是(既没有事件,又没有超时)当前任务被信号中断(信号没有处理),do_select调用就终止;
if (retval || !__timeout || signal_pending(current))
break;
if(table.error)
{
retval = table.error;
break;
}

    /// 将当前任务挂起
__timeout = schedule_timeout(__timeout);/// 超时时间是在不断的更新的,返回值是最新的超时时间
}

current->state = TASK_RUNNING;

poll_freewait(&table);
/*
* Up-to-date the caller timeout.
*/
*timeout = __timeout;
return retval;
}



2.  关于sys_select 发生信号中断时的返回值问题:

Linux 2.6.25中有过这么一段注释:

/* If an application puts its timeval in read-only memory, we don't want the Linux-specific update to 

the timeval to cause a fault after the select has completed successfully. However, because we're not 

updating the timeval, we can't restart the system call. */

因为内核存在可能无法更新阻塞时间的问题,所以不能当发生信号中断时,无法重新启动该系统调用;

因此用户需要自己去重新启动该系统调用。

对于慢系统调用(可能永久阻塞的系统调用),存在被信号中断的现象,很多系统调用在发生中断时,是

可以重新调用的,但也有例外,比如connect,至于connect为什么不能重新调用,会专门讲到!这里仅

引出该问题

到。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值