您的位置:首页 > 运维架构 > Linux

Unix/Linux C++应用开发-文件系统编程

2013-09-11 21:29 591 查看
Linux系统本身设计类Unix而来,系统由C语言设计实现。同样Linux也支持丰富的底层系统调用库提供给应用程序的文件操作处理。事实上C++语言自身许多类库的方法的封装,内部基本都采用封装系统调用提供统一简洁便利的接口方式。对于Linux系统,针对文件操作除了提供系统方法操作以外,C语言也提供了标准文件操作库,而C++语言由于基于C语言发展而来,因此C++文件编程除了本身提供的标准文件库以外,也支持系统文件操作API与C语言标准文件库。本章主要介绍Linux系统下针对文件I/O提供的系统调用方法,了解Linux系统针对文件操作提供的底层支持,有助于增加对Linux系统下文件处理的底层理解。

18.1 Linux文件系统简介

Linux系统支持多种文件系统,比如基本的日志文件系统、虚拟文件系统以及Linux内核针对实际文件系统的支持。此为Linux系统基本重要特征之一,包容多种文件系统意味着可以与更多的其它操作系统很好的并存。一个完整的文件系统操作主要由文件属性、文件系统的属性以及目录操作、文件操作等构成,本小节主要针对Linux系统的文件系统基本结构操作作简单全面的介绍。

18.1.1 Linux系统文件基本类型

Linux系统文件类型与文件名不同,大部分应用中使用的是普通文件操作。Linux系统中常见的文件类型有普通文件、目录、字符设备文件、块设备文件、符号链接文件等。下面将会就以上不同类别的文件作一个简单的介绍说明。

不同的文件类型在Linux系统中采用不同的符号表示,普通文件使用第一个‘-’符号表示,当前Linux系统shell下使用ll
–lst命令列出工作目录下所有文件,该命令操作结果如下所示。

[developer@localhost c]$ ll -lst

-rwx------ 1root root 1051 Sep 9 23:09 testLocation.cpp

-rwx------ 1root root 1051 Sep 9 22:01 testLocation.cpp.bak

-rwx------ 1root root 613 Apr28 15:44 testmap.cpp

-rwx------ 1root root 246 May22 08:34 testString.cpp

drwx------ 1root root 0 Sep1 23:28 testsuanfa

上述ll –lst命令列出的文件中第一个符号为‘-’表示当前操作的文件为普通文件。上述ll –lst命令列出当前目录下文件情况。结果中第一列为该文件属性说明,第二列为文件数量说明,第三列为文件的拥有者,第四列则为文件所属的用户组,第五列为文件大小说明,第六列为文件创建时间以及最后一列为文件名称说明。

上述命令结果基本说明针对普通文件、目录文件与字符设备文件等都有同样的说明。下面将会通过普通文件来说明Linux系统下文件属性等相关信息情况。命令ls –lst结果下文件将会有七列说明项,第一项主要用于显示文件的属性,该属性占用10个字节分别由一个文件类型的标识符与三组文件权限说明组成。文件属性说明中第一个字符为文件类型标识符,‘-’表示普通文件、‘d’表示目录文件、‘c’表示字符设备文件、‘b’块设备文件以及‘s’表示网络通信中的套接口文件。

随后跟着三组文件权限属性说明,每个分组的权限属性为三个字符表示,如第一个分组表示文件拥有者针对文件的权限,第二个分组同样为三位字符组成表示同属于一个组的用户对该文件访问权限,第三个分组表示其它用户对该文件的权限说明。而Linux系统中对文件的操作权限有三种说明,使用字符r、w、x分别表示操作文件可读、可写以及可执行。

第二项表示文件数量,需要注意的是如果当前文件为目录,则该项数量表示当前目录下的文件数;第三项与第四项则表示当前文件的用户拥有者与文件所属的用户组;第五列为文件的大小说明,主要以字节为单位表示当前文件的大小。第六列为文件创建时间说明,其时间说明分别为月、日与年。最后一列为文件的文件名,在Linux系统下会根据不同类型的文件显示为不同颜色表示。

上述针对普通文件属性等描述对于目录文件、设备文件、符号链接文件等都适用,仅仅在文件详细信息描述中采用第一项第一个字符来表示,其余所有的属性说明都相同。其中目录文件为Linux系统组织文件的一种特殊文件,而Linux系统从前面介绍来看也是通过目录组织文件分层来表示的。另外Linux系统将外部设备看作文件来管理使用,用户操作外部设备如同操作普通文件一样简单方便。

设备文件主要包含两种形式,一种为字符设备文件主要为打印机、终端等一类设备的文件表示,通常这类文件可以使用字节流方式操作。另一种为块设备文件,主要如存储磁盘等一类的设备,可以作为块文件随机访问读写。Linux系统如同Unix系统将一切都可以看作文件来操作,Linux系统下针对不同外设以及存储设备存在多种不同类型文件,这些不同类型文件有着相当专业的用途,实际软件应用中通常操作文件系统中普通文件应用比较常见,另外在网络通信中套接口文件等也比较常见。

18.1.2 Linux系统文件属性

从上述小结文件介绍来看,每类文件都有其基本属性定义,这些属性主要包含文件类型、访问权限、文件属主、文件属组以及文件长度与访问、修改时间等。Linux内核中针对文件属性定义维护着一个属性结构体stat,该结构体中不同数据成员来分别表示所定义文件实例相关的属性,结构体stat定义如下。

struct stat

{

dev_t st_dev; //文件设备

ino_t st_ino; //文件节点号

mode_t st_mode; //文件类型与权限信息

nlink_t st_nlink; //文件符号链接数

uid_t st_uid; //文件用户ID

gid_t st_gid; //文件属组ID

dev_t st_rdev; //设备文件号

off_t st_size; //文件大小

blksize_t st_blksize; //最大I/O块长度

blkcnt_t st_blocks; //分配给文件的块单元数量,每个单元为512字节

time_t st_atime; //文件最后一次访问时间

time_t st_mtime; //文件最后一次内容修改时间

time_t st_ctime; //文件结构最后一次修改时间

};

Linux系统针对属性结构体提供了相应的属性操作系统调用方法,用于在实际应用程序中根据处理需要获取、修改相应操作文件的属性。系统调用文件属性操作主要包含三个函数,系统中方法原型如下。

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

int stat(const char* file_name,struct stat*buf);

int fstat(int filedes,struct stat *buf);

int lstat(const char *file_name,struct stat*buf);

上述三个系统调用函数有一个共同点即离不开文件属性结构体的定义,通常应用程序中需要在外部定义文件属性结构体,然后作为参数将其对象指针传入进去,内部通过传入指定的文件名或者文件描述符将该文件的属性取到对应的结构体中,此时获取属性的结构体即可以在外部使用。

函数stat主要根据指定需要获取属性的文件名file_name参数,从而获取该文件对应的属性。而fstat是根据文件打开操作返回的文件描述符来操作获取对应文件属性的,lstat函数主要用于根据连接文件名获取到对应的文件属性结构信息。另外三个系统调用成功处理返回值为0,否则返回-1,相应标准错误存放在errno中。

下面将会通过一个C++封装获取文件属性信息接口应用程序,演示C++程序在实现文件系统时的基本操作封装使用情况,该程序将会充分演示文件属性系统调用的使用方法。该实例代码编辑如下所示。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter1801.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示

/**

* 实例chapter1801

* 源文件chapter1801.cpp

* 文件属性处理实例

*/

#include <iostream>

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

using namespace std;

/**

*文件属性枚举定义.

*内部定义几种文件基本属性标记值.

*/

enum FileAttribute { Exists = 0x01, //文件存在

RegularFile= 0x02, //普通文件

Directory = 0x04 , //文件目录

ReadOnly = 0x10}; //文件只读

/**

*获取当前文件最后修改时间.

*@param path 输入处理文件名参数.

* @param time 输入时间结构体变量.

*@return 处理成功返回0,否则返回-1

*/

int getFileLastModifiedTime(const string&path, time_t& time)

{

structstat myStat;

if(stat(path.c_str(), &myStat) < 0)

{

return-1;

}

time= myStat.st_mtime;

return0;

}

/**

*获取当前文件长度.

*@param path 输入处理文件名参数.

*@return 处理成功返回文件长度

*/

size_t getFileLength(const string& path)

{

structstat myStat;

if(stat(path.c_str(), &myStat) == 0)

{

returnmyStat.st_size;

}

return0;

}

/**

*获取当前文件基本类型属性.

*@param path 输入处理文件名参数.

*@return 处理成功返回文件属性标记结果

*/

int getFileAttributeFlags(const string&path)

{

structstat myStat;

if(stat(path.c_str(), &myStat) == 0)

{

intret = Exists;

if(myStat.st_mode & S_IFDIR) ret |= Directory;

if(myStat.st_mode & S_IFREG) ret |= RegularFile;

if((myStat.st_mode & S_IWUSR)==0) ret |= ReadOnly;

returnret;

}

return0;

}

/*主程序入口*/

int main()

{

stringpath_name; //定义文件路径全名

intresult; //定义标记变量

cout<<"Pleaseinput path path_name:"<<endl; //提示从键盘输入文件路径全名

cin>>path_name; //输入文件路径全名

cout<<"Thispath_name is:"<<endl; //提示输入文件名属性

result= getFileAttributeFlags (path_name); //调用文件基本属性求取方法,将返回结果赋给标记变量

if((result& RegularFile) != 0) //将结果标记变量与文件属性中普通文件项作与运算

{

cout<<"Thisis a regular file!"; //如果表达式计算为非零值,表明输入的文件为普通文件

}

elseif((result & Directory) != 0) //将结果标记变量与文件属性中目录文件项作与运算

{

cout<<"Fileis a directory!"; //如果表达式计算为非零值,表明输入的文件为目录文件

}

if((result& ReadOnly) != 0) //将结果标记变量与文件属性中的只读属性项作与运算

{

cout<<"AndFile is read-only!"<<endl; //如果表达式计算为非零值,表明输入的文件为只读

}

else

{

cout<<"AndFile isn't read-only!"<<endl; //否则为非只读文件

}

time_ttime; //定义时间结构变量

if(!getFileLastModifiedTime(path_name,time)) //调用获取文件最后修改方法,传入时间变量与文件名

{

cout<<"FileModify time:"<<endl; //提示输出文件修改时间

structtm *w; //

charbuffer[15]; //定义缓冲区,并设定为15个字节大小

w= localtime(&time); //传入时间结构变量,调用本地时间求取方法

sprintf(buffer,"%4d%02d%02d%02d%02d%02d", //格式化求取时间的字符串

w->tm_year+ 1900,w->tm_mon + 1, w->tm_mday,

w->tm_hour,w->tm_min, w->tm_sec);

cout<<buffer<<endl;

}

size_tsize; //定义size_t型变量,表示文件的长度

size= getFileLength (path_name); //调用获取文件长度方法,根据传入文件名计算文件大小

cout<<"Filesize:"<<size<<endl; //打印输出文件大小

return0;

}

本实例主要演示文件属性基本操作处理使用情况,程序主要包含主函数与三个操作函数,三个操作函数分别用于获取当前文件最后修改时间(getFileLastModifiedTime)、获取当前文件长度(getFileLength)以及获取当前文件基本类型属性(getFileAttributeFlags),具体程序解释见程序剖析部分。

2.编辑makefile

Linux系统平台下需要编译源文件为chapter1801.cpp,相关联工程makefile文件编辑内容如下所示。

OBJECTS=chapter1801.o

CC=g++

chapter1801: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1801

clean:

rm -fchapter1801 core $(OBJECTS)

submit:

cp -f-r chapter1801 ../bin

cp -f-r *.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后make submit命令提交程序文件至本实例bin目录下,通过cd命令定位至实例bin目录,执行该程序运行结果如下所示。

[developer@localhost src]$make

g++ -c-o chapter1801.o chapter1801.cpp

g++ chapter1801.o -g -o chapter1801

[developer@localhost src]$make submit

cp -f -r chapter1801 ../bin

cp -f -r *.h ../include

[developer@localhost src]$cd ../bin

[developer@localhost src]$pwd

/home/ocs/users/wangfeng/Linux_c++/chapter18/chapter1801/bin

[developer@localhost src]$./chapter1801

Please input path path_name:

/home/ocs/users/wangfeng/Linux_c++/chapter18/chapter1801/bin/chapter1801

This path_name is:

This is a regular file!And File isn'tread-only!

File Modify time:

20090107061638

File size:16051

4.程序剖析

本实例主要采用C++应用程序封装文件属性系统调用stat实现相关文件属性处理功能,上述主要涉及文件属性处理应用的三个操作,分别为指定文件最后修改时间的获取、指定文件大小获取以及文件基本类型属性的获取。

上述三个方法分别采用C++语言封装成三个操作函数接口,分别为getFileLastModifiedTime、getFileAttributeFlags与getFileLength。方法接口getFileLastModifiedTime主要对外提供两个参数,第一个为指定文件的路径全名、第二个为需要获取的时间变量,该方法内部主要根据stat方法是用获取其数据成员st_mtime表示文件最后修改时间,成功获取则返回0,否则系统调用不成功返回-1。

第二个方法接口操作为getFileLength,用户获取指定文件的长度,该方法提供一个参数入口即为需要操作的文件路径全名,根据传入的路径全名来调用stat方法获取其结构体成员st_size,并直接返回,否则返回整型值0。

最后方法接口getFileAttributeFlags主要用于判断指定文件属于普通文件还是目录文件,以及判断当前目录或者文件是否为只读权限。该方法接口提供一个参数为指定文件全名,根据输入的文件全名,调用stat方法,获取指定文件的基本属性。随后通过采用其属性st_mode文件类型与权限字段信息来判断是否为文件、目录并且是否为只读权限。该方法通过定义全局枚举来标识相应的文件类型与权限,该枚举中主要4个变量说明,分别表示文件存在、普通文件、目录以及只读权限。

方法内部调用stat获取文件属性后,将其属性结构体字段中st_mode将与文件类型宏定义常量符以及权限常量符进行&运算,如果结果为真证明指定的文件拥有该项属性,从而外部调用判断可以得出指定文件类型以及只读权限的信息。该方法调用成功,返回大于0的整型数,否则直接返回0。

此处需要注意的是Linux系统针对文件属性中相关类别定义了一系列的宏常量符号,分别表示文件属性中不同的类别信息。比如针对文件类型就有S_IFREG(普通文件)、S_IFDIR(目录文件)、S_IFBLK(块文件)以及S_IFCHR(字符文件)等标志值。另外针对文件属性中访问权限也提供了一系列的宏常量符号定义,比如S_IRUSR(文件用户读取权限)以及S_IWUSR(文件用户写权限)等。具体符号定义与值得说明可以通过查看系统中stat.h文件即可。

实例主程序中,定义相关变量,随后调用获取文件类型属性方法接口,根据传入的文件全名外部进行判断处理,将调用该方法结果与相应的标识进行&运算,如果结果成真则表示为对应文件类型以及是否为只读权限。之后定义时间变量,调用获取修改时间方法,由于该变量中存放的是长整型时间,不方便查看,所以根据需要进行了时间格式化处理,使之按照正常的顺序格式显示。最后调用文件长度获取方法直接返回指定文件的大小。

18.2 Linux系统目录操作

Linux系统下目录也是文件的一种,作为特殊文件,Linux系统也提供了相应的系统调用来操作文件系统中的目录。目录操作主要包含目录创建、删除、读取与定位几个部分,目录基本操作在实际应用程序中也常有应用。比如电信行业中计费系统处理,由于资料文件关联性经常需要扫描相应处理的数据目录,查看文件是否到齐后才能统一处理。此过程中经常涉及文件目录读取扫描操作。

另外一个C++应用程序实现Linux下标准文件系统操作也离不开针对系统目录文件系统调用的封装,由于考虑到通用以及功能单一性,系统调用提供的接口在应用中可以采用C++应用程序封装应用于实际更加具体的功能需求处理。

下面将会简单介绍Linux系统下针对目录文件提供的基本操作支持情况,最后会通过一个扫描目录实例程序来演示目录操作在实际应用程序中的使用。Linux系统针对目录操作提供的方法基本原型如下。

//目录文件创建

#include <sys/stat.h>

#include <sys/types.h>

int mkdir(constchar *pathname,mode_t mode);

//目录文件打开

#include <sys/types.h>

#include <dirent.h>

DIR *opendir(const char *name);

//目录文件读取

#include <unistd.h>

#include <linux/dirent.h>

#include <linux/unistd.h>

int readdir(unsignedint fd,struct dirent *dirp,unsigned int count );

//目录文件关闭

#include <sys/types.h>

#include <dirent.h>

int closedir(DIR*dirent);

//目录文件删除

#include <unistd.h>

int rmdir(constchar *pathname);

//获取当前工作目录

#include <unistd.h>

char *getcwd(char*buf,size_t size);

char *get_current_dir_name(void);

char *getwd(char *buf);

上述系统调用可能在不同的内核中相关接口稍有变化,笔者当前使用的Linux系统为32位内核版本2.4的redhat操作系统。上述系统调用根据man查询系统中提供的帮助说明页列出,由于目录也作为文件的一种特殊形式处理,那么文件的相关操作在目录上同样存在。

常见的目录操作包含上述六个,分别为目录创建、打开、读取、关闭、删除以及获取目录操作。首先目录文件的创建函数,该方法在系统中与shell下提供的mkdir命令相同,用于根据函数参数中指定的目录名pathname以及指定的目录访问权限mode,在当前工作目录下创建一个新目录。如果指定的目录已经存在则该操作会失败。该方法成功创建一个目录返回整型值为0,否则为-1,相应的标准错误值会写入errno中可供诊断。

目录操作的第二个方法为打开文件操作,该方法传入参数为一个指定的目录文件名,通过内部处理打开指定目录文件,并且返回一个指向目录结构DIR的指针,类似于文件打开的描述符,该指向目录结构体指针对应着一个特定的打开的目录。该方法成功打开目录文件则返回相应的指向DIR结构体指针,否则返回NULL,相应的错误号放入errno。

