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{
short l_type; //F_RDLCK,F_WRLCK,or F_UNLCK
off_t l_start; //相对于l_whence的偏移量
short l_whence; //SEEK_SET,SEEK_CUR,or SEEK_END
off_t l_len; //长度(单位字节),0表示锁到文件尾
pid_t l_pid; //使用F_GETLK时有效,返回持有锁的进程
}

备注

①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)_高级文件操作:文件锁的更多相关文章

  1. 第17章 内存映射文件(3)_稀疏文件(Sparse File)

    17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...

  2. 第3章 文件I/O(7)_高级文件操作:存储映射

    8. 高级文件操作:存储映射 (1)概念: 存储映射是一个磁盘文件与存储空间的一个缓存相映射,对缓存数据的读写就相应的完成了文件的读写. (2)mmap和munmap函数 头文件 #include&l ...

  3. mysql学习之路_高级数据操作

    关系 将实体与实体的关系,反应到最终数据表的设计上来,将关系分为三种,一对多,多对多,多对多. 所有关系都是表与表之间的关系. 一对一: 一张表的一条记录一定只对应另外一张表的一条记录,反之亦然. 例 ...

  4. (python数据分析)第03章 Python的数据结构、函数和文件

    本章讨论Python的内置功能,这些功能本书会用到很多.虽然扩展库,比如pandas和Numpy,使处理大数据集很方便,但它们是和Python的内置数据处理工具一同使用的. 我们会从Python最基础 ...

  5. C/C++:提升_头文件的使用

    C/C++:提升_头文件的使用 ◇写在前面 学到现在,很多人编写程序时只会使用一个文件.这样在代码量较小的时候,更利于表达程序,但是随着代码量的逐步增加,程序的思维逻辑不是我们一下子就可以完全理清的, ...

  6. Linux常用命令_(文件权限)

    Linux权限管理: 二.权限管理1.3种基本权限在Linux中,将使用系统资源的人员分为4类:超级用户.文件或目录的属主.属主的同组人和其他人员.超级用户拥有对Linux系统一切操作权限,对 于其他 ...

  7. Asp.Net Web Api 2 实现多文件打包并下载文件示例源码_转

    一篇关于Asp.Net Web Api下载文件的文章,之前我也写过类似的文章,请见:<ASP.NET(C#) Web Api通过文件流下载文件到本地实例>本文以这篇文章的基础,提供了Byt ...

  8. gis空间分析案例_坐标文件高斯投影变换地理处理工具

    gis空间分析案例_坐标文件投影变换地理处理工具 商务科技合作:向日葵,135—4855__4328,xiexiaokui#qq.com 功能: 对文件进行投影变换 特点: 1. 地理处理工具,可以与 ...

  9. ca76a_c++_流文件打开输入输出文件模式p773

    /*ca76a_c++_流文件打开输入输出文件模式利用文件流打开文件进行输入与输出时的选项in.out.app(附加模式).ate((end)文件打开后,定于文件结尾).trunc(裁剪).binar ...

随机推荐

  1. Linux 内核链表实现和使用(一阴一阳,太极生两仪~)

    0. 概述 学习使用一下 linux 内核链表,在实际开发中我们可以高效的使用该链表帮我们做点事, 链表是Linux 内核中常用的最普通的内建数据结构,链表是一种存放和操作可变数据元 素(常称为节点) ...

  2. 深入理解uwsgi和gunicorn网络模型

    前言: 去年10月份建了一个python技术群,到现在为止人数已经涨到700人了.最一开始我经常在群里回应大家的问题,不管是简单还是困难的,我都会根据自己的经验来交流. 让人新奇的是一些初学者关注最多 ...

  3. 利用security.js实现RSA加密

    在通常的http协议的网站中直接提交数据可以通过信息抓取从而暴露提交者所提交的信息,特别是注册时的密码和登录时的密码容易被泄露. 那么怎么防止这种现象呢? 很多人会想到加密技术,对没错,本文所讲的就是 ...

  4. GTK安装

    上面是linux下GTK+配置所需要的库,关于各个库的功能,查看http://www.gtk.org/overview.php,至于库的下载在http://www.gtk.org/download/l ...

  5. CentOS7安装OpenStack(Rocky版)-09.安装Cinder存储服务组件(控制节点)

    本文分享openstack的Cinder存储服务组件,cinder服务可以提供云磁盘(卷),类似阿里云云盘 ----------------------- 完美的分隔线  -------------- ...

  6. chapter02“良/恶性乳腺癌肿瘤预测”的问题

    最近比较闲,是时候把自己以前看的资料整理一下了. LogisticRegression:由于在训练过程中考虑了所有的样本对参数的影响,因此不一定获得最佳的分类器,对比下一篇 svm只用支持向量来帮助决 ...

  7. stm32 看门狗配置

    1.独立看门狗: 1) 取消寄存器写保护(向 IWDG_KR 写入 0X5555) IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);  //使能或者失能 2) ...

  8. HPU 1437: 王小二的求值问题

    1437: 王小二的求值问题 时间限制: 1 Sec 内存限制: 128 MB提交: 141 解决: 31 统计 题目描述 题意超级简单,求一个序列的次大值. 输入 多组输入,每个测试实例占两行,包括 ...

  9. 【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.3 日历

    日历 日历可以单独应用于规则中,也可以和timer结合使用在规则中使用.通过属性calendars来定义日历.如果是多个日历,则不同日历之间用逗号进行分割. 在Drools中,日历的概念只是将日历属性 ...

  10. java设计模式--创建型模式(一)

    2016-04-24 10:10:34 创建型模式:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 注意:工厂模式可以分为三类: 1)简单工厂模式(Simple Factory) 2)工厂 ...