Мы не приводим результат работы пpoгрaммы, но она, судя по всему, работает правильно. Выполнение этой программы не дает возможности утверждать, что в ней нет ошибок. Если результат оказывается неправильным, то можно сказать с уверенностью, что что-то не так. Но успешное выполнение программы еще ни о чем не говорит. Ядро могло выполнить сначала одну программу, затем другую, и если они не выполнялись параллельно, мы не имеем возможности увидеть ошибку. Увеличить шансы обнаружения ошибки можно, изменив функцию main таким образом, чтобы последовательный номер увеличивался 10000 раз, и запустив 20 экземпляров программы одновременно. Если начальное значение последовательного номера в файле было 1, мы можем ожидать, что после завершения работы всех этих процессов мы увидим в файле число 200001.
Пример: упрощение с помощью макросов
В листинге 9.3 установка и снятие блокировки занимали шесть строк кода. Мы должны выделить место под структуру, инициализировать ее и затем вызвать fcntl. Программы можно упростить, если определить следующие семь макросов, которые взяты из раздела 12.3 [21]:
#define read_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define readw_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_RDlCK, offset, whence, len)
#define write_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define writew_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define un_lock(fd, offset, whence, len) \
lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len)
#define is_read_lockable(fd, offset, whence, len) \
lock_test(fd, F_RDLCK, offset, whence, len)
#define is_write_lockable(fd, offset, whence, len) \
lock_test(fd, F_WRLCK, offset, whence, len)
Эти макросы используют наши функции lock_reg и lock_test, текст которых приведен в листингах 9.4 и 9.5. С ними нам уже не нужно заботиться об инициализации структур и вызове функций. Первые три аргумента специально сделаны совпадающими с первыми тремя аргументами функции lseek.
Мы также определяем две функции-обертки, Lock_reg и Lock_test, завершающие свое выполнение с возвратом ошибки fcntl, и семь макросов с именами, начинающимися с заглавной буквы, чтобы эти функции вызывать.
С помощью новых макросов мы можем записать функции my_lock и my_unlock из листинга 9.3 как
#define my_lock(fd) (Writew_lock(fd, 0, SEEK_SET, 0))
#define my_unlock(fd) (Un_lock(fd, 0, SEEK_SET, 0))
//lib/lock_reg.c
1 #include "unpipc.h"
2 int
3 lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
4 {
5 struct flock lock;
6 lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */
7 lock.l_start = offset; /* сдвиг по отношению к l_whence */
8 lock.l_whence = whence; /* SEEK SET. SEEK CUR, SEEK END */
9 lock.l_len = len; /* количество байтов (0 – до конца файла) */
10 return(fcnt(fd, cmd, &lock)"); /* –1 в случае ошибки */
11 }
//lib/lock_test.c
1 #include "unpipc.h"
2 pid_t
3 lock_test(int fd, int type, off_t offset, int whence, off_t len)
4 {
5 struct flock lock;
6 lock.l_type = type; /* F_RDLCK or F_WRLCK */
7 lock.l_start = offset; /* сдвиг по отношению к l_whence */
8 lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */