IO进程学习——3

一、文件I/O

read

函数功能:

​ 从文件读取数据到缓冲区

函数原型:

​ ssize_t read(int fd, void *buf, size_t count);

函数头文件:

​ #include <unistd.h>

函数参数:

参数 解释
fd 需要读取文件的文件描述符
buf 程序存储数据的缓冲区首地址
count 想要读取的字节数

函数返回值:

状态 解释
成功 返回:读取的字节数
失败 返回:-1,并设置全局errno

write

函数功能:

​ 程序向文件中写入内容

函数头文件:

​ #include <unistd.h>

函数原型:

​ ssize_t write(int fd, const void *buf, size_t count);

函数参数:

参数 解释
fd 写入文件的文件描述符
buf 缓冲区
count 写入字符的个数

函数返回值:

状态 解释
成功 返回:成功写入的字符数
失败 返回:-1,并且设置errno

lseek

函数功能:

​ 获取文件中的指针位置

函数头文件:

​ #include <sys/types.h> ​ #include <unistd.h>

函数原型:

​ off_t lseek(int fd, off_t offset, int whence);

函数参数:

参数 解释
fd 文件描述符
offset 偏移量
whence 从哪个位置开始偏移(SEEK_SET / SEEK_CUR / SEEK_END)

函数返回值:

​ 返回当前位置距离文件开头的距离

二、文件属性和目录

stat函数

函数功能:

​ 获取文件的详细属性信息

函数头文件:

​ #include <sys/types.h> ​ #include <sys/stat.h> ​ #include <unistd.h>

函数原型:

​ int stat(const char *pathname, struct stat *statbuf);

函数参数:

参数 解释
pathname 需要查看的文件的路径
statbuf 接收文件信息的结构体变量名

函数返回值:

​ 成功返回:0

​ 失败返回:-1,并且设置errno

stat结构体:

struct stat {
               dev_t     st_dev;         /* 文件所在设备的 ID */
               ino_t     st_ino;         /* inode 编号 文件在文件系统内的唯一标识 */
               mode_t    st_mode;        /* 文件类型和权限 */
               nlink_t   st_nlink;       /* 硬链接个数 */
               uid_t     st_uid;         /* 文件所有者的 UID */
               gid_t     st_gid;         /* 文件所属组的 GID */
               dev_t     st_rdev;        /* 设备 ID(特殊文件有效) */
               off_t     st_size;        /* 文件整体大小,单位字节 */
               blksize_t st_blksize;     /* 块大小(缓冲区) */
               blkcnt_t  st_blocks;      /* 占用块数(大小512B) */
}

inode 信息 (对应 stat 结构体)

类型 内容
身份标识 - st_ino:inode 编号(文件系统内唯一)
- st_dev:存储设备 ID(区分不同磁盘分区)
类型与权限 - st_mode:文件类型(普通文件、目录、设备等,通过 S_ISREG 等宏判断) + 权限位(读、写、执行)
所有权 - st_uid:文件所有者的 UID
- st_gid:文件所属组的 GID
链接 - st_nlink:硬链接数量(ln 创建硬链接时该值增加)
存储细节 - st_size:文件大小(字节,对普通文件有效)
- st_blksize:文件系统最优 I/O 块大小
- st_blocks:占用的 512 字节块数量(反映物理存储占用)
特殊文件 - st_rdev:设备文件的主 / 次设备号(如 /dev/sda 的设备 ID)

inode内的宏定义函数(类型+权限):

类型

宏定义 文件类型 字符表示
S_ISREG(m) 普通文件(regular file) -
S_ISDIR(m) 目录(directory) d
S_ISCHR(m) 字符设备文件(character device) c
S_ISBLK(m) 块设备文件(block device) b
S_ISFIFO(m) FIFO(命名管道,named pipe) p
S_ISLNK(m) 符号链接(软链接,symbolic link) l
S_ISSOCK(m) 套接字文件(socket) s

权限

宏定义 八进制 描述
S_ISUID 04000 设置用户 ID 位(set-user-ID bit)
S_ISGID 02000 设置组 ID 位(set-group-ID bit)
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 其他用户有执行权限

opendir/fdopendir

函数的功能: 打开目录文件,获取操作目录文件的指针 函数使用需要添加的头文件: #include <sys/types.h> #include <dirent.h> 函数原型: DIR *opendir(const char *name) ; DIR *fdopendir(int fd); 函数的参数说明:

​ name:需要打开的文件夹的路径

​ fd:已经打开了的目录的文件描述符

函数的返回值说明: 成功——返回:一个目录流的指针 失败——返回:NULL,并设置errno

DIR结构体:

struct dirent {
    ino_t          d_ino;       /* inode 编号 */
    off_t          d_off;       /* 目录偏移量,已不常用 */
    unsigned short d_reclen;    /* 该条记录(结构体)的长度 */
    unsigned char  d_type;      /* 文件类型 */
    char           d_name[256]; /* 文件名称 */
};

readdir

函数功能:

​ 当使用opendir后,通过readdir来读目录流中的文件信息,当读到末尾或者发生错误的时候,返回NULL

函数使用头文件:

​ #include <dirent.h>

函数原型:

​ struct dirent *readdir(DIR *dirp);

函数参数:

​ dirp——文件目录流(directory stream)指针

函数返回值:

​ 成功——返回:DIR类型指针

​ 读到结尾(读完)——返回:返回NULL,但是errno不会改变

​ 失败——返回:返回NULL,errno会被更改

三、库

库是一种可执行代码的二进制形式,都是别人编写好的程序的集合

静态库

​ 编译时链接到程序中的库

(效率上比动态库快,但因为将代码包含进来,代码体量会变大)

制作流程

1. 编译成.o文件
  	1. gcc	-c	xxx.c	-o	xxx.o
2. 将.o文件生成最终的静态库
  	1. ar	crs	lib库名.a	xxx.o	xxx.o  xxx.o
3. 使用静态库
    1. gcc	main.c	-L	库的路径	-l	库名

静态库的优势和劣势

优势:

  1. 程序运行时不再需要库的支持
  2. 程序的效率高

劣势:

  1. 链接时将库的内容链接到了可执行程序内部因此可执行程序体量变大
  2. 更新不方便

动态库

​ 编译时不会链接,程序运行时才会找到库的所在进行链接动作

(效率上比静态库慢,但是体量小,而且可以随库的更新而动态更新)

动态库的制作

  1. 1. 编译生成与地址无关的.o文件
       1. gcc	-fPIC	-c	xxx.c	-o	xxx.o	
    2. 生成动态库
       1. gcc	-shared	-o	lib库名.so	xxx.o	xxx.o
    3. 使用库
       1. gcc	main.c	-L	库的路径	-l	库名
    

解决动态库在运行时找不到的问题,三种方案:

  1. 将动态库放到/usr/lib目录下
  2. 使用export命令临时导入库的运行环境变量
    1. export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:库路径
  3. 在/etc/ld.so.conf.d目录下创建配置文件
    1. sudo vi mylib.conf
    2. 在文件内写入库的路径
    3. 使用ldconfig指令使配置生效

动态库的优势和劣势

优势:

  1. 更新方便
  2. 代码体积小

劣势:

  1. 运行的效率低下,在运行时需要找库
  2. 运行时必须要存在库

四、作业

1.利用stat和opendir指令模拟ls命令

/*===============================================
*   文件名称:ls_eg.c
*   创 建 者:青木莲华
*   创建日期:2025年08月06日
*   描    述:利用stat和opendir函数模拟ls命令
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>



int main(int argc, char *argv[])
{ 
    
    DIR *dirp = opendir(argv[1]);
    char path[256] = {0};
    
    if(dirp == NULL)
    {
        perror("opendir");
        return -1;
    }
    
    struct stat *file_st = (struct stat *)malloc(sizeof(struct stat));
    struct dirent *d = NULL;
    while(1)
    {
        memset(path,0,sizeof(path));    
        d = readdir(dirp);
        if(d == NULL)
        {
            //perror("readdir");
            break;
        }
        //不显示隐藏文件
        if(d->d_name[0] == '.')
        {
            continue;
        }
        //拼接路径
        strcat(path,argv[1]);
        strcat(path,"/");
        strcat(path,d->d_name);

        int ret = stat(path,file_st);
        if(ret == -1)
        {
            perror("stat");
            return -1;
        }
        //输出处理
        if(S_ISREG(file_st->st_mode))
            printf("-");
        else if(S_ISDIR(file_st->st_mode))
            printf("d");
        else if(S_ISCHR(file_st->st_mode))
            printf("c");
        else if(S_ISBLK(file_st->st_mode))
            printf("b");
        else if(S_ISFIFO(file_st->st_mode))
            printf("p");
        else if(S_ISLNK(file_st->st_mode))
            printf("l");
        else if(S_ISSOCK(file_st->st_mode))
            printf("s");
        //USR
        if(file_st->st_mode & S_IRUSR)
            printf("r");
        else
            printf("-");
        if(file_st->st_mode & S_IWUSR)
            printf("w");
        else
            printf("-");
        if(file_st->st_mode & S_IXUSR)
            printf("x");
        else
            printf("-");
        //GROUP
        if(file_st->st_mode & S_IRGRP)
            printf("r");
        else
            printf("-");
        if(file_st->st_mode & S_IWGRP)
            printf("w");
        else
            printf("-");
        if(file_st->st_mode & S_IXGRP)
            printf("x");
        else
            printf("-");  
        //OTHER
        if(file_st->st_mode & S_IROTH)
            printf("r");
        else
            printf("-");
        if(file_st->st_mode & S_IWOTH)
            printf("w");
        else
            printf("-");
        if(file_st->st_mode & S_IXOTH)
            printf("x");
        else
            printf("-");
        
        //格式化输出
        printf(" %-2ld %-8ld %s\n",
                file_st->st_nlink,
                file_st->st_size,
                d->d_name);
        //printf(" %-8s\t大小: %ld\n",d->d_name,file_st->st_size);
    }
    return 0;
} 

运行截图

alt

2.封装数据结构相关的库

1.静态库

运行截图

alt

2.动态库

运行截图

alt