文件操作与系统调用


前言

Linux系统中有一个重要的概念:一切皆文件,本节主要介绍文件系统以及系统调用(文件操作)。

一、文件系统有哪些?

文件系统主要分为:存储设备文件系统伪文件系统虚拟文件系统

1.1 存储设备文件系统

这种文件系统主要是为了解决如何高效管理存储器空间的问题 在Linux下常用有ext2ext3ext4的类型格式,Windows常用的有FAT32NTFSexFAT
我们主要介绍ext4格式:从ext3改进而来,从2008年结束实验期,进入稳定版。支持1EB的分区,单个文件最大支 持16TB,支持无限的子目录数量。

Linux下,可以通过如下命令查看系统当前存储设备使用的文件系统:df -T,从图中可以看出,主机的硬盘设备为/dev/mmcblk1p2,它使用的文件系统类型为ext4,挂载点是根目录/

在这里插入图片描述

1.2 伪文件系统

Linux内核还提供了procfssysfsdevfs等伪文件系统,伪文件系统存在于内存中,通常不占用硬盘空间,它以文件的形式,向用户提供了访问系统内核数据的接口。

procfs是进程文件系统,通常自动挂载在根目录下的/proc文件夹,可以通过cat /proc/cpuinfo查看CPU信息,ls /proc查看proc目录
如图所示/proc包含了非常多以数字命 名的目录,这些数字就是进程的PID号

在这里插入图片描述

sysfs通常会自动挂载在根目录下的sys文件夹,提供了一些关于设备内核模块文件系统以及内核组件的信息
sysfs文件系统在内核加载驱动时,根据系统上的设备和总线构成导出的分级目录,它是系统上设备的直观反应,每个设备在sysfs下都有唯一的对应目录,用户可以通过具体设备目录下的文件访问设备

在这里插入图片描述

1.3 虚拟文件系统

为了使不同的文件系统共存,Linux内核在用户层与具体文件系统之前增加了虚拟文件系统中间层,它对复杂的系统进行抽象化,对用户提供了统一的文件操作接口。无论是ext2/3/4、FAT32、NTFS存储的文件,还是/proc、/sys提供 的信息还是硬件设备,无论内容是在本地还是网络上,都使用一样的openreadwrite来访问,使得“一切皆文件”的理念被实现

在这里插入图片描述

二、系统调用

系统调用(System Call)是操作系统提供给用户程序调用的一组“特殊”函数接口API,文件操作就是其中一种类型。Linux提供的系统调用包含以下内容:

类别系统调用示例
进程控制fork, clone, exit, setpriority 等创建、中止、设置进程优先级的操作
文件系统控制open, read, write 等对文件的打开、读取、写入操作
系统控制reboot, stime, init_module 等重启、调整系统时间、初始化模块的系统操作
内存管理mlock, mremap 等内存页上锁重、映射虚拟内存操作
网络管理sethostname, gethostname 设置或获取本主机名操作
socket控制socket, bind, send 等进行TCP、UDP的网络通讯操作
用户管理setuid, getuid 等设置或获取用户ID的操作
进程间通信包含信号量、管道、共享内存等操作

现在通过文件操作的两个代码,来演示使用C标准库系统调用方式的差异

C标准库:使用fopen创建文件、使用fwrite写入内容,使用fflush确保缓冲区的内容写到文件,然后使用fseek重置文件位置指针,使用fread把文件的内容读出,最后调用fclose关闭文件

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

//要写入的字符串
const char buf[] = "filesystem_test:Hello World!\n";
//文件描述符
FILE *fp;
char str[100];

int main(void)
{
   //创建一个文件
   fp = fopen("filesystem_test.txt", "w+");
   //正常返回文件指针
   //异常返回NULL
   if(NULL == fp){
      printf("Fail to Open File\n");
      return 0;
   }
   //将buf的内容写入文件
   //每次写入1个字节,总长度由strlen给出
   fwrite(buf, 1, strlen(buf), fp);
   //写入Embedfire
   //每次写入1个字节,总长度由strlen给出
   fwrite("Embedfire\n", 1, strlen("Embedfire\n"),fp);
   //把缓冲区的数据立即写入文件
   fflush(fp);
   //此时的文件位置指针位于文件的结尾处,使用fseek函数使文件指针回到文件头
   fseek(fp, 0, SEEK_SET);
   //从文件中读取内容到str中
   //每次读取100个字节,读取1次
   fread(str, 100, 1, fp);
   printf("File content:\n%s \n", str);
   fclose(fp);
   return 0;
}

系统调用对文件进行操作,常用的有openwritereadlseekclose

  1. 先调用了open函数以可读可写的方式打开一个文本文件。open与fopen的返回值功能类似,都是文件描述符,不过open使用非负整数来表示正常,失败时返回-1,而fopen失败时返回NULL
  2. 创建文件后调用write函数写入了“pwdn”、“lsn”这样的字符串
  3. 使用read函数读取内容前,先调用lseek函数重置了文件指针至文件开头处读取。与C库文件操作的区别write和read之间不需要使用fflush确保缓冲区的内容并写入,因为系统调用的文件操作是没有缓冲区的。
  4. 最后关闭文件,释放文件描述符
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

//文件描述符
int fd;
char str[100];

int main(void)
{
   //创建一个文件
   fd = open("testscript.sh", O_RDWR|O_CREAT|O_TRUNC, S_IRWXU);
   //文件描述符fd为非负整数
   if(fd < 0){
      printf("Fail to Open File\n");
      return 0;
   }
   //写入字符串pwd
   write(fd, "pwd\n", strlen("pwd\n"));
   //写入字符串ls
   write(fd, "ls\n", strlen("ls\n"));
   //此时的文件指针位于文件的结尾处,使用lseek函数使文件指针回到文件头
   lseek(fd, 0, SEEK_SET);
   //从文件中读取100个字节的内容到str中,该函数会返回实际读到的字节数
   read(fd, str, 100);
   printf("File content:\n%s \n", str);
   close(fd);
   return 0;
}

这个时候我们需要考虑一个问题,C标准库系统调用区别就在于库函数带缓冲区
主要表现如下:

  1. 系统调用会影响系统的性能。执行系统调用时,Linux需要从用户态切换至内核态,执行完毕再返回用户代码,所以减少系统调用能减少这方面的开销
  2. 如果写少量数据的话,直接执行write可能会更高效,但如果是频繁的写入操作,由于fwrite缓冲区可以减少调用write的次数,这种情况下使用fwrite能更节省时间
  3. 硬件本身会限制系统调用本身每次读写数据块的大小,使用fopen缓冲区可以尽量满足数据长度要求时才执行系统调度,减少空间开销
  4. 在需要对硬件进行确定的控制时,我们更倾向于执行系统调用

总结

介绍了文件系统和系统调用中文件操作,对比了C标准库和系统调用。

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值