一、什么是文件锁定
对于锁这个字,你们一定不会陌生,由于我们生活中就存在着大量的锁,它们各个方面发挥着它的作用,现今世界中的锁的功能都可归结为一句话,就是制止个别人做个别事,比如,锁具就是制止不仅屋主之外的人步入这个房屋,你步入不到这个房屋,也就不能使用房屋上面的东西。
而由于程序常常须要共享数据,而这一般又是通过文件来实现的,试想一个情况深度linux,A进程正在对一个文件进行写操作,而另一个程序B须要对同一个文件进行读操作,并以读取到的数据作为自己程序运行时所须要的数据,这会发生哪些情况呢?进程B可能会读到错乱的数据,由于它并不知道另一个进程A正在改写这个文件中的数据。
为了解决类似的问题,就出现了文件锁定,简单点来说,这是文件的一种安全的更新方法,当一个程序正在对文件进行写操作时,文件才会步入一种暂时状态,在这个状态下,假如另一个程序尝试读这个文件,它还会手动停出来等待这个状态结束。Linux系统提供了好多特点来实现文件锁定,其中最简单的方式就是以原子操作的方法创建锁文件。
用回之前的反例就是,文件锁就是当文件在写的时侯,制止其他的须要写或则要读文件的进程来操作这个文件。
二、创建锁文件
创建一个锁文件是极其简单的,我们可以使用open系统调用来创建一个锁文件,在调用open时oflags参数要降低参数O_CREAT和O_EXCL标志,如file_desc=open("/tmp/LCK.test",O_RDWR|O_CREAT|O_EXCL,0444);就可以创建一个锁文件/tmp/LCK.test。O_CREAT|O_EXCL,可以确保调用者可以创建出文件,使用这个模式可以避免两个程序同时创建同一个文件,假如文件(/tmp/LCK.test)早已存在,则open调用都会失败linux 文件锁,返回-1。
假如一个程序在它执行时,只须要独占某个资源一段很短的时间,这个时间段(或代码区)一般被称作临界区,我们须要在步入临界区之前使用open系统调用创建锁文件,之后在退出临界区时用unlink系统调用删掉这个锁文件。
注意:锁文件只是充当一个指示器的角色,程序间须要通过互相协作来使用它们,也就是说锁文件只是建议锁,而不是强制锁,并不会真正制止你读写文件中的数据。
可以瞧瞧下边的事例:源文件文件名为filelock1.c,代码如下:
#includeunistd.h#includestdlib.h#includestdio.h#includefcntl.h#includeerrno.hintmain(){constchar*lock_file="/tmp/LCK.test1";intn_fd=-1;intn_tries=10;while(n_tries--){//创建锁文件n_fd=open(lock_file,O_RDWR|O_CREAT|O_EXCL,0444);if(n_fd==-1){//创建失败printf("%d-Lockalreadypresent",getpid());sleep(2);}else{//创建成功printf("%d-Ihaveexclusiveaccess",getpid());sleep(1);close(n_fd);//删掉锁文件,释放锁unlink(lock_file);sleep(2);}}return0;}
同时运行同一个程序的两个实例,运行结果为:

