vxworks下怎样使驱动支持select机制

来源:互联网 发布:apache无法访问根目录 编辑:程序博客网 时间:2024/05/22 10:45

当有如下几种情况发生时,需要考虑使驱动支持 select机制:

·                    任务声明一个超时机制来等待设备I/O。一个UCP socket任务需要设定一个超时机制来等待一个数据包的到来。

·                    驱动支持多个设备,任务可以等待他们其中的任何一个。例如:多个管道可用被用于不同的数据优先级。

·                    任务等待不同的I/O设备。例如:服务进程可能同时使用pipes和sockets。

要想使一个设备支持select机制,首先要声明SEL_WAKEUP_LIST 结构体,这个结构体统称被定义为设备描述符的一部分, xxDevCreate( ) 函数调用selWakeupListInit( )来初始化这个结构体。当任务调用 select( ),selectLib 将会调用哪个设备函数ioctl( )中的FIOSELECT 或FIOUNSELECT 。

对于FIOSELECT, 设备执行如下操作:

1.           Add the SEL_WAKEUP_NODE (providedas the third argument of ioctl( )) to the SEL_WAKEUP_LIST bycalling selNodeAdd( ).

2.           Use the routine selWakeupType( ) to checkwhether the task is waiting for data to read from the device (SELREAD) or if thedevice is ready to be written (SELWRITE).

3.           If the device is ready (for reading orwriting as determined by selWakeupType( )), the driver calls the routine selWakeup( ) to make surethat the select( ) call in thetask does not pend. This avoids the situation where the task is blocked but thedevice is ready.

对于FIOUNSELECT ,函数执行selNodeDelete( ) ,将SEL_WAKEUP_NODE 从wakeup链表中删除。

当设备可以使用时,调用selWakeupAll 解锁所有等待该设备的任务,这个在设备的ISR中使用的比较广泛,但在其他的地缝也是可以使用的。

 

 

select()的作用应该是这样的:


1.由任务调用select():

void myTask()
{
...
select(width, readFds, writeFds, exceptFds, timeout);
...
}
select()意思是说:若readFds集合中的任一个fd可读,或writeFds中的任一个fd可写,或exceptFds中的任一个fd出异常(目前VxWorks还不支持),或者timeout指定的时间超时,则select()返回;否则任务myTask()处于阻塞状态,直到上述的条件之一得到满足。


2. select()的伪代码“可能”类似于:

select()
{
// selWakeupNode结构可能是malloc得到的,或者静态的。不清楚。
selWakeupNode->taskId = taskIdSelf();

selWakeupNode->type= SELREAD;
for (each fd in readFds)
ioctl(fd, FIOSELECT, (int)selWakeupNode);

selWakeupNode->type= SELWRITE;
for (each fd in writeFds)
ioctl(fd, FIOSELECT, (int)selWakeupNode);

semTake(tcb->semSelect,timeout); // 进入阻塞状态;睡觉去了

selWakeupNode->type= SELREAD;
for (each fd in readFds)
ioctl(fd, FIOUNSELECT, selWakeupNode);

selWakeupNode->type= SELWRITE;
for (each fd in writeFds)
ioctl(fd, FIOUNSELECT, selWakeupNode);
}
FIOSELECT命令是告诉驱动程序:“我对您设备的可读/可写感兴趣,请在满足条件时务必通知我。别忘了,我叫selWakeupNode->taskId,我对selWakeupNode->type感兴趣。”
睡眠醒来后,任务又说:“ok,都别管我了。”

3. (2)中的每一个ioctl调用都会转换为和fd相关联的驱动程序的xxIoctl()的调用:

xxIoctl(pXxDev,<I>function</I>, arg)
{
switch (<I>function</I>) {
...
case FIOSELECT:
if ((retval = selNodeAdd(&pXxDev->selWakeupList, // 可能有多个任务要处理,用链表记住它们
(SEL_WAKEUP_NODE *)arg)) == OK) {
if ((selWakeupType(arg) == SELREAD && ‘当前可读’) ||
(selWakeupType(arg) == SELWRITE && ‘当前可写’))
selWakeup(arg); // 不用等待,醒来吧
}
return retval;

caseFIOUNSELECT: // 该任务对我不感兴趣了...
return selNodeDelete(&pXxDev->selWakeupList, 
(SEL_WAKUP_NODE *)arg);
...
}
}
字符设备(character device)驱动程序如果要支持select,必须处理FIOSELECT和FIOUNSELECT命令。一般在设备控制结构中定义select-wakeup-list,并在创建设备时初始化。


4. xx设备在发生中断时,ISR被调用:

xxInt(pXxDev)
{
// 读状态寄存器

if (''接收到数据'') // 可读
selWakeupAll(&pXxDev->selWakeupList, SELREAD);

if (''数据已发送'') // 可写
selWakeupAll(&pXxDev->selWakeupList, SELWRITE);

...
}
中断函数中根据情况唤醒所有等待可读/可写的任务。selWakeup()和selWakeupAll()都是I/O系统中的函数,看不到源码。猜想它们的伪码如下:

selWakeup(node)
{
semGive(node->taskId->semSelect); // 唤醒等待的任务
}

selWakeupAll(list,type)
{
for (each node in list)
if (node->type == type)
semGive(node->taskId->semSelect); // 唤醒等待的任务
}


原创粉丝点击