【从零开始的嵌入式生活】文件I/O 2——文件IO和库

请添加图片描述
今天正式进入应用层的学习,今天主要学习文件IO和系统调用的相关库的使用,这部分内容是第一部分的延展。那么,让我们开始吧。

🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
全文大约阅读时间: 60min



文件IO——介绍

posix(可移植操作系统接口)定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符
Linux下, 标准IO基于文件IO实现

标准io文件io(低级IO)
打开fclose,freopen,fdopenopen
关闭fcloseclose
getc,fgetc,getchar,fgets,gets,freadread
putc,fputc,putchar,fputs,puts,fwritewrite

文件描述符

每个打开的文件都对应一个文件描述符。
文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符。
文件描述符从0开始分配,依次递增。
文件IO操作通过文件描述符来完成。
上节课有说过三个默认的文件打开表象,分别就是012对应stdin stdout stderrr


文件操作

open函数

#include <fcntl.h>
int open(const char *path, int oflag, mode_t mode);

成功时返回文件描述符;出错时返回EOF
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限
只能打开设备文件

参数作用
pathname打开文件名
flagsO_RDONLY:只读方式打开
O_WRONLY:可写方式打开文件
O_RDWD:读写方式打开(前三个参数互斥)
O_CREAT:如果该文件不存在,就创建一个新文件,并用第三个参数为其设置权限
O_NOCTTY:如果文件为终端, 终端不可以作为调用open()系统调用的那个进程的控制终端
O_TRUNC:如果文件已经存在,那么打开文件时先删除文件中原有数据
O_APPEND:以添加方式打开文件,对文件的写操作会在文件末尾进行
mode被打开的文件存取权限,八进制,比如777

close函数

#include  <unistd.h>
int  close(int fd);
  • 成功时返回0;出错时返回EOF
  • 程序结束时自动关闭所有打开的文件
  • 文件关闭后,文件描述符不再代表文件

read函数

#include  <unistd.h>
ssize_t  read(int fd, void *buf, size_t count);
  • 成功时返回实际读取的字节数;出错时返回EOF
  • 读到文件末尾时返回0
  • buf是接收数据的缓冲区
  • count不应超过buf大小

综合应用代码

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(){

        int fd;
        int n = 0;
        char buf[2];

        fd = open("1.txt", O_RDONLY|O_EXCL, 0666);

        if(fd){
                while(read(fd, buf, 1) > 0){
                        n++;
                }
                printf("n=%d\n", n);

        }
        close(fd);
        return 0;
}

write函数

#include  <unistd.h>
ssize_t  write(int fd, void *buf, size_t count);
  • 成功时返回实际写入的字节数;出错时返回EOF
  • buf是发送数据的缓冲区
  • count不应超过buf大小

示例代码

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(){

       int fd;
       int n = 0;
      char buf[11];

       fd = open("1.txt", O_RDWR|O_APPEND, 0666);

       if(fd){
               fgets(buf, 10, stdin);
               write(fd, buf, 10);
       }
       close(fd);
       return 0;
}

lseek函数

#include  <unistd.h>
off_t  lseek(int fd, off_t offset, intt whence);
  • 成功时返回当前的文件读写位置;出错时返回EOF
  • 参数offset和参数whence同fseek完全一样

目录操作

opendir函数

#include  <dirent.h>
DIR  *opendir(const char *name);
  • DIR是用来描述一个打开的目录文件的结构体类型
  • 成功时返回目录流指针;出错时返回NULL

readdir函数

#include  <dirent.h>
struct  dirent *readdir(DIR *dirp);
  • struct dirent是用来描述目录流中一个目录项的结构体类型
  • 包含成员char d_name[256] 参考帮助文档
  • 成功时返回目录流dirp中下一个目录项;
  • 出错或到末尾时时返回NULL
    综合示例代码
#include <dirent.h>
#include <stdio.h>

int main(){
       DIR *dir;
       struct dirent *dent;

       dir = opendir(".");

       if(dir)
               while((dent = readdir(dir)) != NULL)
                       printf("%s\n", dent->d_name);
       return 0;
}

chmod/fchmod函数用来修改文件的访问权限:

#include  <sys/stat.h>
int  chmod(const char *path, mode_t mode);
int  fchmod(int fd, mode_t mode);
  • 成功时返回0;出错时返回EOF
  • root和文件所有者能修改文件的访问权限

stat/lstat/fstat函数用来获取文件属性:

#include  <sys/stat.h>
int  stat(const char *path, struct stat *buf);
int  lstat(const char *path, struct stat *buf);
int  fstat(int fd, struct stat *buf);
  • 成功时返回0;出错时返回EOF
  • 如果path是符号链接stat获取的是目标文件的属性;而lstat获取的是链接文件的属性

struct stat是存放文件属性的结构体类型:

struct stat {
   dev_t         st_dev;       //文件的设备编号
   ino_t         st_ino;       //节点
   mode_t        st_mode;      //文件的类型和存取的权限
   nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
   uid_t         st_uid;       //用户ID
   gid_t         st_gid;       //组ID
   dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
   off_t         st_size;      //文件字节数(文件大小)
   unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
   unsigned long st_blocks;    //块数
   time_t        st_atime;     //最后一次访问时间
   time_t        st_mtime;     //最后一次修改时间
   time_t        st_ctime;     //最后一次改变时间(指属性)
};

通过系统提供的宏来判断文件类型:

st_mode & 0170000
  • S_ISREG(st_mode) 0100000
  • S_ISDIR(st_mode) 0040000
  • S_ISCHR(st_mode) 0020000
  • S_ISBLK(st_mode) 0060000
  • S_ISFIFO(st_mode) 0010000
  • S_ISLNK(st_mode) 0120000
  • S_ISSOCK(st_mode) 0140000

通过系统提供的宏来获取文件访问权限:

  • S_IRUSR 00400 bit:8
  • S_IWUSR 00200 7
  • S_IXUSR 00100 6
  • S_IRGRP 00040 5
  • S_IWGRP 00020 4
  • S_IXGRP 00010 3
  • S_IROTH 00004 2
  • S_IWOTH 00002 1
  • S_IXOTH 00001 0

综合示例

#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>

int main(){
        DIR *dir;
        struct dirent *dent;
        struct stat buf;

        dir = opendir(".");

        if(dir){
                while((dent = readdir(dir)) != NULL){
                        stat(dent->d_name, &buf);
                        printf("%s %d\n", dent->d_name, (int)buf.st_size);

                }
        }
        return 0;
}

程序库

库的基本概念

库是一个二进制文件,包含的代码可被程序调用,事先编译好的,可以复用的代码。
标准C库、数学库、线程库……
库有源码,可下载后编译;也可以直接安装二进制包 /lib/usr/lib
在OS上运行的程序基本上都要使用库。使用库可以提高开发效率。
Windows和Linux下库文件的格式不兼容
Linux下包含静态库和共享库


静态库特点

编译(链接)时把静态库中相关代码```复制到``可执行文件中
特点:

  • 程序中已包含代码,运行时不再需要静态库
  • 程序运行时无需加载库,运行速度更快
  • 占用更多磁盘和内存空间
  • 静态库升级后,程序需要重新编译链接

静态库创建

  1. 确定库中函数的功能、接口
  2. 编写库源码hello.c
    #include <stdio.h>
    void hello(void) {
    printf(“hello world\n”);
    return;
    }
  3. 编译生成目标文件
    $ gcc -c hello.c -Wall
  4. 创建静态库 hello
    $ ar crs libhello.a hello.o
  5. 查看库中符号信息
    $nm libhello.a
    hello.o:
    0000000 T hello
    U puts
  6. 编写应用程序test.c
    #include <stdio.h>
    void hello(void);
    int main() {
    hello();
    return 0;
    }
  7. 编译test.c 并链接静态库libhello.a
    $ gcc -o test test.c -L. -lhello
    $ ./test
    hello world

共享库

  • 编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码
  • 程序不包含库中代码,尺寸小
  • 多个程序可共享同一个库
  • 程序运行时需要加载库
  • 库升级方便,无需重新编译程序
  • 使用更加广泛

共享库创建

  1. 确定库中函数的功能、接口
  2. 编写库源码hello.c
    #include <stdio.h>
    void hello(void) {
    printf(“hello world\n”);
    return;
    }
  3. 编译生成目标文件
    $ gcc -c -fPIC hello.c -Wall
  4. 创建共享库 common
    $ gcc -shared -o libcommon.so.1 hello.o
  5. 为共享库文件创建链接文件
    $ ln -s libcommon.so.1 libcommon.so
  6. 符号链接文件命名规则
    lib<库名>.so
  7. 编写应用程序test.c
    #include <stdio.h>
    #include “common.h”
    int main() {
    hello();
    return 0;
    }
    其中common.h为
    void hello();
  8. 编译test.c 并链接共享库libcommon.so
    $ gcc -o test test.c -L. -lcommon

共享库使用

执行程序

./test
./test: error while loading shared libraries: libcommon.so
cannot open shared object file : No such file or directory

添加共享库的加载路径(环境变量)

export  LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./test
hello  world
bye!
  • 把库拷贝到/usr/lib和/lib目录下(极度不推荐)
  • LD_LIBRARY_PATH环境变量中添加库所在路径
  • 添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新

写在最后

文件IO今日完结,我尽量一天一更,大家和我一起变强呀!明天开始进入多线程编程的一章!最后三连即可提高学习效率!!!


另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XingleiGao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值