目录文件读取,用于扫描当前目录下基本信息,通常目录文件下基本信息使用最多的为列出当前目录下的文件。实际软件文件处理中获取当前指定目录下文件信息有着广泛应用,比如在电信行业经常需要根据业务完整性让相应传来的文件资料在一定数量之后才开始操作以及一些文件接口操作中等都会使用到获取当前目录信息操作。目录作为特殊文件的一种,上述方法通过相应的文件描述符fd,将指定目录下的内容读取到目录信息结构体指针dirp所指向的空间,供后续处理。该方法成功执行返回整型数0,否则返回-1,相应标准错误写入符号errno中。

目录创建、打开、读取都完成之后,作为一个良好的编程习惯,自然不能忘记关闭已经打开的目录文件。方法主要参数为指向目录信息结构体指针dirent,根据获取当前目录的结构体信息指针,关闭当前指针的目录。该方法成功关闭文件返回整型值为0,否则返回-1,错误值写入errno。

目录删除函数rmdir,该方法根据传入的目录名,删除指定存在的目录。需要注意的是该目录必须为空目录,即下层不存在普通文件或者目录数据。该方法成功执行返回整型值0,否则返回-1,相应的标准错误写入错误符errno。

应用程序开发中时长可能需要使用到Linux系统下当前的工作目录路径,Linux系统提供了三种方法来实现需要的工作目录获取功能。方法一getcwd共有两个参数,参数一为指向缓冲区定义的指针,用于存放获取到的工作目录绝对路径。参数二用于指定当前buf所指缓冲区大小,使用时需要注意的是缓冲区的指定必须足够存放获取到的工作路径。如果缓冲区指针指向的空间为NULL,该方法会根据传入的size参数来自动分配内存,如果指定的size也为0,则方法会根据工作目录字符串长度来决定使用malloc分配的内存空间,应用程序使用完后可以使用对应free释放资源。该方法成功调用将获取到的工作目录路径存放到指定的缓冲区中,返回自动分配的字符串指针,否则返回NULL,错误值写入errno。

获取工作目录的第二个方法为get_current_dir_name,该方法为GNU下扩展支持。如果系统环境变量中配置了pwd命令,那么该方法内部将会使用malloc动态定义一个足够大的数组空间用于存放获取到的当前工作目录路径名,方法成功调用后会直接将工作目录路径名返回。

第三个获取工作目录的方法由BSD支持定义,该方法不会使用动态内存申请空间来存放获取到的工作路径。文件系统内部定义维护一个最大路径长度PATH_MAX,根据传入的buf指针,该方法会将获取的路径放入缓冲区中。需要注意的是PATH_MAX依赖于不同的文件系统定义,从可移植以及安全的角度来讲,该方法并不被推荐使用。

介绍完毕上述Linux系统针对目录文件提供的常见操作方法,下面将会通过一个应用实例来演示目录操作在实际开发中的应用情况,更多的目录操作可以根据Linux系统man获取。实例代码编辑如下。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter1802.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示

/**

* 实例chapter1802

* 源文件chapter1802.cpp

* 目录操作实例

*/

#include <iostream>

#include <string>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <dirent.h>

#include <list>

using namespace std;

/**

*获取Linux当前工作目录.

*@param path 输入处理文件名参数.

*@return 处理成功返回0,否则返回-1

*/

int getLinuxCurrentDirectory(string& path)

{

charbuffer[512];

if(getcwd(buffer, 512) == 0)

{

return-1;

}

path = string(buffer);

return0;

}

/**

*扫描并获取当前目录下的文件.

*@param path
输入处理文件名参数.

* @param directory_file_list
输入文件列表list变量.

*@return 处理成功返回0,否则返回-1

*/

int listLinuxDirectory(const string&path,list<string> & directory_file_list)

{

directory_file_list.clear();

DIR* pDir = opendir(path.c_str());

if (pDir == 0)

{

return-1;

}

structdirent* pDirent;

while( (pDirent = readdir(pDir)) != NULL)

{

stringfoundPath = string(pDirent->d_name);

if(foundPath != (".") && foundPath != (".."))

{

directory_file_list.push_back(foundPath);

}

}

closedir(pDir);

return0;

}

/*主程序入口*/

int main()

{

stringpath; //定义字符串变量,表示文件路径名

list<string>directoryfileList; //定义list容器变量,存放目录文件列表

list<string>::iteratoriter; //定义容器遍历指针,用于遍历其中数据

if(getLinuxCurrentDirectory(path)< 0) //获取当前工作路径

{

cout<<"Getcurrent work Directory fail!"<<endl; //获取工作路径失败提示信息

}

else

{

cout<<"Currentwork path:"<<path<<endl; //否则打印成功获取的工作路径名

}

cout<<”pleaseinput deal path:”<<endl; //提示输入需要获取文件列表的目录名

cin>>path; //输入路径名

if(listLinuxDirectory(path,directoryfileList)< 0) //扫描并获取指定目录下的文件

{

cout<<"Listunder Directory file fail!"<<endl; //获取指定目录下文件失败提示信息

}

else

{

cout<<"currentDirectory:"<<endl; //打印输出当前目录文件提示

for(iter= directoryfileList.begin();iter != directoryfileList.end();iter++) //循环遍历文件列表容器

{

cout<<*iter<<""<<*(iter++)<<endl; //打印输出当前目录下的文件

}

}

return0;

}

本实例主要演示了Linux下目录基本操作使用情况,程序主要由主函数与两个目录基本操作函数构成,两个目录操作函数分别实现获取当前工作目录和扫描获取指定目录下文件列表的工作,具体程序讲解见程序剖析部分。

2.编辑makefile

Linux系统平台下需要编译源文件为chapter1802.cpp,相关联工程makefile文件编辑内容如下所示。

OBJECTS=chapter1802.o

CC=g++

chapter1802: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1802

clean:

rm -fchapter1802 core $(OBJECTS)

submit:

cp -f-r chapter1802 ../bin

cp -f-r *.h ../include

上述makefile文件套用了上个实例模板。之所以其中采用变量定义替换的方式,目的就是为了方便编译程序的替换。从makefile工程文件中可以看出,布局是相同的。不同的地方仅仅是代码的文件名、生成可执行程序的名称等,大大方便了每次都要重新编写一遍编译命令的编辑方式

3.编译运行程序

当前shell下执行make命令,编译生成可执行程序文件,通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,该程序运行结果如下所示。

[developer @localhost src]$make

g++ -c-o chapter1802.o chapter1802.cpp

g++ chapter1802.o -g -o chapter1802

[developer @localhost src]$make submit

cp -f -r chapter1802 ../bin

cp -f -r *.h ../include

[developer @localhost src]$cd ../bin

[developer @localhost src]$./chapter1802

Current workpath:/home/ocs/users/wangfeng/Linux_c++/chapter18/chapter1802/bin

input deal path:

/home/ocs/users/wangfeng/Linux_c++/chapter18/chapter1802/src

current Directory:

chapter1802.o makefile

chapter1802 chapter1802.cpp

4.程序剖析

本实例中主要实现Linux系统下两个目录相关操作的封装,一个为获取当前工作目录,另一个根据获取到的当前工作目录情况来列出当前工作目录下的所有目录以及文件并存放在相应的list容器中。首先实例中封装实现的getLinuxCurrentDirectory方法操作,该方法主要通过系统调用getcwd实现获取当前工作目录的功能。

该方法接口提供一个string型的字符串对象path,作为参数传入方法内部。该方法内部首先定义一个缓冲区buffer,大小为512字节,用于存放获取到的工作目录路径名。随后调用getcwd方法,将缓冲区变量名以及对应的大小传入,获取成功后将缓冲区中的工作路径名强制转换为string类型赋给path,供函数接口外部使用。该方法执行成功返回0,否则返回-1表示获取工作目录失败。

第二个目录操作方法接口listLinuxDirectory主要用于获取当前指定目录下的文件,包括目录文件与普通文件。该方法主要两个参数,第一个参数path用于传入指定的路径名,第二个参数则为STL中list容器对象,主要用于存放获取到的指定目录下的文件。该方法的内部首先将传入的list容器调用clear方法清空,然后调用opendir方法根据传入的指定路径名path。打开目录操作返回一个DIR对象指针,随后判断该指针是否为空,为空则直接返回-1。

定义目录结构信息结构体dirent对象指针pDirent,之后通过一个while循环,该循环内通过调用readdir函数,以读取打开的目录对应的操作返回结果判断为条件。如果读取总是成功,即当前目录下存在文件或目录,则执行while循环体内部代码,否则目录内容读取完毕则退出循环体。

该方法while循环体内部,每当读取到相应内容后,将返回结果给结构体信息对象指针pDirent,随后将该指针指向的目录结构体信息中的成员d_name值返回给字符串对象foundPath,随后判断读取到的内容是不是当前目录下“.”“..”两个目录,如果不是则将其放入对应的目录内容列表中。所有目录内容都读取完毕后,调用closeDir方法根据指向目录的指针关闭打开的当前目录。

18.3 Linux系统文件I/O操作

Linux系统内核针对文件系统操作处理提供了一系列的系统调用方法,通过这些系统调用为用户应用程序与系统内核搭建一个操作接口。开发者可以根据系统内核提供的文件API,从而轻松的在Linux系统应用程序中处理文件。Linux系统文件API主要包含文件创建、删除、打开、读写以及控制等,下面将会详细介绍Linux系统内核提供的文件操作API的应用情况。

