在 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()
包装器)与互斥锁结合使用。在等待的线程上:
- 锁定互斥体
- 检查等待条件(第一个函数还是运行吗?)
- 如果是,请致电
lck_mtx_sleep_deadline()
- 解锁互斥锁
根据您的情况,您可能希望将步骤 2+3 设为 while()
循环。
在你的"first function"中:
- 锁定互斥体
- 清除等待条件(增量通道)
- 发送事件唤醒。 (例如
thread_wakeup_prim((event_t) event, oneThread, THREAD_AWAKENED);
)
- 解锁互斥。
你可能也可以直接用较低级别的 msleep()
/wakeup()
来完成所有这些,但我不太熟悉那个 API.
您使用 msleep
有两个问题。
首先,channel
是一个不透明的值,不是"pointer to boolean value";你不增加它,它不会被调用修改。相反,您只需确保它是唯一的,即不被其他不相关的 msleep
调用使用。约定是使用一个相关数据结构的内存地址来实现唯一性。如果在您的情况下您不能确保 msleep
在 wakeup
之前被调用,那么您将使用 Mach semaphore,而不是 msleep
/wakeup
.
另一个问题是,正如@mrdvlpr 已经在评论中指出的那样,msleep
需要一个互斥锁才能唤醒。如果你用 mtx=NULL
调用它,它会无限期地休眠并且在同一个频道上调用 wakeup
不会有任何效果。如果你想稍后用 wakeup
或 wakeup_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
未按预期调用的情况中恢复。
我在我的 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()
包装器)与互斥锁结合使用。在等待的线程上:
- 锁定互斥体
- 检查等待条件(第一个函数还是运行吗?)
- 如果是,请致电
lck_mtx_sleep_deadline()
- 解锁互斥锁
根据您的情况,您可能希望将步骤 2+3 设为 while()
循环。
在你的"first function"中:
- 锁定互斥体
- 清除等待条件(增量通道)
- 发送事件唤醒。 (例如
thread_wakeup_prim((event_t) event, oneThread, THREAD_AWAKENED);
) - 解锁互斥。
你可能也可以直接用较低级别的 msleep()
/wakeup()
来完成所有这些,但我不太熟悉那个 API.
您使用 msleep
有两个问题。
首先,channel
是一个不透明的值,不是"pointer to boolean value";你不增加它,它不会被调用修改。相反,您只需确保它是唯一的,即不被其他不相关的 msleep
调用使用。约定是使用一个相关数据结构的内存地址来实现唯一性。如果在您的情况下您不能确保 msleep
在 wakeup
之前被调用,那么您将使用 Mach semaphore,而不是 msleep
/wakeup
.
另一个问题是,正如@mrdvlpr 已经在评论中指出的那样,msleep
需要一个互斥锁才能唤醒。如果你用 mtx=NULL
调用它,它会无限期地休眠并且在同一个频道上调用 wakeup
不会有任何效果。如果你想稍后用 wakeup
或 wakeup_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
未按预期调用的情况中恢复。