文件描述符
文件描述符是一个非负的小整数,当我们创建或者打开文件时,内核便会返回一个文件描述符,来标识这个打开或者创建的文件。
标准输入,标准输出,标准错误
当我们开启一个进程时会自动打开这三个描述符,STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO这三个宏定义的描述符分别为0,1,2。这些通常是在unistd.h中定义的。我们可以通过这些描述符进行从终端来进行输入输出操作。
#include
#include
int main()
{
char a[10];
read(STDIN_FILENO,a,10);
write(STDOUT_FILENO,a,20);
}
无缓冲的函数
我们这节主要介绍的是无缓冲的I/O函数,无缓冲表示这些函数都是系统调用,我们在讨论原子的操作的时候无缓冲的函数是很重要的。
- read函数
- write函数
- open函数
- lseek函数
- close函数
open函数
函数原型
#include
int open(const char *path,int oflag /, mode_t mode/);
- 这个函数的作用便是打开一个文件,若成功返回文件描述符,错误返回-1。
- 第一个参数表示文件的路径,第二个参数表示文件的打开方式,第三个函数是可选的。
- 我们介绍一下第二个参数的宏定义
- 只能选一个
- O_WRONLY 表示文件以只读的方式打开
- O_RDONLY 表示文件以只写的方式打开
- O_RDWR 表示文件以读写的方式打开
- 可以选多个
- O_APPEND 表示将读写指针移动到最后,将写入的内容添加到文件末尾,如果- 直接写会覆盖原来的内容。
- O_CREAT 当使用这个时,必须有第三个参数。表示创建一个文件。
- O_NONBLOCK 表示将此文件以非阻塞的方式打开。
- O_TRUNC 这个参数表示将文件读写指针阶段为0
#include
#include
#include
#include
int main()
{
int fp = open("aa.txt",O_WRONLY|O_APPEND);
char a[10]="dsdsdfs";
write(fp,a,strlen(a));
printf("%s",a);
}
测试:
root@ubuntu:/home/sun/project# cat aa.txt
dsjfskjfsds
fdssdjk
dsdsdfs
dsdsdfs111111root@ubuntu:/home/sun/project#
将内容添加到文件末尾
read函数
函数原型
#include
ssize_t read(int fd,void *buf,size_t nbytes);
- 第一个参数表示文件描述符,第二个参数表示任意类型的缓冲区,第三个参数表示一个无符号整形数。
- 这个函数的返回值类型为有符号整形,因为如果出错返回-1,所以是有符号的,正常情况下返回读到字符的个数,当达到文件结尾时返回0。
- 这个函数读取字符时会将换行符也读取出来,比如我们有两行数据,我们可以一次性读取出来。
- 但是当从终端读取时只能读取一行
- 有一些情况会出现读取的字符数小于我们要求的字符数。
- 当我们要求读取100个时,但是文件里只有30个,便会读取30个,返回30,当下次再读取时便会返回0。
- 当我们从终端读取时,每次只能读取一行,可能会少于我们的要求。
- 当我们从网络中读取时可能会少于我们的要求。
#include
#include
#include
int main()
{
char a[100];
read(STDIN_FILENO,a,10);
printf("%s",a);
}
测试:
root@ubuntu:/home/sun/project# ./test
gfdjhskjalksaldsasfjediskkasjdkdndfcska
gfdjhskjal
#include
#include
int main()
{
char a[10];
read(STDIN_FILENO,a,20);
printf("%s",a);
}
测试:
root@ubuntu:/home/sun/project# ./test
1234567890122345
1234567890122345
�Fl�root@ubuntu:/home/sun/project#
可以看到当我们输入的字符数大于我们的缓冲区时便会出现错误。
write函数
函数原型
#include
ssize_t write(int fd,const void *buf,size_t nbytes);
- 这个函数表示向一个文件中写入字符。
- 成功返回写入的字符数,失败返回-1。
- 当写入时文件的读写指针会做出相应的偏移。
close函数
这个函数表示关闭打开的文件
lseek函数
每打开一个文件便会返回一个文件描述符,但是内核会维护一个文件偏移量,正常打开文件文件偏移量为0,但是当我们使用O_APPEND时便会将文件偏移量指向最后一个字符,我们的读写操作都是从文件偏移量的位置开始的,当我们读取或写入一个字节,文件的偏移量也会增加1,lseek这个函数便是来操作文件偏移量的,可以使用lseek函数来操作我们想要从任意位置的读写位置。
函数原型
#include
off_t lseek(int fd,off_t offset,int whence)
返回值:成功返回文件的新的偏移量,失败返回-1
参数解释
- 第一个参数表示文件描述符
- 第二个参数表示文件的相对偏移
- 第三个参数为SEEK_SET,将文件偏移量设置为距离文件开头offset个字节
- 第三个参数为SEEK_CUR,将文件偏移量设置为距离现在的偏移量的offset个字节
- 第三个参数为SEEK_END,将文件偏移量设置为文件长度加offset
- 这个函数只能对普通的文件设置文件偏移量,不能对管道 FIFO 网络套接字设置偏移量
#include
#include
#include
#include
#include
int main()
{
int fd=open("aa.txt",O_RDONLY);
int a=lseek(fd,2,SEEK_SET);
char buf[10];
read(fd,buf,10);
printf("%d %s",a,buf);
}
文件共享
我们可以在多个进程之间打开同一个文件,我们将介绍多个进程同时操作同一个文件时相互产生的影响。
- 每个进程在进程表中都有一个记录,这个记录中包含这个进程打开的文件,记录文件描述符标志和文件表项的指针。
- 文件表项包含文件状态标志(读写和是否阻塞等),当前偏移量
和v节点的指针。 - v节点和i节点包含文件一些信息,文件的当前长度,文件的所有者等信息。
上面这个图便是当一个进程两次打开同一个文件时的情况,每个文件描述符都有自己的文件表项,因此每个文件描述符可能有不同的读写或者是否阻塞和不同的偏移量等信息,但是这两个文件描述符一个共同的v和i节点,因此他们的所有者和文件的长度等信息是共享的,我们可以知道lseek函数作用的便是文件表项的当前偏移量,当我们使用这两个文件描述符进行都操作时是没有相互影响的,但是当我们同时对这个文件进行写操作时是有影响的,因此我们接下来便开始讲解进行写操作时相互的影响。