8Linux学习——文件锁定

------文件锁定-------

   当一个程序正在对文件进行写操作时,文件就会进入暂时状态,在这个状态下,如果另外一个程序尝试读这个文件,它就会自动停下等待这个状态的结束。Linux 提供了多种特性来实现文件的锁定。

  Linux的常见锁定文件有两种:

  第一种:以原子操作的方式创建锁文件。所谓“原子操作”就是创建锁文件时,系统将不允许其他的事情发生。这就给程序一种方式来确保它创建的文件是唯一的,而且这个文件不可能被其他程序在同一时刻创建

  第二种:是用文件锁的方式,锁定文件的一部分,从而可以独享文件这一部分读写的访问

  锁文件只是建议锁,不是强制锁。

1.创建锁文件

实验一:


///lock1.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

int main()
{
	int fd;
	fd = open("./LCK.test",O_RDWR | O_CREAT | O_EXCL,0444); "tmp/LCK.test",可以把创建的文件保存
到临时文件目录里面,系统会自动删除的
	if(fd == -1)	printf("Open error:%m\n"),exit(-1);
	else
	  printf("Open succeeded\n!");
	exit(0);
	
}

实验解析:

  第一次运行,打开文件成功,再次运行,文件已经存在。上程序不是很完美。

实验:协调性锁文件


///lock2.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <error.h>

const char *lock_file = "/tmp/LCK.test2";

int main()
{
	int fd;
	int i = 10;
	while(i--)
	{
		fd = open(lock_file,O_RDWR | O_CREAT | O_EXCL, 0444);
		if( fd == -1)
		{
			printf("%d - Lock already present \n",getpid());
			sleep(3);
		}
		else
		{  // 临界区从这里开始
			printf("%d -I have exclusive access \n",getpid());
			sleep(1);
			close(fd);
			unlink(lock_file);
			sleep(1);  /// 在这里结束
		}
	}
	exit(0);
}


$ rm -f /tmp/LCK.test2 --------- 确保锁文件不存在

$ ./main & ./main   ----- 这个命令在后台运行main 的一个副本,前台运行另一个副本。

实验解析: 通过创建一个唯一的锁文件/tmp/LCK.test2来访问临界资源。当一个进程访问这个文件时,另外一个进程访问这个文件肯定是失败的。如果感觉两个不明显的话,你可以试试多个,

$ ./main  &  ./main  &  ./main  &  ./main  &  ./main ----------------哈哈大笑果可想而知,你要按好多次Ctrl + C

2. 区域锁定

  区域锁定出现,是因为锁文件方式并不适用于访问大型的共享文件。如果一个大文件,由一个程序写入数据,但却由不同的程序同时对这个文件进行更新。处理程序不能等待记录程序结束,所以需要一些协调方法来提供对同一个文件的并发访问。linux提供了2种方法来实现:一是fcntl系统调用和lockf调用。

       #include <unistd.h>
        #include <fcntl.h>

         int fcntl(int fd, int cmd, ... /* arg */ );

说明:fcntl 对一个打开的文件描述符进行操作,并能根据command参数的设置完成不同的任务。

cmd: 

       F_GETLK --------------- 获取fd(第一个参数),打开文件锁信息。

       F_SETLK ----------------对fd指向的文件的某个区域就 加锁 或者 解锁

       F_SETLKW ------------这个命令与SETTLK作用相同,但是在无法获取锁时,等待直到获取成功为止。

这个函数的全称是:

   int  fcntl(

                           int fd;           ///被加锁的文件描述副

                           int  cmd;      /// 锁的操作方式

                            struct flock  *lk;  //  锁的描述

       )

                    返回值:
                            0:加锁成功
                            -1:加锁失败

---------------------------------------------------------------------------

          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(F_GETLK only) */
               ...
           };


--------------------------------------------------------------------------------

l_type成员的取值

F_RDLCK   ----- 共享(或读)锁。

F_UNLCK   ----------- 解锁,用来清楚锁。

