进程间共享数据的保护,需要进程互斥锁。与线程锁不同,进程锁并没有直接的C库支持,但是在Linux平台,要实现进程之间互斥锁,方法有很多,大家不妨回忆一下你所了解的。下面就是标准C库提供的一系列方案。
1、实现方案
不出意外的话,大家首先想到的应该是信号量(Semaphores)。对信号量的操作函数有两套,一套是Posix标准,另一套是System V标准。
Posix信号量
[cpp] view plaincopyprint?
1. sem_t *sem_open(const char *name, int oflag);
2. sem_t *sem_open(const char *name, int oflag, mode_t mode,unsigned int value);
3. int sem_init(sem_t *sem, int pshared, unsigned int value);
4. int sem_wait(sem_t *sem);
5. int sem_trywait(sem_t *sem);
6. int sem_timedwait(sem_t *sem, const struct timespec*abs_timeout);
7. int sem_close(sem_t *sem);
8. int sem_destroy(sem_t *sem);
9. int sem_unlink(const char *name);
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned intvalue);
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_close(sem_t *sem);
int sem_destroy(sem_t *sem);
int sem_unlink(const char *name);
System V信号量
[cpp] view plaincopyprint?
1. int semget(key_t key, int nsems, int semflg);
2. int semctl(int semid, int semnum, int cmd, ...);
3. int semop(int semid, struct sembuf *sops, unsigned nsops);
4. int semtimedop(int semid, struct sembuf *sops, unsigned nsops, structtimespec *timeout);
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, structtimespec *timeout);
线程锁共享
其实还有另外一个方案:线程锁共享。这是什么呢,我估计了解它的人不多,如果你知道的话,那可以称为Linux开发牛人了。
线程锁就是pthread那一套C函数了:
[html] view plaincopyprint?
1. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
2. intpthread_mutex_destroy (pthread_mutex_t *mutex);
3. intpthread_mutex_trylock (pthread_mutex_t *mutex);
4. int pthread_mutex_lock(pthread_mutex_t *mutex);
5. intpthread_mutex_timedlock (pthread_mutex_t *restrict mutex, const struct timespec*restrict abstime);
6. intpthread_mutex_unlock (pthread_mutex_t *mutex);
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t*mutexattr);
int pthread_mutex_destroy (pthread_mutex_t *mutex);
int pthread_mutex_trylock (pthread_mutex_t *mutex);
int pthread_mutex_lock (pthread_mutex_t *mutex);
int pthread_mutex_timedlock (pthread_mutex_t *restrict mutex, const structtimespec *restrict abstime);
int pthread_mutex_unlock (pthread_mutex_t *mutex);
但是这只能用在一个进程内的多个线程实现互斥,怎么应用到多进程场合呢,被多个进程共享呢?
很简单,首先需要设置互斥锁的进程间共享属性:
[html] view plaincopyprint?
1. int pthread_mutexattr_setpshared(pthread_mutexattr_t*mattr, int pshared);
2. pthread_mutexattr_tmattr;
3. pthread_mutexattr_init(&mattr);
4. pthread_mutexattr_setpshared(&mattr,PTHREAD_PROCESS_SHARED);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared);
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
其次,为了达到多进程共享的需要,互斥锁对象需要创建在共享内存中。
最后,需要注意的是,并不是所有Linux系统都支持这个特性,程序里需要检查是否定义了_POSIX_SHARED_MEMORY_OBJECTS宏,只有定义了,才能用这种方式实现进程间互斥锁。
2、平台兼容性
我们来看看这三套方案的平台移植性。
· 绝大部分嵌入式Linux系统,glibc或者uclibc,不支持_POSIX_SHARED_MEMORY_OBJECTS;
· 绝大部分嵌入式Linux系统,不支持Posix标准信号量;
· 部分平台,不支持System V标准信号量,比如Android。
3、匿名锁与命名锁
当两个(或者多个)进程没有特殊关系(比如父子进程共享)时,我们只能通过约定好的名字来访问同一个锁,这就是命名锁。然而,如果我们有其他途径定位一个锁,那么匿名锁是更好的选择。这三套方案是否都支持匿名锁与命名锁呢?
· Posix信号量
通过sem_open创建命名锁,通过sem_init创建匿名锁,其实sem_init也支持进程内部锁。
· System V信号量
semget中的key参数可以看成是名字,所以支持命名锁。该方案不支持匿名锁。
· 线程锁共享
不支持命名锁,支持匿名锁。
4、缺陷
在匿名锁与命名锁的支持上,一些方案是有不足的,但这还是小问题,更严重的问题是异常状况下的死锁问题。
与多线程环境不一样的是,在多进程环境中,一个进程的异常退出不会影响其他进程,但是如果使用了进程互斥锁呢?假如一个进程获取了互斥锁,但是在访问互斥资源的代码中crash了,或者遇到信号退出了,那么其他等待同一个锁的进程(内部某个线程)就挂死了。在多线程环境中,程序异常整个进程退出,不需要考虑异常时锁的释放,多进程环境则是一个实实在在的问题。
System V信号量通过UNDO方式可以解决该问题。但是如果考虑到平台兼容性等问题,这三个方案仍不能满足需求,我会接着介绍一种更好的方案。
在《进程互斥锁》中,我们看到了实现进程互斥锁的几个常见方案:Posix信号量、System V信号量以及线程锁共享,并且分析了他们的平台兼容性以及严重缺陷。这里要介绍
一种安全且平台兼容的进程互斥锁,它是基于文件记录锁实现的。
1、文件记录锁
UNIX编程的“圣经”《Unix环境高级编程》中有对文件记录锁的详细描述。
下载链接:http://dl02.topsage.com/club/computer/Unix环境高级编程.rar
记录锁(recordlocking)的功能是:一个进程正在读或修改文件的某个部分时,可以阻止其他进程修改同一文件区。对于UNIX,“记录”这个定语也是误用,因为UNIX内核根本没有使用文件记录这种概念。一个更适合的术语可能是“区域锁”,因为它锁定的只是文件的一个区域(也可能是整个文件)。
2、平台兼容性
各种UNIX系统支持的记录锁形式:
系统 |
建议性 |
强制性 |
fcntl |
lockf |
flock |
POSIX.1 |
* |
* |
|||
XPG3 |
* |
* |
|||
SVR2 |
* |
* |
* |
||
SVR3 SVR4 |
* |
* |
* |
* |
|
4.3BSD |
* |
* |
* |
||
4.3BSDReno |
* |
* |
* |
可以看成,记录锁在各个平台得到广泛支持。特别的,在接口上,可以统一于fcntl。
建议性锁和强制性锁之间的区别,是指其他文件操作函数(如open,read、write)是否受记录锁影响,如果是,那就是强制性的记录锁,大部分平台只是建议性的。不过,对实现进程互斥锁而言,这个影响不大。
3、接口描述
[cpp] view plaincopyprint?
1. #include<sys/types.h>
2. #include<unistd.h>
3. #include<fcnt1.h>
4. int fcnt1(int filedes, int cmd, .../* struct flock *flockptr */);
#include <sys/types.h>
#include <unistd.h>
#include <fcnt1.h>
int fcnt1(int filedes, int cmd, .../* struct flock *flockptr */);
对于记录锁,cmd是F_GETLK、F_SETLK或F_SETLKW。第三个参数(称其为flockptr)是一个指向flock结构的指针。
[cpp] view plaincopyprint?
1. struct flock {
2. short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */
3. short l_whence; /* How to interpret l_start: SEEK_SET,SEEK_CUR, SEEK_END */
4. off_t l_start; /*Starting offset for lock */
5. off_t l_len; /* Numberof bytes to lock */
6. pid_t l_pid; /* PID ofprocess blocking our lock
7. };
struct flock {
short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK*/
short l_whence; /* How to interpret l_start: SEEK_SET,SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
};
以下说明fcntl函数的三种命令:
· F_GETLK决定由flockptr所描述的锁是否被另外一把锁所排斥(阻塞)。如果存在一把锁,它阻止创建由flockptr所描述的锁,则这把现存的锁的信息写到flockptr指向的结构中。如果不存在这种情况,则除了将ltype设置为F_UNLCK之外,flockptr所指向结构中的其他信息保持不变。
· F_SETLK设置由flockptr所描述的锁。如果试图建立一把按上述兼容性规则并不允许的锁,则fcntl立即出错返回,此时errno设置为EACCES或EAGAIN。
· F_SETLKW这是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。如果由于存在其他锁,那么按兼容性规则由flockptr所要求的锁不能被创建,则调用进程睡眠。如果捕捉到信号则睡眠中断。
4、实现方案
如何基于记录锁实现进程互斥锁?
1、需要一个定义全局文件名,这个文件名只能有相关进程使用。这需要在整个系统做一个规划。
2、规定同一个进程互斥锁对应着该文件的一个字节,字节位置称为锁的编号,这样可以用一个文件实现很多互斥锁。
3、编号要有分配逻辑,文件中要记录已经分配的编号,这个逻辑也要保护,所以分配0号锁为系统锁。
4、为了实现命名锁,文件中要记录名称与编号对应关系,这个对应关系的维护也需要系统锁保护。
这些逻辑都实现在一个FileLocks类中:
[cpp] view plaincopyprint?
1. class FileLocks
2. {
3. public:
4. FileLocks();
5. ~FileLocks();
6. size_t alloc_lock();
7. size_t alloc_lock(std::string const & keyname);
8. void lock(size_t pos);
9. bool try_lock(size_t pos);
10.void unlock(size_t pos);
11.void free_lock(size_t pos);
12.voidfree_lock(std::string const & keyname);
13.private:
14.int m_fd_;
15.};
16.
17.inline FileLocks &global_file_lock()
18.{
19.static FileLocksg_fileblocks( "process.filelock" );
20.return g_fileblocks;
21.}
class FileLocks
{
public:
FileLocks();
~FileLocks();
size_t alloc_lock();
size_t alloc_lock(std::stringconst & keyname);
void lock(size_t pos);
bool try_lock(size_t pos);
void unlock(size_t pos);
void free_lock(size_t pos);
void free_lock(std::string const& keyname);
private:
int m_fd_;
};
inline FileLocks & global_file_lock()
{
static FileLocks g_fileblocks("process.filelock" );
return g_fileblocks;
}
这里用了一个FileLocks全局单例对象,它对应的文件名是“/tmp/filelock”,在FileLocks中,分别用alloc()和alloc(keyname)分配匿名锁和命名锁,用free_lock删除锁。free_lock(pos)删除匿名锁,free_lock(keyname)删除命名锁。
对锁的使用通过lock、try_lock、unlock实现,他们都带有一个pos参数,代表锁的编号。
有了FileLocks类作为基础,要实现匿名锁和命名锁就很简单了。
4.1、匿名锁
[cpp] view plaincopyprint?
1. class FileMutex
2. {
3. public:
4. FileMutex()
5. :m_lockbyte_(global_file_lock().alloc_lock())
6. {
7. }
8. ~FileMutex()
9. {
10.global_file_lock().free_lock(m_lockbyte_);
11.}
12.void lock()
13.{
14.global_file_lock().lock(m_lockbyte_);
15.}
16.bool try_lock()
17.{
18.returnglobal_file_lock().try_lock(m_lockbyte_);
19.}
20.void unlock()
21.{
22.global_file_lock().unlock(m_lockbyte_);
23.}
24.protected:
25.size_t m_lockbyte_;
26.};
class FileMutex
{
public:
FileMutex()
:m_lockbyte_(global_file_lock().alloc_lock())
{
}
~FileMutex()
{
global_file_lock().free_lock(m_lockbyte_);
}
void lock()
{
global_file_lock().lock(m_lockbyte_);
}
bool try_lock()
{
returnglobal_file_lock().try_lock(m_lockbyte_);
}
void unlock()
{
global_file_lock().unlock(m_lockbyte_);
}
protected:
size_t m_lockbyte_;
};
需要注意的是,进程匿名互斥锁需要创建在共享内存上。只需要也只能某一个进程(比如创建共享内存的进程)调用构造函数,其他进程直接使用,同样析构函数也只能调用一次。
4.2、命名锁
命名锁只需要构造函数不同,可以直接继承匿名锁实现
[cpp] view plaincopyprint?
1. class NamedFileMutex
2. : public FileMutex
3. {
4. public:
5. NamedFileMutex(std::stringconst & key)
6. :m_lockbyte_(global_file_lock().alloc_lock(key))
7. {
8. }
9. ~NamedFileMutex()
10.{
11.m_lockbyte_ = 0;
12.}
13.};
class NamedFileMutex
: public FileMutex
{
public:
NamedFileMutex(std::string const& key)
: m_lockbyte_(global_file_lock().alloc_lock(key))
{
}
~NamedFileMutex()
{
m_lockbyte_ = 0;
}
};
需要注意,命名锁不住析构时删除,因为可能多个对象共享该锁。
linux下文件锁定有两种:一种是以原子操作方式创建锁文件;另一种是允许锁定文件的一部分,从而独享对这一部分内容的访问。
1、锁文件
许多应用程序只需要能够针对某个资源创建一个锁文件,然后其他程序通过检查这个文件来判断它们是否被允许访问这个资源。创建锁文件使用fcntl.h头文件(楼主机器上位于/usr/include下)定义的open系统调用,并带上O_CREAT和O_EXCL标志。这样就以原子操作完成两项工作:确定文件不存在,然后创建
[cpp] view plaincopyprint?
1. #include<unistd.h>
2. #include <stdlib.h>
3. #include<stdio.h>
4. #include<fcntl.h>
5. #include<errno.h>
6.
7. int main()
8. {
9. int file_desc;
10.int save_errno;
11.
12.file_desc =open("/tmp/LCK.test", O_RDWR | O_CREAT | O_EXCL, 0444);
13.if (file_desc == -1) {
14.save_errno = errno;
15.printf("Openfailed with error %d\n", save_errno);
16.}
17.else {
18.printf("Opensucceeded\n");
19.}
20.exit(EXIT_SUCCESS);
21.}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int file_desc;
int save_errno;
file_desc =open("/tmp/LCK.test", O_RDWR | O_CREAT | O_EXCL, 0444);
if (file_desc == -1) {
save_errno = errno;
printf("Open failedwith error %d\n", save_errno);
}
else {
printf("Opensucceeded\n");
}
exit(EXIT_SUCCESS);
}
不过在第二次运行以上程序的时候,会提示错误:open failed with error 17(文件已存在,错误码在/usr/include/asm-generic/error-base.h),如果想让程序再次执行成功,就必须删除那个锁文件。在c语言调用中,我们可以使用unlink函数(定义于/usr/include/unistd.h)。另外,以上的代码也不是很合理的,只有open没有close,正常情况下,应该加上以下两行:
[cpp] view plaincopyprint?
1. (void)close(file_desc);
2. (void)unlink("/tmp/LCK.test");
(void)close(file_desc);
(void)unlink("/tmp/LCK.test");
关于unlink,可以参考http://bbs.chinaunix.net/thread-1920145-1-1.html的说法。unlink原型如下:
[html] view plaincopyprint?
1. #include <unistd.h>
2. int unlink (__constchar *__name)
#include <unistd.h>
int unlink (__const char *__name)
函数功能:删除目录项,并将由__name所引用文件的链接计数减1,当链接计数为0时,文件被删除。
关于unlink的使用,可以参考《unix环境高级编程》第17章,17.3.2唯一链接,在listen前,先unlink以防文件已经存在,accept后再unlink,防止下次调用处问题。曾经楼主在ACE的unix域套接字ACE_LSOCK上遇到addressin use,其实就是锁文件已存在没有删除的问题。
2、区域锁定
区域锁定出现,是因为锁文件方式并不适用于访问大型的共享文件。如果一个大文件,由一个程序写入数据,但却由不同的程序同时对这个文件进行更新。处理程序不能等待记录程序结束,所以需要一些协调方法来提供对同一个文件的并发访问。linux提供了2种方法来实现:一是fcntl系统调用和lockf调用。
一个使用fcntl锁定文件的例子如下:
[cpp] view plaincopyprint?
1. #include<unistd.h>
2. #include <stdlib.h>
3. #include<stdio.h>
4. #include<fcntl.h>
5.
6. const char *test_file ="/tmp/test_lock";
7.
8. int main() {
9. int file_desc;
10.int byte_count;
11.char *byte_to_write = "A";
12.struct flock region_1;
13.struct flock region_2;
14.int res;
15.
16./* open a filedescriptor */
17.file_desc =open(test_file, O_RDWR | O_CREAT, 0666);
18.if (!file_desc) {
19.fprintf(stderr,"Unable to open %s for read/write\n", test_file);
20.exit(EXIT_FAILURE);
21.}
22.
23./* put some data inthe file */
24.for(byte_count = 0;byte_count < 100; byte_count++) {
25.(void)write(file_desc,byte_to_write, 1);
26.}
27.
28./* setup region 1, ashared lock, from bytes 10 -> 30 */
29.region_1.l_type =F_RDLCK;
30.region_1.l_whence =SEEK_SET;
31.region_1.l_start = 10;
32.region_1.l_len = 20;
33.
34./* setup region 2, anexclusive lock, from bytes 40 -> 50 */
35.region_2.l_type =F_WRLCK;
36.region_2.l_whence =SEEK_SET;
37.region_2.l_start = 40;
38.region_2.l_len = 10;
39.
40./* now lock the file*/
41.printf("Process%d locking file\n", getpid());
42.res = fcntl(file_desc,F_SETLK, ®ion_1);
43.if (res == -1) fprintf(stderr,"Failed to lock region 1\n");
44.res = fcntl(file_desc,F_SETLK, ®ion_2);
45.if (res == -1)fprintf(stderr, "Failed to lock region 2\n");
46.
47./* and wait for awhile */
48.sleep(60);
49.
50.printf("Process%d closing file\n", getpid());
51.close(file_desc);
52.exit(EXIT_SUCCESS);
53.}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
const char *test_file = "/tmp/test_lock";
int main() {
int file_desc;
int byte_count;
char *byte_to_write ="A";
struct flock region_1;
struct flock region_2;
int res;
/* open a file descriptor */
file_desc = open(test_file,O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, "Unableto open %s for read/write\n", test_file);
exit(EXIT_FAILURE);
}
/* put some data in the file*/
for(byte_count = 0; byte_count< 100; byte_count++) {
(void)write(file_desc,byte_to_write, 1);
}
/* setup region 1, a sharedlock, from bytes 10 -> 30 */
region_1.l_type = F_RDLCK;
region_1.l_whence = SEEK_SET;
region_1.l_start = 10;
region_1.l_len = 20;
/* setup region 2, anexclusive lock, from bytes 40 -> 50 */
region_2.l_type = F_WRLCK;
region_2.l_whence = SEEK_SET;
region_2.l_start = 40;
region_2.l_len = 10;
/* now lock the file */
printf("Process %d lockingfile\n", getpid());
res = fcntl(file_desc, F_SETLK,®ion_1);
if (res == -1) fprintf(stderr,"Failed to lock region 1\n");
res = fcntl(file_desc, F_SETLK,®ion_2);
if (res == -1) fprintf(stderr,"Failed to lock region 2\n");
/* and wait for a while */
sleep(60);
printf("Process %d closingfile\n", getpid());
close(file_desc);
exit(EXIT_SUCCESS);
}
程序首先创建一个文件,然后以读写方式打开,添加一些数据。接着在文件中设置2个区域,第一个是0-30,用读(共享)锁;第二个是40-50,用写(独占)锁,然后调用fcntl来锁定这2个区域。
fcntl参数提供了3个命令选项
F_GETLK、F_SETLK、F_SETLKW,l_type提供的选项有F_RDLCK、F_UNLCK、F_WRLCK,分别为读锁,解锁,写锁
测试锁的程序如下:
[cpp] view plaincopyprint?
1. #include<unistd.h>
2. #include<stdlib.h>
3. #include<stdio.h>
4. #include<fcntl.h>
5.
6.
7. const char *test_file ="/tmp/test_lock";
8. #define SIZE_TO_TRY 5
9.
10.voidshow_lock_info(struct flock *to_show);
11.
12.
13.int main() {
14.int file_desc;
15.int res;
16.struct flockregion_to_test;
17.int start_byte;
18.
19./* open a filedescriptor */
20.file_desc =open(test_file, O_RDWR | O_CREAT, 0666);
21.if (!file_desc) {
22.fprintf(stderr,"Unable to open %s for read/write", test_file);
23.exit(EXIT_FAILURE);
24.}
25.
26.for (start_byte = 0;start_byte < 99; start_byte += SIZE_TO_TRY) {
27./* set up the regionwe wish to test */
28.region_to_test.l_type= F_WRLCK;
29.region_to_test.l_whence= SEEK_SET;
30.region_to_test.l_start= start_byte;
31.region_to_test.l_len =SIZE_TO_TRY;
32.region_to_test.l_pid =-1;
33.
34.printf("TestingF_WRLCK on region from %d to %d\n",
35.start_byte, start_byte+ SIZE_TO_TRY);
36.
37./* now test the lockon the file */
38.res = fcntl(file_desc,F_GETLK, ®ion_to_test);
39.if (res == -1) {
40.fprintf(stderr,"F_GETLK failed\n");
41.exit(EXIT_FAILURE);
42.}
43.if(region_to_test.l_pid != -1) {
44.printf("Lockwould fail. F_GETLK returned:\n");
45.show_lock_info(®ion_to_test);
46.}
47.else {
48.printf("F_WRLCK -Lock would succeed\n");
49.}
50.
51./* now repeat the testwith a shared (read) lock */
52.
53./* set up the regionwe wish to test */
54.region_to_test.l_type= F_RDLCK;
55.region_to_test.l_whence= SEEK_SET;
56.region_to_test.l_start= start_byte;
57.region_to_test.l_len =SIZE_TO_TRY;
58.region_to_test.l_pid =-1;
59.
60.printf("TestingF_RDLCK on region from %d to %d\n",
61.start_byte, start_byte+ SIZE_TO_TRY);
62.
63./* now test the lockon the file */
64.res = fcntl(file_desc,F_GETLK, ®ion_to_test);
65.if (res == -1) {
66.fprintf(stderr,"F_GETLK failed\n");
67.exit(EXIT_FAILURE);
68.}
69.if(region_to_test.l_pid != -1) {
70.printf("Lockwould fail. F_GETLK returned:\n");
71.show_lock_info(®ion_to_test);
72.}
73.else {
74.printf("F_RDLCK -Lock would succeed\n");
75.}
76.
77.} /* for */
78.
79.close(file_desc);
80.exit(EXIT_SUCCESS);
81.}
82.
83.voidshow_lock_info(struct flock *to_show) {
84.printf("\tl_type%d, ", to_show->l_type);
85.printf("l_whence%d, ", to_show->l_whence);
86.printf("l_start%d, ", (int)to_show->l_start);
87.printf("l_len %d,", (int)to_show->l_len);
88.printf("l_pid%d\n", to_show->l_pid);
89.}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
const char *test_file = "/tmp/test_lock";
#define SIZE_TO_TRY 5
void show_lock_info(struct flock *to_show);
int main() {
int file_desc;
int res;
struct flock region_to_test;
int start_byte;
/* open a file descriptor */
file_desc = open(test_file,O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, "Unableto open %s for read/write", test_file);
exit(EXIT_FAILURE);
}
for (start_byte = 0; start_byte< 99; start_byte += SIZE_TO_TRY) {
/* set up the region wewish to test */
region_to_test.l_type =F_WRLCK;
region_to_test.l_whence =SEEK_SET;
region_to_test.l_start =start_byte;
region_to_test.l_len =SIZE_TO_TRY;
region_to_test.l_pid =-1;
printf("Testing F_WRLCKon region from %d to %d\n",
start_byte,start_byte + SIZE_TO_TRY);
/* now test the lock onthe file */
res = fcntl(file_desc,F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr,"F_GETLK failed\n");
exit(EXIT_FAILURE);
}
if (region_to_test.l_pid !=-1) {
printf("Lock wouldfail. F_GETLK returned:\n");
show_lock_info(®ion_to_test);
}
else {
printf("F_WRLCK -Lock would succeed\n");
}
/* now repeat the testwith a shared (read) lock */
/* set up the region wewish to test */
region_to_test.l_type =F_RDLCK;
region_to_test.l_whence =SEEK_SET;
region_to_test.l_start =start_byte;
region_to_test.l_len =SIZE_TO_TRY;
region_to_test.l_pid =-1;
printf("Testing F_RDLCKon region from %d to %d\n",
start_byte, start_byte +SIZE_TO_TRY);
/* now test the lock onthe file */
res = fcntl(file_desc,F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr,"F_GETLK failed\n");
exit(EXIT_FAILURE);
}
if (region_to_test.l_pid !=-1) {
printf("Lock wouldfail. F_GETLK returned:\n");
show_lock_info(®ion_to_test);
}
else {
printf("F_RDLCK -Lock would succeed\n");
}
} /* for */
close(file_desc);
exit(EXIT_SUCCESS);
}
void show_lock_info(struct flock *to_show) {
printf("\tl_type %d,", to_show->l_type);
printf("l_whence %d,", to_show->l_whence);
printf("l_start %d, ",(int)to_show->l_start);
printf("l_len %d, ",(int)to_show->l_len);
printf("l_pid %d\n",to_show->l_pid);
}
(以上大部分内容来自《linux程序设计》)