文件描述符,open 函数

本文详细介绍了Linux系统中的文件IO概念,包括文件类型、系统IO和标准IO的区别。重点讨论了系统IO中的open函数,用于打开和创建文件,以及close函数用于关闭文件。文中通过示例代码解释了文件描述符的使用,强调了其在系统启动时为标准输入、输出和错误预留的特殊意义,并讨论了open函数的拓展参数,如O_CREAT和O_TRUNC。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、文件IO学习大纲。
1、文件IO概念、文件概念、文件类型。
2、访问文件方法一  -> 系统IO  打开?/读取?/写入?/关闭?
3、系统IO文件描述符概念?文件描述符与文件的关系?文件描述符的值。
4、文件偏移量概念。
5、系统IO应用实例:LCD液晶、触摸屏。
6、系统IO另外一种访问文件的方式:内存映射。 -> 针对LCD液晶
7、访问文件方法二  -> 标准IO  打开?/读取?/写入?/关闭? 
8、标准IO函数:处理字符/字符串
9、目录IO:访问目录,切换目录,读取目录,关闭目录。

二、文件IO概念?
1、什么是文件?
在linux下,一切都是文件。
除了我们平时常见的文件:1.txt/2.jpg/3.bmp/4.mp3是文件之外,linux系统还会把硬件设备当作是文件,例如:LED灯、触摸屏、LCD液晶屏幕,蜂鸣器,ADC,这些硬件设备在linux的眼中,都是文件来的。

2、什么是IO?
IO      -> input/output  -> 输入/输出
文件IO  -> 对文件数据输入/输出   -> 写入数据到文件/从文件中读取数据出来。

3、如何实现文件读取/写入?
不需要用户写自定义函数,因为在linux下,已经有现成的函数来实现。

访问文件方式有两种:
系统IO  -> 系统调用  ->  2   System calls  -> 系统IO接口都是在第2手册。
标准IO  -> 库调用    ->  3   Library calls -> 标准IO接口都是在第3手册。
 
4、使用系统IO与标准IO访问有什么区别?
系统IO来处理文件,没有缓冲区,直接按字节来处理。
标准IO来处理文件,有缓冲区,按块来处理。

作用对象:
访问硬件设备文件时(LED灯、触摸屏、LCD液晶)  -> 直接使用系统IO来处理。
例如: 写一个温湿度传感器驱动   -> 使用系统IO来访问温湿度传感器驱动

访问普通文件(1.txt/2.bmp/3.jpg/4.mp3)  -> 标准IO来处理。
例如:访问test.txt这个文件               -> 使用标准IO来访问。

5、文件类型。  -> 7种。
'-'   普通文件   -> 标准IO
'd'   目录文件   -> 目录IO
'l'   链接文件   
'p'   管道文件   -> 系统IO
's'   套接字文件 
'c'   字符设备文件 -> 系统IO
'b'   块设备文件   -> 系统IO

三、如何使用系统IO访问文件?
1、如何打开文件?  -> open()   -> man 2 open
函数功能:open and possibly create a file
     //打开和创建文件。

头文件:
    #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);

参数:
    pathname:需要打开的那个文件的路径名。 (绝对路径/相对路径)
    flags:
         O_RDONLY:只读
         O_WRONLY:只写
         O_RDWR:可读可写

返回值:
    成功:新的文件描述符file descriptor(其实就是一个最小,非负整数,没有使用过的)
    失败:-1

注意:open函数什么时候会打开失败?
1)你打开的路径不存在时。
2)如果文件本身的权限不允许,那么操作权限不对,也会失败。
   例如: 文件本身的权限: "-wx-wx-wx",如果以O_RDONLY/O_RDWR去打开文件,那么就会失败。

2、如何关闭文件?  -> close()  -> man 2 close
函数功能:close a file descriptor
    //关闭掉一个文件描述符

头文件:
     #include <unistd.h>

函数原型:
    int close(int fd);

参数:
    fd:需要关闭的那个文件的文件描述符。

