qnx之中断控制

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);
        }
    }
    ...
}
  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值