步步LINUX C--文件I/O操作

熟悉linux系统的同学都知道,所有的设备在linux下都被当作文件来处理,因此了解文件的I/O操作是十分重要的。


1/具体来说,有以下几种文件类型(控制台ls -l命令的第一列即为文件类型):

符号 类型 符号 类型
s 套接字文件 d 目录文件
l 链接文件 b 块设备文件
c 字符设备文件 p 管道文件
- 普通文件

另外,有几个特殊的文件,标准输入,标准输出,标准出错

类型 文件描述符 说明
标准输入 0 它是命令的输入,默认是键盘,也可以是文件或其他命令的输出。使用'<'或'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
#include 
int 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);

关闭函数
#include
int 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更改链接文件本身的所有者而不是所指向文件的所有者。

修改文件名
#include
int rename(const char *oldfname, const char *newfname);

成功后返回0;出错返回-1。
若oldfname指向普通文件且newfname存在指向普通文件,则newfname文件被删除,重命名成功。如果newfname存在指向目录文件,出错。
若oldfname指向目录文件且newfname存在指向目录文件,则newfname文件被删除,重命名成功。如果newfname存在指向普通文件,出错。

修改文件长度
#include
#include
int 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操作基本总结完毕。一些特殊的文件,管道文件会在进程通信中谈到,套接字文件会在网络编程中谈到。


(完)

你可能感兴趣的:(Linux,C语言)