fcntl F_GETLK 总是返回真

Posted

技术标签:

【中文标题】fcntl F_GETLK 总是返回真【英文标题】:fcntl F_GETLK always return true 【发布时间】:2015-05-16 12:46:23 【问题描述】:

我正在尝试使用锁定文件创建单个实例守护程序,但 fcntl() 似乎没有按预期工作...

int creat_lock_file (char * pid_fn)

  struct flock pid_lck = F_WRLCK, SEEK_SET,   0,      0,     0 ;

  /* Open/Create pid file */
  int pid_fd = open (pid_fn, O_CREAT | O_WRONLY, 0640);
  if (pid_fd == -1)
  
    syslog (LOG_ERR, "Unable to open PID file > [Errno: %s]",    strerror(errno));
    return -1;
  

  /* Place write lock on pid file */
  if (fcntl(pid_fd, F_SETLK, &pid_lck) == -1) 
    /* Unhandled error ocured */
    syslog (LOG_ERR, "Unhandled error ocured while locking PID file > [Errno: %s]", strerror(errno));
    close (pid_fd);
    return -1;
  

  /* Write PID to lock file */
  char pid_lock_buf[11];
  sprintf (pid_lock_buf, "%ld\n", (long) getpid ());
  write (pid_fd, pid_lock_buf, strlen (pid_lock_buf)+1);

  return 0;


int get_lock_file_status (char * pid_fn)

  struct flock pid_lck = F_WRLCK, SEEK_SET,   0,      0,     0 ;

  /* Open/Create pid file */
  int pid_fd = open (pid_fn, O_CREAT | O_WRONLY, 0640);
  if (pid_fd == -1)
  
    syslog (LOG_ERR, "Unable to open PID file > [Errno: %s]", strerror(errno));
    return -1;
  

  /* try locking pid file*/
  if(fcntl(pid_fd, F_GETLK, &pid_lck) == -1)
  
    if(errno == EAGAIN || errno == EACCES) /* file already locked, close fd and return -1 */
    
      close (pid_fd);
      return -1;
    
    else /* Unhandled error ocured */
    
      syslog (LOG_ERR, "Unhandled error ocured while locking PID file > [Errno: %s]", strerror(errno));
      close (pid_fd);
      return -1;
    
  

  close (pid_fd);
  return 0;

所以我调用get_lock_file_status 并在它返回-1 时退出以确保没有其他实例正在运行,而不是我做一些事情(fork chdir 等)并调用creat_lock_file 在成功创建一个 pid 文件后创建并锁定一个 pid 文件守护进程...

当编译和运行程序按预期运行时,运行会创建锁定文件并将 pid 写入其中,但是当第二个实例启动时,第二个实例只是打开同一个锁定文件并将它自己的 pid 写入其中!

我做错了什么? get_lock_file_status 中的第二个实例不应该返回 -1 吗?

【问题讨论】:

这似乎是错误的方法。对于编程文件锁,flock() 函数似乎是正确的方法,这个函数,在 linux 上,在 'man 2 flock' 或在:linux.die.net/man/2/flock> 中有详细说明 @user3629249: fcntl(2)F_SET/GET/LK 很好,还有 POSIX,flock(2) 不是(尽管它被广泛使用)。这两种方法有不同的行为w.r.t。锁定继承和其他一些东西。 fcntl() 可以在文件区域上设置锁,flock() 不能。 【参考方案1】:

您以错误的方式检查F_GETLK 的结果。 fcntl(2)F_GETLK 仅在错误时返回 -1。检查是否可以获得锁的正确方法是检查struct flockl_type字段是否设置为F_UNLCK,如下所示:

/* try locking pid file*/
if(fcntl(pid_fd, F_GETLK, &pid_lck) == -1) 
    syslog (LOG_ERR, "Unhandled error ocured while locking PID file > [Errno: %s]", strerror(errno));
    close(pid_fd);
    return -1;

close(pid_fd);
return (pid_lck.l_type == F_UNLCK) ? 0 : -1;

应该可以将 creat_lock_file()get_lock_file_status() 滚动到一个函数中,该函数打开文件(如果文件不存在则创建它),尝试对其设置锁定,并返回锁定是否成功(例如,文件描述符或-1)。

顺便说一句,在将 PID 写入其中之前,您应该将 PID 文件truncate(2) 清零字节。假设您的进程的 PID 为 5,PID 文件中的旧 PID 为 123。写入“5”将使 PID 文件的内容为“523”。将文件截断为零字节可以解决这个问题。 (O_TRUNC 不起作用,因为您在打开文件以测试是否设置了锁定时会清除文件。)

在程序退出时使用unlink(2) 删除 PID 文件也很常见。这样,文件的不存在表明守护程序没有运行(尽管它不是万无一失的,因为进程或系统可能会崩溃)。

【讨论】:

您好,感谢您的回答,当我阅读您的回答时,我想到了您所说的测试文件是否存在不是一个好主意,因为程序可能很粗鲁,没有时间unlink()文件,但是如果我读取文件并尝试查找 pid 以查看是否有任何进程以相同的 pid 运行?这是个好主意吗? @AristosMiliaressis:即使守护进程崩溃并且不删除 PID 文件,使用文件锁定检查守护进程是否正在运行也将起作用。如果守护进程崩溃,那么文件锁会自动移除(因为它与进程绑定),并且新的守护进程实例将能够锁定文件(告诉它没有其他实例在运行)。 用于检查守护进程是否真的在运行,例如shell,检查是否存在与文件中具有相同PID的进程应该可以工作,是的。如果守护进程崩溃并且某些其他进程碰巧获得相同的 PID,它可能失败,但这并不常见。 (您也可以使用ps(1) 检查启动该过程的命令是什么来确定。) 我自己从未在 shell 中完成文件锁定,但这也应该是可能的(例如,如果您希望 shell 脚本执行与您的代码相同的操作)。例如,请参阅此答案:***.com/questions/1715137/…。 @AristosMiliaressis:你找到什么了吗?我稍微编辑了答案。明天我会检查并确保代码在我的机器上运行(尽管它应该通过查看它,前提是您在调用creat_lock_file() 后不要再次调用get_lock_file_status())。

以上是关于fcntl F_GETLK 总是返回真的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的碰撞测试总是返回“真”,为啥图像矩形的位置总是错误的 (0, 0)?

数组上的PHP“如果为真”测试总是返回真?

为啥一个变量对多个值的不等式检查总是返回真?

为啥一个变量对多个值的不等式检查总是返回真?

linux 编程问题 fcntl函数

文件锁的使用[奇牛学院]