Linux-cp命令实现-系统调用和函数区别-文件截断-文件空洞

1、实现CP命令

        vimdiff  file1  file2      vimdiff是Vim编辑器的一个功能,主要用于比较两个或多个文件之间的差异,并在一个Vim窗口中显示这些差异。这个功能特别适合用于比较修改前后的文件,或者比较两个不同版本的文件。

        注意:用read的返回值判断要写的数据长度和停止读写的条件

cp功能实现

#include<43func.h>
int main(int argc,char *argv[]){
    //cp src dest
    ARGS_CHECK(argc,3);
    int fdr = open(argv[1],O_RDONLY);
    ERROR_CHECK(fdr,-1,"open fdr");
    int fdw = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);
    ERROR_CHECK(fdw,-1,"open fdw");
    char buf[4096] = {0};
    while(1){
        memset(buf,0,sizeof(buf));
        ssize_t ret = read(fdr,buf,sizeof(buf));
        if (ret == 0)
        {
            break;
        }
        write(fdw,buf,ret);//ret表示读多少写多少;
    }
    close(fdr);
    close(fdw);
}

        

一个程序读取,一个程序写,用管道连接就可以实现远程拷贝。

性能问题(代码优化)

充分利用缓冲系统来减少指令预测。


系统调用和普通函数的区别:

上下文切换:系统调用:涉及从用户模式切换到内核模式,这需要进行上下文切换。上下文切换的过程包括保存当前用户进程的上下文环境,切换到内核模式,执行系统调用处理函数,然后恢复用户进程的上下文环境。由于上下文切换的开销,系统调用的执行通常比函数调用更加耗时。普通函数:只涉及在程序内部的代码跳转,不需要上下文切换。因此,函数调用的执行速度更快。

功能和权限:系统调用:提供了对操作系统功能和资源的访问权限。它们可以执行特权操作,例如文件系统操作、进程管理、网络通信等。由于系统调用运行在内核模式下,它们可以执行更底层的操作,但同时也受到操作系统的限制。普通函数:只能执行定义在程序中的功能代码。它们没有直接的访问操作系统底层功能的权限,只能通过系统调用间接访问。

调用方式:系统调用:通过软中断或陷阱指令触发。当应用程序需要执行特权操作时,它会使用特定的系统调用号调用对应的系统调用函数。普通函数:通过在程序中调用函数的名称来触发。函数可以是程序内部定义的,也可以是外部库提供的。

作用范围:系统调用:是用户进程与操作系统内核交互的接口,用于请求操作系统内核提供的服务和资源。普通函数:在程序内部定义和使用,用于封装和复用特定的功能代码。

安全性和稳定性:系统调用:由于涉及到操作系统内核和底层资源的访问,系统调用在设计和实现时需要特别考虑安全性和稳定性。不当的系统调用可能导致系统崩溃或数据损坏。普通函数:主要关注功能的正确性和效率,安全性和稳定性的要求相对较低。


用户态使用系统调用时,会陷入到内核态,进入内核态和进入用户态都要消耗时间。

当用户态缓冲区较大时,需要切换用户态和内核态的次数就会变少,节省了时间。

buf越大越好(减少状态切换的次数)。4096字节。

fopen,fread,fwrite, 作为c的库函数,但是要用到用户态缓冲,也会创建文件对象,在内核态中。

fopen在内核中创建了文件对象,在用户态中有一个文件流,所以该函数的buf会写入缓冲(缓冲是在用户态中的文件流实现的),

缓冲的类型:
  1. 全缓冲(Full Buffering)
    • 定义:当填满标准I/O缓存后才进行实际的I/O操作。
    • 典型代表:对磁盘文件的读写操作通常使用全缓冲。
    • 刷新时机:缓冲区满时、执行flush语句、执行endl语句或关闭文件时,会刷新缓冲区进行真正的I/O操作。
  2. 行缓冲(Line Buffering)
    • 定义:在输入和输出中遇到换行符时,执行真正的I/O操作。
    • 典型代表:键盘输入数据通常采用行缓冲,即输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。
    • 刷新时机:与全缓冲类似,但主要区别在于刷新时机是遇到换行符时。
  3. 不带缓冲(No Buffering)
    • 定义:不进行缓冲,数据直接进行I/O操作。
    • 典型代表:标准出错情况stderr是典型的不带缓冲输出,这使得出错信息可以直接尽快地显示出来。
    • 刷新时机:由于不进行缓冲,所以没有特定的刷新时机。

