在 macOS 内核扩展中有效地使用同步

Using synchronization efficiently in macOS kernel extension

我在我的 macOS 内核扩展中使用同步 KPI 来验证一个功能是否在另一个功能启动之前完全执行(当然这两个功能是在不同的线程中执行的)。

这些是同步方法:

msleep(void *channel,lck_mtx_t *mtx,int priority,const char *wmesg,  struct  timespec *timeout);
wakeup(void *channel);

所以channel是一个指向布尔值的指针,表示第一个函数已完全执行。

这是我在第一个函数中的实现:

OSIncrementAtomic(channel);
wakeup(channel);

在另一个函数上,我等待 channel 被设置:

msleep(channel, NULL, 0, "", ts);

但是,如果第一个函数在第二个函数之前终止(这是常见的情况),我 shell 等待 ts 中的超时以获取静脉。

我的问题是,如果 wakeup 已经发生,是否有办法跳过 msleep

谢谢,

lck_mtx_sleep_deadline()(或 IOLockSleepDeadline() 包装器)与互斥锁结合使用。在等待的线程上:

  1. 锁定互斥体
  2. 检查等待条件(第一个函数还是运行吗?)
  3. 如果是,请致电lck_mtx_sleep_deadline()
  4. 解锁互斥锁

根据您的情况,您可能希望将步骤 2+3 设为 while() 循环。

在你的"first function"中:

  1. 锁定互斥体
  2. 清除等待条件(增量通道)
  3. 发送事件唤醒。 (例如 thread_wakeup_prim((event_t) event, oneThread, THREAD_AWAKENED);
  4. 解锁互斥。

你可能也可以直接用较低级别的 msleep()/wakeup() 来完成所有这些,但我不太熟悉那个 API.

您使用 msleep 有两个问题。

首先,channel是一个不透明的值,不是"pointer to boolean value";你不增加它,它不会被调用修改。相反,您只需确保它是唯一的,即不被其他不相关的 msleep 调用使用。约定是使用一个相关数据结构的内存地址来实现唯一性。如果在您的情况下您不能确保 msleepwakeup 之前被调用,那么您将使用 Mach semaphore,而不是 msleep/wakeup.

另一个问题是,正如@mrdvlpr 已经在评论中指出的那样,msleep 需要一个互斥锁才能唤醒。如果你用 mtx=NULL 调用它,它会无限期地休眠并且在同一个频道上调用 wakeup 不会有任何效果。如果你想稍后用 wakeupwakeup_once 唤醒,你需要提供一个有效且锁定的互斥锁而不是 NULL.

在 XNU 中对 msleep 的最小但功能调用没有超时看起来像这样:

lck_grp_t *lck_grp;
lck_mtx_t *lck_mtx;

lck_grp = lck_grp_alloc_init("com.example.mslpex", LCK_GRP_ATTR_NULL);
if (!lck_grp)
    /* handle failure */
lck_mtx = lck_mtx_alloc_init(lck_grp, LCK_ATTR_NULL);
if (!lck_mtx)
    /* handle failure */

/* ... */

lck_mtx_lock(lck_mtx);
error = msleep(channel, lck_mtx, pri|PDROP, "mslpex", NULL);
if (error)
    /* handle failure */
else
    /* handle success */

/* ... */

lck_mtx_free(lck_mtx, lck_grp);
lck_grp_free(lck_grp);

对应的wake-up调用就是:

wakeup(channel);

为了系统稳定性,您可能希望在调用 msleep 时使用超时,以便从 wakeup 未按预期调用的情况中恢复。