只是出于好奇,在 Linux 上实现进程间同步的首选方法是什么?sem*(2)
系统调用家族似乎有一个非常笨拙和过时的界面,而锁定文件的方法有fcntl()
.flock()
和lockf()
.
内部差异是什么(如果有的话),您如何证明使用它们的合理性?
只是出于好奇,在 Linux 上实现进程间同步的首选方法是什么?sem*(2)
系统调用家族似乎有一个非常笨拙和过时的界面,而锁定文件的方法有fcntl()
.flock()
和lockf()
.
内部差异是什么(如果有的话),您如何证明使用它们的合理性?
两者都不。pthread_*
(例如)的实际版本phtread_mutex_t
都允许将变量放在通过创建的共享段中shm_open
。您只需在 init 调用中放置一些额外的参数。
如果不需要,不要使用信号量 ( sem_t
),它们的级别太低并且会被 IO 等中断。
不要滥用文件锁定来进行进程间控制。它不是为此而设计的。特别是,您无法刷新文件元数据(例如锁),因此您永远不知道锁/解锁何时对第二个进程可见。
正如 DarkDust 所指出的,你正在经历丰富的历史中的大量选择。值得我的决策树是这样的:
当一次只有一个进程/线程可以访问时,使用互斥锁。
当两个或更多(但仍然是有限的)进程/线程可以使用资源时,使用信号量。
使用 POSIX 信号量,除非你真的需要 SYSV 信号量具有的东西——例如 UNDO、最后操作的 PID 等。
对文件使用文件锁定,或者如果上述内容在某些方面不符合您的要求。
不同的锁定/信号量实现都在不同的系统上实现。在 System V Unix 上你有semget
/ semop
,POSIX 用sem_init
,sem_wait
和定义了一个不同的实现sem_post
。据我flock
所知,起源于 4.2BSD。
由于它们都获得了一定的意义,Linux 现在支持它们以使移植变得容易。此外,flock
它是一个互斥体(锁定或解锁),但sem*
功能(SysV 和 POSIX)是信号量:它们允许应用程序授予多个并发进程访问权限,例如,您可以允许使用信号量同时访问 4 个进程的资源。您可以使用信号量实现互斥锁,但反之则不行。我记得在 Marc J. Rochkind 的优秀“高级 UNIX 编程”中,他演示了如何通过信号量在进程之间传输数据(非常低效,他这样做只是为了证明可以做到)。但我找不到任何关于效率的可靠信息。
我想这更像是“使用你想要的”。
一个潜在的显着差异可能是资源分配的公平性。我不知道该semget/semop
系列的实现细节,但我怀疑就调度而言,它通常被实现为“传统”信号量。一般来说,我相信释放的线程是在 FIFO 的基础上处理的(第一个等待信号量的线程首先被释放)。我认为文件锁定不会发生这种情况,因为我怀疑(再次只是猜测)处理不是在内核级别执行的。
我有现有的代码来测试用于 IPC 目的的信号量,因此我比较了两种情况(一种 usingsemop
和一种 using lockf
)。我做了一个穷人的测试,然后跑到应用程序的实例。共享信号量用于同步启动。运行 semop 测试时,两个进程几乎同步完成了 300 万次循环。另一方面,lockf 循环就没有那么公平了。一个进程通常会完成,而另一个进程只完成了一半的循环。
semop 测试的循环如下所示。semwait
andsemsignal
函数只是调用的包装器semop
。
ct = myclock();
for ( i = 0; i < loops; i++ )
{
ret = semwait( "test", semid, 0 );
if ( ret < 0 ) { perror( "semwait" ); break; }
if (( i & 0x7f ) == 0x7f )
printf( "\r%d%%", (int)(i * 100.0 / loops ));
ret = semsignal( semid, 0 );
if ( ret < 0 ) { perror( "semsignal" ); break; }
}
printf( "\nsemop time: %d ms\n", myclock() - ct );
两种方法的总运行时间大致相同,尽管有时由于调度的不公平性,lockf 版本实际上总体上更快。一旦第一个进程完成,另一个进程将拥有大约 150 万次迭代的无竞争访问权限,并且运行速度极快。
当运行无竞争(单进程获取和释放锁)时,semop 版本更快。100 万次迭代大约需要 2 秒,而 lockf 版本大约需要 3 秒。
这是在以下版本上运行的:
[]$ uname -r
2.6.11-1.1369_FC4smp