【Linux】基础IO—1

这篇具有很好参考价值的文章主要介绍了【Linux】基础IO—1。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

文章目录

前言

先来段代码回顾C文件接口

"w"写文件

"a"追加文件

"r"读文件

输出信息到显示器,你有哪些方法

stdin & stdout & stderr

提炼一下对文件的理解(第一阶段)

理解文件并采用系统调用接口来访问文件

总结



前言

世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!


提示:以下是本篇文章正文内容,下面案例可供参考

先来段代码回顾C文件接口

"w"写文件

#include <stdio.h>
#include <string.h>

int main()
{
	FILE* fp = fopen("myfile", "w");
	if (!fp) {
		printf("fopen error!\n");
	}

	const char* msg = "hello bit!\n";
	int count = 5;
	while (count--) {
		fwrite(msg, strlen(msg), 1, fp);
	}

	fclose(fp);

	return 0;
}

我们要进行文件操作,前提是我们的程序跑起来。文件的打开和关闭,是CPU在执行我们的代码。

以“w”的方式打开文件:

1、如果文件不存在,就在当前路径下,新建指定的文件;

2、默认打开文件的时候,就会先把目标文件清空。

echo "hello bite" > log.txt

>(输出重定向):

1、文件不存在,就新建文件;

2、打开文件,先清空,再写入

>>(追加重定向):

不会清空文件,会在文件内容后面添加内容

"a"追加文件

#include <stdio.h>
int main()
{
    // 系统怎么知道当前创建的log.txt文件在这个路径下呢?
    // 因为我们在运行文件操作的时候,执行我们所写的代码,执行的时候,就已经变成了一个进程了,
    // 所以,我们建立log.txt文件时,默认会结合我们当前进程所在路径,拼上我们的log.txt,创建log.txt文件。
    // 我们进程在启动时,所处的路径,叫做当前进程的当前工作路径。
    FILE* fp = fopen("log.txt", "a");
    if (NULL == fp)
    {
        perror("fopen");
        return 1;
    }

    fprintf(fp, "helloworld, %d, %s, %lf\n", 10, "whb", 3.14);

    fclose(fp);
    return 0;
}

"r"读文件

#include <stdio.h>
#include <string.h>

int main()
{
    FILE* fp = fopen("myfile", "r");
    if (!fp) {
        printf("fopen error!\n");
    }

    char buf[1024];
    const char* msg = "hello bit!\n";
    while (1)
    {
        //注意返回值和参数,此处有坑,仔细查看man手册关于该函数的说明
        ssize_t s = fread(buf, 1, strlen(msg), fp);
        if (s > 0) {
            buf[s] = 0;
            printf("%s", buf);
        }
        if (feof(fp)) {
            break;
        }
    }

    fclose(fp);
    return 0;
}

输出信息到显示器,你有哪些方法

#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;
}
int fprintf(FILE *stream,const char *format,...)
// 向显示器当中进行打印,把指定的内容按照指定的格式,写到特定的文件当中

stdin & stdout & stderr

  • C默认会打开三个输入输出流,分别是stdin(键盘), stdout(显示器), stderr(显示器)
  • 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

文件操作详解

提炼一下对文件的理解(第一阶段)

打开文件:本质其实是进程(struct task_struct)打开文件(struct file)!!!

文件没有被打开的时候,在哪里?在磁盘当中。

进程能打开很多文件吗?可以。

系统当中可不可以存在很多进程呢?可以。

很多清空下,OS内部,一定存在大量的被打开的文件。

那么OS要不要把这些被打开的文件进行管理呢?答案是要的。先描述,再组织!所以每一个被打开的文件,在OS内部,一定要存在对应的描述文件属性的结构体。类似PCB。

每一个语言对文件的操作方法都是不一样的。

理解文件并采用系统调用接口来访问文件

a、操作文件,本质:进程在操作文件。进程的文件的关系。

b、文件 -> 磁盘 -> 外设 -> 硬件 -> 向文件中写入,本质是向硬件中写入 -> 用户没有权利直接在硬件内写入 -> OS是硬件的管理者 -> 通过OS写入 -> OS必须给我们提供系统调用接口(OS不相信任何人) -> 我们用的C/C++/...中操作文件的方法,都是对系统调用接口的封装!

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问, 先来直接以代码的形式,实现和上面一模一样的代码:

 系统调用的文件操作
 man 2 open  2号手册,系统调用 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);
 第一个参数:打开的文件是谁,可以带路径,也可以直接写文件名;如果只有文件名,就在当前的路径下创建文件;
 第二个参数:想要怎么创建这个文件,flags是一个整数,但是flags可以传递很多标记位;
 第二个参数:flags是一个整数,是32个比特位。用比特位来进行标记位的传递。 ---- OS设计很多系统调用接口的常见方法  本质:位图
 第三个参数:文件的起始权限
 返回值:是一个整数,文件描述符;失败:返回-1,错误码被设置
 man 2 close
 #include <unistd.h>
 int close(int fd); 参数:open()函数返回的整数
 man 2 write  向文件当中写入
 #include <unistd.h>
 ssize_t write(int fd,const void *buf,size_t count);// 把指定的缓冲区写入指定的文件里
 第二个参数:指定一个缓冲区,缓冲区的起始地址
 第三个参数:缓冲区的大小

【Linux】基础IO—1,Linux,linux,运维,服务器

操作系统为每一个被打开的文件,都创建了内核数据结构(struct file),用于文件的描述,包含了文件的属性;struct file内部中会包含了两个个指针,一个指针会指向我们在系统当中对应的一段与该文件所对应的,叫做文件内核级的缓存(其实就是OS给我们申请的一块内存);另一个指针,指向的是操作底层方法的指针表(比如:一些硬件设备的操作方法)。

文件 = 属性 + 内容

  • 属性初始化 struct file    
  • 文件内容写到缓存里:日后读、写就在缓存里进行,然后再把数据刷新到磁盘里。

所以,每一个文件都要有一个:文件内核级的缓存
最终我们对OS内的文件的管理,转化成了对 struct file的内核数据结构的管理。
进程task_struct里有一个指针:struct files_struct *files,这个指针指向的是struct files_struct空间,空间里有一个指针数组(struct file *fd array[N]),这个指针数组中的每一个元素都是被打开文件的地址,而指针数组的下标是fd(文件描述符)

操作系统只认文件描述符fd,只要拿到了fd就可以对文件进行操作。

无论读写,都必须在合适的时候,让OS把文件的内容读到文件缓存区中。write、read函数,本质都是在拷贝数据。

open()在干什么呢?

1、创建file;

2、开辟文件缓冲区的空间,加载文件数据(延后);

3、查进程的文件描述符表;

4、file地址,填入对应的文件描述符表下标中;

5、返回下标。

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

int main()
{
    const char* message = "hello Linux file!\n";
    // 可以直接向“1”里面打印内容,“1”就是标准输出流
    write(1, message, strlen(message));

    // C语言的方式向标准输出流里打印内容
    fprintf(stdout, "hello: %d\n", 10);// 第一个参数是stdout的话,和printf()函数的结果一样
    fflush(stdout);// 刷新数据

    // 0:标准输入(键盘)  1:标准输出(显示器)  2:标准输出(显示器) 
    // 0、1、2已经被占用了,所以open()函数的返回值从3开始
    int fda = open("loga.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    printf("fda: %d\n", fda);// 3
    int fdb = open("logb.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    printf("fdb: %d\n", fdb);// 4
    int fdc = open("logc.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    printf("fdc: %d\n", fdc);// 5
    int fdd = open("logd.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    printf("fdd: %d\n", fdd);// 6

    umask(0);// 权限掩码设置为0  就近原则:有设置的掩码,就用设置的;没有,就用系统的
    // system call
    // O_WRONLY:以只写的方式打开    O:代表open的意思
    // O_CREAT:如果文件不存在,创建这个文件
    // O_TRUNC:清空文件内容
    // O_APPEND:打开文件,用追加模式
    // 0666:文件的起始权限,起始权限也会和umask权限掩码处理,所以,不一定是0666的权限
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
    // open()系统调用接口,以写的方式打开,默认不清空文件内容,下次写入文件是以覆盖式的写入内容

    if(fd < 0)
    {
        perror("open");
        return 1;
    }

    const char *message = "hello Linux file!\n";
    //const char *message = "abcdefg\n";
    //const char *message = "123";
    write(fd, message, strlen(message));// C语言中有'\0',而Linux中没有'\0'的规则

    close(fd);
    return 0;
}

O_RDONLY: 只读打开

O_WRONLY: 只写打开

O_RDWR : 读,写打开 这三个常量,必须指定一个且只能指定一个

O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写

 怎么理解write(1, message, strlen(message))向一个整数里写,就相当于在一个文件里写呢?
 文件描述符fd,fd的本质是:内核的进程和文件映射关系的数组的下标。

 man 2 umask  //2号手册的权限掩码

 #include <sys/types.h>
 #include <sys/stat.h>
 mode_t umask(mode_t mask);//在程序运行时,动态的设置权限的掩码

我们这样讲可能还会有些抽象,我们直接找到原码来看一下这些结构体之间的关系:

【Linux】基础IO—1,Linux,linux,运维,服务器

【Linux】基础IO—1,Linux,linux,运维,服务器

而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数 组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件。

我们要对文件进行操作,我们可以用系统调用,也可以用语言提供的文件方法,但是最好还是用语言提供的文件方法。为什么呢?

因为系统不同,系统调用接口可能不一样,系统调用的方法不具有跨平台性。

语言为什么具有跨平台性?
比如:C语言文件的操作函数,底层都是用系统调用接口实现的,而不同的操作平台,系统调用接口可能不一样,就比如:C语言中的fopen()函数就使用不同的OS(windows、maxos、Linux)的系统调用接口实现fopen()函数,那么在编译的时候,不同的OS会生成不同的标准库,当然这些标准库,也都是C标准库,我们每再一个平台下,都需要下载对应平台的标准库,它们标准库中实现的函数都叫fopen(),所以C语言具有跨平台性。

我有一个显示器,但是可以有很多终端。

ls /lib64/libc.so 
//C语言的动态库(文件)

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


总结

好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。文章来源地址https://www.toymoban.com/news/detail-846270.html

到了这里,关于【Linux】基础IO—1的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux 服务器运维】定时任务 crontab 详解 | 文末送书

    本文思维导图概述的主要内容: 1.1 什么是 crontab Crontab 是一个在 Unix 和 Linux 操作系统上 用于定时执行任务 的工具。它允许用户创建和管理计划任务,以便在特定的时间间隔或时间点自动运行命令或脚本。Crontab 是 cron table 的缩写, cron 指的是 Unix 系统中的一个后台进程,它

    2024年02月08日
    浏览(92)
  • 【Linux运维】shell脚本检查服务器内存和CPU利用率

    在管理服务器时候写了一个 shell脚本,在服务上实现每天凌晨3点查系统的指定文件夹下的容量大小,如果超过10G就要删除3天前的内容,还要时刻查询内存和cpu利用率,如果超过80%就要提示用户出现过载 将以上代码保存为一个.sh文件,然后通过crontab在每天凌晨3点运行即可:

    2024年02月09日
    浏览(67)
  • Linux多路IO复用技术——epoll详解与一对多服务器实现

    本文详细介绍了Linux中epoll模型的优化原理和使用方法,以及如何利用epoll模型实现简易的一对多服务器。通过对epoll模型的优化和相关接口的解释,帮助读者理解epoll模型的工作原理和优缺点,同时附带代码实现和图解说明。

    2024年02月05日
    浏览(44)
  • Linux学习记录——사십일 高级IO(2)--- Select型服务器

    select就是多路转接IO。select能以某种形式,等待多个文件描述符,只要有哪个fd有数据就可以读取并全部返回。就绪的fd,要让用户知道。select等待的多个fd中,一定有少量或者全部都准备好了数据。 nfds输入型参数,表示select等待的多个fd中,fd对应的数 + 1 剩下四个参数都是输

    2024年01月16日
    浏览(53)
  • Linux搭建Web服务器(一)——阻塞与非阻塞、同步与异步、Linux五种IO模型

    目录 0x01 阻塞与非阻塞、同步与异步 阻塞与非阻塞 同步与异步 总结 0x02 Unix、Linux上的五种IO模型 阻塞(blocking) 非阻塞(non-blocking——NIO) IO复用(IO multiplexing) 信号驱动(signal-driven) 异步(asynchronous) 为了理清楚这几个概念,我们可以从 数据就绪 以及 数据读写 层面

    2023年04月10日
    浏览(66)
  • Linux学习记录——사십삼 高级IO(4)--- Epoll型服务器(1)

    poll依然需要OS去遍历所有fd。一个进程去多个特定的文件中等待,只要有一个就绪,就使用select/poll系统调用,让操作系统把所有文件遍历一遍,哪些就绪就加上哪些fd,再返回。一旦文件太多了,遍历效率就显而易见地低。epoll是为处理大批量句柄而作了改进的poll,句柄就是

    2024年01月18日
    浏览(52)
  • Linux服务器常见运维性能测试(1)综合跑分unixbench、superbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月04日
    浏览(82)
  • Linux本地部署1Panel服务器运维管理面板并实现公网访问

    1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等 下面我们介绍在Linux 本地安装1Panel 并结合cpolar 内网穿透工具实现远程访问1Panel 管理界面 执行如下命令一键安装 1Panel: 安

    2024年02月04日
    浏览(98)
  • 【Linux网络编程】TCP并发服务器的实现(IO多路复用select)

    服务器模型主要分为两种, 循环服务器 和 并发服务器 。 循环服务器 : 在同一时间只能处理一个客户端的请求。 并发服务器 : 在同一时间内能同时处理多个客户端的请求。 TCP的服务器默认的就是一个循环服务器,原因是有两个阻塞 accept函数 和recv函数 之间会相互影响。

    2024年02月03日
    浏览(84)
  • Linux学习记录——사십사 高级IO(5)--- Epoll型服务器(2)(Reactor)

    本篇基于上篇代码继续改进,很长。关于Reactor的说明在后一篇 上面的代码在处理读事件时,用的request数组是临时的,如果有数据没读完,那么下次再来到这里,就没有这些数据了。所以得让每一个fd都有自己的缓冲区。建立一个Connection类,然后有一个map结构,让这个类和每

    2024年01月20日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包