fcntl как узнать, какой процесс удерживает файл блокировки?

Я новичок в блокировке fcntl и следую этому примеру, чтобы создать образец блокировки в Linux с использованием кода c: http://www.informit.com/articles/article.aspx?p=23618&seqNum=4

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


person RLe    schedule 17.07.2018    source источник
comment
@AnttiHaapala извини, я изменил вопрос   -  person RLe    schedule 18.07.2018
comment
Хорошо, одно предложение делает его намного понятнее: D   -  person Antti Haapala    schedule 18.07.2018


Ответы (2)


Как описано на странице man 2 fcntl, вы можете использовать F_GETLK для получить идентификатор процесса с конфликтующей блокировкой (если конфликтующая блокировка связана с процессом). Так, например,

/* Return 0 if descriptor locked exclusively, positive PID if
   a known process holds a conflicting lock, or -1 if the
   descriptor cannot be locked (and errno has the reason).
*/
static pid_t  lock_exclusively(const int fd)
{
    struct flock  lock;
    int           err = 0;

    if (fd == -1) {
        errno = EINVAL;
        return -1;
    }

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    if (!fcntl(fd, F_SETLK, &lock))
        return 0;

    /* Remember the cause of the failure */
    err = errno;

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;
    lock.l_pid = 0;
    if (fcntl(fd, F_GETLK, &lock) == 0 && lock.l_pid > 0)
        return lock.l_pid;

    errno = err;
    return -1;
}

Обратите внимание, что fd должен быть открыт для чтения и записи. Я рекомендую использовать open(path, O_RDWR | O_NOCTTY) или open(path, O_WRONLY | O_NOCTTY). Закрытие любого файлового дескриптора для того же файла снимет блокировку.

Кто-то может сказать, что переустановка lock участников перед вторым fcntl() вызовом не нужна, но я бы предпочел проявить осторожность.

Что касается того, как сообщить об этом, я бы просто использовал

int    fd;
pid_t  p;

fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) {
    fprintf(stderr, "%s: Cannot open file: %s.\n",
                    path, strerror(errno));
    exit(EXIT_FAILURE);
}

p = lock_exclusively(fd);
if (p < 0) {
    fprintf(stderr, "%s: Cannot lock file: %s.\n",
                    path, strerror(errno));
    exit(EXIT_FAILURE);
} else
if (p > 0) {
    fprintf(stderr, "%s: File is already locked by process %ld.\n",
                    path, (long)p);
    exit(EXIT_FAILURE);
}

/* fd is now open and exclusive-locked. */

Пользователь всегда может запустить, например. ps -o cmd= -p PID, чтобы узнать, что это за команда (или вы можете попробовать прочитать /proc/PID/cmdline в Linux).

person Nominal Animal    schedule 17.07.2018
comment
Спасибо. часть if (fcntl (fd, F_GETLK, & lock) == 0 && lock.l_pid ›0) отлично работает! - person RLe; 18.07.2018

Из примера кода:

printf ("locking\n");
/* Initialize the flock structure. */
memset (&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
/* Place a write lock on the file. */
fcntl (fd, F_SETLKW, &lock);

printf ("locked; hit Enter to unlock... ");

Вам необходимо изменить fcntl (fd, F_SETLKW, &lock); на:

if (fcntl (fd, F_SETLK, &lock) == -1) {
  printf ("File is locked by pid %i\n", lock.l_pid);
  return 0;
}

Команда F_SETLKW блокируется, если не может получить блокировку. F_SETLK вернется, если не сможет получить блокировку. На самом деле код также должен проверять errno == EACCESS или errno == EAGAIN после получения возвращаемого значения -1.

person Tom D    schedule 17.07.2018
comment
Привет, если я изменю код таким образом, я распечатаю его, поскольку файл заблокирован pid 0. Кроме того, оба процесса блокируются одновременно. - person RLe; 18.07.2018