在linux下用文件描述符来表示设备文件和普通文件。文件描述符是一个整型的数据,所有对文件的操作都得通过文件描述符来实现。文件描述符是文件系统中连接用户空间和内核空间的枢纽。当打开或创建一个文件时,内核空间创建相应的结构,并生成一个整型的变量传递给用户空间的对应进程。进程用这个文件描述符来对文件进行操作。用户空间对文件进行操作时,比如读、写一个文件时,将文件描述符作为参数传递给read或write,读写函数的系统调用到达内核时,内核解析作为文件描述符的整型变量,找出对应的设备文件,运行相应的函数,并返回用户空间结果。
文件描述符的范围是0~OPEN_MAX,是一个非负整数,它是一个索引值,指向在内核中每个进程所打开的文件记录表,同时它也是一个有限的资源,在使用完毕后需要及时释放。文件描述符的值仅仅在同一个进程中有效,即不同的进程的文件描述符,同一个值很可能描述的不是同一个文件。
Linux系统中有3个已经分配的文件描述符,即标准输入(stdin,对应的文件描述符的值为0)、标准输出(stdout,其对应的文件描述符的值为1)和标准错误(stderr,其对应的文件描述符的值为2)。
open()函数、create()函数:打开文件、创建新文件
函数原型为:
#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);
根据用户设置的标志flags和模式mode在路径pathname下建立或打开一个文件。open()函数打开pathname指定的文件,当函数成功执行时,返回一个整型的文件描述符。执行出错返回-1。pathname所指定的为一个字符串变量,这个变量的长度在不同的系统下其最大长度有区别,通常是1024个字节。当所给的路径长度大于1024字节时,系统会对字符串进行截断,仅选择最前面的字节进行操作。
函数的参数flags用于设置文件打开后允许的操作方式,可以为只读(O_RDONLY)、只写(O_WRONLY)和读写(O_RDWR)进行读写。在打开文件时必须指定这三种模式之一。三个模式中O_RDONLY通常定义为0,O_WRONLY定义为1,O_RDWR定义为2。
除此之外,flags的选项还有一些可选的参数:
O_APPEND 使每次对文件进行写操作时都追加到文件的尾端
O_CREAT 如果文件不存在则创建,当使用此选项时,第三个参数mode需要同时设 定,用来说明新文件的权限
O_EXCL 查看文件是否存在。如果同时指定了O_CREAT,而文件已经存在时,会返 回错误。用这种方法可以安全地打开一个文件。
O_TRUNC 将文件长度截断为0。如果文件存在,并且成功打开,则会将其长度截断 为0。
使用O_TRUNC选项,即对需要清空的文件进行归零操作。O_NONBLOCK打开文件为非阻塞方式,如果不指定此选项,默认打开方式为阻塞方式,即对文件的读写操作需要等待操作的返回状态。其中mode参数用户表示打开文件的权限,mode的使用必须结合flags的O_CREAT使用,否则是无效的。
mode参数的值和意义:
| 选项 | 值 | 意义 |
| S_IRWXU | 00700 | 用户(文件所有者)有读写和执行的权限 |
| S_IRUSR | 00400 | 用户(文件所有者)有读的权限 |
| S_IWUSR | 00200 | 用户(文件所有者)有写的权限 |
| S_IXUSR | 00100 | 用户(文件所有者)有执行的权限 |
| S_IRWXG | 00070 | 组用户(文件所有者)有读写和执行的权限 |
| S_IRGRP | 00040 | 组用户(文件所有者)有读的权限 |
| S_IWGRP | 00020 | 组用户(文件所有者)有写的权限 |
| S_IXGRP | 00010 | 组用户(文件所有者)有执行的权限 |
| S_IRWXO | 00007 | 其他用户(文件所有者)有读写和执行的权限 |
| S_IROTH | 00004 | 其他用户(文件所有者)有读的权限 |
| S_IWOTH | 00002 | 其他用户(文件所有者)有写的权限 |
| S_IXOTH | 00001 | 其他用户(文件所有者)有执行的权限 |
以上值的5位数字的含义为:
第1位表示设置用户ID
第2位表示设置组用户ID
第3位表示用户自己的权限
第4位表示组用户的权限
第5位表示其他用户的权限
例如,要创建一个用户可读(4)、可写(2)、可执行(1),组用户没有权限,其他用户可读可执行的文件,并设置用户ID位,则值为10705。
open()函数示例:在当前目录下打开一个名为test.txt文件。
创建一个openeg.c的文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd=-1; //文件描述符声明
char filename[]="test.txt"; //要打开的文件名
fd=open(filename,O_RDWR); //打开文件的方式为可读可写方式
if(fd == -1) //打开失败
{
printf("Open file %s failed !\nfd:%d\n",filename,fd);
}
else
{
printf("Open file %s succeed !\nfd:%d\n",filename,fd);
}
return 0;
}

之后,再执行编译
命令:gcc openeg.c -o openeg

最后,执行

文件打开成功了,显示文件描述符的值为3。在Linux下如果之前没有其他文件打开,第一个调用打开文件成功的程序,返回的描述值为最低值,即3。因为值为0、1、2的文件描述符分配给了系统,表示标准输入、标准输出和标准错误。在Linux在可以直接对这3个描述符进行操作,而不用打开、关闭。
open()函数不仅可以打开一般的文件,而且可以打开设备文件,例如“dev/sdal”文件,即磁盘的第一个分区。
O_CREAT可以创建文件,与O_EXCL结合使用可以编写容错的程序。例如:创建openeg_1.c文件并编译执行
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd=-1; //文件描述符声明
char filename[]="test.txt"; //要打开的文件名
fd=open(filename,O_RDWR | O_CREAT | O_EXCL,S_IRWXU); //打开文件的方式为可读可写方式
if(fd == -1)
{
printf("Open file %s has already exits !Reopen it !\n",filename);
fd = open(filename,O_RDWR);
printf("fd:%d\n",fd);
}
else
{
printf("Open file %s succeed !\nfd:%d\n",filename,fd);
}
return 0;
}


如果将openeg_1.c文件里的要打开的文件写为test1.txt,则会出现如下结果:

test1.txt文件不存在,则创建
创建文件的函数除了可以在打开时创建外还可以使用creat()函数来创建一个新文件,函数原型为:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char* pathname, mode_t mode);
函数creat()函数等于一个open()的缩写版本,等价于
open(const char* pathname,(O_CREAT | O_WRONLY | O_TRUNC));
create()的返回值与open()一样。
close()函数:关闭文件函数
close()函数关闭一个打开的文件,即关闭之前打开文件所占用的资源。此函数实际上是关闭一个文件描述符,关闭以后此文件描述符不再指向任何文件,从而此描述符可以再次使用。当函数执行成功时返回0,错误时返回-1。
函数原型为:
#include <unistd.h>
int close(int fd);
在打开文件之后,必须关闭文件。如果一个进程中没有正常关闭文件,在进程退出的时候系统会自动关闭打开的文件。在打开文件的时候,系统给文件分配的描述符是当前进程中最小的文件描述符的值,这个值一般情况下时递增的,而每个进程中的文件描述符的数量是有大小限制的。如果在一个进程中频繁地打开文件而又忘记关闭文件,当系统的文件描述符达到最大限制地时候,就会因为没有文件描述符可以分配而造成文件打开失败。如果只打开文件而不关闭,那么当文件描述符地值为1024的时候,由于超过了系统的可分配最大值,所以造成文件打开错误。
read()函数:读取文件函数,从打开的文件中读取数据,用户可以对读入的数据进行操作。
函数原型为:
#include <unistd.h>
ssize_t read(int fd,void* buffer,size_t count);
read()函数从文件描述符fd对应的文件中读取count字节的数据,放到buffer开始的缓冲区。如果count的值为0,read()函数返回0,不进行其他操作;如果count值大于SSIZE_MAX,结果不可预知。
read()函数执行成功时,函数返回1,执行发生错误时,函数返回-1。如果已经读取到文件的结尾,则返回0.返回值的类型为ssize_t类型,这种类型的数据是一个符号数,实际使用时可以返回int或long型数据。
read()函数中的参数fd是一个文件描述符,通常是open()函数或者creat()函数成功执行后的返回值;参数buffer是一个指针,指向缓冲区地址的开始位置,读入的数据将保存在此缓冲区中;参数count表示每次要读取的字节数,通常用这个变量来表示缓冲区的大小,因此,count的值不要超过缓冲区的大小,否则会造成缓冲区的溢出。
在实际使用read()函数时,不一定能够读取够count请求读取的字节数的数据,在以下几种情况下,实际读取到的数据小于count请求读取的字节数。
(1)读取普通文件时,文件中剩余的字节数小于count。
(2)从终端设备读取数据时,其默认的长度小于count。例如终端缓冲区的大小为256,count请求字节数为1024。
(3)从网络读取数据时,缓冲区大小可能小于读取请求的数据大小。
read()函数示例:从demo.txt中读取数据

demo.txt文件的内容是:

openeg.c文件的内容:
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd=-1; //文件描述符
char filename[]="demo.txt";
int i;
char buffer[10];
ssize_t size=-1;
fd=open(filename,O_RDONLY);
if(-1 == fd) //文件不存在则创建
{
printf("打开文件%s失败,fd=%d\n",filename,fd);
}
else
{
printf("文件%s打开成功,fd=%d\n",filename,fd);
}
while(size)
{
size=read(fd,buffer,10);
if(-1 == size)
{
close(fd);
printf("文件读取出现错误!\n");
return -1;
}
else
{
if(size > 0)
{
printf("读取到%d字节数据:",size);
for(i=0;i<size;i++)
{
printf("%c",*(buffer+i));
}
printf("\n");
}
else
{
printf("文件读取完毕!\n");
}
}
}
return 0;
}
编译并运行:
gcc readeg.c -o readeg

write()函数:写文件函数。向打开的文件中写入数据,将用户的数据保存到文件中。
函数原型为:
#include <unistd.h>
ssize_t write(int fd,const void* buffer,size_t count);
write()函数向文件描述符为fd的文件中写入数据,数据大小由count指定,buffer为要写入数据的指针,write()函数执行成功后返回成功写入文件的数据的字节数。当操作对象是普通文件时,写文件的位置从当前开始,操作成功后,写的位置会增加写入字节数的值。如果在写文件的时候指定了O_APPEND选项,每次写操作之前,会将写操作的位置移到文件的结尾处。
write()函数示例:向demo.txt文件中写入“ABC”
demo.txt

writeeg.c
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd=-1; //文件描述符
char filename[]="demo.txt";
int i;
ssize_t size=-1;
char buffer[]="ABC";
fd=open(filename,O_RDWR);
if(-1 == fd)
{
printf("文件%s打开失败,fd=%d\n",filename,fd);
}
else
{
printf("文件%s打开成功,fd=%d\n",filename,fd);
}
size=write(fd,buffer,strlen(buffer));
printf("写入文件%s%d字节数据!\n",filename,size);
close(fd);
return 0;
}
编译并运行结果:
gcc writeeg.c -o writeeg

以上即是我所整理的关于文件操作的个别简单函数,剩下的会在以后更新~
特别说明,此博文的部分内容和代码来源于宋敬彬,孙海滨等编著的Linux网络编程一书,在此表示感谢!!

被折叠的 条评论
为什么被折叠?



