[UNIX]C语言中的文件操作函数

文件描述符

对于内核而言,所有打开的文件都是通过文件描述符引用.文件描述符通常是一个非负整数.

按照惯例,UNIX系统shell把文件描述符0与进程的标准输入关联,文件描述符1与进程的标准输出关联,文件描述符2与标准错误关联.

<unistd.h>中定义了符号常量STDIN_FIELNO,STDOUT_FILENO,STDERR_FILENO.

文件描述符的范围是0~OPEN_MAX-1.

文件的类型

  1. 普通文件
  2. 目录文件
  3. 块特殊文件
  4. 字节特殊文件
  5. 命名管道
  6. 套接字
  7. 符号链接

open和openat


int open(const char *path, int aflag, ...));

int openat(int fd, const char *path, int aflag, ...);

path是要打开或创建的文件的路径,aflag用来说明此函数的多个选项.

openopenat函数返回的文件描述符是最小未用的描述符.

creat


int creat(const char *path, mode_t mode);


在早期,open的第二个参数只能是0,1,2.无法打开一个不存在的文件,所以需要creat.

他的打开方式只能是只写方式,需要打开,关闭,打开,才能.

现在可以替换成


open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); //只写

open(path, O_RDWR | O_CREAT | O_TRUNC, mode); //读写

close


int close(int fd);

fd是被操作文件的文件标识符.

当一个进程中止,内核会关闭他的所有文件,所以有时不必显式调用close.

lseek


off_t lseek(int fd, off_t offset, int whence);

常常有一个非负整数表示文件偏移量,代表文件和文件开头距离的字节数.

除非使用O_APPEND参数,否则默认打开文件之后偏移量是0.

可以用这个函数调整偏移量.

如果调整成功,返回新的偏移值;否则返回-1,并且errno设置成ESPIPE.

whence参数 作用
SEEK_SET 把文件偏移量设置为距离文件开始offset个字节处
SEEK_CUR 把文件偏移量设置为当前值加offset
SEEK_END 把文件偏移量设置为文件长度加offset

lseek仅仅将偏移量记录在内核中,不会引起IO操作,只是影响之后的IO操作.

偏移量可以大于文件长度,写的时候文件会被加长.

read


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

成功返回读取到的文件数,如果已经到底尾端,返回0,出错返回-1.

从当前的文件偏移量开始读,返回之前设置新的文件偏移量.

ssize_t是带符号的size_t,为了返回-1.

write


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

写入之后的偏移值为写入前的偏移值加写入量.

打开文件的数据结构

每个进程都在进程表中有一个记录,记录中包含一张打开文件描述符表,每个描述符占一项,每一项记录了文件描述符和指向文件表项的指针.

内核维护一张文件表,每一项包含文件状态标识,文件偏移量,指向该文件v节点的指针.

每个打开文件都有一个v节点结构.

多个进程打开同一个文件时,他们各自有一个描述符表,描述符表指向各自的文件表项,但是文件表项的v节点指针指向同一个v节点.

O_APPEND

如果两个进程同时在一个文件的末尾添加内容,可能A进程定位到末尾之后被挂起,B进程开始写,之后A进程将B进程写的东西覆盖.

如果打开时加上O_APPEND参数,每次写的时候都会把偏移量定位到最后面,不再需要一个lseek函数.

pread/pwrite


ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);

ssize_t pwrite(int fd, void *buf, size_t nbytes, off_t offset);

相当于原子地移动偏移量并操作.操作之后不更新文件偏移量.

dup/dup2


int dup(int fd);

int dup2(int fd, int fd2);

返回一个新的文件描述符,指向fd所指向的文件文件表项(也就是和原本的fd偏移量也相同).

前者返回的文件描述符一定是最小的可用的文件描述符.

后者指定复制成fd2,如果fd2被占用则先关闭.如果fd==fd2,sma叶不干,返回fd2.

不然返回-1.

也可以fcntl(fd, F_DUPFD, fd2),新描述符必须大于等于fd2.

sync,fsync和fdatasync


void sync(void);

int fsync(int fd);

int fdatasync(int fd);

sync是把所有修改过的块写入队列,然后就返回,不等待写入完成.

fsync函数只对fd所指向的文件生效,并且等待写入完成.

fdatasync只影响文件的数据部分(其他数据只在必要情况下更新).

fcntl


int fcntl(int fd, int cmd, ...);

有以下功能

  1. 复制一个已有的描述符(F_DUPSF, F_DUPFD_CLOEXEC)
  2. 获取/设置文件描述符标志(F_GETFD, F_SETFD)
  3. 获取/设置文件状态标志(F_GETFL, F_SETFL)
  4. 获取/设置异步IO所有权(F_GETOWN, F_SETOWN)
  5. 获取/设置记录锁(F_GETLK, F_SETLK,F_SETLKW)

stat


int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, struct stat *restrict buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int fststat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);

成功返回0,否则返回-1.

stat是一个结构体,包含了文件的一些属性.

文件访问权限

st_mode值 含义
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

所有者ID是文件的属性,有效者ID是进程的属性.

进程能否操作文件需要对比这几个ID和所设置的权限.root用户除外.

access, faccessat


int access(const char *pathname, int mode);

int faccessat(int fd, const char *pathname, int mode, int flag);

测试文件权限.

mode 说明
F_OK 文件是否存在
R_OK 文件读权限
W_OK 文件写权限
X_OK 文件运行权限
原文地址:https://www.cnblogs.com/zzidun-pavo/p/14335337.html