Существуют ли какие-либо преимущества для использования двоичного семафора вместо мьютекса для взаимного исключения в критическом разделе очереди?
Для класса ОС в настоящее время мне нужно создать потокобезопасную очередь в ядре linux, с которой один взаимодействует с использованием системных вызовов.
Теперь для критических разделов мое чувство кишки состоит в том, что я хотел бы использовать функции mutex_lock
и mutex_unlock
в заголовке mutex.h
. Однако мне сказали, что вместо этого можно использовать двоичный семафор с down_interruptible
и up
в заголовке semaphore.h
и что было бы лучше.
Я прочитал Разницу между двоичным семафором и мьютексом. Из этого я понимаю, что основным преимуществом мьютекса является то, насколько сильно он обеспечивает право собственности, и что преимущество семафора заключается в том, что, поскольку он не обеспечивает права собственности, вы можете использовать его в качестве механизма синхронизации между двумя (несколькими?) различными потоками.
Мой вопрос в том, каковы преимущества двоичного семафора, если вы используете его точно так же, как и мьютекс. Явным образом, если бы я писал:
down()
/* critical */
up()
таким же образом, как я сделал бы
mutex_lock()
/* critical */
mutex_unlock()
Есть ли преимущество в производительности, потому что оно менее безопасно, чем мьютекс? Я что-то пропустил?
Вот небольшой фрагмент кода, который я хочу сделать потокобезопасным, если вы хотите больше контекста (это мой первый C-проект):
#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;
}
Ответы
Ответ 1
В отсутствие эмпирических данных я бы привел цитату из книги Linux Kernel Development
Это (т.е. мьютекс) ведет себя подобно семафору со счетом одного, но имеет более простой интерфейс, более эффективную производительность и дополнительные ограничения на его использование.
Кроме того, существует множество ограничений, которые применяются к мьютексам, но не к семафорам. Такие вещи, как процесс, не могут выйти, удерживая мьютекс. Более того, если опция CONFIG_DEBUG_MUTEXES
kernel включена, то все ограничения, применяемые к мьютексам, гарантируются путем отладки чеков.
Итак, если нет веской причины не использовать мьютекс, это должен быть первый выбор.
Ответ 2
По умолчанию фиксирующий примитив является спин-блокировкой. Мьютексы имеют смысл только в том случае, если вам нужно спать, удерживая замок, которого вы определенно не видите в вышеупомянутом образце кода.
#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;
Странный порядок, указатель node должен быть первым или последним.
/* 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;
Почему вы инициализируете их и почему вы устанавливаете указатель на 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;
}