18.3.1 Linux系统文件API简介

介绍Linux系统文件API之前,需要对Linux下文件描述符有一个认识。Linux下文件描述符为一个整数,用来表示每个被当前应用所打开的文件。随后的所有针对文件的操作基本都通过该文件描述符来进行。描述符为一个非负的整数,从0开始依次递增标识每个被打开的对应文件,当然Linux系统针对每个应用进程打开的文件数量有一个最大值的限制,这些值通常通过相应的配置可以作修改。

系统中的描述符有着一定的划分,比如0表示标准输入、1表示标准输出以及2表示标准错误等。系统会维护一个文件描述符的数组来组织和保存相应打开文件的描述符,用户只需要通过相应的系统调用获取打开文件的描述符就可以针对文件作其它所需要的操作。

Linux系统针对文件系统操作提供的API主要包括文件创建、打开、读写、关闭、删除、定位以及文件操作的控制和文件锁相关的系统调用方法。对应的系统函数调用包括creat(创建文件)、open(打开文件)、read(读取文件)、write(写入文件)、close(关闭文件)、unlink(删除文件)、lseek(文件定位)、fcntl(文件控制)以及针对文件锁提供的flock方法等。下面将会针对提及常用的文件操作系统调用作使用讲述。

18.3.2 Linux系统调用之文件创建与打开操作

Linux系统调用针对文件创建于打开操作提供两个系统方法,分别为creat与open函数,其中open函数根据需要可以设定为创建或者打开已经存在的文件。两个系统调用原型如下所示。

#include <sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

int creat(constchar *pathname,mode_t mode);

int umask(int )

int open(constchar *pathname,int flags);

int open(constchar *pathname,int flags,mode_t mode);

该类系统调用需要包含一些头文件定义,其中sys/types.h头文件定义包含系统提供的数据类型原始定义,sys/stat.h头文件主要包含文件状态的定义,头文件fcntl.h则包含文件控制定义内容。文件创建与打开系统调用包含creat与两个open重载函数定义。

函数creat与open调用成功时都会返回相应文件描述符,否则返回值为-1表示失败。其中方法creat主要参数有两个,第一个为需要创建文件指定的目录与文件名,第二个参数表示文件创建的模式定义。不同的文件模式指定可以针对文件处理作出多种限制。其中umask系统调用主要用于屏蔽掉指定文件操作权限,通常与creat方法配合使用。

open函数主要用于创建或者打开存在的文件,当指定打开的文件不存在时,根据flags参数设定可以重新创建一个指定文件并打开。该函数调用成功依然会返回一个文件描述符,失败时返回值为-1,具体系统提供的错误号可以根据man来查询该方法的帮助信息。open函数提供两个重载实现方法,第一个只包含两个参数,一个参数为指定文件全名,另一个参数则为文件创建或打开的方式标志。该标志值有多种分别表示文件打开读写方式、文件打开一般方式以及文件同步方式三类。

第一类文件打开读写主要包含3个标志值,分别为O_RDONLY文件只读方式、O_WRONLY文件只写方式以及O_RDWR文件读写方式三种。而第二类则主要包含O_CREAT、O_EXCL、O_APPEND以及O_TRUNC等多种文件操作方式。常见文件操作应用中该类前4个标志值比较常用,其余可以通过man帮助查询其使用说明。

其中O_CREAT标志表示打开文件操作调用时,如果指定的文件不存在,则创建并打开该文件;而O_EXCL一般与O_CREAT一起通过或运算使用,表明如果打开的文件不存在则创建指定的文件,如果指定文件已经存在则报错;O_APPEND标志表明文件打开将会以追加的方式,即保存原始指定文件内容,直接从原文件末尾开始文件操作;而与之相反的O_TRUNC标志值则表明存在的文件打开后会被清空其内容。以上后两个设置应用在日志文件中有着应用,比如保存当天日志文件时,遇到需要记录的数据打开文件后需要采用追加的方式,如此才能保证日志的连续性。

另外flags参数还包括一类文件方式设定,即文件同步方式。主要包含如O_SYNC表示打开的文件作一些写入操作都会阻塞进程,直到文件写入操作完成之后等这类标志值,实际使用中并不需要记忆如此多的标志值,只需要在使用时学会通过man来查询其说明即可。

open函数第三个参数mode用于创建新文件时,指定其新文件访问方式控制。主要包含当前文件属主访问权限、文件属组内其它成员访问权限以及Linux系统其它用户访问权限的设定。该标志值通过八进制数表示,提供了一系列的标志值来表明不同情况下文件访问权限的控制,供文件打开调用时设定。同样这些标志值可以通过man
open查询系统中指定调用说明来获取,具体标志值描述此处不作多讲述,这里主要介绍文件处理操作相关方法。

下面将会通过一个完整C++封装打开或创建文件的系统调用实例来演示上述文件操作系统调用在实际应用中的使用情况。该方法封装可以提供灵活的文件组合方式创建打开文件,实例代码编辑如下。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter1803.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示

/**

* 实例chapter1803

* 源文件chapter1803.cpp

* 封装创建和打开文件操作实例

*/

#include <iostream>

#include <string>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

using namespace std;

/**

*打开或者创建文件的方法枚举定义.

*内部定义几种文件创建或打开的标记值.

*/

enum OpenOrCreateMethod{ OpenOrCreate_Exist, //只打开存在的文件

OpenOrCreate_Append, //以追加方式打开文件,并且保存文件内容

OpenOrCreate_Excl, //只创建不存在的文件

OpenOrCreate_Trunc}; //打开或创建新的文件,并且销毁原来文件内容

/**

*文件读或者写的模式枚举定义.

*内部定义两种文件写或读基本属性标记值.

*/

enum FileReadOrWriteMode{ Read_Access = 0x01, //申请读权限标记

Write_Access = 0x02}; //申请写权限标记

/**

*文件属性枚举定义.

*内部定义几种文件基本属性标记值.

*/

enum FileAttributes{ FileExists = 0x01, //文件存在标记

RegularFile= 0x02, //普通文件标记

DirectoryFile=0x04, //目录文件标记

FileHidden= 0x08, //隐藏文件标记

FileReadOnly= 0x10}; //文件只读标记

/**

*打开文件方法.

* @param path 输入处理文件名参数.

* @param accessFileMode 输入存取文件标记参数.

*@param openMethod 输入文件打开或者创建方式

*@param fileAttributes
输入文件属性标记

* @return 处理成功返回打开文件的描述符,否则返回-1

*/

int openFile(const string& path, const intaccessFileMode,

constOpenOrCreateMethod openMethod,

constint fileAttributes)

{

intflags = 0; //定义标记变量

intpermFlags = 0; //定义文件权限标记变量

if ((accessFileMode & Read_Access) && (accessFileMode &Write_Access) ) //判断是否读写权限模式打开

//文件

{

flags|= O_RDWR; //标记变量与读写权限标志值之间进行按位或运算

//如果if判断结构中条件为真,则flags标记值赋值为读

//写都打开的标记值

}

else if (accessFileMode & Read_Access) //判断是否只读权限模式打开文件

{

flags |= O_RDONLY; //如果if判断结构中条件为真,则flags标记值赋值为只

//读标记值

}

else if (accessFileMode & Write_Access) //判断是否只写权限模式打开文件

{

flags |= O_WRONLY; //如果if判断结构中条件为真,则flags标记值赋值为只

//写标记值

}

else

{

cout<<"file open error"<< errno <<endl; //否则输出相应文件打开错误信息

return -1; //返回整型-1值表明文件打开失败

}

switch(openMethod) //选择文件打开方式

{

case OpenOrCreate_Exist: //打开已经存在的文件,如果设置存取文件的模式为写权

//限,同时定位文件末尾进行写数据

if (accessFileMode &Write_Access) flags |= O_APPEND; //设置相应的标记值

break;

case OpenOrCreate_Append: //打开文件,如果该文件已经存在,同时定位至文件末尾进行

//数据写入;否则新创建一个文件

flags |= O_CREAT | O_APPEND; //设置相应的标记值

break;

case OpenOrCreate_Excl: //创建文件,如果创建的文件已经存在,则打开文件出错;否

//则创建新的文件

flags |= O_CREAT | O_EXCL; //设置相应的标记值

break;

case OpenOrCreate_Trunc: //如果该文件已经存在,并且以可写的方式打开时,此标志位

//会令文件长度清为0,里面的数据清除;否则创建新文件

flags |= O_CREAT | O_TRUNC; //设置相应的标记值

break;

}

if (openMethod == OpenOrCreate_Excl ||openMethod == OpenOrCreate_Trunc) //判断打开方式,设置文

//件权限

{

if (fileAttributes & FileReadOnly) //判断文件设置为只读权限

permFlags = S_IRUSR; //则设置permFlags权限标记为只读标记值

else

permFlags = (S_IRUSR | S_IWUSR);//否则设置为可读写标记值

}

int fd = open(path.c_str(), flags,permFlags); //调用系统提供的打开文件操作,传入文件路径名,文件创建

//或者打开方式标记,以及权限标记

if (fd == -1) //系统文件打开操作将会返回文件句柄值,通过判断该标记值

//判断文件打开是否失败

{

cout<<"fileopen(path.c_str(), flags, permFlags) error= "<< errno <<endl; //如果文件创建、打开失败,则输

//出错误信息

return -1; //返回-1值

}

return fd; //否则文件打开或者创建成功,返回句柄供操作该文件的方法

//使用

}

