我们需要为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(并且图像闪烁)。它使我相信事件的执行存在问题。
如果您有任何建议,请分享。谢谢。