F_WRLCK  -----------独占(或写)锁。

 l_whence的取值必须是:(l_whence 定义了 l_start 的相对偏移量

SEEK_SET ----文件头

SEEK_CUR --- 当前位置

SEEK_END ----  文件尾

l_start 是该区域的第一个字节,l_len 参数定义了该区域的字节数。


实验: 使用fcntl 锁定文件


//lock3.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

const char *test_file = "/tmp/test_lock";

int main()
{
	int fd;
	int i;
	char *byte_to_write = "A";
	struct flock region_1;
	struct flock region_2;
	int res;
	// 打开一个文件描述符
	fd = open(test_file,O_RDWR | O_CREAT,0666);
	if(! fd)
	{
		fprintf(stderr,"Unable to open  %s for read/write \n",test_file);
		exit(EXIT_FAILURE);
	}
	// 给文件添加一些数据
	for(i=0; i<100; ++i)
	{
		write(fd,byte_to_write,1);
	}
	// 把文件的10-30字节设为区域1,并在其上设置共享锁
	region_1.l_type = F_RDLCK; // 共享锁
	region_1.l_whence = SEEK_SET;
	region_1.l_start = 10;
	region_1.l_len = 20;
	/// 把文件的40-50 字节设置为区域2,并在其上设置独占锁
	region_2.l_type = F_WRLCK;
	region_2.l_whence = SEEK_SET;
	region_2.l_start = 40;
	region_2.l_len = 10;
	// 现在锁定文件
	printf("Process %d  正在给文件加锁\n",getpid());
	res = fcntl(fd,F_SETLK,&region_1);
	if(res == -1)
	fprintf(stderr,"区域1加锁失败 \n");
	res = fcntl(fd,F_SETLK,&region_2);
	if(res == -1)
	fprintf(stderr,"区域2加锁失败 \n");
	
	sleep(60);
	printf("Process %d c正在关闭文件 \n",getpid());
	close(fd);
	exit(EXIT_SUCCESS);
}


实验解析:程序首先创建一个文件,并以可读可写方式打开它。如下如所示


实验测试文件上的锁


// lock4.c
#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 fd;
	int res;
	struct flock region_to_test;
	int i;
	// 打开一个文件描述符
	fd = open(test_file,O_RDWR | O_CREAT, 0666);
	if( !fd)
	{
		fprintf(stderr," %s 不能打开文件去读去写",test_file);
		exit(EXIT_FAILURE);
	}
	for(i=0; i<99; i +=SIZE_TO_TRY)
	{
		// 设置希望测试的文件区域
		region_to_test.l_type = F_WRLCK;
		region_to_test.l_whence = SEEK_SET;
		region_to_test.l_start = i;
		region_to_test.l_len = SIZE_TO_TRY;
		region_to_test.l_pid = -1; ///持有锁的进程标识符
		
		printf("正在测试的F_WRLCK的区域从 %d 到 %d\n",i,i+SIZE_TO_TRY);
		/// 现在测试文件上的锁
		res = fcntl(fd, F_GETLK,&region_to_test);// 读锁方式操作
		if( res == -1)
		{
		 	 fprintf(stderr, "读锁 失败\n");
		 	 exit(EXIT_FAILURE);
		}
		if(region_to_test.l_pid != -1)
		{
			printf("锁定失败。F_GETLK returned :\n");
			show_lock_info(&region_to_test);
		}
		else
		{
			printf("F_WRLCK - 锁定将会成功\n");
		}
		// 用共享(读)锁重复测试一次,再次设置希望测试的文件区域
		region_to_test.l_type = F_RDLCK;
		region_to_test.l_whence = SEEK_SET;
		region_to_test.l_start  =  i;
		region_to_test.l_len = SIZE_TO_TRY;
		printf("测试F_RDLCK 的区域从%d到 %d \n",i,i+SIZE_TO_TRY);
		
		// 再次测试文件上的锁
		res = fcntl(fd,F_GETLK, &region_to_test);
		if( res == -1)
		{
			fprintf(stderr,"F_GETLK 失败!\n");
			exit(EXIT_FAILURE);
		}  
		if( region_to_test.l_pid != -1)
		{
			printf("加锁失败.F_GETLK 返回:\n");
			show_lock_info(&region_to_test);
		}
		else
		{
			printf("F_RDLCK-加锁成功!\n");
		}
		
	}
	close(fd);
	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);
}


说明: lock4程序是测试lock3的,所以先运行lock4,再运行lock4 来测试锁

命令:

--------------

$ gcc lock3.c -olock3

$ gcc lock4.c -olock4

$ ./lock3 &

$ ./lock4

--------------


总结:文件的锁定使文件的读取更加安全,主要涉及的函数有fcntl .这个函数主要对文件区域的锁定。适合与大型共享文件的锁定。对于此函数的应用,需要注意参数的灵活使用。







 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值