//简单的文件拷贝
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc !=3 )
{
printf("pls input message format as:%s src_file des_file\n",argv[0]);
exit(EXIT_FAILURE);
}
int fd_src,fd_dst;
fd_src = open(argv[1],O_RDONLY|O_CREAT,0644);
if(-1 == fd_src)
{
perror("open");
exit(EXIT_FAILURE);
}
fd_dst = open(argv[2],O_WRONLY|O_CREAT,0644);
if(-1 == fd_dst)
{
perror("open");
exit(EXIT_FAILURE);
}
char buf[1024];
int ret = 0;
memset(buf,'\0',1024);
while(1)
{
ret = read(fd_src,buf,1024);
if(ret == -1)
{
perror("read");exit(EXIT_FAILURE);
}
else if(ret == 0)
break;
else
write(fd_dst,buf,ret);
}
close(fd_src);
close(fd_dst);
return 0;
}
守望者成才网(http://watchmen.cn/)
本课目标
(1)编程目标:实现磁盘文件的拷贝操作。cp
(2)理解系统调用、文件描述符以及对文件及文件描述符操作。
主要知识点
(1)认识系统调用与内核对象。
(2)如何访问磁盘文件内容。
(3)磁盘文件内容的操作。
(4)对文件描述符的操作。
课程内容
一、内核空间与用户空间,文件描述符
Linux 操作系统内核态(空间)与用户态(空间)。
磁盘文件也是由 OS 来管理,因此,要访问需要通过系统调用来实现。怎么让一个程序(进程)与
要操作的文件建立联系。Watchmen 成才网(版权所有)http://www.watchmen.cn
01_Linux 高级程序设计 配套视频 Watchmen 出品@版权所有
Posix 磁盘文件内容管理 修订时间:2014-05-10 V0.9 页码 2/7
要去访问磁盘的文件,必须通过系统调用来返回一个与该文件相关联的 ID,这个 ID 就是文件描述
符 file descriptor。
Linux 操作系统提供了 open 系统调用,任何的进程要去访问一个文件,首先使用 open 打开这个文
件,系统将返回一个编号,即与这个文件相关联的文件描述符。
(1)用户程序从用户空间向内核空间提交了打开申请。
(2)操作系统会在内核中去检查请求是否合法,如果合法,在内核中申请这个打开文件相关的信
息(读写位置,在磁盘中的位置…..全用 struct files 来存储)。并且添加到当前进程的 PCB 块中的打开文
件列表数组中。对应的这个数组下标即文件描述符值。
(3)将这个文件描述符返回给用户空间,用户空间接下来对这个文件进行读写就通过这个编号
值。
系统默认为每个进程打开了三个文件:
printf/scanf
文件描述符 0(标准输入,键盘 STDIN_FILENO),1(标准输出,显示器 STDOUT_FLIENO),2
(标准错误输出,显示器 STDERR_FILENO)
二 、IO 系统调用函数
建立与断开联系 open/close
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, //打开文件路径
int flags); //打开的方式等 flags
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
只读方式 O_READONLY
只写方式 O_WRITEONLY
读写方式 O_RDWR
追加方式 O_APPEND
/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
located on an ext2 file system */
#define O_ACCMODE 0003
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_APPEND 02000
#define O_CREAT 0100 /* not fcntl */
#define O_EXCL 0200 /* not fcntl */
#define O_NOCTTY 0400 /* not fcntl */
#define O_TRUNC 01000 /* not fcntl */
#define O_NONBLOCK 04000
#define O_NDELAY O_NONBLOCK
#define O_SYNC 010000
#define FASYNC 020000 /* fcntl, for BSD compatibility */
#define O_DIRECT 040000 /* direct disk access hint - currently ignored */
#define O_LARGEFILE 0100000 //大于 2G 的文件
#define O_DIRECTORY 0200000 /* must be a directory */
#define O_NOFOLLOW 0400000 /* don't follow links */
#define O_ATOMICLOOKUP 01000000 /* do atomic file lookup */
O_CREAT|O_RDWR
如果创建文件, 这个新文件的权限是什么?怎么来描述?Watchmen 成才网(版权所有)http://www.watchmen.cn
01_Linux 高级程序设计 配套视频 Watchmen 出品@版权所有
Posix 磁盘文件内容管理 修订时间:2014-05-10 V0.9 页码 4/7
int open(const char *pathname, int flags, mode_t mode);
新创建一个文件的真正权限是 mode & ~umask
这个文件打开成功,将返回一个新的文件描述符值,后面针对这个文件的操作就使用这个文件描述
符。
读写文件内容 read/write
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//从 fd 所指向的文件中读取 count 字节到 buf 为首地址的内存空间中。
ssize_t write(int fd, const void *buf, size_t count);
//往 fd 所指向的文件中写入 count 字节,这个内容存放在 buf 为首地址的内存空间中。
文件位置的修改 lseek
对当前文件的读写位置进行定位。可以在文件中添加空洞。
off_t lseek(int fd, off_t offset, int whence);
SEEK_SET
The offset is set to offset bytes.
SEEK_CUR
The offset is set to its current location plus offset bytes.
SEEK_END
The offset is set to the size of the file plus offset bytes.
也可以用这个函数来实现文件大小的获取。把文件的读写位置设置为文件结束,因为这个函数返回
当前读写位置距离文件头的偏移字节数。
lseek+write=pwrite
lseek+read =pread
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
三、 文件描述符操作
(1)复制功能。用处,第 9 章管道时会提到重定向的应用 cat test>test.txt。
复制后,两个文件描述符都指向同一个文件表项,即使用这两个文件描述符中的一个就会影响读写
相关的信息。
dup/dup2/fcntl
int dup(int oldfd); //复制 oldfd 这个文件描述符,返回在调用此函数前最小未使用的文件描述符Watchmen 成才网(版权所有)http://www.watchmen.cn
01_Linux 高级程序设计 配套视频 Watchmen 出品@版权所有
Posix 磁盘文件内容管理 修订时间:2014-05-10 V0.9 页码 5/7
值。
close(0);
dup(3);
这个代码可以实现输入的重定向。默认从 0 读数据,现在 0 指向的表项被复制为 3,这样从 0 读实
际上就是从 3 指向的文件读,也就实现的输入的重定向。
int dup2(int oldfd, int newfd);
把 oldfd 复制为 newfd,如果 newfd 已经对应一个打开的文件,则先关闭它。
dup2(3,0);
int fcntl(int fd, int cmd, ... /* arg */ );
cmd:F_DUPFD.
fd_dup = fcntl(new_copy_fd, F_DUPFD);
(2)锁定功能。并发的环境下,除了使用并发的工具来保护共享文件外,也可以使用文件锁。
flock
int flock(int fd, int operation);
LOCK_SH Place a shared lock. More than one process may hold a shared lock for a given file at a
given time.共享读。
LOCK_EX Place an exclusive lock. Only one process may hold an exclusive lock for a given file at a
given time.排它锁。
LOCK_UN Remove an existing lock held by this process.
LOCK_NB 非阻塞式申请。
强调一下,这里指的是锁定文件描述符。锁定这个文件表项。防止被其它的进程访问,锁整个文
件。
如果要锁定某一部分,可以使用 fcntl 函数。
fcntl(fd,F_SETLKW,struct flock*);
第三个参数如下定义:
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start: 类似于 lseek 函数
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */ 起始位置偏移
off_t l_len; /* Number of bytes to lock */ 结束位置偏移
pid_t l_pid; /* PID of process blocking our lock 进程号
(F_GETLK only) */
...
}; Watchmen 成才网(版权所有)http://www.watchmen.cn
01_Linux 高级程序设计 配套视频 Watchmen 出品@版权所有
Posix 磁盘文件内容管理 修订时间:2014-05-10 V0.9 页码 6/7
(3)属性控制。权限状态,拥有者等。
fcntl:当前的读写状态,当前文件描述符所在的进程等系列信息。参阅书上的 5.2.7 内容。
(4)提高效率,同步磁盘。
因此,可以使用 mmap 函数来将某个打开的文件映射到虚拟地址空间,以后操作这个虚拟地址空间
就类似于操作这个文件。具体在第 2 章的内存管理视频中已经介绍。
fcntl 锁定文件示例
进程 A 进程 B
都需要对同一个文件进行写入操作
为了让定入操作是排它的,使用文件锁操作。
本章示例代码:实现大于 2G 的文件的拷贝操作。如果文件大于 2G,偏移超过 int_32 类型的限制,
为了实现大文件的拷贝,必须使用宏。
(1)在所有的头文件包含加加上
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
(2)在编译时,加上宏
gcc –D_LARGEFILE64_SOUCR –D_FILE_OFFFSET_BITS=64
编程作业
(1)获取当前系统信息。包括 cpu 的频率,内存利用率。