前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >初识Linux · 文件(2)

初识Linux · 文件(2)

作者头像
_lazy
发布2024-10-16 14:43:55
400
发布2024-10-16 14:43:55
举报
文章被收录于专栏:Initial programming

前言:

由前文文件(1)的介绍,我们引出了三个问题:

高级语言和系统调用函数之间是否存在关系? fd返回值的012和C语言打开的三个流是否有什么关系? 不同的宏和不同函数的选项之间是否存在某种关系?

这是我们文件一里面引发的问题,那么在文件(2)里面呢,我们通过对文件深层次的理解,就会知道,以上三个问题的答案了。

那么现在,我们就进入主题吧。

文件描述符fd

我们再看一眼open函数和write函数:

返回值的描述是:返回值新的文件描述符,如果出错了返回的是-1。

那么我们研究的是这个返回值。

首先,我们知道打开文件的是进程,而非我们,那么文件的集中管理,实际上是由进程的task_struct有一个文件指针,struct file_struct* files,指向的一块文件结构体->struct files_struct,对于该结构体而言,管理的是另一个结构体,即由文件组成的结构体,它们之间的连接方式是双向链表的方式。

就像这样,那么file_struct和file,两个结构体之间是如何产生联系的呢?

此时,文件描述符就出场了:

files_struct里面有一块空间,通过下标访问,而下标指向的,就是一个一个的struct file。所以每次拿到了fd之后,就能对文件进行操作。

但是光这样,好像对文件理解并没有加深多少。

我们写的数据在哪里呢?

我们使用函数,里面的数据是放在哪里呢?难道是有了文件描述符fd我们就可以直接对files结构体操作了?不是的,因为文件存储的地方是在磁盘上,而非OS里面,所以我们一定是会和磁盘扯上一定关系的,那么:

在OS里面有一块空间,缓存空间,将数据加载到了缓存里面,此时没有出错,就可以对文件,也就是已经先描述再操作的的文件对象里面,将数据进行写入即可。

那么我们简单总结上面的过程就是:

文件 = 属性 + 内容,我们要修改内容,也就是要和外设打交道,那么需要OS层面管理。为了修改外设的属性,所以需要类似驱动程序操作,此时OS层面的进程,也就是打开文件的软件,加上管理文件的多个结构体,结合文件描述符fd进行操作。

那么系统调用函数open整个过程要干的事就是:

1 创建文件file 2 开辟文件缓冲区 加载文件数据 3 查找对应的文件描述符fd 4 通过file地址将数据移动 5 返回下标。


默认的三个流

文件描述符fd我们已经理解了,根据上文012是默认打开的三个流:0 对应的标准输入,1对应的标准输出,2对应的标准错误,输入比如键盘,输出和错误都是对应的显示器。

那么不同的外设,输入输出都应该有自己的一套体系,或者说有对应的函数,但是函数名往往都是不相同的,在OS层面如何进行集中管理呢?

在OS层面存在一种结构叫做:VFS,即virtual file system。

因为每个file对象,都有一个函数指针,虽然函数名不同,但是可以将函数的参数,返回值等弄的大差不差呗。此时,访问对应的外设,OS层面可以通过VFS层,对不同的外设进行访问。

可是我们说了这么多,如何证明呢?

我们可以用file对象里面的一个变量证明,_fileno,返回值就是对应的文件描述符,而C语言的FILE指针,本质是经过typedef的,封装的是系统里面的file,所以FILE指针直线的对象肯定也是有_fileno的,我们使用如下代码证明:

代码语言:javascript
复制
int main()
{
    FILE* fp = fopen("log.txt","w");
    printf("fp->%d\n",fp->_fileno);

    printf("stdin->%d\n",stdin->_fileno);
    printf("stdout->%d\n",stdout->_fileno);
    printf("stderr->%d\n",stderr->_fileno);

    return 0;
}

此时,证明完毕。


最后总结

由上文可以得出,高级语言想要使用文件描述符fd,一定是会经过封装的,不然对于外设层面是没有办法使用的。

那么,既然显示器也是个文件,我们不妨尝试对显示器这个文件进行写入,前提是我们如何知道显示器的文件在哪里呢?

我们可以进入到根目录的dev,dev代表设备,其中:

dev目录里面的三个流也是间接证明了。

同时,我们进入到pts目录,再开一个终端,就会发现:

也就是说,新开的终端的文件是5号,那么,我们可以:

代码语言:javascript
复制
int main()
{
    int fd = open("/dev/pts/5", O_WRONLY|O_APPEND);
    if(fd < 0) return 1;
    const char *message = "hahaha\n";
    while(1)
    {
        write(fd, message, strlen(message));
        sleep(1);
    }

    close(fd);
    return 0;
}

此时,就在一直打印了。

所以,高级语言的所有文件操作函数,都是对系统调用的封装!!

那么,我们之后是推荐使用高级语言的函数还是系统调用呢?

当然是高级语言的了,因为系统调用的函数不具有跨平台性!!

文件我们也算是理解了部分了,下文之后,就是应用层面的了,比如重定向,比如语言级别的缓冲区。

感谢阅读!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 文件描述符fd
  • 默认的三个流
  • 最后总结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档