当buf给file缓冲区传输数据时,一般等缓冲区满了才会传输数据到用户态的文件流

使用用户态的fopen,fread,fwrite,

优势:(零碎写入读写文件使用文件流时,可以最大程度上保证用户态和内核态切换次数处于比较少的情况。

通过缓冲区,可以将多次小型的读写操作合并成一次较大的操作,从而提高了磁盘I/O的效率)

劣势:(拷贝次数更多。当数据从磁盘读入到用户态的缓冲区时,或者从用户态的缓冲区写入到磁盘时,都会发生内存拷贝。这些拷贝操作会增加CPU的开销。

在用户态和内核态之间维护缓存可能会导致缓存一致性问题。如果内核态的缓冲区或磁盘上的数据被其他进程或系统调用修改,而用户态的缓冲区没有及时更新,就可能导致数据不一致。

文件流缓冲区的管理(如分配、释放、刷新等)需要额外的代码和逻辑来处理,这增加了编程的复杂性。)


文件截断:ftruncate(按字节截断文件)可以用于创建一个固定大小的文件。

#include <43func.h>
int main(int argc ,char *argv[]){
    ARGS_CHECK(argc,2);
    int fd = open(argv[1],O_RDWR);
    ERROR_CHECK(fd,-1,"open");
    int ret = ftruncate(fd,20);
    ERROR_CHECK(ret,-1,"ftruncate");
}

大文件截断成小文件就直接去掉,小文件截断成大文件直接补0.二进制0.。截断是直接影响文件大小。

stat命令在Linux系统中用于显示文件或文件系统的状态信息

用该系统调用截断成40960大小。

8个BLOCK,一个block假设为512byte,实际上给了4096空间,但具体大小只有414byte。所以形成文件空洞。

文件总大小为40960,4096分配了磁盘。

操作系统不会给空出来的区域分配磁盘,可以把空出来的区域叫做文件空洞。

文件空洞的概念

定义
在UNIX文件系统中,文件位移量(offset)可以大于文件的当前长度。当对这样的文件进行写操作时,文件会被“撑大”,并在文件中构成一个空洞(hole)。空洞是文件中没有实际写入数据的部分,由重复的0表示。

特点

  1. 不占用磁盘空间:在写入数据之前,空洞并不占据磁盘空间。文件系统会将其解释为0的子串,并将对应链表的指针设为空。
  2. 预留磁盘空间:虽然空洞本身不占用磁盘空间,但文件系统会扣减程序可用的磁盘空间数值大小,以实现预留。
  3. 读写行为:使用read函数读取空洞部分时,返回的数据是0。使用cp命令拷贝的文件,空洞部分不会被拷贝,因此生成的同样文件占用磁盘空间较小。

使用场景

1. 下载数据

  • 迅雷下载:在下载过程中,迅雷会先创建一个与最终文件大小相同的空洞文件,以便在多线程下载时从不同的地址写入数据。这样可以确保即使磁盘空间被其他程序占用,下载也不会因磁盘空间不足而中断。
  • 预留磁盘空间:在下载大文件时,利用文件空洞可以确保磁盘空间被预留,从而避免下载过程中因磁盘空间不足而中断。

2. 数据库文件

  • 压缩和优化:数据库文件通常包含大量空洞。通过使用文件空洞填充技术,可以压缩数据库文件的大小,提高存储效率和读写性能。

3. 网络开发

  • 优化传输:在网络传输中,文件空洞填充技术可以用于压缩网络数据包的大小,减少数据传输的时间和带宽占用。例如,在文件传输协议中,可以将逻辑上的空洞视为本地主机上的零字节,以减少传输数据量。

4. 虚拟机存储

  • 优化存储:在虚拟机环境中,文件空洞填充可以用于优化虚拟机磁盘的存储空间,提高存储效率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值