白话/图示 select函数的执行流程

linux0.12实现的sys_select()系统调用,用来管理指定的文件句柄(即文件I节点,亦即文件)是否准备好,如果有准备好的文件句柄,则返回准备好的文件句柄数到调用select()函数的进程,继续执行后面的被阻塞的指令,如果没有准备好,则返回-1(表示错误),或者0,表示正常情况下没有就绪的文件可以用。此时,只要对select返回的值进行判断,就知道是否有文件已经准备好了。

在linux0.12中,只实现了tty和pipe的select()阻塞功能,对tty的阻塞是在当需要读取键盘的输入数据而此时数据队列是空时,对pipe的阻塞是在当读端要读取数据但是管道是空的,或者写端要写数据,而管道是满时的情况。

每个文件句柄都对应了一个文件I节点,也就是对应了一个文件,同时,每个文件的内存I节点都有一个i_wait的指针用于记录等待这个文件的进程控制块,当有一个进程因为要等待这个I节点时,它的进程控制块就会被记录在这个I节点的i_wait指针里。由于等待进程指针只能保存一个进程控制块的地址,当有多个进程在等待同一个资源时,就必须采用一种机制来管理这些进程。在调用select()函数来阻塞进程的情况之外,对多个进程同时阻塞在一个资源上的管理,采用的是sleep_on()的方式来管理,既最后被阻塞的进程记录在资源的等待指针上,而这个最后被阻塞的进程里记录了之前被阻塞的进程的进程控制块,再上次被阻塞的,又记录了上上次阻塞的进程。。。。。。就这样循环的记录着。

而sys_select()调用采用了一个等待列表数组的方式来管理被阻塞在同一资源上进程的,其方式是跟通常的阻塞管理是一样的,但是由于它把采用数组的方式管理,同时它的赋值的语法比较奇怪,所以比较难理解。

static void add_wait(struct task_struct ** wait_address, select_table * p)
{
 int i;

 if (!wait_address)
  return;
 for (i = 0 ; i < p->nr ; i++)
  if (p->entry[i].wait_address == wait_address)
   return;
 p->entry[p->nr].wait_address = wait_address;
 p->entry[p->nr].old_task = * wait_address;
 *wait_address = current;
 p->nr++;
}

wait_address是I节点的等待进程控制块的指针的地址,而不是进程控制块的本身,要通过获得进程控制块的地址获得进程控制块,需要采用取值语句,即*wait_address来获得。那么p->entry[p->nr].wait_address = wait_address;的含义既是把即将要阻塞进程的I阶段的等待队列指针i_wait的地址&i_wait给到了管理进程阻塞的列表中,注意,此时只是给了资源等待队列的指针的地址,而这个地址里的值是可以改变的,只有重新给这个地址赋值就好。接下来的p->entry[p->nr].old_task = * wait_address;语句,才是把等待在I节点上的进程赋值给了进程管理表中的上一个进程等待指针的位置上,也就是这个位置记录了上一个被阻塞的进程的控制块;只有当执行了*wait_address = current这个语句时,才重新给资源的阻塞进程指针赋值,也就是说现在的阻塞进程的I节点的等待队列指针上的进程是当前进程,也即是p->entry[p->nr].wait_address = wait_address;此时的值是current,而old_task记录的是上次被阻塞的进程了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值