第3章 文件I/O(6)_高级文件操作:文件锁
7. 高级文件操作:文件锁
(1)文件锁分类
分类依据 |
类型 |
说明 |
按功能分 |
共享读锁 |
文件描述符必须读打开 一个进程上了读锁,共它进程也可以上读锁进行读取 |
独占写锁 |
文件描述符必须写打开 一个进程上了写锁,其它进程就不能上写锁和读锁进行读写操作 |
|
按类型分 |
建议锁 |
要求上锁文件的进程都要检测是否有锁的存在,并尊重巳有的锁。这也是Linux默认的锁类型。 |
强制锁 |
由内核和系统执行的锁。 |
(2)fcntl函数
头文件 |
#include<unistd.h> |
函数 |
int fcntl(int fd, int cmd, struct flock* lock); |
返回值 |
若成功返回新文件描述符,出错返回-1 |
功能 |
若成功则依赖于cmd,出错为-1。 |
参数 |
cmd:F_SETLK、F_GETLK和F_SETLKW(阻塞版的fcntl函数) struct flock{ |
备注 |
①l_type:表示锁的类型,F_RDLCK为共享读锁,F_WRLCK为独占写锁,F_UNLCK解锁。 ②l_whence、l_start:要加锁或解锁的区域起始地址,则这两者共同决定。注意,区域的起始地址可以在文件尾端或越过尾端开始,但不能从文件起始位置之前开始。 ③l_len:表示区域的长度。如果为0,则表示锁的区域从其起点(由l_start和l_whence共同决定)开始,直到最大可能位置为止。也就是不管整个文件。为了锁整个文件,通常的作法是l_whence为SEEK_SET,l_start和l_len都设为0 ④fcntl函数可以作用于建议锁也就可以作用于强制锁。 |
(3)建议锁和强制锁
①建议性锁:要求每个使用文件的进程都要主动检查该文件是否有锁存在(可以通过fcntl函数通过F_GETLK来检查)。如果有,则合作的进程必须主动加锁,以防止对文件的破坏。如果一个进程加锁,而其它的进程不加锁,则这个锁无法约束不加锁进程对文件的进行读写操作的行为。因为锁只是建议性存在,并不强制执行。为了让这个锁起作用,要求其它进程要尊重这个锁的存在,主动去加锁。Linux默认是采用建议性锁,它们依靠程序员遵守这个约定。(要么合作的进程都加锁,要么都不加锁,否则锁的作用无法发挥出来!)
②强制性锁:当文件被上锁来进行读写操作时,在锁定该文件的进程释放该锁之前,内核会强制阻止任何对该文件的读或写违规访问,每次读或写访问都得检查锁是否存在。也就是强制性锁机制,让锁变得名副其实,真正达到了锁的效果。而不是像建议性锁机制,当其它进程不加锁时,它只是个一只纸老虎!
(4)开启强制性锁
①重新挂载文件系统所在的分区:mount -o remount,mand /dev/sda5 /
②设置文件的组ID位,关闭组执行位:chmod g+s,g-x file.txt
【编程实验】多进程同时写同一文件
//io.h
#ifndef __IO_H__
#define __IO_H__
#include <sys/types.h> extern void copy(int fdin, int fdout);//文件复制
extern void set_fl(int fd, int flag); //设置文件状态标志
extern void clr_fl(int fd, int flag); //取消文件状态标志 //文件锁
extern int lock_reg(int fd, int cmd, short type,
off_t offset, short whence, off_t length);
#define READ_LOCKW(fd, offset, whence, length) \
lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, length)
#define READ_LOCK(fd, offset, whence, length) \
lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, length) #define WRITE_LOCKW(fd, offset, whence, length) \
lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, length)
#define WRITE_LOCK(fd, offset, whence, length) \
lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, length) #define UNLOCKW(fd, offset, whence, length) \
lock_reg(fd, F_SETLKW, F_UNLCK, offset, whence, length)
#define UNLOCK(fd, offset, whence, length) \
lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, length)
#endif
//io.c
#include "io.h"
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> //编译命令:gcc -o obj/io.o -Iinclude -c src/io.c #define BUFFER_LEN 1024 //与分区文件块大小一致。可以通过
//tune2fs -l /dev/sda1命令查看 void copy(int fdin, int fdout)
{
char buffer[BUFFER_LEN]; ssize_t size; //保证从文件开始处复制
lseek(fdin, 0L, SEEK_SET);
lseek(fdout, 0L, SEEK_SET); while((size = read(fdin, buffer, BUFFER_LEN)) > ){
if(write(fdout, buffer, size) != size)
{
fprintf(stderr, "write error: %s \n", strerror(errno));
exit();
}
} if (size < )
{
fprintf(stderr, "read error: %s\n",strerror(errno));
exit(); //return 1;
}
} void set_fl(int fd, int flag) //设置文件状态标志
{
//获取原来的文件状态标志
int val = fcntl(fd, F_GETFL); //增加新的文件状态标志
val |= flag; //重新设置文件状态标志
if(fcntl(fd, F_SETFL, val) < )
{
perror("fcntl error");
}
} void clr_fl(int fd, int flag) //取消文件状态标志
{ //获取原来的文件状态标志
int val = fcntl(fd, F_GETFL, val); //清除指定的文件状态标志(置0)
val &= ~flag; //重新设置文件状态标志
if(fcntl(fd, F_SETFL, val) < )
{
perror("fcntl error");
}
} //文件锁
int lock_reg(int fd, int cmd, short type, off_t offset, short whence, off_t length)
{
struct flock flock; flock.l_type = type;
flock.l_start = offset;
flock.l_whence = whence;
flock.l_len = length;
//flock.l_pid 加锁的进程号,会由操作系统自己填入,当F_GETLK时可以获取到 if(fcntl(fd, cmd, &flock) < ){
perror("fcntl error");
return ;
} return ;
}
//lock_write.c
#include "io.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h> //演示多进程对加独占写锁的文件同时写入的问题。 int main(int argc, char* argv[])
{
if(argc < ){
printf("usage: %s content file locktype\n", argv[]);
exit();
} ssize_t size = strlen(argv[]) * sizeof(char);
int fd = open(argv[], O_WRONLY | O_CREAT, );
if(fd < ){
perror("open error");
exit();
} //查看文件被哪个进程锁住:
struct flock flock;
flock.l_start=;
flock.l_type=F_RDLCK;
flock.l_len=;
flock.l_whence=SEEK_SET;
if(fcntl(fd, F_GETLK, &flock) < )
{
perror("fcntl error");
exit();
} printf("current pid: %d,is locked by another process[pid:%d]\n",getpid(),flock.l_pid); //sleep(5); //加文件锁(注意Linux默认是建议锁,为了防止同多进程同时写文件(O_WRONLY),要求其它
//合作的进程也要同时加O_WRONLY或O_RDONLY。如果其它进程如果不加锁,也是可以直接读
//或写该文件的。因为它是建议锁,为体现锁的功能,其他进程必须遵守加锁的约定,即
//在遵守加锁的约定后,第二个进程想要对文件加锁必须要等到第一个进程释放文件锁后,
//才可以获取锁并进行加锁操作。
if(!strcmp("lock", argv[])){
WRITE_LOCKW(fd, , SEEK_SET, ); //独占写锁
printf("lock success\n");
} //写入content的内容,为了演示交替写入,将字符一个个写入
char* p = argv[];
int i = ;
for(i=; i<size; i++){
if(write(fd, p+i, ) != ){
perror("write error");
exit();
} printf("%d success write one character[-%c-]!\n",
getpid(), *(p+i)); sleep();
} //释放锁
if(!strcmp("lock", argv[])){
UNLOCKW(fd, , SEEK_SET,);
printf("unlock success\n");
printf("unlock pid: %d\n", getpid());
} close(fd); return ;
}
//start.sh
#因为是建议锁,所以两个进程必须同时加锁,锁的功能才能起作用
bin/lock_write aaaaaa lock.txt lock & #加共享读写锁
bin/lock_write AAAAAA lock.txt lock & #加共享读写锁 #如果进程1加锁,进程2不加锁。则锁的作用将不起作用,这就是
#建议性锁的问题,即要求如果锁要起作用,则大家都要加锁。
#bin/lock_write aaaaaa lock.txt lock & #加共享读写锁
#bin/lock_write AAAAAA lock.txt nolock & #注意,这里不加锁,上一个进程的锁将无法约束这里写的行为。
第3章 文件I/O(6)_高级文件操作:文件锁的更多相关文章
- 第17章 内存映射文件(3)_稀疏文件(Sparse File)
17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...
- 第3章 文件I/O(7)_高级文件操作:存储映射
8. 高级文件操作:存储映射 (1)概念: 存储映射是一个磁盘文件与存储空间的一个缓存相映射,对缓存数据的读写就相应的完成了文件的读写. (2)mmap和munmap函数 头文件 #include&l ...
- mysql学习之路_高级数据操作
关系 将实体与实体的关系,反应到最终数据表的设计上来,将关系分为三种,一对多,多对多,多对多. 所有关系都是表与表之间的关系. 一对一: 一张表的一条记录一定只对应另外一张表的一条记录,反之亦然. 例 ...
- (python数据分析)第03章 Python的数据结构、函数和文件
本章讨论Python的内置功能,这些功能本书会用到很多.虽然扩展库,比如pandas和Numpy,使处理大数据集很方便,但它们是和Python的内置数据处理工具一同使用的. 我们会从Python最基础 ...
- C/C++:提升_头文件的使用
C/C++:提升_头文件的使用 ◇写在前面 学到现在,很多人编写程序时只会使用一个文件.这样在代码量较小的时候,更利于表达程序,但是随着代码量的逐步增加,程序的思维逻辑不是我们一下子就可以完全理清的, ...
- Linux常用命令_(文件权限)
Linux权限管理: 二.权限管理1.3种基本权限在Linux中,将使用系统资源的人员分为4类:超级用户.文件或目录的属主.属主的同组人和其他人员.超级用户拥有对Linux系统一切操作权限,对 于其他 ...
- Asp.Net Web Api 2 实现多文件打包并下载文件示例源码_转
一篇关于Asp.Net Web Api下载文件的文章,之前我也写过类似的文章,请见:<ASP.NET(C#) Web Api通过文件流下载文件到本地实例>本文以这篇文章的基础,提供了Byt ...
- gis空间分析案例_坐标文件高斯投影变换地理处理工具
gis空间分析案例_坐标文件投影变换地理处理工具 商务科技合作:向日葵,135—4855__4328,xiexiaokui#qq.com 功能: 对文件进行投影变换 特点: 1. 地理处理工具,可以与 ...
- ca76a_c++_流文件打开输入输出文件模式p773
/*ca76a_c++_流文件打开输入输出文件模式利用文件流打开文件进行输入与输出时的选项in.out.app(附加模式).ate((end)文件打开后,定于文件结尾).trunc(裁剪).binar ...
随机推荐
- GSM中时隙、信道、突发序列、帧的解释
刚从论坛中看到有人问GSM中时隙.信道.突发序列.帧知识.今天我们数字通信正好上到这一块,我就根据我知道的和网上搜索的回答! 1.时分多路复用技术 FDMA:频分多址 TDMA:时分多址 CDMA:码 ...
- 用virtualbox虚拟机无法上网的解决方法
用virtualbox虚拟机无法上网的解决方法 首先保证你的本机是可以正常上网的 启动虚拟机系统前,选择安装好的虚拟PC,点击"设置"按钮,然后切到"网络&quo ...
- TypeError: HashUpdate fail
关于crypto的md5加密报错: 代码: var crypto = require('crypto'); var md5 = crypto.createHash('md5'); //crypto模块 ...
- Maven3的环境配置
1 需要准备一个Maven的包,名字叫:apache_maven_3.3.9(在百度中搜索自行下载) 2 在eclipse中点击window→preferences→maven→Installatio ...
- 有道词典 安卓版 更新日志 - imsoft.cnblogs
手机词典 更新日志: 2014年4月23日 v5.0.4版本 · 新增“全球发音”功能,带来世界各国的英文口音,体验原汁原味的英语发音: · 新增“发现频道”,打造移动 ...
- 【HDU5421】Victor and String(回文树)
[HDU5421]Victor and String(回文树) 题面 Vjudge 大意: 你需要支持以下操作: 动态在前端插入一个字符 动态在后端插入一个字符 回答当前本质不同的回文串个数 回答当前 ...
- CF1142E/1143B Lynyrd Skynyrd
CF1142E/1143B Lynyrd Skynyrd 开始读错题了,以为是连续的一段,敲完后才发现是 \(subsequence\) ... 考虑对于 \(a\) 中的每个 \(a_i\) 找到它 ...
- CTF-练习平台-Misc之 隐写2
二.隐写2 下载文件后解压,发现是一个png图片,依照老套路查看属性,没有发现 用WinHex打开,在图片文件中,修改图片宽度,将箭头处的A改为F,保存后打开图片 发现flag(对于png的文件格式详 ...
- 记录一次服务器CPU 100%的解决过程
昨天客户反馈业务系统很慢,而且偶尔报错. 查看nginx日志: [root@s2 nginx]# tail log/error.log 2017/03/14 12:54:46 [error] 1704 ...
- 解决:VS2010 调试器无法继续继续运行该进程,无法启动调试
工具栏上突然出现 Model Project选项插件,在调试项目的时候突然出现错误: 按照网上的一些方法弄了后还是同样报错,把本地代码删除后从库上重现拉下来的项目依然报错,到这里就明白不是项目本身问题 ...