在队列的关键部分使用二进制信号量而不是互斥量进行互斥是否有任何优势?

m0m*_*eni 10 c multithreading mutex semaphore linux-kernel

对于OS类,我当前必须在linux内核中创建一个线程安全队列,该队列使用系统调用进行交互.

现在对于关键部分,我的直觉是我想要使用标题中的mutex_lockmutex_unlock函数mutex.h.但是,有人告诉我,我可以改用二进制信号与down_interruptibleupsemaphore.h头,并且它会更好.

我已经阅读了二进制信号量和互斥量之间的区别:从中我了解到,互斥体的主要优点是它强制执行所有权的强度,以及信号量的优势在于,因为它不强制实施所有权,所以您可以使用它作为两个(多个?)不同线程之间的同步机制.

我的问题是二进制信号量的优点是什么,如果你以与互斥量完全相同的方式使用它.如果我写的话更明确:

down()
/* critical */
up()
Run Code Online (Sandbox Code Playgroud)

就像我一样

mutex_lock()
/* critical */
mutex_unlock()
Run Code Online (Sandbox Code Playgroud)

是否有一些性能优势,因为它不如互斥锁安全?我错过了什么吗?


如果你想要更多的上下文(这是我的第一个C proj),这里有一小段我想要线程安全的代码片段:

#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;
}
Run Code Online (Sandbox Code Playgroud)

byt*_*ire 11

在缺乏经验证据的情况下,我引用了Linux Kernel Development一书

它(即互斥体)的行为类似于计数为1的信号量,但它具有更简单的接口,更高效的性能以及对其使用的额外约束.

此外,有许多约束适用于互斥锁,但不适用于信号量.像过程这样的东西在拿着互斥锁时无法退出.此外,如果CONFIG_DEBUG_MUTEXES启用了内核选项,则通过调试检查可以确保适用于互斥锁的所有约束.

因此,除非有充分理由不使用互斥锁,否则应该是首选.

  • 我也会这样做. (3认同)

emp*_*nth 5

默认的锁定原语是自旋锁.只有在你拿着锁的时候需要睡觉时,互斥锁才有意义,你在前面提到的代码示例中肯定没有.

#define MESSAGE_MAX_SIZE 512

typedef struct list_head list_node;
Run Code Online (Sandbox Code Playgroud)

为什么?

/* Create message struct */
typedef struct {
  size_t size;
  list_node node;
  char data[MESSAGE_MAX_SIZE];
} Message;
Run Code Online (Sandbox Code Playgroud)

奇怪的顺序,节点指针应该是第一个或最后一个.

/* 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) {
Run Code Online (Sandbox Code Playgroud)

花括号应该在下一行.为什么签名类型的长度?

  long res = 0;
  Message *msg = 0; 
Run Code Online (Sandbox Code Playgroud)

为什么要初始化这些她,为什么要将指针设置为0而不是NULL?

  if (len < 0) return EINVAL;
Run Code Online (Sandbox Code Playgroud)

return语句应该在下一行.另请注意,如果类型为无符号开头,则此条件不相关.

  if (len > MESSAGE_MAX_SIZE) return E2BIG;

  msg = kmalloc(sizeof(Message), GFP_KERNEL);
Run Code Online (Sandbox Code Playgroud)

为什么不sizeof(*msg)

  if (msg == 0) return ENOMEM;

  res = copy_from_user(msg->data, data, len);
  if (res != 0) return EFAULT;
Run Code Online (Sandbox Code Playgroud)

这泄漏了消息.

  /* Start Critical Section */

  my_q.size++;
  list_add_tail(&msg->node, &my_q.head);

  /* End Critical Section   */

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果copy_from_user失败,则泄漏已分配的内存.一般来说,锁定是一个相当微妙的主题,你在错误的(或者更确切地说,这里是奇怪的)方向.现在您已经获得了更好的关键字(自旋锁),您知道要google什么或者更好,但要问谁在教你.格式化是关于做正确的事情 - 当你为给定的代码库编写代码时,你坚持使用所述代码库中的约定. (2认同)