【发布时间】:2019-04-29 16:01:58
【问题描述】:
我想直接从输入设备读取键盘输入。从这样的文件中读取需要 root 权限,对于程序的其余部分,我不需要也不想要。
我的计划是以root权限启动程序,然后启动一个工作线程,最后在主线程中删除root权限。然后主线程可以发送请求,要求工作线程处理下一个键盘输入。
我已经构建了这个最小的例子:
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
cnd_t wakeup;
mtx_t mutex;
enum request {
REQUEST_NOTHING,
REQUEST_PING,
REQUEST_TERMINATION
} request;
int daemon(void *arg) {
(void)arg;
int retval;
retval = mtx_lock(&mutex);
assert(retval == thrd_success);
for(;;) {
request = REQUEST_NOTHING;
retval = cnd_wait(&wakeup, &mutex);
assert(retval == thrd_success);
switch(request) {
case REQUEST_NOTHING:
break;
case REQUEST_PING:
puts("pong.");
break;
case REQUEST_TERMINATION:
retval = mtx_unlock(&mutex);
assert(retval == thrd_success);
return 0;
default:
assert(false);
}
}
}
void send(enum request req) {
int retval;
retval = mtx_lock(&mutex);
assert(retval == thrd_success);
request = req;
retval = mtx_unlock(&mutex);
assert(retval == thrd_success);
// TODO race condition: worker thread my not be listening yet
retval = cnd_signal(&wakeup);
assert(retval == thrd_success);
}
int main() {
int retval;
retval = mtx_init(&mutex, mtx_plain);
assert(retval == thrd_success);
retval = cnd_init(&wakeup);
assert(retval == thrd_success);
thrd_t thread;
retval = thrd_create(&thread, daemon, NULL);
assert(retval == thrd_success);
puts("ping.");
send(REQUEST_PING);
// TODO wait for the worker thread to complete
send(REQUEST_TERMINATION);
retval = thrd_join(thread, NULL);
assert(retval == thrd_success);
cnd_destroy(&wakeup);
mtx_destroy(&mutex);
}
我目前还没有实现特权的东西,但我已经有足够的问题了:
-
cnd_signal不表示信号是否被任何人接收到:解除阻塞当前等待由 cond 指向的条件变量的一个线程。 如果没有线程被阻塞,什么都不做并返回
thrd_success。
来源:cppreference.com这会导致竞争条件,因为主线程需要在写入
request之前锁定互斥锁,但工作线程也需要在等待信号之前锁定互斥锁。 -
主线程不等待工作线程完成。在本例中这不是问题,因为 (1) 主线程在发送下一个请求之前必须等待获得锁,而 (2) 线程可以简单地退出前加入。
在我的实际程序中,我需要等待。显而易见的解决方案是引入另一对
cnd_t和mtx_t允许工作线程唤醒主线程。但是对于这样一个简单的问题,这似乎过于复杂了。
我找不到很多资源来演示 C11 的线程库的使用,并且可能一起走错了路。我会很感激一些反馈,也许是上述问题的解决方案。
【问题讨论】:
标签: c linux multithreading race-condition worker