Linux内核类似Windows的事件实现

时间:2019-05-07 14:35:13

标签: c events linux-kernel synchronization

我们需要为Linux实现类似Windows的内核事件。这些功能旨在充当相应的 KeInitializeEvent KeSetEvent KeResetEvent KePulseEvent KeWaitForSingleObject < / em>从Windows内核。 同步事件在此处称为自动重置,而 Notification 事件称为手动重置。这是代码:

Event.h

#define WAIT_FOREVER     -1

#define event_init(event, manual_reset, initial_state) __event_init(event, manual_reset, initial_state)
#define event_set(event) __event_set(event)
#define event_reset(event) __event_reset(event)
#define event_pulse(event) __event_pulse(event)
#define event_wait(event, ms_timeout) __event_wait(event, (ms_timeout == WAIT_FOREVER) ? (WAIT_FOREVER) : ((ms_timeout * HZ) / 1000))

typedef struct _wait_t
{
    atomic_t b;
    wait_queue_head_t q;
    struct list_head list;
} wait_t;

typedef struct _event_t
{
    struct list_head Wait;
    bool AutoReset;
    bool State;
} event_t;

void __event_init_lib(void);
void __event_init(event_t *event, bool manual_reset, bool initial_state);
bool __event_set(event_t *event);
bool __event_reset(event_t *event);
bool __event_pulse(event_t *event);
status_t __event_wait(event_t *event, time_t timeout);

Event.c

wait_t g_stor[100];
spinlock_t g_lock;

void __event_init_lib(void)
{
    wait_t *ptr;

    for (int i = 0; i < ARRAY_SIZE(g_stor); ++i)
    {
        ptr = &g_stor[i];
        atomic_set(&ptr->b, 2);
        init_waitqueue_head(&ptr->q);
        INIT_LIST_HEAD(&ptr->list);
    }

    spin_lock_init(&g_lock);
}

void __event_init(event_t *event, bool manual_reset, bool initial_state)
{
    INIT_LIST_HEAD(&event->Wait);

    event->State = initial_state;
    event->AutoReset = !manual_reset;
}

status_t __event_wait(event_t *event, time_t timeout)
{
    bool b;
    wait_t *ptr;
    status_t status;

    spin_lock(&g_lock);
    if (event->State)
    {
        if (event->AutoReset) event->State = false;
        spin_unlock(&g_lock);
        return s_success;
    }
    for (int i = 0; i < ARRAY_SIZE(g_stor); ++i)
    {
        ptr = &g_stor[i];
        if (atomic_cmpxchg(&ptr->b, 2, 0) == 2) break;
    }
    list_add_tail(&ptr->list, &event->Wait);            // note: we need to insert in the end of the list
    spin_unlock(&g_lock);

    if (timeout == WAIT_FOREVER) wait_event(ptr->q, b = (atomic_cmpxchg(&ptr->b, 1, 2) == 1));
    else wait_event_timeout(ptr->q, b = (atomic_cmpxchg(&ptr->b, 1, 2) == 1), timeout);

    if (b) status = s_success;
    else status = s_timeout;

    return status;
}

bool __event_set(event_t *event)
{
    bool PrevState;
    struct list_head *entry;
    wait_t *Wait;

    //if (!event->AutoReset && event->State) return true;
    spin_lock(&g_lock);
    PrevState = event->State;
    event->State = true;
    if (!PrevState && !list_empty(&event->Wait))             // check if we became signaled right now
                                                             // and we have waiters
    {
        if (event->AutoReset)
        {
            entry = event->Wait.next;
            Wait = container_of(entry, wait_t, list);
            atomic_set(&Wait->b, 1);
            wake_up(&(Wait->q));
            event->State = false;
            list_del(entry);
        }
        else
        {
            entry = event->Wait.next;
            while (entry != &event->Wait)
            {
                Wait = container_of(entry, wait_t, list);
                atomic_set(&Wait->b, 1);
                wake_up(&(Wait->q));
                entry = entry->next;
                list_del(entry->prev);
            }
        }
    }
    spin_unlock(&g_lock);
    return PrevState;
}

bool __event_reset(event_t *event)
{
    bool PrevState;

    spin_lock(&g_lock);
    PrevState = event->State;
    event->State = false;
    spin_unlock(&g_lock);
    return PrevState;
}

bool __event_pulse(event_t *event)
{
    bool PrevState;
    struct list_head *entry;
    wait_t *Wait;

    spin_lock(&g_lock);
    PrevState = event->State;
    if (!PrevState && !list_empty(&event->Wait))           // check if we became signaled right now
                                                           // and we have waiters
    {
        if (event->AutoReset)
        {
            entry = event->Wait.next;
            Wait = container_of(entry, wait_t, list);
            atomic_set(&Wait->b, 1);
            wake_up(&(Wait->q));
            list_del(entry);
        }
        else
        {
            entry = event->Wait.next;
            while (entry != &event->Wait)
            {
                Wait = container_of(entry, wait_t, list);
                atomic_set(&Wait->b, 1);
                wake_up(&(Wait->q));
                entry = entry->next;
                list_del(entry->prev);
            }
        }
    }
    event->State = false;
    spin_unlock(&g_lock);
    return PrevState;
}

我认为每个等待线程都需要自己的条件变量,因为如果我们有一个条件变量并将其设置为 true ,新的服务员可能会无意到达并通过 wait_event 甚至不睡觉因此,我们需要维护条件变量列表,因此要唤醒正确的线程,我们还需要多个等待队列。另外, ReactOS 来源建议事件保留了服务员列表。

由于我们不能在内核模式下使用线程本地存储变量(至少以不简单的方式),所以我决定实现等待块数组。当我们需要在列表中插入waiter时,我们循环该数组以搜索免费的wait块。这使我相信,我们需要像 ReactOS 那样使用单个全局锁(调度程序锁),而不是为每个事件对象使用单独的锁。

我们需要从Windows移植的摄像机驱动程序的事件对象。一切似乎都正常,但是每秒帧数有时会从14 fps下降到10(并且图像闪烁)。它使我相信事件的执行存在问题。

如果您有任何建议,请分享。谢谢。

0 个答案:

没有答案