熟悉linux系统的同学都知道,所有的设备在linux下都被当作文件来处理,因此了解文件的I/O操作是十分重要的。
1/具体来说,有以下几种文件类型(控制台ls -l命令的第一列即为文件类型):
符号
类型
符号
类型
s
套接字文件
d
目录文件
l
链接文件
b
块设备文件
c
字符设备文件
p
管道文件
-
普通文件 另外,有几个特殊的文件,标准输入,标准输出,标准出错
类型
文件描述符
说明
标准输入
0
它是命令的输入,默认是键盘,也可以是文件或其他命令的输出。使用'
标准输入
1
它是命令的输出,默认是屏幕,也可以是其他文件。使用'>'或'1>'符号进行重定向。
标准出错
2
它是命令出错信息的输出,默认是屏幕,也可以是其他文件。使用'2>'符号进行重定向。
2/LINUX通过文件权限,来管理文件的读/写/执(wrx)行权限。
3/文件操作函数总结:
在开始列写具体函数之前,我们首先列出文件I/O中的常用参数表:
flag参数(头文件:fcntl.h)
取值
含义
O_RDONLY
以只读方式打开文件
O_WRONLY
以只写方式打开文件
O_RDWR
以读写方式打开文件
O_CREAT
若要打开的文件不存在,则创建一个。权限在mode参数中说明
O_EXCL
与O_CREAT配合使用以验证一个文件是否存在
O_TRUNC
如果文件存在,且以只读或只写方式打开,则将其长度截短为0
O_NOCTTY
如果文件描述符指向终端设备,则不将此设备分配为此进程控制终端
O_APPEND
写入时追加到文件结尾
O_NONBLOCK
将后续的I/O操作设置为非阻塞方式
O_NONELAY
功能不那么完善的O_NONBLOCK
O_SYNC
只有数据被写入外存或其他设备之后操作才返回
mode参数(头文件:fcntl.h)
取值
八进制
含义
S_ISUID
04000
设置用户识别号
S_ISGID
02000
设置组号
S_SVTX
01000
粘贴位
S_IRUSR
00400
文件所有者的读权限位
S_IWUSR
00200
文件所有者的写权限位
S_IXUSR
00100
文件所有者的执行权限位
S_IWGRP
00040
该组用户的读权限位
S_IRGRP
00020
该组用户的写权限位
S_IXGRP
00010
该组用户的执行权限位
S_IROTH
00004
其他组用户的读权限位
S_IWOTH
00002
其他组用户的写权限位
S_IXOTH
00001
其他组用户的执行权限位
S_IRWXU
00600
文件所有者的读+写+执行权限
S_IRWXG
00060
该组用户的读+写+执行权限
S_IRWXO
00006
其他组用户的读+写+执行权限
whence参数(头文件:sys/types.h)
取值
含义
SEEK_SET
将该文件的位移量设置为距文件开始处offset个字节处
SEEK_CUR
将该文件的位移量设置为距当前位置offset个字节处。offset可正可负。
SEEK_END
将该文件的位移量设置为距文件结尾处offset个字节处offset可正可负。
cmd参数(头文件:fcntl.h)
取值
相应操作
F_DUPFD
复制一个现存文件描述符
F_GETFD
获得文件描述符
F_SETFD
设置文件描述符
F_GETFL
获得文件状态标志
F_SETFL
设置文件状态标志
F_GETOWN
获得异步I/O权
F_SETOWN
设置异步I/0权
F_GETLK
获得记录锁
F_SETLK
设置记录锁,不等待
F_SETLKW
设置记录锁,必要时等待
下面是文件操作的函数以及功能的简单描述:
文件描述符的I/O操作
打开函数
#include#include#includeint open( const char * pathname, int flags);
int open( const char * pathname,int flags, mode_t mode);
两个函数在成功后都返回文件描述符,以用作后续操作,并且将该文件的引用计数器值加1;出错返回-1。mode参数可以直接使用八进制表示。
创建函数
#include int creat(const char *pathname, mode_t mode);
成功则返回文件描述符,以用作后续操作;出错返回-1。创建成功后以只写方式打开文件。
creat函数等效于open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
如果想创建后以读写方式打开,则
open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode);
关闭函数
#includeint close(int fd);
成功后该文件的引用计数器值减1,并且返回0;出错返回-1。
定位函数
#include off_t lseek(int fd, off_t offset, int whence);
成功后返回新的文件偏移量;出错返回-1。offset为正数时向后移动,为负数时向前移动。
读函数
#include ssize_t read(int fd, void *buf, size_t nbytes);
成功后返回读取到的字节数;出错返回-1。
读谱通文件:读到nbytes之前已经达到文件结尾,则返回读到的字节数。下一次调用时返回0;
读终端设备:通常一次最多读一行;
读网络设备:网络中的缓冲限制可能造成实际读取的字节数小于nbytes;
读面向记录的设备:一次只能读一个字符。
写函数
#include ssize_t write(int fd, const void *buf, size_t nbytes);
成功后返回写入的字节数;出错返回-1。
write+lseek可以产生空洞文件。
文件的属性操作
在学习文件属性操作函数的时候,我们会发现很多函数的名称十分熟悉。这些不是控制台的命令吗?没错,控制台就是通过调用这些函数实现指定的shell命令。
修改权限
#include #include int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
两个函数功能一致,一个针对文件路径操作,一个针对文件描述符操作。成功后返回0;出错返回-1。
使用者需具有对该文件的所有权或者为ROOT用户方可操作成功。
修改所有者
#include #include int chown(const char *pathname,uid_t owner,gid_t group);
int fchown(int fd,uid_t owner,gid_t group);
int lchown(const char *pathname,uid_t owner,gid_t group);
前两个函数功能一致,一个针对文件路径操作,一个针对文件描述符操作。成功后返回0;出错返回-1。
lchown更改链接文件本身的所有者而不是所指向文件的所有者。
修改文件名
#includeint rename(const char *oldfname, const char *newfname);
成功后返回0;出错返回-1。
若oldfname指向普通文件且newfname存在指向普通文件,则newfname文件被删除,重命名成功。如果newfname存在指向目录文件,出错。
若oldfname指向目录文件且newfname存在指向目录文件,则newfname文件被删除,重命名成功。如果newfname存在指向普通文件,出错。
修改文件长度
#include#includeint truncate(const char * pathname,off_t len);
int ftruncate(int fd,off_t len);
两个函数功能一致,一个针对文件路径操作,一个针对文件描述符操作。成功后返回0;出错返回-1。
修改文件性质
#include #include #include int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
fcntl的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。第一个返回新的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。
文件的其他操作
文件状态
#include #include int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
前两个函数功能一致,一个针对文件路径操作,一个针对文件描述符操作。成功后返回0;出错返回-1。
lstat函数返回链接文件的相关信息,而不是所指向文件的相关信息。
文件描述符操作
#include int dup (int fd);
int dup2(int fd, int fd2);
两个函数复制文件描述符,成功后都返回新的文件描述符;出错返回-1。
这个函数通常用来重定向,例如dup2(fd,1),可将输出重定向到文件描述符所指文件。
同步函数
#include void sync(void);
int fsync(int fd) ;
成功后返回0;出错返回-1。用于强制将系统缓存区内容写入文件。
目录函数
#include #include #include #include int mkdir(const char* pathname,mode_t mode);
int rmdir(const char* pathname);
DIR *opendir(const char *pathname);
struct dirent *readdir(DIR *dp);
int closedir(DIR *dp);
成功后返回0;出错返回-1。
rmdir函数只能用于删除空目录。
工作目录
#include int chdir(const char *path );
int fchdir(int fd);
char *getcwd(char *buf, size_t size);
前两个函数功能一致,一个针对文件路径操作,一个针对文件描述符操作。成功后返回0;出错返回-1。
getcwd函数用于获取当前工作目录。size要足够容纳这个字符串。
链接文件
#include #include int link(const char *pathname1, const char *pathname2);
int unlink(const char *pathname);
int remove(const char *pathname);
int symlink(const char *actualpath, const char *sympath);
int readlink(const char *pathname, char *buf, int bufsize);
前四函数成功后返回0;出错返回-1。readlink函数成功后返回读取字节数;出错返回-1。
link函数用于创建硬链接,symlink用于创建符号链接。
至此文件I/O操作基本总结完毕。一些特殊的文件,管道文件会在进程通信中谈到,套接字文件会在网络编程中谈到。
(完)