您的位置:首页 > 其它

qnx之中断控制

2016-07-17 09:14 302 查看
qnx之中断控制

学习目的:

1.qnx微内核怎样操作硬件中断?

2.我们怎样用代码操作中断?

3.不同的中断操作策略

学习概要

1.中断的概念

2.中断服务子程序中的进程间通信

3.程序架构

1.中断的概念



可抢占中断调度:



不可抢占中断调度:



中断有下特性:

1.它的优先级比任何的线程都高

2.可被高优先级中断抢占(需要平台支持)

3.能被用户空间程序操作(驱动被动态加载,而不是存在内核空间)

4.被内核调用(有内核特权)

中断潜在时间:



影响因素:

1.中断失能花费的时间

2.相同或更高优先级中断服务程序花费时间

3.中断其他的服务程序花费时间

4.特定屏蔽中断花费时间(通常是共享中断和InterruptAttachEvent())

调度潜在时间:



影响因素:

1.花费在其他中断服务程序的时间

2.被调度的线程优先级高低

3.花费在已经处于READY状态的更高优先级线程的时间

因为中断的优先级比线程高,所以,花费在中断服务程序的时间直接影响到线程调度。

中断调度和中断操作策略

中断相关函数调用:

id = InterruptAttach (int intr,
struct sigevent *(*handler)(void *, int),
void *area, int size, unsigned flags);

id = InterruptAttachEvent (int intr, struct sigevent *event,
unsigned flags);

InterruptDetach (int id);

InterruptWait (int flags, uint64_t *reserved);

InterruptMask (int intr, int id);

InterruptUnmask (int intr, int id);

InterruptLock (struct intrspin *spinlock);

InterruptUnlock (struct intrspin *spinlock);


注:在调用以上函数之前,必须要先调用ThreadCtl(_NTO_TCTL_IO, 0),以取得I/O特权。

InterruptEnable (void);

InterruptDisable (void);

以上两个函数只用于non-SMP系统,所以并不通用,我们以该使用先两个函数:

InterruptLock();

InterruptUnlock();

一个简单的例子

struct sigevent event;
main ()
{
ThreadCtl (_NTO_TCTL_IO, 0);
SIGEV_INTR_INIT (&event);
id = InterruptAttachEvent (intnum, &event, ...);
for (;;)
{
InterruptWait (0, NULL);
// do the interrupt work here, at thread priority
InterruptUnmask (intnum, id);
}
}


注:当内核控制时, 它将屏蔽掉中断,并且使用event来处理适当的调度。在本例中,因为使用的是SIGEV_INTR,所以 InterruptWait()将不会阻塞。

怎样将中断和event相联系:



一旦一个event和中断相关联后,内核将自动对中断解除屏蔽。

传递给 InterruptAttachEvent()的参数是返回的真实event。

当中断产生以及内核调用时,内核将在线程调度之前自动屏蔽中断,以保证它在的安全性。

例子:

struct sigevent event; //创建一个event

const struct sigevent *
handler (void *not_used, int id) //返回一个实际的event
{
if (check_status_register()) // place-holder for hw code
{
return (&event);
}
else
{
return (NULL);
}

}

main ()
{
ThreadCtl (_NTO_TCTL_IO, 0);
SIGEV_INTR_INIT (&event);
id = InterruptAttach (intnum, handler, NULL, 0, ...);
for (;;)
{
InterruptWait (0, NULL);
// do some or all of the work here
}
}


注:在本例中,这是一个用户提供的中断程序。将有一个work必须在timing reasons中断优先级被完成。中断程序和线程将共享这个work。中断将完成时间关键任务(the time-critical work),然后线程将被唤醒去做非时间关键任务(the non-time-critical work)(数据分析,并传递给其他线程)。///参考linux的底半部,顶半部

在以上的中断程序中, check_status_register() 是去检查硬件的各种状态寄存器,检查硬件是否产生了中断,或者清除中断源。这些在level-sensitive architectures上都是需要的,因为中断将被共享,并且内核将在中断链的最后产生一个EOI(外部中断的中断结束命令),所以我们必须在返回之前清除中断。这也就是为什么在使用InterruptAttachEvent()时,内核要在线程调度前进行中断屏蔽。

怎样将中断服务程序handle和中断向量相联系?



注:一旦一个handler和中断相关联,内核就将对中断解除屏蔽。

area的好处是它可以被传递给你的中断服务程序的第一个参数。它让一个中断程序处理多个中断。

并且它避免了使用全局变量。

logical interrupt中断号是:

在启动时就定义了,在板子的buildfile中。bulid file在QNX-TATGET/cpu/boot/build中。

例如:
# Interrupt Assignments
# ---------------------
#
# vector: 0 (PPC800_INTR_IRQ0)
# trigger: falling edge
# device: unassigned
...
# vector: 15 (PPC800_INTR_LVL7)
# trigger: N/A
# device: Programmable Interval Timer interrupt (system timer)
...
# vector: 0x8001001e (PPC800_INTR_CPMSCC1)
# trigger: N/A
# device: CPM SCC1


