Введение

Секблок (сокращение от sequence lock) — специальный механизм блокировки, используемый в Linux для обеспечения быстрых записей в общие переменные между двумя параллельными подпрограммами операционной системы. Семантика стабилизировалась, начиная с версии 2.5.59, и присутствует в стабильных ядрах серии 2.6.x. Секблоки были разработаны Стивеном Хеммингером и первоначально назывались frlocks, основанными на более ранних работах Андреа Арканджели. Первая реализация была в коде времени x86-64, где требовалась синхронизация с пользовательским пространством, где использование реальной блокировки было невозможно. Это механизм согласованности чтения-записи, который позволяет избежать ситуации, когда писатель оказывается в состоянии "голода". Секблок состоит из хранилища для сохранения порядкового номера в дополнение к блокировке. Блокировка предназначена для синхронизации между двумя процессами-записывающими, а счетчик — для индикации согласованности для читающих. Помимо обновления общих данных, процесс-записывающий увеличивает порядковый номер как после получения блокировки, так и перед её освобождением. Читающие процессы считывают порядковый номер до и после чтения общих данных. Если порядковый номер оказывается нечётным в любой из этих точек, значит, процесс-записывающий захватил блокировку во время чтения данных, и данные могли быть изменены. Если порядковые номера различаются, значит, процесс-записывающий изменил данные во время чтения. В любом из этих случаев читающие процессы просто повторяют попытку (используя цикл), пока не прочитают один и тот же чётный порядковый номер до и после чтения. Читающий процесс никогда не блокируется, но может потребоваться повторить попытку, если идёт запись; это ускоряет чтение в случае, когда данные не были изменены, поскольку не требуется захват блокировки, как это было бы при использовании традиционной блокировки чтения-записи. Кроме того, процессы-записывающие не ждут читающих, в то время как при использовании традиционных блокировок чтения-записи они ждут, что может привести к потенциальному истощению ресурсов в ситуации, когда много читающих (поскольку процесс-записывающий должен ждать, пока не останется читающих). Благодаря этим двум факторам, секблоки более эффективны, чем традиционные блокировки чтения-записи в ситуациях с большим количеством читающих и малым количеством записывающих. Недостатком является то, что при слишком активной записи или медленной работе читающего процесса может возникнуть взаимная блокировка (и читающие процессы могут оказаться в состоянии "голода"). Эта техника не подходит для данных, содержащих указатели, поскольку любой процесс-записывающий может сделать указатель, по которому уже прошел читающий процесс, недействительным. Обновление блока памяти, на который указывает указатель, допустимо при использовании секблоков, но обновление самого указателя запрещено. В случае, когда необходимо обновлять или изменять сами указатели, предпочтительнее использовать синхронизацию обновления копии при чтении (read-copy-update). Впервые это было применено для обновления системного счётчика времени. Каждый раз при прерывании по таймеру обновляется текущее время; существует множество читающих время процессов для внутреннего использования операционной системой и приложениями, но записи происходят относительно редко и только по одному разу за раз. Например, код BSD-счётчика времени, по-видимому, использует аналогичную технику. Одна из тонкостей использования секблоков для счётчика времени заключается в том, что невозможно пошагово выполнить его отладку. Логика повторных попыток будет постоянно срабатывать, поскольку отладчик достаточно медленный, чтобы гарантировать постоянное возникновение гонки при чтении.