/*主程序入口*/

int main()

{

stringpathfile; //主程序中定义字符串对象表示文件路径名

intfd; //定义文件句柄变量

cout<<"Pleaseinput pathfile:"<<endl; //输出输入文件路径名的提示信息

cin>>pathfile; //从屏幕输入该文件路径名

fd =openFile(pathfile,Read_Access|Write_Access,OpenOrCreate_Excl,RegularFile);//调用文件打开操作,并

//且设置相应的读写、以及打开文件方式

cout<<"Filefd:"<<fd<<endl; //打印获取的文件句柄值,查看文件打开是否成功

return0;

}

本实例主要通过枚举定义Linux文件相关属性参数,采用C++语言封装灵活的Linux文件创建与打开操作。本程序主要由主函数与文件创建、打开两个函数组成,具体程序讲解见程序剖析部分。

2.编辑makefile

Linux平台下需要编译的源文件为chapter1803.cpp,相关联的makefile工程文件内容编辑如下。

OBJECTS=chapter1803.o

CC=g++

chapter1803: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1803

clean:

rm -fchapter1803 core $(OBJECTS)

submit:

cp -f -rchapter1803 ../bin

cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后make submit提交程序文件至本实例bin目录中,随后通过cd命令定位至实例bin目录,该可执行程序运行结果如下所示。

[developer@localhost src]$make

g++ -c -ochapter1803.o chapter1803.cpp

g++ chapter1803.o -g -o chapter1803

[developer@localhost src]$make submit

cp -f -r chapter1803 ../bin

cp -f -r *.h ../include

[developer@localhost src]$cd../bin

[developer@localhost src]$./chapter1803

Please input pathfile:

/home/ocs/users/wangfeng/Linux_c++/chapter18/chapter1803/bin/testFile.txt

File fd:3

[developer@localhost src]$ll

总用量 16

-rwxr-xr-x 1 ocs linkage 15923 1月 7 06:56 chapter1803

-rw------- 1 ocs linkage 0 1月 7 06:57 testFile.txt

4.程序剖析

本实例实际是在Linux系统文件打开操作系统调用open之上使用C++封装实现了openFile接口,该封装实现的方法主要用于创建或打开指定文件。实例中首先定义三个枚举类型变量,分别表示低级文件操作中的文件打开方式,文件读写方式以及文件的基本属性。三个枚举类型变量中实际为C++的中的标识,该标识用于内部组合采用实际系统调用中的各种权限模式。

创建或打开文件方法openFile共提供四个参数,第一个参数path用于表示指定创建或打开的路径全名,第二个参数accessFileMode则用于指定创建或打开文件的读写模式,第三个参数openMethod为枚举类型OpenOrCreateMethod的对象,用于表示文件创建或打开的方式。最后一个参数fileAttributes表示文件的基本属性,即指定为普通文件、目录或者只读文件。

创建或打开文件方法内部首先定义两个整型变量分别为flags与permFlags表示文件创建或打开的方式以及指定的权限模式,并且初始化为0。内部先根据传入的文件打开时的存取权限信息,根据传入的标志值分别与读取、写入方式作&运算,如果提供两个标志值意味着文件打开方式为可读写即指定flags为O_RDWR。否则分别设置相应传入的打开方式对应的读或者写的方式,如果没有指定则默认输出出错信息,并通过errno号来说明相应错误。

方法内部随后通过switch结构来判断相应的文件创建或打开的基本方式,是以追加的方式O_APPEND打开文件,还是以O_CREAT| O_APPEND组合、O_CREAT
| O_EXCL组合还是O_CREAT | O_TRUNC组合方式来创建或者打开文件,根据传入的参数openMethod来决定。后通过if控制结构来设定文件打开时的权限模式。

if结构内部通过比较,文件属性是否指定为只读文件,如果为只读文件,则设置S_IRUSR只读权限,否则设置可读写权限S_IRUSR | S_IWUSR。最后调用Linux下系统调用open方法,传入相应的创建或者打开文件路径全名,传入文件打开的基本方式以及文件创建的基本权限标记,创建或打开指定文件。该方法返回一个文件描述符整型值,如果打开或创建成功将会返回对应的描述符,否则返回-1。最后成功情况下openFile方法返回整型文件描述符fd。

本实例程序主函数中,首先定义字符串对象pathfile表示路径全名,根据输入的路径名加上文件名,调用了openFile方法,本实例指定需要打开或创建的文件为pathfile,文件以可读写的方式打开即Read_Access|Write_Access,并且指定文件打开或创建方法OpenOrCreate_Excl,实际程序内部为O_CREAT
| O_EXCL即表示当指定文件不存在时,创建该文件,否则文件openFile调用方法出错。最后指定文件属性为普通文件,即内部实现拥有了可读写权限。

本实例实际运行时,根据提示传入相应的路径全名信息,随后调用openFile方法创建该文件,并通过ll –lst命令查看了新创建文件的权限为可读写。再次执行该程序创建同样文件时,根据设定会调用失败而直接返回。

18.3.3 Linux系统调用之文件读写操作

文件创建打开之后,自然需要通过写入以及读取方法来操作文件。Linux系统对于写文件与读文件提供了对应的write与read方法,该方法提供原型定义如下所示。

#include <unistd.h>

ssize_t write(int fd,const void *buf,size_t count);

ssize_t read(int fd,void *buf,size_t count);

使用系统调用文件读写方法需要包含常量符号定义的头文件unistd.h,才能正确调用涉及该头文件中常量符号的函数。实际上类型ssize_t在其头文件中定义为无符号整型,表明两个函数返回读写数据的字节数。两个方法操作成功时都会返回相应读写文件数据成功的字节数。

函数write主要用于往指定的缓冲区中写入count字节到描述符为fd的文件中,成功写入数据后该函数会返回实际写入数据的字节数,发生错误时会返回-1,相应的错误代码存放在errno中,可以在系统中查询到相关说明。

而函数read与之类似,只是数据走向的过程相反。read函数将指定文件描述符的文件读取count个字节数据,存放至指定的buf缓冲区中。调用该方法成功时返回同样为读取文件的实际字节数,错误则返回-1,错误代码存放入errno中可以查询帮助定位错误原因。

下面将会通过一个C++方法封装实现文件读写操作实例,来演示系统调用read与write函数的使用实际应用方式,同时提供C++封装相关操作的方法思路,实例代码编辑如下所示。

/**

*低级读取文件操作接口封装.

* @param fd 文件描述符.

* @param buffer 读取操作缓冲区.

* @param len 读取文件数据长度.

* @return 处理成功返回数据长度,否则返回-1

*/

int readFile(int fd, char* buffer, size_t len)

{

if (fd < 0 || !buffer) //判断传入文件描述符与缓冲区是否有效

{

return -1; //如果两者中之一不成功则返回-1

}

size_t readSize = read(fd, buffer, len); //调用系统文件读取方法,返回读取文件数据长度

if (readSize < 0) //判断读取文件是否成功

{

return -1; //如果读取文件数据长度小于0,表示不成功则返回-1

}

return readSize; //否则返回读取成功的数据长度

}

/**

*低级读取文件操作接口封装.

* @param fd 文件描述符.

* @param buffer 写入操作缓冲区.

* @param len 写入文件数据长度.

* @return 处理成功返回0,否则返回-1

*/

int writeFile(int fd, char* buffer, size_t len)

{

if (fd < 0 || !buffer) //判断传入的文件描述符与缓冲区是否有效

{

return -1; //如果两者之一存在不符合要求则返回-1

}

int writeSize=0, currWriteSize=0; //定义变量分别表示最终写入数据长度、当前写入数据长度

while(writeSize<len) //判断最终写入数据变量是否超出要求写入数据的长度

{

currWriteSize= write(fd, buffer+writeSize, len-writeSize); //如果没有,则写入len长度的数据

if(currWriteSize<0) //判断写入成功的数据长度是否小于0

{

cout<< "write file error,errno=" << errno <<endl; //如果写入不成功则打印其错误号

return -1; //返回-1表示写入操作不成功

}

writeSize+= currWriteSize; //将当前累加的写入数据长度累加至最终写入数据长度变量

}

return 0;

}

本实例主要采用C++封装实现了两个文件低级操作接口,分别为readFile与writeFile表示文件读取与写入操作功能。本实例接口主要配合上小节openFile方法配合使用,通过获取到指定文件的描述符fd来读取或者写入指定长度的数据,本实例演示由初学者可以配合上小节实例代码来实践验证。

封装实现的两个文件操作接口中,第一个readFile方法接口提供三个参数,参数fd表示指定文件的描述符,参数buffer为读取文件数据后存放的缓冲区,而参数len则表示读取文件数据的长度。该方法内部首先判断获取的文件描述符以及相应的缓冲区指针是否有效,有效后则调用系统提供的read方法,传入文件描述符,缓冲区指针以及相应指定的读取长度,读取文件中数据并返回真正读取的长度值放入readSize中,判断该读取到的数据长度,如果不小于0则直接返回已经读取的长度给调用者。