interruptAttachEvent and InterruptAttach的flag参数:

1._NTO_INTR_FLAGS_END

内核包含了给每个中断的处理函数和events的队列。这个参数表示新的处理函数和event应该被添加在队列的结尾而不是开头。

2._NTO_INTR_FLAGS_PROCESS

将处理程序/event与进程向关联。通常 它们将与绑定的线程相关联。如果这个线程死了,这个处理程序也就取消绑定。但是如果有这个参数,即使线程死了,处理程序也不会被取消绑定。

你应该使用进程中的event,如pulse或signal

注:如果你使用了这个标志,你的中断handler将不会返回event.sigev_notify of _SIGEV_INTR,因为它将去绑定的可能不再存在的线程。

3._NTO_INTR_FLAGS_TRK_MSK

这个参数指定了当应用app与中断断开绑定时,内核将使用合适的数字来取消中断屏蔽,以保证中断函数正常。通常使用这个flag。

控制中断:

1.使用InterruptMask() and InterruptUnmask() 来对中断进行屏蔽和解屏蔽。

2.为了对一个中断解屏蔽,你必须使用与屏蔽相同的数字来操作。

3.使用 InterruptLock() and InterruptUnlock() 来失能和使能中断。在SMP中使用自旋锁来同步中断

注:Mask/unmask是在PIC(Programmable Interrupt Controller)中进行

Disable/enable 是在CPU中进行。

绑定和解除绑定Attach & Detach?



注解:在开始时,内核屏蔽了所有中断,来避免软件不关心的硬件中断开销。

当一个中断发生时,内核将控制和决定是哪一个中断。它将遍历这个中断的所有handlers和event。对于enent,它将event入队。对于handler,它将准备MMU来启动进程,然后调用handler。如果handler返回一个event,它将入队。在所有的event和handler都完成时,内核将给PIC一个EOI。一旦所用中断都完成时,内核将遍历所有event队列,然后发生调度,然后返回到最高的READY状态的线程。



注解:这个例子展示的内容对于支持共享中断的bus(PCI)来说很有用。

这儿有两个主要的硬件中断架构:edge-sensitive and level-sensitive.

在edge-sensitive架构中,不论硬件中断线状态合适改变,中断都将被注册。存在问题:如果一个设备触发一个中断,中断服务服务程序(ISR)将被调用。如果另一个设备也触发了这个中断,在中断服务程序复位了第一个中断前,非额外中断将被生成给系统,因为当总线正在被第一个设备驱动时,这儿没有第二个edge了。

在level-sensitive架构中,当中断线是激活状态时,中断也被当做激活。当内核产生一个EOI,如果还存在激活状态的中断线时,ISR将被立即再次调用来服务下一个设备。因此在此架构中,ISR必须要清理中断源,或者在返回之前,屏蔽它。

这种清理clear在process level不会被执行。屏蔽mask在InterruptAttachEvent()中是自动执行的。

应该绑定一个handler还是event?

1.对于qnx系统,内核是一个单一故障点。

绑定handler会增加单一故障点,event不会。

2 . event的debug更简单。(ISR不能单步debug)

3 . 在线程中执行h/w操作时,有完整的系统功能。

4.event比handler有更小的系统开销。

(使用event时,不需要MMU工作来取得进程空间操作权)

5 . 为每个中断调度线程会产生更多的系统开销

6 . handler比线程调度有更小的latency。

2.中断服务子程序中的进程间通信

方法:

1.SIGEV_INTR/InterruptWait()

最简单,最快速;必须服务一个线程;queue深度只有1

2.pulse

在频道中能有多个线程来等待接收消息;能入队;最复杂

3.Signal

使用一个signal handler是最费资源的,但是在使用sigwaitinfo()时,比pulse更快一点;能入队

等待中断发生最简单的方法:

InterruptWait (reserved, reserved);


等待线程必须是一开始绑定handler的线程。

使用pulse来通知:

#define INTR_PULSE _PULSE_CODE_MINAVAIL
struct sigevent event;
main ()
{
...
chid = ChannelCreate( 0 );
coid = ConnectAttach( ND_LOCAL_NODE, 0, chid, _NTO_SIDE_CHANNEL, 0 );
SIGEV_PULSE_INIT( &event, coid, MyPriority, INTR_PULSE, 0 );
InterruptAttach( intnum, handler, NULL, 0, _NTO_INTR_FLAGS_TRK_MSK );
for (;;)
{
rcvid = MsgReceive( chid, ... );
if (rcvid == 0)
{
// we got a pulse
}
}
}
const struct sigevent *
handler (void *area, int id)
{
// do whatever work is required
return (&event); // wake up main thread
}


或者:

const struct sigevent *
intHandler (void *not_used, int id)
{
...
if (nothing_to_report)
{
return (NULL);
}
else
{
if (low_priority_event)
{
return (&lowpri_event);
}
else
{
return (&highpri_event);
}
}
...
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: