版权声明:本文为博主原创文章,转载请注明博客地址: https://cloud.tencent.com/developer/article/1433347
在Linux下,一切皆文件。这是我们嵌入式Linux开发与应用这门课的老师经常挂在嘴边的一句话。足以体现出在Linux操作系统中,对于一切资源的管理都是对文件的操作。
Linux系统中每一个分区都是一个文件系统,都有自己的目录层次。Linux会将这些在不同分区的,单独的文件系统按一定的方式形成一个系统的总目录层次结构。
Linux下可以通过shell命令来操作文件,但是功能有一定限制;我们也可以通过系统调用或者C语言的库函数对文件进行操作
Linux既然采用了树形结构的目录形式,整个OS只有一棵文件树,这样方便OS对文件进行统一管理。Linux操作系统中的这颗文件树的树根叫做根文件系统,用“/”表示,可以通过使用cd /命令直接到达根目录。各个磁盘是通过挂载以文件夹的形式访问
根文件系统:
Linux文件分类:
通过ls -l可以查看文件类型和属性

结果分多行显示,距离说明一下每行显示的意义。例如第一行exec这个文件的信息行。首先,我们看到这行以“-”开头,表示exec是一个普通文件。同时注意到第三行以d开头,这说明new是一个目录文件。
接着看第一个符号后面的信息,注意到后面仍旧有9个字符。这9个字符分成3组,即每3个一组,”w"表示可写,“r”表示可读,“x”表示可执行。第一组3个符号表示的是文件拥有者对该文件的权限;第二组3个符号表示该文件所在组的其他拥有者对该文件的权限;第3组表示系统其他用户对该文件的权限。
继续可以看到有个数字,对于普通文件,这个数字表示链接数,对于目录文件来说这个数字表示第一级子目录数。接下来的两组信息分别是用户名和组名,然后是文件大小(单位是字节),接着是文件最后的修改日期,最后就是文件名。
Linux文件描述符
在Linux下当一个进程打开文件的时候,OS会返回相应的文件描述符,程序为了处理该文件必须使用这个文件描述符。文件描述符是一个正整数。一般而已,当一个进程启动的时候,他会打开3个文件:标准输入,标准输出,标准错误。这3个文件对应的文件描述符分别是0,1,2.通常使用宏:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO.文件描述符是一个索引,指向内核中打开文件的记录表。
Linux操作系统给我们提供了6个系统调用create,open,write,close,read,lseek。系统调用是不带缓冲区的。他们是POSIX标准提供的。这些函数需要的头文件#include<fcntl.h>,#include<sys/types.h>,#include<sys/stat.h>。
create函数用于创建一个文件,它的功能可以被open函数取代,open函数由3个参数的时候,就可以当做create函数使用,这时如果文件不存在,open就会创建这样一个文件。例如:open(path,O_WRONLY|O_CREAT|O_TURNC,mode);并且open弥补了create的一个不足之处是:create创建的文件是以只写方式打开所创建的文件,所以当需要读取的时候,需要先close,然后在open一次。现在则可以这样:open(path,O_RDWR|O_CREAT|O_TRUNC,mode);
mode值包含了对文件的访问权限位。正如上面描述的一样,每个文件有9个访问权限位,并且可以分为3组。
mode | 含义 |
|---|---|
S_IRUSR | 用户读 |
S_IWUSR | 用户写 |
S_IXUSR | 用户执行 |
S_IRGRP | 组读 |
S_IWGRP | 组写 |
S_IXGRP | 组执行 |
S_IROTH | 其他读 |
S_IWOTH | 其他写 |
S_IXOTH | 其他执行 |
如果打开的文件是在某个目录文件下,那么该目录应该是可执行的,因为对于目录文件而言,可执行代表着搜索位,我们可以找该目录下的文件。目录的读只代表我们可以读取该目录的文件列表,不能进行其他操作。如果当前打开了一个文件,如果是root用户的进程,那么它肯定能访问该文件。如果进程是文件所有者执行的,那么对文件的权限取决于第一组的权限;如果进程是文件所有者所在组或者附属组之一,那么对文件的权限取决于第二组权限。若进程是其他用户执行的,那么对文件的操作取决于第三组权限。
在使用open函数打开一个文件的时候,最常用的三个参数是:O_WRONLY(只写),O_RDONLY(只读),O_WRRD(可读可写)
另外两种是:O_EXEC(执行),O_SEARCH(搜索,应用于目录)。另外open打开的文件,返回的文件描述符一定是最小的未使用描述符。path所指定的路径可以是绝对路径,也可以是相对路径。
read函数用于从已打开的文件中读取数据
如果read成功,返回读取到的字节数。若已到达文件尾端,返回0。读取出错返回-1.
write函数用于讲数据写入已打开的文件中
如果写入成功,返回以写字节数,否则,返回-1.
close函数用于关闭文件
关闭一个文件并释放该进程加在该文件上的所有锁。当一个进程终止的时候,会自动关闭它打开的所有文件。所有有时候并不显式的使用close关闭文件。返回0表示成功,返回-1表示错误。
lseek函数用于移动文件的读写位置。
每个打开文件都有一个与其相关联的“当前文件偏移量”。用于计算从文件开始处的字节数。通常,读写都是从当前文件偏移量处开始的,并使用偏移量增加所读写的字节数。系统默认该偏移量为0。可以使用lseek函数来指定一个打开文件的偏移量。
一个简单的例子如下:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int fd,size;
int l;
char str[] = {"This is My Schoolnumber:1507050314"};
char tmp[51] = {0};
fd = creat("hello.txt",(S_IRUSR|S_IWUSR)); //在当前目录创建一个hello.txt文件,他是可读可写的。
write(fd,str,strlen(str)); //写入This is My Schoolnumber:这句话
close(fd); //关闭文件
open("hello.txt",O_RDONLY); //以只读方式打开文件
read(fd,tmp,strlen(str)); //读文件
close(fd); //关闭文件
printf("%s\n",tmp);
return 0;
}打印结果如下:

