锁区域创建锁文件对于资源的排他访问,例如串口,是相当合适的,但是对于访问大的共享文件就是太好了。假如我们拥有一个由一个程序写入的大文件,但是是由许多不同的程序进行持续更新的。当一个程序正在记录一些在较长的时间内所得到的数据,并且正在为其他的一些程序进行处理时就会出现这样的情况。这些正在处理的程序并不会等待日志程序结束--他们是连续运行的--所以他们需要一些合作的方法从而可以提供对于同一个文件的同时访问。
我们可以通过锁住文件的一个区域来到达这种结果,这样只是文件的某一个区域被锁住,但是其他程序可以访问程序的其他部分。这称之为文件段(file-segment),或是文件区域(file-region)。Linux有两种方法可以做到这一点:使用fcntl系统调用与使用lockf调用。我们会主要了解fcntl接口,因为这是最经常用到的接口。lockf是相对较为简单的,并且在Linux上只是fcntl的替换接口用法。然而,fcntl与lockf锁机制不可以同时工作:他们使用不同的底层实现,所以我们不能混用这两种调用;只使用这一种或是另一种。
我们在第3章介绍了fcntl调用。其定义如下:
#include <fcntl.h>
int fcntl(int fildes, int command, ...);
fcntl在文件描述符上进行操作,并且依据command参数可以执行不同的任务。而我们所感兴趣的有关文件锁的三个:
❑ F_GETLK
❑ F_SETLK
❑ F_SETLKW
当我们使用这些命令时,第三个参数必须是一个指向struct flock的指针,所以实际上的原型形式如下:
int fcntl(int fildes, int command, struct flock *flock_structure);
flock结构是依赖于实现的,但是他至少包含下面的成员:
❑ short l_type;
❑ short l_whence;
❑ off_t l_start;
❑ off_t l_len;
❑ pid_t l_pid;
l_type成员可以是几个值中的一个,这些值通常定义在fcntl.h中。如下表所示:
值 描述
F_RDLCK 共享锁(或读锁)。多个进程可以在文件的相同区域(或重叠)具有一个共享锁。如果任何进程在文件的某一部分具有一个共享锁,其他的进程就不可以在相同的区域获得排他锁。为了获得一个共享锁,文件必须使用读或是读写访问模式打开。
F_UNLCK 解锁;用于清除锁。
F_WRLCK 排他锁(或写锁)。在文件的某一个特定区域只可以有一个进程获得排他锁。一旦有一个进程具有一个这样的锁,其他的进程就不可以在此区域上获得任何锁类型。要获得一个排他锁,文件必须以写或是读写模式打开。
l_whence成员定义了一个文件中的区域--一个连续的字节集合。l_whence的值必须是SEEK_SET,SEEK_CUR,SEEK_END中的一个(定义在unistd.h)中。他们分别对应于开始位置,当前位置以及文件结尾。l_whence定义了相对于l_start的偏移,l_start为区域的第一个字节。通常,这个值为SEEK_SET,所以l_start通常由文件的开始处算起。l_len参数定义了区域中的字节数。
l_pid参数用于报告存放锁的进程。由后面的F_GETLK更详细的描述了这一点。
文件中的第一个字节在任意时刻只能具有一种锁类型,或者是共享锁,或者是排他锁,或者是解锁。
fcntl调用的命令与选项有几种组合,下面我们会依次进行讨论。
F_GETLK命令第一个命令是F_GETLK。他会获得打开的文件fildes的锁信息。他并不会尝试为文件加锁。这个调用进程传递他希望创建的锁的类型信息,并且使用F_GETLK命令的fcntl调用会返回阻止加锁的信息。
flock结构所使用的值如下表所示:
值 描述
l_type 或者是共享锁F_RDLCK,或者是排他锁F_WRLCK
l_whence SEEK_SET,SEEK_CUR,SEEK_END其中的一个
l_start 感兴趣文件区域的起始字节
l_len 感兴趣的文件区域中的字节数
l_pid 带有锁的进程的标识符
一个进程可以使用F_GETLK来确定一个文件区域的加锁状态。他应该设置flock结构来标识他所需要的锁类型并且定义所感兴趣的文件区域。如果fcntl调用成功则会返回一个非-1的值。如果文件已经具有会阻止所请求执行锁的锁时,他就会使用相应的信息来覆盖flock结构。如果请求锁成功,flock结构则不会发生变化。如果F_GETLK调用不能获取相应的信息,他就会返回-1来标识失败。
如果F_GETLK调用成功(例如,他返回一个非-1的值),调用程序必须检测flock结构的内容以确定他是否被修改。因为l_pid的值会被设置为锁进程的值(如果查找成功),这是一个确定flock结构是否发生变化的合理区域。
F_SETLK命令这个集合会尝试加锁或是解锁fildes所指向的文件区域。flock结构中会用到的值(与F_GETLK所用到的值不同)如下表所示:
值 描述
l_type l_type可以只读或是共享的F_RDLCK或是F_WRLCK。或者是只读或是共享的F_RDLCK;或者是排他或是写入的F_WRLCK;或者是解锁的F_UNLCK。
l_pid 不使用
如果加锁成功,fcntl会返回一个非-1的值;如果失败,则会返回-1。函数调用会立刻返回。
F_SETLKW命令F_SETLKW命令与上面的F_SETLK命令相似,所不同的是如果他不能获得锁,他就会等待,直到可以获得锁为止。一旦这个调用开始等待,他就只会在可以获得锁或是有信号发生时才返回。我们会在第11章讨论信号
程序在一个文件上加的所有锁都会在相应的文件描述符关闭时进行原子清除。当程序结束时也会自动执行这些动作。
使用锁进行读写操作
当我们在一个文件区域上使用锁时,使用底层的read与write调用来访问文件中的数据是相当重要的,而不是高层的fread与fwrite。这是必须的,因为fread与fwrite会在库在执行数据的读写缓冲,所以执行一个fread调用来读取一个文件的前100个字节也许(事实上,通常会这样)会读取多于100个字节,并且在库中缓冲其余的数据。如果程序然后使用fread来读取接下来的100个字节,他实际上会读取缓冲在库中的数据,并且不允许底层的read调用由文件中读取更多的数据。
要了解为什么这是一个问题,考虑两个程序要更新同一个文件。假设这个文件由200个字节的全0数据组成。第一个程序首先运行,并且在文件的前100个字节上获得了一个写锁。然后他使用fread来在这个100个字节中进行读取。然而,正如我们在前面的章节所看到的,fread会一次读取直到BUFSIZ字节的数据,所以实际上他会将所有文件读取到内存中,但只会将前100个字节传递回程序。
然后第二个程序启动。他在程序的后100个字节上获得一个写锁。这也会成功,因为第一个程序只锁住了前100个字节。第二个程序在100到199字节上写入2,然后关闭文件,解锁,退出。第一个程序然后锁住文件的后100个字节,并且调用fread来读取。因为数据进行了缓冲,程序实际上看到的是100个0,而不是文件中实际存在的100个2。当我们使用read与write时就不会出这样的问题。
上面所描述的加锁也许会有些复杂,但是使用起来并没有描述的那样困难。
试验--使用fcntl锁住文件下面让我们来看一下文件锁是如何工作的:lock3.c。要试验文件锁,我们需要两个文件:一个用于锁住文件,一个用于测试。第一个程序实现锁功能。
1 程序代码以必要的文件包含和变量声明开始:
#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;
2 打开一个文件描述符:
file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, “Unable to open %s for read/write\n”, test_file);
exit(EXIT_FAILURE);
}
3 在文件中写入一些数据:
for(byte_count = 0; byte_count < 100; byte_count++) {
(void)write(file_desc, byte_to_write, 1);
}
4 使用共享锁设置区域1,由10到30字节:
region_1.l_type = F_RDLCK;
region_1.l_whence = SEEK_SET;
region_1.l_start = 10;
region_1.l_len = 20;
5 使用排他锁设置区域2,由40到50字节:
region_2.l_type = F_WRLCK;
region_2.l_whence = SEEK_SET;
region_2.l_start = 40;
region_2.l_len = 10;
6 现在锁住文件:
printf(“Process %d locking file\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”);
7 然后等待一会:
sleep(60);
printf(“Process %d closing file\n”, getpid());
close(file_desc);
exit(EXIT_SUCCESS);
}
工作原理这个程序首先创建了一个文件,以读写的方式打开,并向其中填充一些数据。然后他设置两个区域:第一个由10到30字节,使用共享锁,而第二个由40到50字节,使用排他锁。然后程序调用fcntl来锁住两个区域,并且在程序关闭文件退出之前等待一会。
就程序本身而言,程序并没有多大用处。我们需要另一个文件来测试文件锁,lock4.c。
试验--在文件上测试文件锁下面我们来编写一个程序来测试文件上不同区域的锁类型。
1 如平常一样,我们的程序代码以必要的文件包含和变量声明开始:
#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;
2 打开一个文件描述符:
file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, “Unable to open %s for read/write”, test_file);
exit(EXIT_FAILURE);
}
for (start_byte = 0; start_byte < 99; start_byte += SIZE_TO_TRY) {
3 设置我们希望测试的文件区域:
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_WRLCK on region from %d to %d\n”,
start_byte, start_byte + SIZE_TO_TRY);
4 测试文件锁:
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 would fail. F_GETLK returned:\n”);
show_lock_info(®ion_to_test);
}
else {
printf(“F_WRLCK - Lock would succeed\n”);
}
5 使用共享锁重复此操作。再次设置我们希望测试的文件区域:
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_RDLCK on region from %d to %d\n”,
start_byte, start_byte + SIZE_TO_TRY);
6 再次测试文件锁:
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 would fail. F_GETLK returned:\n”);
show_lock_info(®ion_to_test);
}
else {
printf(“F_RDLCK - Lock would succeed\n”);
}
}
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);
}
要测试文件锁,我们首先需要运行lock3程序;然后我们运行lock4程序来测试锁文件。我们可以用下面的命令来使得lock3程序在后台运行:
$ ./lock3 &
$ process 1534 locking file
返回命令提示符是因为lock3在后台运行,然后我们用下面的命令来运行lock4程序:
$ ./lock4
我们得到的程序输出如下所示:
Testing F_WRLOCK on region from 0 to 5
F_WRLCK - Lock would succeed
Testing F_RDLOCK on region from 0 to 5
F_RDLCK - Lock would succeed
...
Testing F_WRLOCK on region from 10 to 15
Lock would fail. F_GETLK returned:
l_type 0, l_whence 0, l_start 10, l_len 20, l_pid 1534
Testing F_RDLOCK on region from 10 to 15
F_RDLCK - Lock would succeed
Testing F_WRLOCK on region from 15 to 20
Lock would fail. F_GETLK returned:
l_type 0, l_whence 0, l_start 10, l_len 20, l_pid 1534
Testing F_RDLOCK on region from 15 to 20
F_RDLCK - Lock would succeed
...
Testing F_WRLOCK on region from 25 to 30
Lock would fail. F_GETLK returned:
l_type 0, l_whence 0, l_start 10, l_len 20, l_pid 1534
Testing F_RDLOCK on region from 25 to 30
F_RDLCK - Lock would succeed
...
Testing F_WRLOCK on region from 40 to 45
Lock would fail. F_GETLK returned:
l_type 1, l_whence 0, l_start 40, l_len 10, l_pid 1534
Testing F_RDLOCK on region from 40 to 45
Lock would fail. F_GETLK returned:
l_type 1, l_whence 0, l_start 40, l_len 10, l_pid 1534
...
Testing F_RDLOCK on region from 95 to 100
F_RDLCK - Lock would succeed
工作原理对于文件中的每五个字节组,lock4设置一个区域结构来测试文件锁,然后程序使用这个区域结构进行测试以确定其为读锁还是写锁。返回的显示了区域字节数,相对于零字节的偏移量,这会使得锁请求失败。因为返回结构的l_pid部分包含当前使得文件锁住的程序的进程标识,程序将其设置为-1,然后当fcntl调用返回时,程序会检测其是否发生了改变。如果所测试的区域并没有被加锁,l_pid就不会发生改变。
要理解程序的输出,我们需要查看所包含的fcntl.h文件,由此我们可以了解l_type的值为1是由F_WRLCK的值为1时得来的,而l_type的值为0是由F_RDLCK的值为0得来的。所以,l_type的值为1告诉我们由于排他的写锁而加锁失败,而l_type的值0是由已经存在读锁而引起的。在文件中这块区域lock3程序并没有加锁,所以共享锁与排他锁都是成功的。
由10到30字节,我们可以看到他可能会获得一个共享锁,因为lock3程序此处存在的是共享锁,而不是排他锁。在40到50字节区域,两种类型的锁都会失败,因为lock3程序在此处区域加了排他锁。
分享到:
相关推荐
在“Web数据管理四次实验代码和实验报告.zip”这个压缩包中,我们可以推测这是一个关于Web数据管理和处理的课程或项目实践。实验通常是为了让学生更好地理解理论知识,并将其应用于实际场景。以下是根据这个主题可能...
数据管理技术的发展阶段、数据描述的术语、数据抽象的四个级别 数据管理技术的发展阶段可以分为四个阶段:人工管理阶段、文件系统阶段、数据库阶段和高级数据库阶段。在数据库阶段,数据管理的特点包括采用数据模型...
主数据管理实施四部曲是指企业在实施主数据管理时所遵循的四个关键步骤,这四个步骤分别是:摸家底、建体系、接数据、抓运营。下面将详细阐述这四个阶段。 1. 摸家底: 在实施主数据管理之前,企业首先需要全面了解...
用友主数据管理解决方案 用友主数据管理解决方案是企业级的主数据管理解决方案,旨在帮助企业解决主数据管理问题,提高业务效率和决策效率。该解决方案通过集成多个系统和数据库,提供统一的主数据视图,帮助企业...
主数据管理实施四部曲是实现高效、准确、一致的主数据管理的关键步骤。这四部曲包括“摸家底”、“建体系”、“接数据”和“抓运营”,每一步都对企业数据治理的成功起到决定性作用。 首先,摸家底是主数据管理的第...
第四维度:数据管理组织 ------------------------ * 级别1:无组织,数据管理组织混乱、无章法 * 级别2:初步组织,数据管理组织初步建立 * 级别3:标准组织,数据管理组织标准化 * 级别4:优化组织,数据管理组织...
四、安全管理责任 数据分类分级管理制度需要明确责任部门的安全管理责任。这些责任包括: * 数据的日常管理和维护 * 数据的安全策略和程序的制定和实施 * 数据的备份和恢复 * 数据的访问控制和身份验证 五、数据...
本资源摘要信息将从华为制造业数据治理、数据管理与数据中台架构解决方案PPT.pptx中提取相关知识点,总结了数据治理、数据管理、数据中台架构等方面的重要概念和技术。 一、数据治理 * 数据治理是指对组织内所有...
#### 四、数据管理和治理 在一个组织内部,常常存在同一概念的多种表示方法。因此,需要对数据架构、建模、治理、管理制度以及元数据和数据质量进行管理,所有这些都有助于人们更好地理解和使用数据。当数据跨越多...
DAMA金字塔图是数据管理知识体系的核心概念,通过四个阶段,将数据、信息和知识逐层递进,最后透过数据进行挖掘、分析和决策。该图形象地展示了数据管理的整个过程,从数据的收集到知识的产生。 本PPT材料总结了...
二、数据资产管理活动职能 (一) 数据模型管理 (二) 数据标准管理 (三) 数据质量管理 (四) 主数据管理 (五) 数据安全管理 (六) 元数据管理 (七) 数据开发管理 (八) 数据资产流通 (九) 数据价值评估 (十) 数据资产运营...
### 数据管理知识体系(第二版)—— DAMA-DMBOK #### 一、引言与背景 在当今数字化时代,数据已经成为企业运营的核心资产之一。随着云计算、大数据、人工智能等新兴技术的发展,数据管理的重要性日益凸显。企业...
四、数据管理组织架构 * 数据管理组织架构的三个层次:决策层、管理协调层、执行层 * 决策层:信息科技指导委员会和信息科技管理委员会 * 管理协调层:数据管理领导小组和秘书 * 执行层:数据管理执行组 五、数据...
### 数据管理知识体系功能框架解析 #### 一、引言 在当今信息时代,数据管理对于每一个组织机构来说都是至关重要的。无论是被称为数据管理、数据资源管理还是企业信息管理,组织机构越来越意识到其所拥有的数据是...
3. 组织职能升级变迁:设立专门的数据管理团队,负责数据的全生命周期管理。 4. 管理手段自动智能:利用自动化工具和AI技术,提升数据处理效率和准确性。 5. 应用范围不断扩大:数据资产管理不仅限于内部业务,还...
此外,我们还可以看到一些三级和四级的数据流程图,如学生基本信息录入管理数据流图(第 3 层)、学生异动信息录入管理数据流图(第 3 层)、学生考勤信息录入管理数据流图(第 3 层)等这些三级和四级的数据流程图...
8. 报废设备数据管理:报废设备中的数据应备份后清除,废弃介质妥善处理,防止信息泄露。 9. 计算机病毒管理:设立专人负责防病毒工作,建立防治制度,定期检查和清除病毒。 10. 专用计算机管理:营业用计算机不得...
元数据管理在大数据领域扮演着至关重要的角色,它提供了对数据资产的理解,使得数据分析和决策制定更为高效。本文主要关注的是一款名为Atlas的元数据管理工具,它在Hadoop生态系统中发挥着核心作用,用于管理和追踪...
四、主数据管理的实施步骤 主数据管理的实施步骤包括:主数据识别、规划与设计、数据采集、数据处理、数据分发和维护。 1. 主数据识别:识别企业中的主数据,包括客户信息、产品信息、供应商信息、员工信息等。 2....
资产治理旨在降低数据管理的成本并提高效率。具体措施包括: 1. 数据存储治理,形成闭环,确保数据的稳定性和可访问性。 2. 数据计算治理工具,通过工具赋能资产治理,提升治理效率。 3. 治理领域和方法策略,制定...