Application|linux应用编程笔记(5)系统调用文件编程方法实现文件复制

摘要:介绍了系统调用文件编程常用的函数,以及学习这些函数的一般步骤,最后通过一个实例使用上述函数实现了文件复制功能。


一、系统调用文件编程常用函数
创建
int creat(const char *filename, mode_t mode);
参数mode指定新建文件的存取权限。


打开
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open函数有两个形式,其中pathname是要打开的文件名(包含路径名称,缺省是认为在当前路径下面),flags可以取下面的一个值或者是几个值的组合:
标志 含义
O_RDONLY 以只读的方式打开文件
O_WRONLY 以只写的方式打开文件
O_RDWR 以读写的方式打开文件
O_APPEND 以追加的方式打开文件
O_CREAT 创建一个文件
O_EXEC 如果使用了O_CREAT而且文件已经存在,就会发生一个错误
O_NOBLOCK 以非阻塞的方式打开一个文件
O_TRUNC 如果文件已经存在,则删除文件的内容
O_RDONLY、O_WRONLY、O_RDWR三个标志只能使用任意的一个


以O_CREAT为标志的open实际上实现了文件创建的功能,因此,下面的函数等同creat()函数:
int open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
mode可以是以下情况的组合:
S_IRUSR 用户可以读
S_IWUSR 用户可以写
S_IXUSR 用户可以执行
S_IRWXU 用户可以读、写、执行
S_IRGRP 组可以读
S_IWGRP 组可以写
S_IXGRP 组可以执行
S_IRWXG 组可以读写执行
S_IROTH 其他人可以读
S_IWOTH 其他人可以写
S_IXOTH 其他人可以执行
S_IRWXO 其他人可以读、写、执行
S_ISUID 设置用户执行ID
S_ISGID 设置组的执行ID


除了可以通过上述宏进行“或”逻辑产生标志以外,也可以自己用数字来表示,Linux总共用5个数字来表示文件的各种权限:第一位表示设置用户ID;第二位表示设置组ID;第三位表示用户自己的权限位;第四位表示组的权限;最后一位表示其他人的权限。每个数字可以取1(执行权限)、2(写权限)、4(读权限)、0(无)或者是这些值的和。例如,要创建一个用户可读、可写、可执行,但是组没有权限,其他人可以读、可以执行的文件,并设置用户ID位。那么,我们应该使用的模式是1(设置用户ID)、0(不设置组ID)、7(1+2+4,读、写、执行)、0(没有权限)、5(1+4,读、执行)即10705:
open("test", O_CREAT, 10705);
【Application|linux应用编程笔记(5)系统调用文件编程方法实现文件复制】上述语句等价于:
open("test", O_CREAT, S_IRWXU|S_IROTH|S_IXOTH|S_ISUID );
如果文件打开成功,open函数会返回一个文件描述符,以后对该文件的所有操作就可以通过对这个文件描述符进行操作来实现。


读写
在文件打开以后,我们才可对文件进行读写,Linux中提供文件读写的系统调用是read、write函数:
int read(int fd, const void *buf, size_t length);
int write(int fd, const void *buf, size_t length);
其中参数buf为指向缓冲区的指针,length为缓冲区的大小(以字节为单位)。函数read()实现从文件描述符fd所指定的文件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数。函数write把length个字节从buf指向的缓冲区中写到文件描述符fd所指向的文件中,返回值为实际写入的字节数。

定位
对于随机文件,我们可以随机的指定位置读写,使用如下函数进行定位:
int lseek(int fd, offset_t offset, int whence);
lseek()将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。参数whence可使用下述值:
SEEK_SET:相对文件开头
SEEK_CUR:相对文件读写指针的当前位置
SEEK_END:相对文件末尾
offset可取负值,例如下述调用可将文件指针相对当前位置向前移动5个字节:
lseek(fd, -5, SEEK_CUR);
由于lseek函数的返回值为文件指针相对于文件头的位置,因此下列调用的返回值就是文件的长度:
lseek(fd, 0, SEEK_END);


关闭
当我们操作完成以后,我们要关闭文件了,只要调用close就可以了,成功则返回0,失败返回-1,其中fd是我们要关闭的文件描述符:
int close(int fd);

dup和dup2
int dup(int oldfd);
int dup2(int oldfd, int newfd);
如果调用成功,这两个函数都返回新分配或指定的文件描述符,如果出错则返回-1。dup返回的新文件描述符一定该进程未使用的最小文件描述符,这一点和open类似。dup2可以用newfd参数指定新描述符的数值。如果newfd当前已经打开,则先将其关闭再做dup2操作,如果oldfd等于newfd,则dup2直接返回newfd而不用先关闭newfd再复制。
newfd=dup(oldfd);
dup2(oldfd,newfd);
是相同的,但是第一句的newfd是系统分配的,未使用的最小描述符,而第二句的newfd可以自己指定。结果都是让newfd指向oldfd。


读取目录
struct dirent *readdir(DIR *dir);
说明:返回参数dir目录流的下个目录进入点. 结构dirent定义如下:
struct dirent{
ino_t d_ino; //此目录进入点的inode
ff_t d_off; //目录文件开头至此目录进入点的位移
}


二、系统调用文件编程学习方式
1.借助工具书,查找函数名和用法,例如《Unix环境高级编程》
2.借助Linux 下man命令,例如:#manclose,就可以看到返回值,参数,需要的头文件等等。
3.实践编写代码


三、利用上述系统调用函数实现文件复制功能
首先,要实现一个文件复制的功能,要做到以下几步:
1.打开源文件。
2.打开目标文件。
3.读取源文件。
4.写入目标文件。
5.关闭文件。
我们根据上面的步骤会用到的函数,open,read,write和close,他们需要的头文件包括:
#include
#include
#include
#include

编写的函数如下:

#include #include #include #include int main(int argc,char *argv[]) { intfd_s=0; //定义源文件描述符 intfd_t=0; //定义目标文件描述符 charbuf[256]; //读出来的数据存放在这里,堆栈上的空间,省着点用 intcount=0; //读出来实际的字节返回给count/*以只读方式打开源文件,这里argv[1]存放源文件的路径名*/ /*判断打开是否成功*/ fd_s=open(argv[1],O_RDONLY); if(fd_s<0) { printf("opensource file error,exit!\n"); } /*以读写方式打开源文件,这里argv[2]存放源文件的路径名*/ /*如果目标文件不存在就创建它,666是创建的权限,然后判断打开是否成功*/ fd_t=open(argv[2],O_RDWR|O_CREAT,0666); if(fd_t<0) { printf("opentarget file error,exit!\n"); } /*读取源文件数据,每次实际读写的字节数返回给count,读写的数据存放在buf里面*/ /*判断count是否不为0,这样就继续循环往里写*/ while(count=read(fd_s,buf,256)) { write(fd_t,buf,count); }/*关闭打开的两个文件*/ close(fd_s); close(fd_t); return0; }




按照下面步骤编译运行:
Application|linux应用编程笔记(5)系统调用文件编程方法实现文件复制
文章图片

最后得到的结果如下:
Application|linux应用编程笔记(5)系统调用文件编程方法实现文件复制
文章图片

和我之前输入到源文件里的内容一样,成功!
这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!

    推荐阅读