2016-10-27 5 views
10

Для класса ОС в настоящее время мне нужно создать потокобезопасную очередь в ядре linux, с которой один взаимодействует с использованием системных вызовов.Существуют ли какие-либо преимущества в использовании двоичного семафора вместо мьютекса для взаимного исключения в критическом разделе очереди?

Теперь для критических разделов мое чувство кишки состоит в том, что я хотел бы использовать функции и mutex_unlock в заголовке mutex.h. Однако мне сказали, что вместо этого я мог бы использовать двоичный семафор с down_interruptible и up в заголовке semaphore.h, и было бы лучше.

Я прочитал Difference between binary semaphore and mutex: Из этого я понимаю, что основным преимуществом мьютекса является то, насколько сильно оно обеспечивает право собственности, и что преимущество семафора заключается в том, что, поскольку оно не обеспечивает права собственности, вы можете использовать его как механизм синхронизации между двумя (несколькими?) различными потоками.

Вопрос в том, какие преимущества имеют бинарный семафор, если вы используете его точно так же, как и мьютекс. Более точно, если я писал:

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; 
} 
+0

Будет ли ваша очередь требовать рекурсивной блокировки или иметь дело с инверсией приоритета? –

+0

Нет для инверсии приоритета, и я уверен, что мне также не понадобится рекурсивная блокировка. – m0meni

+2

Тогда в этот момент я думаю, что это сводится к производительности. И когда это производительность, самое лучшее, что нужно сделать, это профилировать и то, и другое. Я чувствую, что если они будут работать примерно одинаково, я бы придерживался мьютекса, но другие, возможно, получили бы более точные ответы. –

ответ

11

В отсутствие эмпирических данных, Я цитирую из книги Linux Kernel развития

он (т.е. мьютекс) ведет себя аналогично к семафору с отсчетом одного, но он имеет простой интерфейс, более эффективную производительность и дополнительные ограничения .

Кроме того, существует множество ограничений, которые применяются к мьютексам, но не к семафорам. Такие вещи, как процесс, не могут выйти, удерживая мьютекс. Более того, если включена опция ядра CONFIG_DEBUG_MUTEXES, все ограничения, применяемые к мьютексам, обеспечиваются путем отладки чеков.

Итак, если нет веской причины не использовать мьютексы, это должен быть первый выбор.

+3

Я тоже пойду этим. –

5

По умолчанию блокирующий примитив является спин-блокировкой. Мьютексы имеют смысл только в том случае, если вам нужно спать, удерживая замок, которого вы определенно не видите в вышеупомянутом образце кода.

#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; 

Почему вы инициализируете их и почему вы устанавливаете указатель на 0, а не в NULL?

if (len < 0) return EINVAL; 

Оператор возврата должен быть на следующей строке. Также обратите внимание, что это условие не было бы релевантным, если бы тип начинался без знака.

if (len > MESSAGE_MAX_SIZE) return E2BIG; 

    msg = kmalloc(sizeof(Message), GFP_KERNEL); 

Почему не SizeOf (* тзд)

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, которые вы нарушаете без видимых причин. Что вы должны сделать в отношении вашего фактического вопроса, было объяснено в первых двух предложениях. Удачи вам в этом. –

+1

Прошу прощения, если я сошел с ума. По личному мнению я имел в виду стиль, который, поскольку это только мой собственный проект, что я не вношу вклад в ядро, я не чувствовал, что это важно. Что касается list_head -> list_node typedef, это потому, что list_head был для меня таким неинтуитивным именем. Что касается таких вещей, как подписанные переменные, это те подписи, на которые мне было поручено работать, поэтому я их сохранил. Что касается утечки памяти, я вызываю kfree() в функцию dequeue, которую я не размещал здесь. Я предпочитаю от 0 до NULL, так как они одно и то же. Один вопрос: почему узел должен быть первым в структуре сообщений? – m0meni

+0

Я тоже удалил свой нижний план. Наверное, меня просто раздражало незапрошенное просмотрение кода, и жалел, что вы подробно остановились на первых строках, которые вы написали, например, когда на самом деле нужно спать, а не просто спин-блокировать. – m0meni