Linux基础IO(1)

简介: Linux基础IO(1)

零、前言


本章主要讲解学习Linux基础IO流的知识


一、C语言文件IO


1、C库函数介绍


具体详解博文: 文件操作超详解CSDN博客


  • 打关文件fopen/fclose:


FILE * fopen(const char* filename, const char* mode);
int fclose (FILE* stream );


  • 读写函数fread/fwrite:


size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );


  • 格式化读写fscanf/fprintf:


int fscanf( FILE *stream, const char *format [, argument ]... );
int fprintf( FILE *stream, const char *format [, argument ]...);


  • 示例1:输出使用


#include <stdio.h>
#include <string.h>
int main()
{
    const char *msg = "hello fwrite\n";
    fwrite(msg, strlen(msg), 1, stdout);
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    return 0;
}


  • 示例2:文件读写


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
    FILE* fp=fopen("cole.txt","w+");//以写读方式打开文件
    if(fp==NULL)
    {
        perror("fopen");
        exit(1);
    }
    char msg[]="linux so easy!\n";
    int len=strlen(msg);
    int cnt=5;
    while(cnt--)
    {
        fwrite(msg,len,1,fp);//写入
    }
    fseek(fp,0,SEEK_SET);//指针回到起始位置
    char buf[128];
    while(1)
    {
        ssize_t s=fread(buf,1,len,fp);
        if(s>0)
        {
            buf[s]=0;//设置结束符
            fwrite(buf,len+1,1,stdout);
        }
        else//读取结束
            break;
    }
    fclose(fp);
    return 0;
}


2、stdin/stdout/stderr


  • 文件原型:


extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;


概念:

任何C程序运行都会默认打开三个输入输出流,分别是:stdin, stdout, stderr


分三个文件流分别对应键盘文件,显示器文件,显示器文件


为什么这里的文件流和外设关联上了:

对于所有外设硬件来说,其本质对应的操作不外乎是读操作和写操作,对于不同外设也就有不同的读写方式


OS要管理硬件设备无非是先描述再组织,由此将属性以及读写操作构成一个结构体,而文件其本身也是属性加读写操作,这样就由文件结构体同一管理文件(包括外设)


在C语言中虽然没有多态,但是结构体中可以储存函数指针,初始化结构体时,将属性写入的同时也将对应的读写函数给写入;对于外设来说,通过对应的文件结构体使用函数指针调用对应的读写函数,也就将数据刷新到对于设备上/从设备上读取数据


由此将普通文件和硬件设备管理组织好,所以对于Linux来说:一切皆文件


为什么C语言默认打开这三个输入输出流:

不仅仅是C语言会默认打开这三个输入输出流文件,几乎是任何语言都会这样,而这就不仅仅是语言层面上的功能了,也是由操作系统所支持的


对于任何语言来说,都有输入输出的需求,而不打开这三个输入出输出流文件,则无法使用这些接口


二、系统文件IO


1、系统调用介绍


操作文件,除了上述C接口(当然C++也有接口,其他语言也有),还可以使用系统接口


  • open接口:


#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);


  • 参数解释:
  1. pathname: 要打开或创建的目标文件
  2. flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags
  3. mode_t:如果没有对应文件需要进行创建的话,就需要指定创建文件的八进制访问权限值




注:这里的参数选项是依靠不同的比特位来标识对应的功能设定,所以这里的异或操作就是将对应比特位置为1,同时函数也是通过对每个比特位进行与操作检查是否该比特位置为了1

  • 原型示例:


#define O_RDONLY         00
#define O_WRONLY         01
#define O_RDWR           02
#define O_CREAT        0100


  • 其他接口:


int close(int fd);
//使用close函数时传入需要关闭文件的文件描述符fd即可,若关闭文件成功则返回0,若关闭文件失败则返回-1
ssize_t write(int fd, const void *buf, size_t count);
//使用write函数,将buf位置开始向后count字节的数据写入文件描述符为fd的文件当中
//如果数据写入成功,实际写入数据的字节个数被返回;如果数据写入失败,-1被返回
ssize_t read(int fd, void *buf, size_t count);
//使用read函数,从文件描述符为fd的文件读取count字节的数据到buf位置当中
//如果数据读取成功,实际读取数据的字节个数被返回;如果数据读取失败,-1被返回


  • 示例:文件读写


#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main()
{
    int fd=open("cole",O_WRONLY|O_CREAT,0644);
    if(fd<0)
    {
        perror("open");
        exit(1);
    }
    char msg[]="i like linux!\n";
    int len=strlen(msg);
    int cnt=5;
    while(cnt--)
    {
        write(fd,msg,len);
    }
    close(fd);
    fd=open("bit",O_RDWR);
    char buf[128];
    while(1)
    {
        ssize_t s=read(fd,buf,len);
        if(s>0)
        {
            buf[s]=0;
            write(1,buf,len+1);
        }
        else
            break;
    }
    close(fd);
    return 0;
}


结果:


2、系统调用和库函数


概念:

对于上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc);而 open close read write lseek 都属于系统提供的接口,称之为系统调用接口


对于系统调用来说,接近底层,使用成本较高,并且不具备可移植性,只在本系统下可以,其他系统不行


对于库函数来说,是在系统暴露的接口上的一个二次开发(最终调用系统调用),在兼容自己语法的特性的同时,具有可移植性(自动根据平台选择自己底层对应的接口)


即可以认为库函数是对系统调用的封装,减低人工学习成本,方便二次开发


示图:


三、文件描述符


1、open返回值


  • 文件描述符fd:


文件描述符就是一个小整数


  • 0 & 1 & 2:


Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2



  • 示例1:


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main()
{
    char buf[1024];
    ssize_t s = read(0, buf, sizeof(buf));//标准输入
    if(s > 0){
        buf[s] = 0;
        write(1, buf, strlen(buf));//标准输出
        write(2, buf, strlen(buf));//标准错误
    }
    return 0;
}


示例2


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main()
{
    int fd1=open("fd1.txt",O_WRONLY|O_CREAT,0644);
    int fd2=open("fd1.txt",O_WRONLY|O_CREAT,0644);
    int fd3=open("fd1.txt",O_WRONLY|O_CREAT,0644);
    int fd4=open("fd1.txt",O_WRONLY|O_CREAT,0644);
    int fd5=open("fd1.txt",O_WRONLY|O_CREAT,0644);
    printf("%d\n",fd1);
    printf("%d\n",fd2);
    printf("%d\n",fd3);
    printf("%d\n",fd4);
    printf("%d\n",fd5);
    return 0;
}


注:从示例中可见,文件描述符就是从0开始的小整数:默认打开0,1,2,再打开则是从后递增


分析:

当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件


于是就有了file结构体,表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。


每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针


所以本质上,文件描述符就是该数组的下标。只要拿着文件描述符,就可以通过PCB到file_struct的指针数组找到对应的文件结构体地址


示图:


2、fd分配规则


文件描述符分配规则:

在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符


示例1:


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd = open("myfile", O_RDONLY);
    if(fd < 0){
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    close(fd);
    return 0;
}


结果:输出3


  • 示例2:


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(0);
    //close(2);
    int fd = open("myfile", O_RDONLY);
    if(fd < 0){
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    close(fd);
    return 0;
}


结果:关闭0输出0,关闭2输出2

相关文章
|
4天前
|
存储 缓存 Linux
Linux IO的奥秘:深入探索数据流动的魔法
Linux I/O(输入/输出)系统是其核心功能之一,负责处理数据在系统内部及与外界之间的流动。为了优化这一流程,Linux进行了一系列努力和抽象化,以提高效率、灵活性和易用性。🚀
Linux IO的奥秘:深入探索数据流动的魔法
|
4天前
|
缓存 监控 IDE
linux如何查看io性能
linux如何查看io性能
|
4天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
4天前
|
Linux
Linux操作系统调优相关工具(三)查看IO运行状态相关工具 查看哪个磁盘或分区最繁忙?
Linux操作系统调优相关工具(三)查看IO运行状态相关工具 查看哪个磁盘或分区最繁忙?
31 0
|
4天前
|
Unix Linux Shell
【探索Linux】P.12(文件描述符 | 重定向 | 基础IO)
【探索Linux】P.12(文件描述符 | 重定向 | 基础IO)
14 0
|
4天前
|
缓存 Linux
Linux 文件IO简单实例
Linux 文件IO简单实例
15 1
|
3天前
|
存储 Linux C语言
|
4天前
|
Unix Linux 开发工具
【探索Linux】P.11(基础IO,文件操作)
【探索Linux】P.11(基础IO,文件操作)
12 0
|
4天前
|
监控 Linux API
Linux内核探幽:深入浅出IO模型
在Linux操作系统中,I/O(输入/输出)模型是一套定义如何处理数据读写的机制,它对系统性能有着重要影响。为了适应不同的应用场景和性能需求,Linux抽象出了多种I/O模型。每种模型都有其独特的特点、底层原理、优劣势以及适用场景。🤓
Linux内核探幽:深入浅出IO模型
|
4天前
|
存储 缓存 安全
Linux IO:打开数据之窗的魔法
Linux I/O(输入/输出)是操作系统中一个至关重要的组成部分,它涉及到数据在内存🧠、存储设备💾、网络接口🌐等之间的传输过程。在Linux中,I/O操作不仅仅是文件读写那么简单,它包括了一系列复杂的机制和策略,旨在提高数据处理的效率,保证系统的稳定性和性能。📊
Linux IO:打开数据之窗的魔法