从运行的结果可以看出两个程序交叉地对对文件进行锁定,并且真实的操作却是,每次调用open函数去检测/tmp/LCK.test1这个文件是否存在,假如存在open调用就失败,显示有进程早已把这个文件锁定了,假如这个文件不存在,就创建这个文件,并显示许可信息。并且这些做法有一定的缺憾,我们可以看见文件/tmp/LCK.test1被创建了好多次,也被unlink删掉了好多次,也就是说我们不能使用早已事先有数据的文件作为这些锁文件,由于假如文件早已存在,则open调用总是失败。
给我的觉得是,这更像是一种对进程工作的协调性安排,更像是二补码讯号量的作用,文件存在为0,不存在为1,而不是真正的文件锁定。
三、区域锁定
我们还有一个问题,就是假如同一个文件有多个进程须要对它进行读写,而一个文件同一时间只能被一个进程进行写操作,并且多个进程读写的区域互不相关,假如总是要等一个进程写完其他的进程能够对其进行读写,效率又太低,这么是否可以让多个进程同时对文件进行读写以增强数据读写的效率呢?
为了解决前面提及的问题,和出现在第二点中的问题,即不能把文件锁定到指定的已存在的数据文件上的问题,我们提出了一种新的解决方案,就是区域锁定。
简单点来说,区域锁定就是,文件中的某个部份被锁定了,但其他程序可以访问这个文件中的其他部份。
但是,区域锁定的创建和使用都比前面说的文件锁定复杂好多。
1、创建区域锁定
在Linux上为实现这一功能,我们可以使用fcntl系统调用和lockf调用,然而下边以fcntl系统调用来讲解区域锁定的创建。
fctnl的函数原理为:
intfctnl(intfildes,intcommand,...);
它对一个打开的文件描述进行操作,并能按照command参数的设置完成不同的任务,它有三个可选的任务:F_GETLK,F_SETLK,F_SETLKW,至于这三个参数的意义下边再深究。而当使用这种命令时,fcntl的第三个参数必须是一个指向flock结构的表针,所以在实际应用中,fctnl的函数原型通常为:intfctnl(intfildes,intcommand,structflock*flock_st);
2、flock结构
确切来说,flock结构依赖具体的实现,并且它起码包括下边的成员:
shortl_type;文件锁的类型,对应于F_RDLCK(读锁,也叫共享锁),F_UNLCK(解锁,也叫消除锁),F_WRLCK(写锁,也叫独占锁)中的一个。
shortl_whence;从文件的那个相对位置开始估算,对应于SEEK_SET(文件头),SEEK_CUR(当前位置),SEEK_END(文件尾)中的一个。
off_tl_start;从l_whence开始的第l_start个字节开始估算。
off_tl_len;锁定的区域的宽度。
pid_tl_pid;拿来记录参持有锁的进程。
成员l_whence、l_start和l_len定义了一个文件中的一个区域,即一个连续的字节集合,比如:
structflockregion;
region.l_whence=SEEK_SET;
region.l_start=10;
region.l_len=20;
则表示fcntl函数操作锁定的区域为文件头开始的第10到29个字节之间的这20个字节。
3、文件锁的类型
从里面的flock的成员l_type的取值我们可以晓得,文件锁的类型主要有三种,这儿对她们进行详尽的解说。
F_RDLCK:
从它的名子我们就可以晓得,它是一个读锁,也叫共享锁。许多不同的进程可以拥有文件同一(或重叠)区域上的读(共享)锁。并且只要任一进程拥有一把读(共享)锁,这么就没有进程可以再获得该区域上的写(独占)锁。为了获得一把共享锁,文件必须以“读”或“读/写”方式打开。
简单点来说就是,当一个进程在读文件中的数据时,文件中的数据不能被改变或改写,这是为了避免数据被改变而使读数据的程序读取到错乱的数据,而文件中的同一个区域能被多个进程同时读取,这是容易理解的,由于读不会破坏数据,或则说读操作不会改变文件的数据。
F_WRLCK:
从它的名子,我们就可以晓得,它是一个写锁,也叫独占锁。只有一个进程可以在文件中的任一特定区域拥有一把写(独占)锁。一旦一个进程拥有了这样一把锁,任何其他进程都未能在该区域上获得任何类型的锁。为了获得一把写(独占)锁,文件也必须以“读”或“读/写”方式打开。
简单点来说,就是一个文件同一区域(或重叠)区域进在同一时间,只能有一个进程能对其进行写操作,而且在写操作进行期间,其他的进程不能对该区域进行读取数据。这个要求是其实易见的,由于假如两个进程同时对一个文件进行写操作,都会使文件的内容错乱上去,而因为写时会改变文件中的数据,所以它也不容许其他进程对文件的数据进行读取和删掉文件等操作。
F_UNLCK:
从它的名子就可以晓得,它用于把一个锁定的区域解锁。
4、不同的command的意义
在后面说到fcntl函数的command参数时,说了三个命令选项,这儿将对它们进行详尽的解说。
F_GETLK命令,它用于获取fildes(fcntl的第一个参数)打开的文件的锁信息,它不会尝试去锁定文件,调用进程可以把自己想创建的锁类型信息传递给fcntl,函数调用都会返回将会制止获取锁的任何信息,即它可以测试你想创建的锁是否能成功被创建。fcntl调用成功时,返回非-1,假若锁恳求可以成功执行,flock结构将保持不变,假如锁恳求被制止,fcntl会用相关的信息覆盖flock结构。失败时返回-1。
所以,假若调用成功,调用程序则可以通过检测flock结构的内容来判定其是否被更改过,来检测锁恳求能够被成功执行,而又由于l_pid的值会被设置成拥有锁的进程的标示符,所以大多数情况下,可以通过检测这个数组是否发生变化来判定flock结构是否被更改过。
使用F_GETLK的fcntl函数调用后会立刻返回。
举个反例来说,比如,有一个flock结构的变量,flock_st,flock_st.l_pid=-1,文件的第10~29个字节早已存在一个读锁,文件的第40~49个字节中早已存在一个写锁,则调用fcntl时,假若用F_GETLK命令,来测试在第10~29个字节中是否可以创建一个读锁,由于这个锁可以被创建,所以,fcntl返回非-1,同时,flock结构的内容也不会改变,flock_st.l_pid=-1。而假如我们测试第40~49个字节中是否可以创建一个写锁时,因为这个区域早已存在一个写锁,测试失败,而且fcntl还是会返回非-1,只是flock结构会被这个区域相关的锁的信息覆盖了,flock_st.l_pid为拥有这个写锁的进程的进程标示符。
F_SETLK命令,这个命令企图对fildes指向的文件的某个区域加锁或解锁,它的功能按照flock结构的l_type的值而定。而对于这个命令来说,flock结构的l_pid数组是没有意义的。假如加锁成功,返回非-1,假若失败,则返回-1。使用F_SETLK的fcntl函数调用后会立刻返回。
F_SETLKW命令,这个命令与后面的F_SETLK,命令作用相同,但不同的是,它在难以获取锁时,即测试不能加锁时,会仍然等待直至可以被加锁为止。
5、例子
看了如此多的说明,可能你已然很乱了,就用下边的事例来整清你的思想吧。
源文件名为filelock2.c,用于创建数据文件,并将文件区域加锁,代码如下:
#includeunistd。h#includestdlib。h#includestdio。h#includefcntl。hintmain(){constchar*test_file="test_lock。txt";intfile_desc=-1;intbyte_count=0;char*byte_to_write="A";structflockregion_1;structflockregion_2;intres=0;//打开一个文件描述符file_desc=open(test_file,O_RDWR|O_CREAT,0666);if(!file_desc){fprintf(stderr,"Unabletoopen%sforread/write",test_file);exit(EXIT_FAILURE);}//给文件添加100个‘A’字符的数据for(byte_count=0;byte_count100;++byte_count){write(file_desc,byte_to_write,1);}//在文件的第10~29字节设置读锁(共享锁)region_1。
l_type=F_RDLCK;region_1。l_whence=SEEK_SET;region_1。l_start=10;region_1。l_len=20;//在文件的40~49字节设置写锁(独占锁)region_2。l_type=F_WRLCK;region_2。l_whence=SEEK_SET;region_2。l_start=40;region_2。l_len=10;printf("Process%dlockingfile",getpid());//锁定文件res=fcntl(file_desc,F_SETLK,®ion_1);if(res==-1){fprintf(stderr,"Failedtolockregion1");}res=fcntl(file_desc,F_SETLK,®ion_2);if(res==-1){fprintf(stderr,"Failedtolockregion2");}//让程序休眠一分钟,用于测试sleep(60);printf("Process%dclosingfile",getpid());close(file_desc);exit(EXIT_SUCCESS);}
下边的源文件filelock3.c用于测试上一个文件设置的锁,测试能否对两个区域都加上一个读锁,代码如下:
#includeunistd.h#includestdlib.h#includestdio.h#includefcntl.hintmain(){constchar*test_file="test_lock.txt";intfile_desc=-1;intbyte_count=0;char*byte_to_write="A";structflockregion_1;structflockregion_2;intres=0;//打开数据文件file_desc=open(test_file,O_RDWR|O_CREAT,0666);if(!file_desc){fprintf(stderr,"Unabletoopen%sforread/write",test_file);exit(EXIT_FAILURE);}//设置区域1的锁类型structflockregion_test1;region_test1.l_type=F_RDLCK;region_test1.l_whence=SEEK_SET;region_test1.l_start=10;region_test1.l_len=20;region_test1.l_pid=-1;//设置区域2的锁类型structflockregion_test2;region_test2.l_type=F_RDLCK;region_test2.l_whence=SEEK_SET;region_test2.l_start=40;region_test2.l_len=10;region_test2.l_pid=-1;//
三、解空锁问题
假如我要给在本进程中没有加锁的区域解锁会发生哪些事情呢?而假如这个区域中其他的进程有对其进行加锁又会发生哪些情况呢?
假如一个进程实际并未对一个区域进行锁定,而调用解锁操作也会成功,然而它并不能解其他的进程加在同一区域上的锁。也可以说解锁恳求最终的结果取决于这个进程在文件中设置的任何锁,没有加锁,但对其进行解锁得到的还是没有加锁的状态。
linux怎么锁定用户
我们首先来创建一个linux用户,(如何创建用户查看经验引用),这儿就不说明如何添加用户了,我这儿早已添加一个用户xiaoming,就已xiaoming为例吧
1linux添加管理员用户
请点击输入图片描述
我们通过root用户把xiaoming这个用户给锁定,通过这个命令passwd-lxiaoming
请点击输入图片描述
查看已给xiaoming这个用户的状态是不是早已锁定了。
请点击输入图片描述
我们来登录一下这个用户,还会提示系统拒绝了这个用户的密码linux 文件锁linux 发邮件,
请点击输入图片描述
我们通过passwd-uxiaoming来解锁这个用户,
请点击输入图片描述
解锁用户之后就再度进行登录就可以登录进去了,我们来查询一下这个用户的状态,
请点击输入图片描述
我们来联接测试一个这个用户是否能登入进去,如右图所示才能登入进去了。
请点击输入图片描述
Linux下如何创建锁文件及相关命令
方式锁文件仅仅是充当一个指示器的角色,程序间须要通过互相协作来使用它们。锁文件只是建议性锁,与此对立的是强制性锁。为创建一个用作指示器的文件,我们使用带O_CREATE和O_EXCL标志的open系统调用。这将使我们以一个原子操作同时完成两项工作:确定文件不存在,之后创建它。实现//file:lock.c#include#include#include#include#includeintmain(){intfile_desc;intsave_errno;file_desc=open(/tmp/LockFile.test,O_RDWRO_CREATO_EXCL,0444);if(file_desc0){save_errno=errno;printf(Openfailedwitherroris%dn,save_errno);}else{printf(Opensucceededn);}exit(EXIT_SUCCESS);}第一次运行程序:$lock输出如下:Opensucceeded我们再度运行程序:$lock输出如下:Openfailedwitherroris17剖析:第一次运行程序时,因为文件并不存在,所以执行成功。对于后续的执行,由于文件早已存在而失败了。倘若程序再度执行成功,必须删掉锁文件。在Linux系统中,一般错误号码17代表的是EEXIST,此错误用以表示一个文件已存在。错误号定义在头文件errno.h或(更常见的)它所包含的头文件中。