2011-09-06 36 views
4

我有不同的进程在Linux中同时访问命名管道,我想让这个访问互斥。如何在Linux和C中使用文件作为互斥体?

我知道有可能实现使用共享内存区域中的互斥锁,但这是一种家庭作业,我有一些限制。

因此,我想到的是在文件上使用锁定原语来实现互斥;我做了一些尝试,但我无法使它工作。

这是我的尝试:

flock(lock_file, LOCK_EX) 

// critic section 

flock(lock_file, LOCK_UN) 

不同的项目将使用不同的文件描述符,但指的是同一个文件。 是否有可能实现类似的目标?你能举一些例子吗?

+2

不。使用互斥锁。 –

+0

我无法使用互斥体!我不想让事情复杂化,但是这必须以这种方式完成 – Simone

+2

也许你应该解释为什么要使用文件而不是真正的互斥体...... – Macmade

回答

3

你的例子和你使用flock (2)一样好(毕竟,这只是一个“顾问”锁(也就是说根本不是锁))。在我的Mac OS X系统上的手册页有几个可能重要的附带条件:

锁在文件上,而不是文件描述符。也就是说,通过dup(2)或fork(2)复制的文件描述符不会导致锁的多个实例,而是多次引用单个锁。如果一个进程上的文件叉持有锁和孩子明确地解锁文件,该 母公司就失去了锁定

进程阻塞,正在等待一个锁可以通过信号唤醒。

两者都提出了可能失败的方法。


//本来是一个评论,但我想在一些长度引述该名男子页

4

标准锁文件技术使用O_EXCL等选项调用open()来尝试创建文件。您使用锁存储进程的PID,因此您可以确定进程是否仍然存在(使用kill()进行测试)。你必须担心并发 - 很多。

步骤:

  • 基于FIFO的名称确定锁定文件的名称
  • 如果使用如果其他进程存在,它存在
    • 存在
    • 检查是否处理
    • 打开锁文件,它已控制(退出时出错,或等待退出)
    • 如果没有其他进程,则删除锁定文件
  • 此时,锁定文件在上次检查时不存在。
  • 尝试使用其他选项中的open()O_EXCL来创建它。
  • 如果有效,您的流程创建了文件 - 您有权继续。
  • 将您的PID写入文件;关闭它。
  • 打开FIFO - 使用它。
  • 完成后(atexit()?)删除锁定文件。

担心如果打开锁定文件并且不读取PID会发生什么......是另一个进程是否创建了它并且尚未将其PID写入其中,还是在此之前死亡?可能最好退后一步 - 关闭文件并重试(可能在随机化后的nanosleep())。如果您多次获取空文件(比如说连续3次),则假定进程已死,并删除锁定文件。

当FIFO打开时,您可以考虑拥有该文件的进程在该文件上保留一个咨询锁。如果锁不存在,则该过程已经死亡。在打开文件和应用锁之间仍然存在TOCTOU(检查时间,使用时间)漏洞窗口。

仔细查看系统上的open()手册页,查看是否有任何其他选项可以帮助您。有时,进程使用目录(mkdir())而不是文件,因为甚至root也不能创建给定目录名称的第二个实例,但是如果知道资源打开的进程的PID,则会遇到问题等。

+0

另一种方式?我也考虑过使用第三个fifo,因为fifo阻止读者在空的时候......这可能吗? – Simone

+0

我不会说这是不可能的,但是使用FIFO来获得对另一个FIFO的独占访问感觉不对,我不知道它是如何工作的。但我没有花太多时间思考它是如何工作的。 –

+0

这种方法是bugprone:PID不能保证是唯一的,可以重复使用。我不明白为什么在打开文件和锁定文件之间会出现TUCTOU。为什么不使用在程序退出时自动释放的'flock'? –

4

我肯定会推荐使用一个实际的互斥(正如在评论中有人建议) ;例如,pthread库提供an implementation。但是如果你想自己做一个用于教育目的的文件,我建议看一下前面发布的this answer,它描述了在Python中这样做的方法。转换为C,它应该看起来像这样(警告:未经测试的代码,使用风险自负;我的C是生锈的):

// each instance of the process should have a different filename here 
char* process_lockfile = "/path/to/hostname.pid.lock"; 
// all processes should have the same filename here 
char* global_lockfile = "/path/to/lockfile"; 
// create the file if necessary (only once, at the beginning of each process) 
FILE* f = fopen(process_lockfile, "w"); 
fprintf(f, "\n"); // or maybe write the hostname and pid 
fclose(f); 

// now, each time you have to lock the file: 
int lock_acquired = 0; 
while (!lock_acquired) { 
    int r = link(process_lockfile, global_lockfile); 
    if (r == 0) { 
     lock_acquired = 1; 
    } 
    else { 
     struct stat buf; 
     stat(process_lockfile, &buf); 
     lock_acquired = (buf.st_nlink == 2); 
    } 
} 
// do your writing 
unlink(global_lockfile); 
lock_acquired = 0; 
+0

不错,但应该避免忙碌的等待。在BSD/Darwin上,可以使用'kqueue','EVFILT_VNODE'和'EVFILT_PROC'。一些方法也可能在Linux上存在。 –

+0

'link'方法的主要问题是,如果程序以异常方式终止(bug/crash/signal/system shutdown),锁将保持不变。 'flock'没有这个问题。 –