目录
一、前言
二、高级主题: fcntl和mmap
1、fcntl
2、mmap
3、Using mmap
一、前言
本文将简单介绍Linux文件和目录,以及如何操作它们(如何创建文件、打开、读、写和关闭,程序如何操作目录,如创建、扫描和删除目录等)。本文大部分内容将会介绍处理文件和目录的各种调用,涵盖了与文件相关的部分主题:(1)文件和设备;(2)系统调用;(3)库函数;(4)低级文件访问;(5)管理文件;(6)标准I/O库;(7)格式化的输入和输出;(8)文件和目录维护;(9)扫描目录;(10)错误;(11)/proc文件系统;(12)fcntl 和 mmap。这些内容本博客不会全部介绍,只是进行部分的基础介绍。
二、高级主题: fcntl和mmap
接下来会介绍几个我们可能会跳过的主题,因为它们很少被使用。但它们可以为一些棘手的问题提供简单的解决方案。
1、fcntl
fcntl 系统调用提供了操作低级文件描述符的更多方法。
我们可以使用 fcntl 系统调用对打开的文件描述符执行多种操作,包括复制它们、获取和设置文件描述符标志、获取和设置文件状态标志,以及管理建议文件锁定。
不同的操作是由命令参数 cmd 的不同值选择的,如 fcntl.h 中定义的那样。根据所选择的命令,系统调用将需要第三个参数 arg:
fcntl (fildes, F_DUPFD, newfd):这个调用返回一个新的文件描述符,其数值等于或大于整数 newfd。新的描述符是描述符字段的副本。根据打开文件的数量和 newfd 的值,这可以有效地与 dup (fildes) 相同。
fcntl (fildes, F_GETFD):该调用返回 fcntl.h 中定义的文件描述符标志,其中包括 FD_CLOEXEC,它确定在成功调用 exec 系列系统调用之一后,文件描述符是否关闭。
fcntl (fildes, F_SETFD, flags):这个调用用于设置文件描述符标志,通常只是 FD_CLOEXEC。
fcntl (fildes, F_GETFL) and fcntl (fildes, F_SETFL, flags):这些调用分别用于获取和设置文件状态标志和访问模式。我们可以使用 fcntl.h 中定义的掩码 O_ACCMODE 来提取文件访问模式。其他标志包括与 O_CREAT 一起使用时传递给打开的第三个参数的标志。注意,我们不能设置所有标志。特别是,我们不能使用 fcntl 设置文件权限。
我们还可以通过 fcntl 实现建议文件锁定。有关更多信息,可以参阅手册的第2节。
2、mmap
UNIX 提供了一种有用的工具,允许程序共享内存,好消息是 Linux 内核的2.0版本和更高版本都包含了这种工具。
mmap (用于内存映射) 函数设置了一个可由两个或多个程序读或写的内存段。一个程序所做的更改会被其他程序看到。
我们可以使用相同的工具来操作文件,也可以使磁盘文件的整个内容看起来像内存中的一个数组。如果文件由可以用 C 结构描述的记录组成,则可以使用结构数组访问更新文件。
这是通过使用具有特殊权限集的虚拟内存段实现的。对该段进行读写操作会导致操作系统对磁盘文件的适当部分进行读写。
mmap 函数创建一个指向与通过打开的文件描述符访问的文件内容相关的内存区域的指针。
通过传递 off 参数,我们可以更改共享段访问的文件数据的开始。打开的文件描述符作为文件传递。可访问的数据量 (即内存段的长度) 是通过 len 参数设置的。
我们可以使用 addr 参数来请求一个特定的内存地址。如果为 0,则自动分配结果指针。这是推荐的用法,因为它很难移植,否则,系统的可用地址范围各不相同。prot 参数用于设置内存段的访问权限。这是以下常量值的位或:
flags 参数控制程序对段所做的更改如何反映在其他地方,这些选项显示在下表中。
msync 函数将内存段的部分或全部更改写回(或从)映射文件。
要更新的段的部分由传递的起始地址 addr 和长度 len 给出。flags 参数控制如何使用下表中所示的选项执行更新。
munmap 函数释放内存段。
下面的程序 mmap.c 显示了一个使用 mmap 和数组样式访问更新结构的文件。2.0以前的 Linux 内核并不完全支持 mmap 的这种使用。该程序可以在Sun Solaris和其他系统上正常工作。
3、Using mmap
首先定义 RECORD 结构,然后创建 NRECORDS 版本,每个版本记录它们的编号。它们被附加到文件 records.dat 中。
#include<unistd.h>
#include<stdio.h>
#include<sys/mman.h>
#include<fcntl.h>
#include<stdlib.h>
typedef struct{
int integer;
char string[24];
} RECORD;
#define NRECORDS (100)
int main()
{
RECORD record,*mapped;
int i,f;
FILE *fp;
fp=fopen("records.dat","w+");
for(i=0;i<NRECORDS;i++){
record.integer=i;
sprintf(record.string,"RECORD-%d",i);
fwrite(&record,sizeof(record),1,fp);
}
fclose(fp);
fp=fopen("records.dat","r+");
fseek(fp,43*sizeof(record),SEEK_SET);
fread(&record,sizeof(record),1,fp);
record.integer=143;
sprintf(record.string,"RECORD-%d",record.integer);
fseek(fp,43*sizeof(record),SEEK_SET);
fwrite(&record,sizeof(record),1,fp);
fclose(fp);
f=open("records.dat",O_RDWR);
mapped-(RECORD*)mmap(0,NRECORDS*sizeof(record),PROT_READ|PROT_WRITE,MAP_SHARED,f,0);
mapped[43].integer=243;
sprintf(mapped[43].string,"RECORD-%d",mapped[43].integer);
msync((void *)mapped,NRECORDS*sizeof(record),MS_ASYNC);
munmap((void *)mapped,NRECORDS*sizeof(record));
close(f);
exit(0);
}
在文件处理的章节中,我们已经了解了 Linux 如何提供对文件和设备的直接访问,也已经了解了库函数如何构建在这些低级函数之上,为编程问题提供灵活的解决方案。
我们只需几行代码就可以编写一个相当强大的目录扫描例程,我们还学习了足够的文件和目录处理知识等等。
以上,Linus 文件处理
祝好