在 queue 的关键部分使用二进制信号量而不是互斥量进行互斥有什么好处吗?
Are there any advantages to using a binary semaphore instead of a mutex for mutual exclusion in a critical section of a queue?
对于 OS class,我目前必须在 linux 内核中创建一个 thread-safe queue,使用系统调用与之交互。
现在对于关键部分,我的直觉是我想在 mutex.h
header 中使用 mutex_lock
和 mutex_unlock
函数。但是,我被告知我可以在 semaphore.h
header 中使用带有 down_interruptible
和 up
的二进制信号量,这样会更好。
我通读了 Difference between binary semaphore and mutex:从中,我了解到互斥锁的主要优点是它强制执行所有权的强度,而信号量的优点是因为它不强制执行所有权,您可以将其用作两个(多个?)不同线程之间的同步机制。
我的问题是,如果您以与互斥锁完全相同的方式使用二进制信号量,它有哪些优势。如果我写得更明确:
down()
/* critical */
up()
和我一样
mutex_lock()
/* critical */
mutex_unlock()
是否有一些性能优势,因为它不如互斥体安全?我错过了什么吗?
如果您需要更多上下文(这是我有史以来的第一个 C 项目),thread-safe 这里有一小段代码:
#define MESSAGE_MAX_SIZE 512
typedef struct list_head list_node;
/* Create message struct */
typedef struct {
size_t size;
list_node node;
char data[MESSAGE_MAX_SIZE];
} Message;
/* Create the linked list queue with dummy head */
struct {
size_t size;
list_node head;
} my_q = { 0, LIST_HEAD_INIT(my_q.head) };
/*
Adds a new item to the tail of the queue.
@data: pointer to data to add to list
@len: size of the data
*/
asmlinkage long sys_enqueue(const void __user *data, long len) {
long res = 0;
Message *msg = 0;
if (len < 0) return EINVAL;
if (len > MESSAGE_MAX_SIZE) return E2BIG;
msg = kmalloc(sizeof(Message), GFP_KERNEL);
if (msg == 0) return ENOMEM;
res = copy_from_user(msg->data, data, len);
if (res != 0) return EFAULT;
/* Start Critical Section */
my_q.size++;
list_add_tail(&msg->node, &my_q.head);
/* End Critical Section */
return 0;
}
在没有经验证据的情况下,我会引用 Linux 内核开发
一书
It (i.e. mutex) behaves similar to a semaphore with a count of one, but it has a
simpler interface, more efficient performance, and additional
constraints on its use.
此外,有许多约束适用于互斥量但不适用于信号量。持有互斥锁时,诸如进程之类的东西无法退出。此外,如果启用 CONFIG_DEBUG_MUTEXES
内核选项,则所有适用于互斥体的约束都将通过调试检查来确保。
因此,除非有充分的理由不使用互斥锁,否则互斥锁应该是首选。
默认的锁定原语是自旋锁。互斥量只有在您需要在持有锁的同时休眠时才有意义,而在上述代码示例中您绝对不需要。
#define MESSAGE_MAX_SIZE 512
typedef struct list_head list_node;
为什么?
/* Create message struct */
typedef struct {
size_t size;
list_node node;
char data[MESSAGE_MAX_SIZE];
} Message;
奇怪的顺序,节点指针应该是第一个或最后一个。
/* Create the linked list queue with dummy head */
struct {
size_t size;
list_node head;
} my_q = { 0, LIST_HEAD_INIT(my_q.head) };
/*
Adds a new item to the tail of the queue.
@data: pointer to data to add to list
@len: size of the data
*/
asmlinkage long sys_enqueue(const void __user *data, long len) {
花括号应该在下一行。为什么是有符号类型的长度?
long res = 0;
Message *msg = 0;
为什么要初始化这些 her,为什么要将指针设置为 0 而不是 NULL?
if (len < 0) return EINVAL;
return 语句应该在下一行。另请注意,如果类型以无符号开头,则此条件将不相关。
if (len > MESSAGE_MAX_SIZE) return E2BIG;
msg = kmalloc(sizeof(Message), GFP_KERNEL);
为什么不是 sizeof(*msg)
if (msg == 0) return ENOMEM;
res = copy_from_user(msg->data, data, len);
if (res != 0) return EFAULT;
这会泄露消息。
/* Start Critical Section */
my_q.size++;
list_add_tail(&msg->node, &my_q.head);
/* End Critical Section */
return 0;
}
对于 OS class,我目前必须在 linux 内核中创建一个 thread-safe queue,使用系统调用与之交互。
现在对于关键部分,我的直觉是我想在 mutex.h
header 中使用 mutex_lock
和 mutex_unlock
函数。但是,我被告知我可以在 semaphore.h
header 中使用带有 down_interruptible
和 up
的二进制信号量,这样会更好。
我通读了 Difference between binary semaphore and mutex:从中,我了解到互斥锁的主要优点是它强制执行所有权的强度,而信号量的优点是因为它不强制执行所有权,您可以将其用作两个(多个?)不同线程之间的同步机制。
我的问题是,如果您以与互斥锁完全相同的方式使用二进制信号量,它有哪些优势。如果我写得更明确:
down()
/* critical */
up()
和我一样
mutex_lock()
/* critical */
mutex_unlock()
是否有一些性能优势,因为它不如互斥体安全?我错过了什么吗?
如果您需要更多上下文(这是我有史以来的第一个 C 项目),thread-safe 这里有一小段代码:
#define MESSAGE_MAX_SIZE 512
typedef struct list_head list_node;
/* Create message struct */
typedef struct {
size_t size;
list_node node;
char data[MESSAGE_MAX_SIZE];
} Message;
/* Create the linked list queue with dummy head */
struct {
size_t size;
list_node head;
} my_q = { 0, LIST_HEAD_INIT(my_q.head) };
/*
Adds a new item to the tail of the queue.
@data: pointer to data to add to list
@len: size of the data
*/
asmlinkage long sys_enqueue(const void __user *data, long len) {
long res = 0;
Message *msg = 0;
if (len < 0) return EINVAL;
if (len > MESSAGE_MAX_SIZE) return E2BIG;
msg = kmalloc(sizeof(Message), GFP_KERNEL);
if (msg == 0) return ENOMEM;
res = copy_from_user(msg->data, data, len);
if (res != 0) return EFAULT;
/* Start Critical Section */
my_q.size++;
list_add_tail(&msg->node, &my_q.head);
/* End Critical Section */
return 0;
}
在没有经验证据的情况下,我会引用 Linux 内核开发
一书It (i.e. mutex) behaves similar to a semaphore with a count of one, but it has a simpler interface, more efficient performance, and additional constraints on its use.
此外,有许多约束适用于互斥量但不适用于信号量。持有互斥锁时,诸如进程之类的东西无法退出。此外,如果启用 CONFIG_DEBUG_MUTEXES
内核选项,则所有适用于互斥体的约束都将通过调试检查来确保。
因此,除非有充分的理由不使用互斥锁,否则互斥锁应该是首选。
默认的锁定原语是自旋锁。互斥量只有在您需要在持有锁的同时休眠时才有意义,而在上述代码示例中您绝对不需要。
#define MESSAGE_MAX_SIZE 512
typedef struct list_head list_node;
为什么?
/* Create message struct */
typedef struct {
size_t size;
list_node node;
char data[MESSAGE_MAX_SIZE];
} Message;
奇怪的顺序,节点指针应该是第一个或最后一个。
/* Create the linked list queue with dummy head */
struct {
size_t size;
list_node head;
} my_q = { 0, LIST_HEAD_INIT(my_q.head) };
/*
Adds a new item to the tail of the queue.
@data: pointer to data to add to list
@len: size of the data
*/
asmlinkage long sys_enqueue(const void __user *data, long len) {
花括号应该在下一行。为什么是有符号类型的长度?
long res = 0;
Message *msg = 0;
为什么要初始化这些 her,为什么要将指针设置为 0 而不是 NULL?
if (len < 0) return EINVAL;
return 语句应该在下一行。另请注意,如果类型以无符号开头,则此条件将不相关。
if (len > MESSAGE_MAX_SIZE) return E2BIG;
msg = kmalloc(sizeof(Message), GFP_KERNEL);
为什么不是 sizeof(*msg)
if (msg == 0) return ENOMEM;
res = copy_from_user(msg->data, data, len);
if (res != 0) return EFAULT;
这会泄露消息。
/* Start Critical Section */
my_q.size++;
list_add_tail(&msg->node, &my_q.head);
/* End Critical Section */
return 0;
}