封装实现的writeFile写入文件数据方法,参数同样为三个,第一个为文件描述符fd,第二个为缓冲区指针,第三个为写入指定的数据长度。内部同样先判断文件描述符与缓冲区指针的有效性,之后定义两个整型变量,一个writeSize表示指定要写入文件数据的步长,另一个currWriteSize表示当前写入数据的长度。所后判断写入文件数据步长小于指定长度,那么调用系统调用write方法,该方法参数指定文件描述符,指定缓冲区从哪里开始将数据写入,并且指定写入多少长度数据。

返回写入数据存放到currWriteSize变量中,所后将已经写入的部分加到步长变量中继续再判断是否全部写入len长度数据,该方法成功返回0,否则返回-1。

18.3.4 Linux系统调用之文件关闭与删除操作

与前面介绍的Linux系统调用中创建与打开文件相对应的,系统调用提供了关闭与删除当前普通文件的操作函数。两个系统调用方法在Linux系统提供的原型如下所示。

#include <unistd.h>

int close(int fd);

int unlink(char *pathname);

Linux系统中文件关闭使用close函数,该方法主要根据传入的参数来关闭指定文件描述符的文件。该函数调用将会返回一个整型值,如果关闭文件成功则会返回整型值0,如果关闭文件出错会返回-1或者一个错误变量值。该错误变量可以为EBADF表明关闭的文件描述符不是一个有效的描述符,EINTR表示方法close调用被一个信号中断,而EIO标志值则表明出现I/O错误。通过系统提供的基本错误标识,可以帮助诊断调用该方法出错时的原因。

而unlink函数用来从文件系统中删除已经存在不需要使用的普通文件,该方法根据输入参数指定的文件全名来删除指定文件。但是需要注意的是指定文件需要在当前文件link的进程数为0的情况下才会真正的删除。如果文件上依然存在进程链接数,只能等到最终链接数为0才会真正的删除文件。该方法同样成功删除文件返回0,否则返回-1或者相应的errno。其中系统针对unlink提供的errno相当的多,通过man
2 unlink可以查询到该方法具体使用以及errno说明。

同样下面依然会采用一个完整实例来演示关闭与删除文件系统调用在实际应用程序中的使用情况,通过关闭删除之前创建的文件来说明该方法的使用,实例代码编辑如下所示。

/**

*关闭指定文件操作封装.

* @param fd 文件描述符.

* @return 处理成功返回0,否则返回-1

*/

int closeFile(int fd)

{

if (fd < 0 || ::close(fd) < 0) //直接判断文件描述符是否有效,调用关闭文件描述符方法

{

return -1; //如果处理不成功直接返回-1

}

return 0; //否则返回0

}

/**

*删除指定文件操作接口封装.

* @param path 文件路径全名.

* @return 处理成功返回0,否则返回-1

*/

int deleteFile(const string& path)

{

int result = 0; //定义结果标记变量

result = unlink(path.c_str()); //直接根据传入的文件全名,调用unlink系统方法删除

if (result < 0) //判断结果标记

{

return -1; //如果返回的标记值小于0则表示删除失败,返回-1值

}

return0; //成功则返回0

}

本实例同样针对系统调用提供的关闭以及删除文件的方法采用C++实现接口封装,该方法接口实现比较简单,内部通过判断传入的描述符以及获取调用系统调用的结果判断来实现相应的功能。

本实例演示同样可以配合上述小结的实例中封装的文件操作接口来实践,至此关于低级文件操作C++封装接口已经形成一个简单小规模的方法集。稍后将会在最后的综合实例中提供将这些封装的低级系统调用实现的C++应用接口封装成提供文件低级操作的类类型,在应用程序中连接使用,让初学者体会C++面向对象自定义类类型的优势。

18.3.5 Linux系统调用之文件控制操作

Linux系统针对指定的文件描述符作不同的控制操作,提供了名为fcntl系统调用函数。该方法将会根据传入的基本命令来操作控制指定描述符的文件。该系统调用方法原型定义如下所示。

#include <unistd.h>

#include <fcntl.h>

int fcntl(int fd,int cmd);

int fcntl(int fd,int cmd,long arg);

int fcntl(int fd,int cmd,struct flock *lock);

该方法使用需要包含如上两个头文件定义,函数主要通过传入的cmd命令来操作控制指定的文件描述符fd。其中arg参数为根据cmd命令需要接受的第三个参数设定。其中cmd命令包含多个标志值定义,比如F_DUPFD表示返回一个最小的大于或者等于参数arg并且可用的描述符,该描述符复制现有的描述符;命令F_GETFD与F_SETFD表示获取与设置文件描述符的标志;命令F_GETFL与F_SETFL表示获取与设置文件状态标志;F_GETOWN与F_SETOEN命令表示获取与设置异步I/O的所有权;最后F_GETLK与F_SETLKW则表示获取与设置文件记录锁。

系统调用文件控制操作方法在C++应用程序处理文件中应用相当少,很少需要封装实现这样的底层操作。此处不在列举C++封装操作实例来演示该方法的使用,但是fcntl在文件锁方面的控制在应用程序中有着一定的应用,下面将会就fcntl方法针对文件锁操作部分作详细的讲述。

18.4 Linux系统文件锁操作及应用

Linux系统下文件锁实现也是多进程之间实现共享数据同步机制的一种,该实现机制主要用于实际应用中遇到多进程同时处理同一文件下的数据同步问题处理。通常应用软件系统中,如果某个进程在操作当前一个文件进行读写,而另一个正在读取该文件的进程获取到的数据可能会遭到破坏,即可能读取到的数据不是正确所需的。这种情况下采用一种针对文件共享处理的方法来避免多进程访问同一文件造成的破坏问题,此时文件锁机制应运而生。

由于Linux基于Unix系统而来,因此针对文件锁操作Linux系统提供两个不同的系统方法。两个不同的方法来自于Linux系统对不同的标准的支持,方法flock来源于BSD标准的支持,而方法fcntl则针对Posix标准文件锁的操作实现。Linux系统下两个不同的文件锁操作方法原型如下所示。

//BSD标准衍生的文件锁操作方法

#include <sys/file.h>

int flock(intfd,int operation);

//Posix标准提出的文件锁操作方法

#include <unistd.h>

#include <fcntl.h>

int fcntl(intfd,int cmd,struct flock *lock);

Linux系统下标准的文件锁存在两种基本类型,即排他锁与共享锁。这两种锁分别对应着文件的可读写锁定情况,因此排他锁和共享锁又可以称为写锁与读锁。所谓的共享锁表现在文件来看,即当前文件可以被多个进程读取,也就是可以拥有多个共享锁。但是在某个特定时刻,文件只能被一个进程写入数据,此时加的应该是排他锁。排他锁一旦加在文件之上,那么该文件不允许其他进程再加任何锁,直到该排他锁释放为止。

从多进程并发操作文件角度来讲,排他锁实现了同一时刻只允许一个进程写入文件数据操作,从而避免共同写入文件数据造成数据一致性问题。而共享锁则最大限度允许不同进程在读取文件时的并发操作。Linux提供的文件锁机制系统调用实现上述最基本的两种锁形式,下面将会针对系统提供文件锁操作方法作详细的使用分析。

从BSD衍生而来的文件锁操作flock函数,该方法主要用于对系统中指定的文件进行加锁操作。该方法拥有两个参数,第一个参数为指定文件的描述符fd,前面已经分析过文件描述符是唯一标识当前文件的;第二个参数同样为一个整型参数变量,表示在指定文件上进行的锁操作,该参数存在一些标志值,用于分别表示文件上不同锁的操作。参数operation的标志值大致提供三个,分别为LOCK_SH(表示创建一个共享锁,文件处理期间共享锁可以被多个应用所拥有)、LOCK_EX(创建一个排他锁,该排他锁某个时刻只能被一个应用拥有)、LOCK_UN(删除当前进程创建的锁)。

Linux系统下文件锁的操作主要介绍Posix标准提供的方法,函数fcntl为文件控制方法重载实现的一种。该函数增加指向结构体flock的指针参数,结构体flock由内核定义维护对应着锁的基本信息,每个锁对应着一个flock结构体实例,众多实例在由系统内核维护。该锁结构体在Linux内核中定义如下。

struct flock

{



short l_type; //文件锁的基本类型

short l_whence; //支持记录锁,表示从当前文件的加锁从什么位置开始

short l_start; //文件记录锁开始偏移位置

off_t l_len; //文件锁锁住记录的长度

pid_t l_pid; //锁所属进程的id



};

相对于BSD衍生的文件锁操作,Posix标准下fcntl方法提供的文件锁操作功能更加的强大,事实上该函数支持文件控制等相当多的操作。从锁信息结构体来看,成员l_type值表示创建锁的基本类型,该类型通常通过方法中参数lock指针来指定设置。标志值F_RDLCK表示当前进程创建的为文件读锁,F_WRLCK表示当前创建的为文件写锁,而F_UNLCK则表示释放当前文件中指定的锁。

结构体成员l_whence的提供表明fcntl方法操作的文件锁支持针对文件中记录加锁的操作,记录锁的出现是很多数据库中都广泛应用的。该成员值设置用于表示文件中记录开始加锁的位置,其中主要包含三个标志值,SEEK_SET表明加锁记录从文件开始位置,SEEK_CUR表明文件加锁位置从文件当前处理的位置开始,而SEEK_END表示从文件的结尾开始。