返回值:
    成功:0
    失败:-1

  练习1: 写程序,访问家目录下test.txt,如果访问成功,则输出"open file success",否则输出"open file error",并关闭文件,如果关闭成功,则输出"close file success",否则输出"close file error"。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    int fd,ret;
    fd = open("/home/gec/test.txt",O_RDWR);
    if(fd >= 0)
    {
        printf("open file suceess!\n");
    }
    else{
        printf("open file error!\n");
    }
    printf("fd = %d\n",fd);
    
    ret = close(fd);
    if(ret == 0)
    {
        printf("close file success!\n");
    }
    else{
        printf("close file error!\n");
    }
    
    return 0;
}

  练习2: 看看访问之后的那个文件的文件描述符是多少?  -> 3
  练习3: 尝试访问一下开发板中/dev/fb0这个文件,访问之后就关闭它。
   
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    int fd;
    fd = open("/dev/fb0",O_RDWR);
    if(fd < 0)
    {
        printf("open file error!\n");
    }
    
    close(fd);
    
    return 0;
}

四、文件描述符?
1、什么是文件描述符?
文件描述符是open函数的返回值,当open()执行成功时,就会返回非负,最小,没有使用过的整数。
例如:
3=open("1.txt");  //3就是代表这个1.txt这个文件。
4=open("2.txt");  //4就是代表这个2.txt这个文件。
A105=open("关国源")  //A105就是代表这个关国源这个文件。

结论:将来需要处理文件时,我们不需要提供文件名字,只需要提供文件对应的文件描述符就可以。

2、访问文件时,发送fd从3开始分配,说明0/1/2已经被占用,究竟是谁在占用?
其实在系统启动时,就会默认打开3个文件,分别是"标准输入","标准输出","标准出错",他们其实是一个宏定义来的,是被定义在一个头文件中,头文件路径:/usr/include/unistd.h

/* Standard file descriptors.  */
#define    STDIN_FILENO    0    //标准输入  -> 对象:键盘
#define    STDOUT_FILENO    1    //标准输出  -> 对象:屏幕
#define    STDERR_FILENO    2    //标准出错  -> 对象:屏幕

  可以理解: 只要系统启动, 0 = open("标准输入")

3、举例子。
假设当前目录下有:a.txt b.txt c.txt d.txt
打开a.txt
打开b.txt
打开c.txt
关闭b.txt
打开d.txt
关闭a.txt
关闭c.txt
打开a.txt
打开c.txt
关闭d.txt
打开d.txt  -> 返回值是多少?    --> 4

  结论:打开一个文件时候,就会得到一个文件描述符。                 -> 申请资源
        关闭一个文件时候,这个文件对应的文件描述符就可以被别人使用。-> 释放资源

4、研究文件描述符有没有最大值?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    int fd;
    while(1)
    {
        fd = open("./a.txt",O_RDWR);
        printf("fd = %d\n",fd);
        if(fd == -1)
        {
            break;
        }
    }
    return 0;
}

范围:0~1023。
记住:打开文件之后,记得要关闭!

五、open函数的拓展参数。

   int open(const char *pathname, int flags, mode_t mode);

pathname:需要打开的文件的路径
flags:
必选(三选一)
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写

可选(0个/多个)  -> 如果选了,就是使用了位或"|"来添加。
O_APPEND:以追加的方式打开文件,在每一次写数据之前,文件的定位都是在末尾。
O_CREAT:如果文件不存在,就会创建。
    如果flags中有O_CREAT,那么mode这个参数就一定要填。
    如果flags中没有O_CREAT,那么mode这个参数就算你填了,也没用。
    
O_TRUNC:如果文件存在并且是一个普通文件,而且打开方式必须是O_RDWR/O_WRONLY,那么这个文件就会被清空。

  例子1:测试O_CREAT是否能创建文件。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    umask(0000);
    
    int fd;
    fd = open("/home/gec/ggy_test.txt",O_RDWR|O_CREAT,0777);
    if(fd < 0)
    {
        printf("open error!\n");
    }
    
    close(fd);
    
    return 0;
}

如果文件是存在的:
O_CREAT        -> 不创建,打开成功
O_CREAT|O_EXCL -> 不创建,打开失败   O_EXCL就是要确保O_CREAT这个参数创建了才会生效!

如果文件是不存在的:
O_CREAT        -> 创建文件并且打开成功
O_CREAT|O_EXCL -> 创建文件并且打开成功

  例子2: 测试O_TRUNC能不能清空?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    umask(0000);
    
    int fd;
    fd = open("./a.txt",O_RDWR|O_TRUNC);
    if(fd < 0)
    {
        printf("open error!\n");
    }
    
    close(fd);
    
    return 0;
}

一般地,O_CREAT|O_TRUNC 连用,会有什么效果?
100%确保运行之后文件是存在的,并且是空白的!


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值