需要注意的是,tmp数组需要全部初始化为0,'\0'的ASCII就是0.这样将打开的文件中读取的文本信息打印的时候才能正常打印,不会乱码。否则不知道在哪儿终止,将会产生乱码。
注意:在使用Linux的系统调用操作文件的时候,是无缓冲的,这点很重要。当你在做少量,大批次写入的时候效率会很低。因此注意使用缓冲(用数组的之类的暂时保存一下),能提高I/O效率。
另外一个测试程序如下:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int fd,size;
int l;
char str1[] = {"This is My Schoolnumber:1507050314\n"};
char str2[] = {"This is My Schoolnumber:1507050316\n"};
char tmp[51] = {0};
fd = creat("hello.txt",(S_IRUSR|S_IWUSR)); //在当前目录创建一个hello.txt文件,他是可读可写的。
write(fd,str1,strlen(str1)); //写入This is My Schoolnumber:这句话
close(fd); //关闭文件
open("hello.txt",O_RDWR|O_APPEND); //以可读可写,写追加方式打开文件
lseek(fd,5,SEEK_CUR); //更改文件偏移量,从5这个位置开始计算偏移量
read(fd,tmp,strlen(str1)); //读文件
write(fd,str2,strlen(str2)); //写文件
close(fd); //关闭文件
printf("%s",tmp);
return 0;
}运行结果如下:

打印的是“is My Schoolnumber”,没有了This。说明更改这个当前文件偏移量成功了。使用cat命令打印Hello.txt文件的内容,可以看到写入也是成功的。
lseek不可以用于管道,FIFO,socket文件。另外lseek的文件偏移量的大小可以大于当前文件的长度,在这种情形下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。文件空洞并不要求在磁盘上占据空间。