结构体成员l_start通常与上述的l_whence成员设置值来确定文件中记录锁的开始位置,该成员主要表示从指定文件位置的偏移量开始加锁。而成员l_len则表示从文件记录位置开始向后的数据长度加锁,也就是记录锁锁住文件的数据长度了。最后一个成员表示当前创建该锁的进程。

上述结构体成员简单说明中,基本通过设置结构体成员值来决定锁的基本类型,记录锁的基本位置,锁记录的长度等信息来创建锁,而fcntl方法中的cmd命令则用来控制是获取当前文件中锁信息,还是创建锁。

cmd命令对应三个锁标志值,用于控制当前方法操作文件锁的功能。标志值F_SETLK表示通过外部设置锁结构体flock的成员l_type来决定当前创建锁的类型,是为读锁、写锁还是释放锁。标志F_GETLK表示当前进程可以通过文件描述符fd表示的文件获取锁的信息,该操作主要表明参数lock指向的结构在外部保存希望查询的锁类型,然后内部检查如果存在该锁将会将该锁的信息结构体返回给lock,如果不存在查询的锁,将会把成员l_type设置为释放锁返回基本信息给lock指针。另外标志值F_SETLKW,与F_SETLK不同的是该标志下的加锁操作一旦存在其它进程锁阻碍了该锁的创建,那么该进程进入休眠状态,直到该锁释放为止。

下面将会通过Posix标准文件锁的操作fcntl操作实现一个完整的文件锁应用实例,来演示实际C++应用程序中文件锁的使用情况,该实例代码编辑如下所示。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter1804.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter1804

* 源文件chapter1804.cpp

* 文件锁进程启动唯一性应用

*/

#include <iostream>

#include <fstream>

#include <unistd.h>

#include <fcntl.h>

#include <errno.h>

#include <string>

using namespace std;

/**

* 运行一次方法声明.

*@param filename 输入处理文件名参数.

* @return 处理成功返回0,否则返回-1

*/

int run_onlyone(const char *filename);

/*主函数入口*/

int main()

{

boolrun_only_flag = false; //定义bool型变量,表示唯一运行标记,初始化值为false

charchoice; //定义选择是否唯一运行标记的变量choice,类型为字符型

stringfull_file_path; //定义字符串类类型对象full_file_path,表明打开或创建的路径名

cout<<"是否要设置进程唯一运行(Y/N)"<<endl; //提示是否要进行进程唯一运行设置信息

cin>>choice; //通过输入对象,输入是否选择进程唯一运行的变量值

if(choice== 'y' || choice == 'Y') //判断输入选择字符是否为Y或y

{

run_only_flag= true; //如果输入为y或Y,则设置唯一运行标记值为true

cout<<"请输入文件路径全名:"<<endl; //提示输入全文件路径名

cin>>full_file_path; //通过输入对象,输入相应的路径名

}

else

run_only_flag= false; //否则设置唯一运行标记值为false

if(run_only_flag) //判断是否进程唯一运行的标记值

{

run_onlyone(full_file_path.c_str()); //如果为true,则执行进程唯一运行的方法

}

while(true) //执行while循环,条件始终为true

{

cout<<"testrun only once!"<<endl; //提示循环打印进程唯一性运行的信息

sleep(60); //睡眠60秒

}

return0;

}

/*运行一次方法的定义实现*/

int run_onlyone(const char *filename)

{

int fd, val; //定义整型变量fd表示创建或者打开文件的描述符,整

//型变量val表示文件控制方法调用的结果

charbuf[10]; //定义字符型数据,长度为10,存放进程标识

if((fd = open(filename, O_WRONLY | O_CREAT,

S_IRUSR| S_IWUSR | S_IRGRP | S_IROTH)) < 0) //if判断语句定义,判断条件调用了函数open方法,

//该方法新创建或打开存在的文件,采用open函数调用

//的结果作为判断条件

{

return-1; //如果open方法调用失败,返回值小于0,则直接返回

//值为-1则表示该方法调用失败

}

structflock lock; //定义设置文件记录锁状态的结构体对象lock

lock.l_type= F_WRLCK; //设置lock对象中l_type成员,设置锁的类型,当前设

//置为F_WRLCK,表示定义一个独占性写锁

lock.l_start= 0; //lock对象的成员l_start、l_whence和l_len用于设置

//对文件是否实现分段锁定操作;

//l_start为0则表示锁定区域开头位置为第一个,具体哪

//个区域的第一个位置由l_whence决定;

lock.l_whence= SEEK_SET; //l_whence为SEEK_SET表明锁定区域从文件开头为

//起始位置

lock.l_len= 0; //设置lock对象中l_len表示锁定区域的大小,设置为0,

//表示锁的区域从其起点开始直至最大可能位置

if(fcntl(fd, F_SETLK, &lock) < 0) //if控制结构中调用fcntl方法,方法调用的结果判断用于

//作为if控制结构的条件体;

//fcntl方法传入打开或创建文件的描述符fd;设置cmd

//命令值为F_SETL,表明该方法用于获取或者释放锁的

//操作;传入锁定信息结构体对象lock;

{

//如果该方法调用失败,则进入if控制体进行运行

if(errno == EACCES || errno == EAGAIN) //如果错误号设置为EACCES或EAGAIN,表明在该文

//件上加独占写锁失败,以此表明进程创建独占写锁已经

//存在,则输出出错信息

{

cout<<"进程已经在运行!"<<endl;

exit(0); //同时程序退出,确保进程运行的唯一性

}

else

return-1; //如果出现其它错误,则返回-1,交给方法调用的上层进

//行处理

}

if(ftruncate(fd, 0) < 0) //如果进程启动加独占写锁成功,则说明没有相同的进程

//存在,通过if控制结构中ftruncate方法来将打开的文

//件进行改变大小处理;

//ftruncate方法根据输入的文件描述符fd,将打开的文件

//按照设置的长度参数为0,将文件内容清空

{

return-1; //该方法如果调用出错,则返回-1,供外部处理使用

}

sprintf(buf,"%d\n", getpid()); //通过sprintf函数将getpid()返回的值作为源参数,根据

//"%d\n"格式定义,格式化存入buf中

//getpid()获取当前进程的id

if(write(fd, buf, strlen(buf)) != strlen(buf)) //通过if控制结构中调用文件write方法,将buf中格式

//化的进程id写入当前文件中

{

return-1; //该方法如果调用出错,则返回-1,供外部处理使用

}

if( (val = fcntl(fd, F_GETFD, 0)) < 0) //

return-1;

val|= FD_CLOEXEC;

if(fcntl(fd, F_SETFD, val) < 0)

return-1;

return0;

}

本实例程序主要用于演示文件锁在程序启动唯一性中的应用情况。程序主要由两个部分组成,函数run_onlyone定义了利用文件锁保证程序启动唯一性的具体流程,主函数主要实现了调用演示程序启动唯一性的过程,具体程序讲解见程序剖析部分。

2.编辑makefile

Linux平台下需要编译源文件为chapter1804.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter1804.o

CC=g++

chapter1804: $(OBJECTS)

$(CC)$(OBJECTS) -g -o chapter1804

clean:

rm-f chapter1804 core $(OBJECTS)

submit:

cp-f -r chapter1804 ../bin

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。

[developer@localhost src]$ make

g++ -c -ochapter1804.o chapter1804.cpp

g++ chapter1804.o -g -o chapter1804

[developer @localhost src]$ make submit

cp -f -r chapter1804../bin

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter1804

是否要设置进程唯一运行(Y/N)

Y

请输入文件路径全名:

/developer /wangfeng/linux_c++/chapter18/chapter1804/src/test.txt

test run only once!

[developer @localhost bin]$ ./chapter1804

是否要设置进程唯一运行(Y/N)

Y

请输入文件路径全名:

/developer /wangfeng/linux_c++/chapter18/chapter1804/src/test.txt

进程已经在运行!

4.剖析程序

从上述实例程序运行结果来看,首先在bin目录下启动可执行程序chapter1804,启动后程序提示是否需要设置进程唯一运行,输入Y之后表明程序需要设置相应的运行唯一性标识。

第二步为输入文件全路径名,即包含文件名和绝对路径。关于全路径名也可以通过环境变量定义加上文件名来组合实现。输入文件全路径名定位至文件test.txt,回车键之后程序调用run_onlyone方法,判断该进程是否已经存在,如果存在提示程序已经运行,新启动的同样的进程立即退出。

如果进程是第一次启动,则run_onlyone方法内部获取到相应的文件锁(写锁,又称为排他锁),之后将该进程的id号写入test.txt文件中。同时主流程中保持循环处理,通过while(true)内部保持60秒的睡眠时间机制保证程序第一次启动循环运行。

随后重复启动该程序,同理按照上述过程输入相关信息。最后因为启动的进程已经存在,提示进程已经在运行而强制退出。此时是因为调用run_onlyone方法后,在获取同样文件的锁时,前面启动的进程针对该文件采用的是排他锁,该进程不释放对该锁的控制,新启动的进程便无法获取该文件的访问权限,由此来保证进程启动的唯一性。

