Пример, на который вы указываете, действительно ошибочен. Декремент абсолютно не гарантированно будет атомарным и почти наверняка не будет.
Но на самом деле, я не думаю, что есть комбинация компилятор / ЦП, которая когда-либо давала бы код, который мог дать сбой. Худшее, что может случиться, - это то, что одно ядро ЦП может завершить закрытие, а затем другое ядро может вызвать открытие и вернуться к работе, поскольку оно имеет устаревшее кэшированное значение флага.
Linux предоставляет для этого atomic_*
функции, а также *_bit
операции с атомными битовыми флагами. Смотрите core_api / atomic_ops.rst в документации по ядру.
Пример правильного и простого шаблона для выполнения его будет выглядеть примерно так:
unsigned long mydriver_flags;
#define IN_USE_BIT 0
static int mydriver_open(struct inode *inode, struct file *filp)
{
if (test_and_set_bit(IN_USE_BIT, &mydriver_flags))
return -EBUSY;
/* continue with open */
return 0;
}
static int mydriver_close(struct inode *inode, struct file *filp)
{
/* do close stuff first */
smp_mb__before_atomic();
clear_bit(IN_USE_BIT, &mydriver_flags);
return 0;
}
Реальный драйвер должен иметь структуру состояния устройства для каждого устройства с mydriver_flags
. Вместо использования одного глобального для всего драйвера, как показано в примере.
Тем не менее, то, что вы пытаетесь сделать, вероятно, не очень хорошая идея. Даже если только один процесс может открыть устройство одновременно, дескрипторы открытого файла процесса совместно используются всеми потоками в процессе. Несколько потоков могут одновременно выполнять read()
и write()
вызовы одного и того же файлового дескриптора.
Если процесс имеет открытый дескриптор файла и вызывает fork()
, этот дескриптор будет унаследован в новом процессе. Это способ, которым несколько процессов могут одновременно открывать устройство, несмотря на вышеуказанное ограничение "одного открытия".
Таким образом, вы все равно должны быть поточно-ориентированными в файловых операциях вашего драйвера, так как у пользователя может быть несколько потоков / процессов, которые одновременно открывают устройство и делают одновременные вызовы. И если вы сделали это безопасным, зачем мешать пользователю делать это? Может быть, они знают, что делают, и будут следить за тем, чтобы их многочисленные открыватели драйвера «по очереди» и не делали звонки, которые конфликтуют?
Также рассмотрите возможность использования флага O_EXCL в открытом вызове, чтобы сделать одно открытое необязательным.