基本文件操作
- 文件操作:
- 在Linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后使用close()关闭文件即可(如果不关闭文件会造成文件的损坏);
- 文件平时是存放在块设备的文件系统文件中的,我们把这种文件叫静态文件,当我们去打开一个文件时,Linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件中的内容读取到内核中特定地址管理存放(叫动态文件);
- 打开文件以后,以后对这个文件的操作,都是针对内存中的这一份动态文件的,并且不是针对静态文件的,当然我们对动态文件进行读写以后,此时内存中动态文件和块设备中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件;这么设计,不直接对块设备直接操作。是因为块设备读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且随机操作,很灵活。
文件描述符:
- 内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符。程序刚刚启动的时候,0(宏 STDIN_FILENO)是标准输入,1(宏 STDOUT_FILENO)是标准输出,2(宏 STDERR_FILENO)是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。POSIX标准要求每次打开文件时(含socket)必须使用当前进程中最小可用的文件描述符号码。
- 文件描述符的作用域就是当前进程,出了这个进程文件描述符就没有意义了。
系统调用(函数) :
open()/creat()函数
用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
所需头文件
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
函数原型
- int open (const char *pathname,int flags)
- int open (const char *pathname, int flags,mode_t mode)
- int creat(const char *pathname, mode_t mode)
函数参数:
- 1.pathname:被打开的文件名(包含路径)
- 2.flags:文件的打开方式
- O_RDONLY:以只读方式打开文件
- O_WRONLY:以只写方式打开文件
- O_RDWR:以读/写方式打开文件
- O_CREAT:如果文件不存在,就创建一个新的文件,并且第三个参数为其设置文件权限
- O_APPEND:以添加方式打开文件,在打开文件的同时,文件指针指向文件的末尾,即将写入的数据添加文件的末尾
- O_TRUNC:若文件已经存在,则删除文件文件中的全部原有数据,并且设置文件大小为0
- O_EXCL:检测文件是否已经存在,如果使用该参数是文件存在,则返回错误信息,此时open是原子操作,防止多个进程同时创建同一个文件
- 3.mode:设置新创建文件的存储权限,只有在flags参数中有O_CREAT时或使用creat()函数时使用;其中R/W/X分别表示读/写/执行;可使用八进制法设置,常用的有0600,0644等;创建文件时,指定文件访问权限。权限同时受 umask 影响。结论为:
文件权限 = mode & ~umask
函数返回值:
- 成功:返回文件描述符(整数);
- 失败:返回-1,设置errno;
close()函数
用于关闭一个被打开的文件。 当一个进程终止时, 所有被它打开的文件都由内核自动关闭。
所需头文件:
- #include <unistd.h>
函数原型:
- int close(int fd)
函数参数:
- fd:欲关闭所对应的文件描述符
函数返回值:
- 成功:0;
- 失败:-1;
write()函数
用于向打开的文件写数据, 写操作从文件的当前指针位置开始, 对磁盘文件进行写操作, 若磁盘已满或超出该文件的长度, 则 write()函数返回失败。
所需头文件:
- #include <unistd.h>
函数原型:
- ssize_t write(int fd,const void *buf,size_t count)
函数参数:
- fd:文件描述符
- buf:指定存储器写入数据的缓冲区
- count:指定写入的字节数
函数返回值:
- 成功:以写的字节数;
- 失败:-1;
read()函数
用于将从指定的文件描述符中读出的数据放到缓存区中, 并返回实际读入的字节数。 若返回 0, 则表示没有数据可读, 即已达到文件尾。 读操作从文件的当前指针位置开始。 当从终端设备文件中读出数据时, 通常一次最多读一行。
所需头文件:
- #include <unistd.h>
函数原型:
- ssize_t read(int fd,void *buf,size_t count)
函数参数:
- fd:文件描述符
- buf:指定存储器读出数据的缓冲区
- count:指定读取的字节数
函数返回值:
- 成功:读到的字节数;
- 0:已读到文件尾;
- 失败:-1;
lseek()函数
用于在指定的文件描述符中将文件指针定位到相应的位置。 它只能用在可定位(可随机访问) 文件操作中。 注意在管道、 套接字和大部分字符设备文件是不可定位的, 所以在这些文件的操作中无法使用 lseek()调用。
每个打开的文件都记录着当前读写位置,打开文件时读写位置是 0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以 O_APPEND方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。
所需头文件:
- #include <sys/types.h>
- #include <unistd.h>
函数原型:
- off_t lseek(int fd,off_t offset,int whence)
函数参数:
- fd:文件描述符
- offset: 偏移量,每一次读写操作所需要移动的距离,单位是字节,可正可负(前移,后移)
- whence:当前位置的基点
- SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小
- SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小
- SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量
函数返回值:
- 成功:返回值是较文件起始位置向后的偏移量;
- 失败:-1;
lseek函数 常见应用:
- 使用 lseek 拓展文件:write 操作才能实质性的拓展文件。单 lseek 是不能进行拓展的。
一般:
lseek(fd,100,SEEK_END)//拓展文件100字节
write(fd, “\0”, 1);//引起io操作
lseek()引起空洞:之前有提到过文件当前偏移量是可以大于当前文件长度的,如果在这种情况下还进行文件写入是允许的,但是会形成文件空洞。空洞的部分用\0代替,但是空洞并不占用磁盘块。
- 通过 lseek 获取文件的大小:lseek(fd, 0, SEEK_END);
#include <stdio.h>
#include <error.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main (int argc,char *argv[])
{
int fd = open(argv[1],O_RDWR);
if(fd == -1){
perror("open error");
exit(1);
}
int len = lseek(fd,0,SEEK_END);//获取文件大小
printf("file len:%d\n",len);
close(fd);
return 0;
}
两个查看文件内容的的指令:
od -tcx filename 查看文件的 16 进制表示形式
od -tcd filename 查看文件的 10 进制表示形式
错误处理函数
错误号:errno
查看错误号:
/usr/include/asm-generic/errno-base.h
/usr/include/asm-generic/errno.h
- perror 函数: void perror(const char *s);
- strerror 函数: char *strerror(int errnum);
#define EPERM 1 /* Operation not permitted /
#define ENOENT 2 / No such file or directory /
#define ESRCH 3 / No such process /
#define EINTR 4 / Interrupted system call /
#define EIO 5 / I/O error /
#define ENXIO 6 / No such device or address /
#define E2BIG 7 / Argument list too long /
#define ENOEXEC 8 / Exec format error /
#define EBADF 9 / Bad file number /
#define ECHILD 10 / No child processes /
#define EAGAIN 11 / Try again /
#define ENOMEM 12 / Out of memory /
#define EACCES 13 / Permission denied /
#define EFAULT 14 / Bad address /
#define ENOTBLK 15 / Block device required /
#define EBUSY 16 / Device or resource busy /
#define EEXIST 17 / File exists /
#define EXDEV 18 / Cross-device link /
#define ENODEV 19 / No such device /
#define ENOTDIR 20 / Not a directory /
#define EISDIR 21 / Is a directory /
#define EINVAL 22 / Invalid argument /
#define ENFILE 23 / File table overflow /
#define EMFILE 24 / Too many open files /
#define ENOTTY 25 / Not a typewriter /
#define ETXTBSY 26 / Text file busy /
#define EFBIG 27 / File too large /
#define ENOSPC 28 / No space left on device /
#define ESPIPE 29 / Illegal seek /
#define EROFS 30 / Read-only file system /
#define EMLINK 31 / Too many links /
#define EPIPE 32 / Broken pipe /
#define EDOM 33 / Math argument out of domain of func /
#define ERANGE 34 / Math result not representable */