函数run_onlyone主要包含一个输入参数,filename为输入的文件全路径名,该参数为一字符串指针。函数内部定义两个整型变量fd和val,fd用于存放打开或创建文件之后的文件描述符,val用于存放获取文件描述符的标志信息。定义拥有10个元素的字符数组buf,用于作为字符串缓冲区。

函数中随后通过if控制结构,内部根据打开或创建文件的方法返回的结果存入变量fd,以此作为if控制结构的判断条件。如果打开或创建文件失败,则直接返回整型数-1。文件打开或创建函数open,第一个参数filename为打开或创建的文件名,第二个参数O_WRONLY
| O_CREAT表示为文件可写并且打开的文件不存在的情况下创建该文件,第三个参数S_IRUSR| S_IWUSR | S_IRGRP | S_IROTH表示该文件所有者具有可读、可写权限,并且用户组和其它用户组对其具有可读权限。

函数run_onlyone接下来定义锁信息结构flock,通过给其成员赋值的方式来设置创建文件锁的相关信息。本实例中锁的类型l_type设置为F_WRLCK,表明该锁为写锁即排它锁,拥有该文件排它锁意味着其它进程访问将会被排斥。l_whence设置为SEEK_SET表明加锁记录从文件开始位置,lock.l_start和lock.l_len赋值为0,表明该锁为文件级,并不需要设置相应的偏移量来表明锁定的记录数据。

随后通过fcntl方法创建排它锁,如果创建不成功则返回-1,如果该锁已经存在则输出进程已经运行信息,之后退出程序。锁创建完毕后,通过ftruncate将指定fd文件描述符所标识的文件清空。通过getpid方法获取到当前进程的pid,并且格式化放入缓字符串冲区buf,随后通过write方法写入文件中。

最后通过fcntl方法,设置标记值为F_GETFD获取文件描述符fd相应的close-on-exec标识值,存储在变量val中。变量val与FD_CLOEXEC作或运算,其结果用于设置文件描述符fd的close-on-exec标识值,因为默认情况下文件描述符close-on-exec状态为0,需要通过FD_CLOEXEC来进行设置。

设置了文件描述符close-on-exec状态值后,调用exec系列的进程控制方法后,如果close-on-exe标志值为0,此文件不会被关闭,如果不为0则文件会被关闭。

主函数中,定义布尔型变量run_only_flag表示是否需要设置进程唯一性运行标识。字符变量choice表示用户输入操作,字符串类型变量full_file_path表示文件全名。通过cout打印输出是否需要设置进程唯一运行的命令操作,通过cin输入相应的相应操作数据。

随后判断输入的操作是否需要进行进程启动唯一性保护,如果选择Y或y则设置run_only_flag为true,提示输入相应的文件全名,否则设置为false。通过if控制结构判断run_only_flag标记,为true则调用相应的run_onlyone方法进行保护。为了测试启动唯一性效果,通过while循环控制结构,设置为死循环方式,此时再次启动同样的进程才可以观察出效果。While循环中输出提示信息,另外通过sleep方法进行了间歇休眠,防止程序霸占cpu,当然该循环体中计算量小,因此对cpu占用不会太高。

18.5 实例18-1 Linux下低级文件操作C++封装实现

通过前面Linux文件系统基本简介以及系统针对文件提供的低级调用,下面将会根据前面介绍的文件以及目录的操作方法与使用情况,使用C++语言实现文件操作底层的类类型封装,封装Linux系统下相关文件的基本操作,可以将该类类型设计为软件组件,在需要实际应用环境中连接使用。

本类类型在前面小节分别封装实现文件低级操作接口的基础上,将这些操作集合在一起,以自定义类型的方式提供给开发者使用,应用程序中可以通过定义该类类型对象,像操作普通内置类型那样方便的操作文件,本实例代码编辑如下所示。

/**

* 实例chapter1805

* 源文件chapter1805.h、chapter1805.cpp

* 指针数组应用于数据排序实例

*/

//文件低级操作类型封装头文件chapter1805.h

#ifndef LOWGRADEFILEOPERATOR_H

#define LOWGRADEFILEOPERATOR_H

#include <iostream>

#include <string>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

using namespace std;

//文件打开或创建方法

/**

*打开或创建文件的枚举定义.

*定义几种文件打开方式标记值.

*/

enum OpenOrCreateMethod{ OpenOrCreate_Exist, //只打开存在的文件

OpenOrCreate_Append, //以文件追加的方式打开,保存文件原来内容

OpenOrCreate_Excl, //只创建不存在的文件

OpenOrCreate_Trunc}; //打开或创建新的文件,并且不保存原文件内容

/**

*文件读或者写的模式枚举定义.

*内部定义两种文件写或读基本属性标记值.

*/

enum FileReadOrWriteMode{ Read_Access = 0x01, //申请读权限标记

Write_Access= 0x02}; //申请写权限标记

/**

*文件属性枚举定义.

*内部定义几种文件基本属性标记值.

*/

enum FileAttributes{ FileExists = 0x01, //文件存在标记

RegularFile= 0x02, //普通文件标记

DirectoryFile= 0x04, //目录文件标记

FileReadOnly= 0x10}; //文件只读标记

/**

*低级文件操作类封装.

*/

class LowGradeFileOperator

{

public:

/**

*打开文件操作方法封装.

* @param path 输入处理文件名参数.

* @param accessFileMode 输入文件存取模式参数.

*@param openMethod 输入文件打开方式标记参数

*@param fileAttributes 输入文件属性标记参数

* @return 处理成功返回0,否则返回-1

*/

intlinuxOpenFile(const string& path,

constint accessFileMode,

constOpenOrCreateMethod openMethod,

constint fileAttributes);

/**

*关闭文件操作方法封装.

* @param fd
输入文件描述符参数.

* @return 处理成功返回0,否则返回-1

*/

intlinuxCloseFile(int fd);

/**

*读取文件操作方法封装.

* @param fd 输入文件描述符参数.

* @param pBuffer 输入读取文件缓冲区.

*@param bufLen 输入读取数据长度.

* @return 处理成功返回0,否则返回-1

*/

intlinuxReadFile(int fd, char* pBuffer, size_t bufLen);

/**

*写入文件操作方法封装.

* @param fd 输入文件描述符参数.

* @param pBuffer 输入写入文件缓冲区.

*@param bufLen 输入写入数据长度.

* @return 处理成功返回0,否则返回-1

*/

intlinuxWriteFile(int fd, const char* pBuffer, size_t bufLen);

/**

*获取当前工作目录操作方法封装.

* @param path 输入文件路径全名参数.

*@return 处理成功返回0,否则返回-1

*/

intgetLinuxCurrentDirectory(string& path);

/**

*获取指定目录文件列表操作方法封装.

* @param path 输入文件路径全名参数.

* @param directory_file_list 输入文件列表容器对象参数.

*@return 处理成功返回0,否则返回-1

*/

intlistLinuxDirectory(const string& path,list<string> &directory_file_list);

/**

*获取文件最后修改时间操作方法封装.

* @param path 输入文件路径全名参数.

* @param time 输入时间结构对象参数.

*@return 处理成功返回0,否则返回-1

*/

intgetFileLastModifiedTime(const string& path, time_t& time);

/**

*获取文件长度操作方法封装.

* @param path 输入文件路径全名参数.

*@return 处理成功返回0,否则返回-1

*/

size_tgetFileLength(const string& path);

/**

*获取文件基本属性操作方法封装.

* @param path 输入文件路径全名参数.

*@return 处理成功返回0,否则返回-1

*/

intgetFileAttributeFlags(const string& path);

};

#endif

该低级文件操作类型封装实例主要列出头文件定义,因为每个具体的方法实现在前面小节都已经实现过,可以参见光盘源码对应章节实例。初学者可以通过前面介绍静态库的使用方法,自行实现该类类型的封装定义实现,将其编译为相应的库并在应用实例程序中尝试使用封装的低级文件操作类LowGradeFileOperator实现基本文件操作功能。

18.6 小结

本章主要介绍了Linux系统下文件系统的基本结构,内核针对文件操作提供了比较低级的系统调用处理。上述小节分别讲述了Linux系统下文件系统中文件的基本类型、文件的基本属性、文件目录操作以及相应的文件IO操作,并且提供了在系统调用之上的C++封装实现。

Linux系统下文件低级操作提供了比较底层的文件系统实现,包括不同类型的文件处理。应用程序中可以使用更加灵活的方式根据内核提供的相依系统调用以及对应的标志值,组合提供功能更加强大的处理接口。底层的操作提供实际为应用扩展提供了很好的平台,Linux系统通常提供的系统调用都是功能比较单一的底层接口,为应用程序与内核之间操作的接口,实际应用中可以使用高级的语言针对低级系统调用封装实现功能更加强大的方法接口。

本章的最后将这些底层接口统一封装成C++面向对象中的类类型,供应用中作为库或者组件来连接使用,初学者从中可体验C++面向对象的基本特性的优势。下面章节将会提供C++中针对文件处理提供的库类型操作,实际上初学者从中可以看出,C++应用程序库核心接口大致也是从低级系统调用封装实现的,但是在此基础上提供了更多的操作功能,让接口操作变得更加简便和功能强大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: