• Linux系统IO


    Linux系统中的IO函数主要包括两大类:标准C库中的函数和Linux系统调用。这些函数可以用于文件操作、网络通信、设备控制等多种IO任务。以下是Linux系统中常用的IO函数和系统调用的概述:

    标准C库IO函数

    这些函数是高级的、封装好的,并且与操作系统的底层实现相隔离,提供了便捷的接口给程序员使用。

    • 文件操作

      • fopen(), fclose():打开和关闭文件。
      • fread(), fwrite():读写文件数据。
      • fprintf(), fscanf():格式化文件读写。
      • fgets(), fputs():读写字符串到文件。
      • fseek(), ftell(), rewind():控制文件内的位置指针。
    • 缓冲操作

      • setbuf(), setvbuf():设置和管理文件流的缓冲。
    • 错误处理

      • perror()feof()ferror():错误和文件结束的检查。

    Linux系统调用

    系统调用提供了更为直接的操作系统功能访问,允许开发者执行低级的文件、网络以及其他IO操作。

    • 文件和目录操作

      • open(), close():打开和关闭文件描述符。
      • read(), write():通过文件描述符读写数据。
      • lseek():移动文件描述符的读写位置。
      • unlink():删除文件链接。
      • stat(), fstat():获取文件状态。
    • 高级文件操作

      • ioctl():设备控制特殊操作。
      • dup(), dup2():复制文件描述符。
    • 目录操作

      • mkdir(), rmdir():创建和删除目录。
      • chdir():改变当前工作目录。
    • 网络通信

      • socket(), bind(), listen(), accept():用于创建和操作套接字,实现网络通信。
      • connect(), send(), recv():建立连接,发送和接收数据。

    示例代码

    以下是一个使用系统调用 open(), read(), write(), 和 close() 来复制文件内容的简单示例:

    #include 
    #include 
    #include 
    #include 
    
    int main() {
        char buffer[1024];
        int source = open("source.txt", O_RDONLY);
        if (source < 0) return -1;
    
        int dest = open("dest.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (dest < 0) {
            close(source);
            return -1;
        }
    
        ssize_t bytes_read, bytes_written;
        while ((bytes_read = read(source, buffer, sizeof(buffer))) > 0) {
            bytes_written = write(dest, buffer, bytes_read);
            if (bytes_written < 0) break;
        }
    
        close(source);
        close(dest);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    这个示例展示了如何直接使用Linux系统调用来进行文件IO操作。这些函数和调用提供了从高级到低级的多样化操作方式,让开发者根据需要选择最合适的工具来实现各种功能。

    perror函数

    在C语言中,perror 函数是一个用于输出错误信息的标准库函数,它能够将描述最后一次错误的字符串输出到标准错误流(stderr)。perror 用于显示与当前errno值相关联的文本消息,这有助于调试程序时了解错误的具体原因。

    函数原型

    perror 的原型定义在 头文件中,其语法非常简单:

    #include 
    
    void perror(const char *s);
    
    • 1
    • 2
    • 3
    • s:一个指向字符串的指针,通常用于提供错误发生时的上下文或额外信息。在输出的错误消息前,这个字符串将被首先打印,后跟一个冒号和一个空格,然后是与当前 errno 值相关联的错误消息。

    功能描述

    当程序中发生错误时(特别是系统调用错误),大多数系统函数会设置全局变量 errno 的值,以表示特定的错误类型。perror 函数读取 errno 的值,并输出相应的错误描述字符串。如果在调用出错的函数之后 errno 没有被另一个函数调用覆盖,那么 perror 可以用来显示有关错误的描述。

    值得注意的是,errno 是由操作系统维护的,在调用系统函数(如 openreadwrite 等)失败时设置。由于 errno 可能在调用其他函数后被修改,因此最好立即在失败的系统调用后使用 perror 或检查 errno

    open函数

    在Linux和其他类Unix操作系统中,open 函数是一个非常重要的系统调用,用于打开或创建文件并返回一个文件描述符,这个文件描述符在后续的文件操作中被用作标识。

    函数原型

    open 函数的原型定义在 头文件中,其语法结构如下:

    #include 
    #include 
    #include 
    
    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • pathname:要打开或创建文件的路径字符串。
    • flags:控制文件打开方式的标志位。这些标志决定函数的行为(如只读、只写、读写、是否创建新文件等)。
    • mode:当创建新文件时,指定文件的权限。这个参数是可选的,仅在创建新文件时需要。

    Flags 参数

    flags 参数控制文件的打开行为,常用的选项包括:

    • O_RDONLY:以只读方式打开文件。
    • O_WRONLY:以只写方式打开文件。
    • O_RDWR:以读写方式打开文件。
    • O_CREAT:如果文件不存在,则创建它。使用这个选项时需要提供 mode 参数。
    • O_TRUNC:如果文件已存在且成功打开(通常与写权限一起使用),则将文件长度截断为0。
    • O_APPEND:写操作会写入文件的末尾。
    • O_NONBLOCK:以非阻塞模式打开文件。
    • O_EXCL:与 O_CREAT 一起使用时,如果文件已存在,则打开失败。这用于测试文件是否存在。

    Mode 参数

    mode 参数指定新创建文件的访问权限,这些权限使用以下常量(定义在 )来设置:

    • S_IRUSRS_IWUSRS_IXUSR:分别设置文件所有者的读、写、执行权限。
    • S_IRGRPS_IWGRPS_IXGRP:设置组的读、写、执行权限。
    • S_IROTHS_IWOTHS_IXOTH:设置其他用户的读、写、执行权限。
    打开文件
    /*
        #include 
        #include 
        #include 
    
        // 打开一个已经存在的文件
        int open(const char *pathname, int flags);
            参数:
                - pathname:要打开的文件路径
                - flags:对文件的操作权限设置还有其他的设置
                  O_RDONLY,  O_WRONLY,  O_RDWR  这三个设置是互斥的
            返回值:返回一个新的文件描述符,如果调用失败,返回-1
    
        errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。
    
        #include 
        void perror(const char *s);作用:打印errno对应的错误描述
            s参数:用户描述,比如hello,最终输出的内容是  hello:xxx(实际的错误描述)
        
    
        // 创建一个新的文件
        int open(const char *pathname, int flags, mode_t mode);
    */
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
    
        // 打开一个文件
        int fd = open("a.txt", O_RDONLY);
    
        if(fd == -1) {
            perror("open");
        }
        // 读写操作
    
        // 关闭
        close(fd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这个例子中,如果 open 函数因为文件不存在或其他原因失败,perror 将输出类似以下的消息:

    Error opening file: No such file or directory
    
    • 1

    这里,“Error opening file” 是 perror 的参数 s 提供的,而 “No such file or directory” 则是根据 errno 的值自动提供的错误描述。

    创建文件
    /*
        #include 
        #include 
        #include 
    
         
            参数:
                - pathname:要创建的文件的路径
                - flags:对文件的操作权限和其他的设置
                    - 必选项:O_RDONLY,  O_WRONLY, O_RDWR  这三个之间是互斥的
                    - 可选项:O_CREAT 文件不存在,创建新文件
                - mode:八进制的数,表示创建出的新的文件的操作权限,比如:0775
                最终的权限是:mode & ~umask
                0777   ->   111111111
            &   0775   ->   111111101
            ----------------------------
                            111111101
            按位与:0和任何数都为0
            umask的作用就是抹去某些权限。
    
            flags参数是一个int类型的数据,占4个字节,32位。
            flags 32个位,每一位就是一个标志位。
    
    */
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
    
        // 创建一个新的文件
        int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
    
        if(fd == -1) {
            perror("open");
        }
    
        // 关闭
        close(fd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    read函数

    在 Linux 编程中,read 函数是一个系统调用,用于从打开的文件描述符中读取数据。它通常用于从文件、管道、套接字等读取数据。这个函数的原型定义在 头文件中,并且它是 POSIX 标准的一部分,因此在大多数 UNIX-like 系统上都可用。

    函数原型

    #include 
    
    ssize_t read(int fd, void *buf, size_t count);
    
    • 1
    • 2
    • 3

    参数

    • fd:文件描述符,是一个整数,指示从哪个文件读取数据。这个文件描述符通常是通过调用 open, socket, pipe, 或其他相关系统调用得到的。
    • buf:一个指向缓冲区的指针,用来存储从文件描述符读取的数据。
    • count:要读取的最大字节数。

    返回值

    • 成功时,返回读取的字节数,如果文件结尾(EOF),则返回 0。
    • 错误时,返回 -1,并设置 errno 来表示错误类型。

    错误

    read 函数在执行中可能会遇到多种错误,其中一些常见的包括:

    • EBADFfd 不是一个有效的文件描述符或不是为读取操作打开的。
    • EFAULTbuf 指向的缓冲区不可访问或在进程的地址空间之外。
    • EINTR:读取操作被信号中断。
    • EINVAL:传递给 read 的参数无效。
    • EIO:发生输入/输出错误。

    wirte函数

    在 Linux 编程中,write 函数也是一个重要的系统调用,用于将数据写入到打开的文件描述符中。这个函数同样定义在 头文件中,并被广泛用于向文件、管道、套接字等写入数据。

    函数原型

    #include 
    
    ssize_t write(int fd, const void *buf, size_t count);
    
    • 1
    • 2
    • 3

    参数

    • fd:文件描述符,是一个整数,指示将数据写入哪个文件。这个文件描述符通常是通过调用 open, socket, pipe, 或其他相关系统调用得到的。
    • buf:指向缓冲区的指针,这个缓冲区包含了要写入文件的数据。
    • count:要写入的字节数。

    返回值

    • 成功时,返回写入的字节数,可能少于请求写入的字节数(例如,磁盘空间不足时)。
    • 错误时,返回 -1,并设置 errno 来表示错误类型。

    错误

    write 函数在执行中可能会遇到多种错误,其中一些常见的包括:

    • EBADFfd 不是一个有效的文件描述符或不是为写入操作打开的。
    • EFAULTbuf 指向的内存区域超出了你的进程的地址空间。
    • EINTR:写入操作被信号中断。
    • EINVALfd 是无效的或不支持写入操作。
    • EIO:发生输入/输出错误。
    • ENOSPC:设备已满,没有空间来完成操作。

    lseek函数

    在 Linux 编程中,lseek 是一个系统调用,用于改变打开的文件描述符的文件偏移量。这个函数允许你重新定位读写文件的位置,非常有用,尤其是在需要读取或写入文件的非连续部分时。

    函数原型

    #include 
    
    off_t lseek(int fd, off_t offset, int whence);
    
    • 1
    • 2
    • 3

    参数

    • fd:文件描述符,是一个整数,代表一个打开的文件。
    • offset:偏移量,根据 whence 的设置,它可以是正数、负数或零。
    • whence:这个参数决定了如何解释 offset
      • SEEK_SET:将文件的读写位置设置为 offset 指定的绝对位置。
      • SEEK_CUR:将文件的读写位置设置为当前位置加上 offset
      • SEEK_END:将文件的读写位置设置为文件大小加上 offset(对于在文件末尾后进行写操作非常有用)。

    返回值

    • 成功时,返回新的文件偏移量,即从文件开头到新位置的字节数。
    • 错误时,返回 -1,并设置 errno 来表示错误类型。

    错误

    lseek 函数可能遇到的一些错误包括:

    • EBADFfd 不是一个有效的打开文件描述符。
    • EINVALwhence 不是 SEEK_SET, SEEK_CUR, 或 SEEK_END;或者尝试设置文件偏移量为负值。
    • ESPIPEfd 关联的是一个管道、套接字或FIFO,这些类型不支持 lseek
    /*  
        标准C库的函数
        #include 
        int fseek(FILE *stream, long offset, int whence);
    
        Linux系统函数
        #include 
        #include 
        off_t lseek(int fd, off_t offset, int whence);
            参数:
                - fd:文件描述符,通过open得到的,通过这个fd操作某个文件
                - offset:偏移量
                - whence:
                    SEEK_SET
                        设置文件指针的偏移量
                    SEEK_CUR
                        设置偏移量:当前位置 + 第二个参数offset的值
                    SEEK_END
                        设置偏移量:文件大小 + 第二个参数offset的值
            返回值:返回文件指针的位置
    
    
        作用:
            1.移动文件指针到文件头
            lseek(fd, 0, SEEK_SET);
    
            2.获取当前文件指针的位置
            lseek(fd, 0, SEEK_CUR);
    
            3.获取文件长度
            lseek(fd, 0, SEEK_END);
    
            4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
            lseek(fd, 100, SEEK_END)
            注意:需要写一次数据
    
    */
    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
    
        int fd = open("hello.txt", O_RDWR);
    
        if(fd == -1) {
            perror("open");
            return -1;
        }
     
        // 扩展文件的长度
        int ret = lseek(fd, 100, SEEK_END);
        if(ret == -1) {
            perror("lseek");
            return -1;
        }
    
        // 写入一个空数据
        write(fd, " ", 1);
    
        // 关闭文件
        close(fd);
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    stat函数

    stat结构体
    struct stat {
    dev_t st_dev; // 文件的设备编号
    ino_t st_ino; // 节点
    mode_t st_mode; // 文件的类型和存取的权限
    nlink_t st_nlink; // 连到该文件的硬连接数目
    uid_t st_uid; // 用户ID
    gid_t st_gid; // 组ID
    dev_t st_rdev; // 设备文件的设备编号
    off_t st_size; // 文件字节数(文件大小)
    blksize_t st_blksize; // 块大小
    blkcnt_t st_blocks; // 块数
    time_t st_atime; // 最后一次访问时间
    time_t st_mtime; // 最后一次修改时间
    time_t st_ctime; // 最后一次改变时间(指属性)
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    st_mode变量

    image-20240421115626732

    /*
        #include 
        #include 
        #include 
    
        int stat(const char *pathname, struct stat *statbuf);
            作用:获取一个文件相关的一些信息
            参数:
                - pathname:操作的文件的路径
                - statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
            返回值:
                成功:返回0
                失败:返回-1 设置errno
    
        int lstat(const char *pathname, struct stat *statbuf);
            参数:
                - pathname:操作的文件的路径
                - statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
            返回值:
                成功:返回0
                失败:返回-1 设置errno
    
    */
    
    #include 
    #include 
    #include 
    #include 
    
    int main() {
    
        struct stat statbuf;
    
        int ret = stat("a.txt", &statbuf);
    
        if(ret == -1) {
            perror("stat");
            return -1;
        }
    
        printf("size: %ld\n", statbuf.st_size);
    
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    lstat函数

    创建软连接

    ln -s a.txt b.txt
    
    • 1

    在 Linux 编程中,lstat 函数类似于 stat 函数,但与 stat 相比,lstat 有一个重要的区别:当被调用的文件是一个符号链接时,lstat 返回的是符号链接本身的信息,而不是链接指向的文件的信息。这使得 lstat 在处理符号链接时特别有用。

    模拟实现ls-l

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    // 模拟实现 ls -l 指令
    // -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt
    int main(int argc, char * argv[]) {
    
        // 判断输入的参数是否正确
        if(argc < 2) {
            printf("%s filename\n", argv[0]);
            return -1;
        }
    
        // 通过stat函数获取用户传入的文件的信息
        struct stat st;
        int ret = stat(argv[1], &st);
        if(ret == -1) {
            perror("stat");
            return -1;
        }
    
        // 获取文件类型和文件权限
        char perms[11] = {0};   // 用于保存文件类型和文件权限的字符串
    
        switch(st.st_mode & S_IFMT) {
            case S_IFLNK:
                perms[0] = 'l';
                break;
            case S_IFDIR:
                perms[0] = 'd';
                break;
            case S_IFREG:
                perms[0] = '-';
                break; 
            case S_IFBLK:
                perms[0] = 'b';
                break; 
            case S_IFCHR:
                perms[0] = 'c';
                break; 
            case S_IFSOCK:
                perms[0] = 's';
                break;
            case S_IFIFO:
                perms[0] = 'p';
                break;
            default:
                perms[0] = '?';
                break;
        }
    
        // 判断文件的访问权限
    
        // 文件所有者
        perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
        perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
        perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';
    
        // 文件所在组
        perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
        perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
        perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
    
        // 其他人
        perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
        perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
        perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';
    
        // 硬连接数
        int linkNum = st.st_nlink;
    
        // 文件所有者
        char * fileUser = getpwuid(st.st_uid)->pw_name;
    
        // 文件所在组
        char * fileGrp = getgrgid(st.st_gid)->gr_name;
    
        // 文件大小
        long int fileSize = st.st_size;
    
        // 获取修改的时间
        char * time = ctime(&st.st_mtime);
    
        char mtime[512] = {0};
        strncpy(mtime, time, strlen(time) - 1);
    
        char buf[1024];
        sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
    
        printf("%s\n", buf);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

  • 相关阅读:
    JavaEE初阶-计算机是如何工作的
    el-upload 上传&表单校验成功后再触发上传
    Windows下后台运行、关闭jar的命令
    C++ vector 效率之capacity()、resize()、reserve()
    Day29_10 JavaWeb之Servlet及Servlet细节
    模板的特化
    WPF利用Path自定义画头部导航条(TOP)样式
    自用函数(持续更新)
    JDK1.8中Date_Time API使用
    Day26:内部类的详解
  • 原文地址:https://blog.csdn.net/qq_36372352/article/details/138039463