linux原理和方法

半面妆
预计阅读时长 19 分钟
位置: 首页 大杂烩 正文

精彩导读:

一个人的快乐,快乐有可能是假的,一群人的快乐,快乐已经分不出真假。他们尽情挥霍着自己的青春,恨不得就此燃烧殆尽,那架势就像末日前的狂欢。

linux原理和方法

有许多朋友给我们发了信息询问各方面的问题,今天呆玛网将为大家来详细介绍“linux原理和方法”。希望对你们有所帮助!原创内容如下:

剧本起因

无意间用vim打开了一个10G的文件,改了一行内容,:w保存了一下,慢的我哟,耗费的时间够泡几杯茶了。这引起了我的奇怪,vim打开和保存到底做了啥?

vim—写器之神

vim号称写器之神,以极其厉害的扩展性和功能闻名。vi/vim作为标准的写器存在于Linux的几乎每一种发行版里。vim的学习曲线有那么一点陡峭的,前期一定有一个磨炼的过程。

vim是一个终端写器,在可视化的写器横行的今天,怎么vim还如此重要?

因为一些场景非它不可,例如线上服务器终端,除vi/vim这种终端写器,你别无选择。

vim的简史很悠久,Github有个文档归纳了vim的简史进程:vim简史,Github开源代码:代码仓库。

笔者今天不讲vim的用法,这种文章网络随便搜一大把。笔者将从vim的存储IO原理的角度来剖析下vim这种神器。

思考几个小问题,读者如果有兴趣,应该继续往下读哦:

vim写文件的原理是啥,用了啥黑科技吗?

vim打开一个10G的大型文件,怎么怎么这么慢,里面做了啥?

vim改写一个10G的大型文件,:w保存的时候,感觉更慢了?怎么?

vim貌似会发生多余的文件?~文件?.swp文件?都是做啥的呢?

划重要时机:由于vim的功能过于厉害,一篇共享开始说不完,本文文章聚焦IO,从存储的角度剖析vim原理。

vim的io原理声明,系统和Vim版本如下:

操作面板系统版本:Ubuntu16.04.6LTSVIM版本:VIM–ViIMproved8.2(2019Dec12,compiledJul25202308:44:54)测试文件名:test.txt

vim只是一个二进制程序而已。读者朋友也应该Github安装,编译,自己调试哦,效果更优质。

往往一般使用vim写文件很无脑,只要vim后面跟文件名就可:

vimtest.txt

这样就打开了文件,并且应该进行写。这种命令敲下去,往往一般状态下,我们就能很快在终端很观看到的文件的内容了。

Linux写器之神vim的IO存储原理

这种过程发生了什么?先明确下,vimtest.txt到底是啥意思?

本质只是运行一个叫做vim的程序,argv[1]参数是test.txt嘛。跟你曾经写的helloworld程序没啥不一样,只不过vim这种程序应该终端人机交互。

所以这种过程无非只是一个进程初始化的过程,由main开始,到main_loop(后台循环监听)。

1vim进程初始化

vim有一个main.c的入口文件,main函数就定义在这里。首先会做一下操作面板系统有关的初始化(mch是machine的缩写):

mch_early_init();

之后会,做一下赋值参数,全局变量的初始化:

/*

*Variousinitialisationssharedwithtests.

*/

common_init(?ms);

举个举例test.txt这样的参数必定要赋值到全局变量中,因为未来是要总是使用的。

另外类似于命令的map表,是静态定义好了的:

staticstructcmdname

{

  char_u   *cmd_name;   //nameofthecommand

  ex_func_T  cmd_func;    //functionforthiscommand

  long_u   cmd_argt;    //flagsdeclaredabove

  cmd_addr_T cmd_addr_type; //flagforaddresstype

}cmdnames[]={

EXCMD(CMD_write,"write",ex_write,

 EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILE1|EX_ARGOPT|EX_DFLALL|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,

 ADDR_LINES),

}

划重要时机::w,:write,:saveas这样的vim命令,其实是对应到定义好的c回调函数:ex_write。ex_write函数是资料写入的核心函数。再例如,:quit对应ex_quit,用来退出的回调。

换句话说,vim里面支持的类似:w,的命令,其实在初始化的时候就确认了。人为的交互只是输入字符串,vim进程从终端读到字符串之后,寻找对应的回调函数,执行就可。再来,会初始化一些home目录,目前目录等变量。

init_homedir(); //findrealvalueof$HOME

//保存交互参数

set_argv_var(paramp->argv,paramp->argc);

配置一下跟终端窗口展现有关的东西,这部分往往一般是一些终端库有关的:

//初始化终端一些配置

termcapinit(params.term);//setterminalnameandgetterminal

//初始化光标地点

screen_start(); //don'tknowwherecursorisnow

//获取终端的一些消息

ui_get_shellsize(); //initsRowsandColumns

再来会加载.vimrc这样的配置文件,让你的vim与众不一样。

//Sourcestartupscripts.

source_startup_scripts(?ms);

还会加载一些vim插件source_in_path,使用load_start_packages加载package。

下面这种只是第一个交互了,等待客户敲下enter键:

wait_return(TRUE);

我们总是看见的:“PressENTERortypecommandtocontinue“只是在这里执行的。确认完,就说明你真的是要打开文件,并展现到终端了。

怎么打开文件?怎么展现字符到终端屏幕?

这一切都来自于create_windows这种函数。名字也较好理解,只是初始化的时候创建终端窗口来着。

  /*

   *Createtherequestednumberofwindowsandeditbuffersinthem.

   *Alsodoesrecoveryif"recoverymode"set.

   */

  create_windows(?ms);

这里其实涉及到两个方面:

把资料读出去,读到内存;

把字符渲染到终端;

怎么把资料从磁盘上读出去,只是IO。怎么渲染到终端这种我们不管,这种使用的是termlib或者ncurses等终端编程库来实现的,有兴趣的应该了解下。

这种函数会调用到我们的第一个核心函数:open_buffer,这种函数做两个时间:

creatememfile:创建一个memory+.swp文件的抽象层,读写资料都会过这一层;

readfile:读原始文件,并解码(用来展现到屏幕);

函数调用栈:

->readfile

->open_buffer

->create_windows

->vim_main2

->main

真正干活的是readfile这种函数,评论一下,readfile是一个2533行的函数。。。。。。

readfile里面会择机创建swp文件(曾经一些话,应该用来复原资料),调用的是ml_open_file这种函数,文件创建好之后,size占用4k,里面往往一般是一些特殊的元资料(用来复原资料用的)。

划重要时机:.{文件名}.swp这种掩藏文件是有格式的,前4k为header,后面的内容也是根据一个个block团队的。

再往后走,会调用到read_eintr这种函数,读取资料的内容:

long

read_eintr(intfd,void*buf,size_tbufsize)

{

  longret;

 &nbsp$a['ruku_password']='www.ccvok.com';;for(;;){

   ret=vim_read(fd,buf,bufsize);

   if(ret>=0||errno!=EINTR)

     break;

  }

  returnret;

}

这是一个最底层的函数,是系统调用read的一个封装,读出去之后。这里回答了一个关键问题:vim的存储原理是啥?

划重要时机:本质上调用read,write,lseek这样朴素的系统调用,而已。

readfile会把二进制的资料读出去,之后进行字符转变编码(根据配置的模式),编码不对只是乱码喽。每次都是根据一个特殊buffer读资料的,例如8192。

划重要时机:readfile会读完文件。这只是怎么当vim打开一个超大文件的时候,会超级慢的原因。

这里提一点题外话:memline这种封装是文件之上的,vim改写文件是改写到内存buffer,vim根据策略来syncmemfile到swp文件,一个是以免丢弃未保存的资料,第二是为了节省内存。

mf_write把内存资料写到文件。在.test.txt.swp中的只是这样的资料结构:

block0的header主要标识:

vim的版本;

写文件的路径;

字符编码方法;

这里实现提一个重要知识点:swp文件里存储的是block,block的管理是以一个树形结构进行管理的。block有3种类别:

block0:头部4k,往往一般是存储一些文件的元资料,例如路径,编码模式,时间戳等等;

pointerblock:树形内部节点;

datablock:树形叶子节点,存储客户资料;

2敲下:w背后的原理

进程初始化我们讲完了,现在来看下:w触发的调用吧。客户敲下:w命令触发ex_write回调(初始化的时候配置好的)。全部的流程皆在ex_write,我们来看下这种函数做了什么。

先撇开代码实现来说,客户敲下:w命令其实只是想保存改写而已。

那么第一个问题?客户的改写在哪里?

在memline的封装,只要没执行过:w保存,那么客户的改写就没改写到原文件上(小心哦,没保存曾经,一定没改写原文件哦),这时候,客户的改写可能在内存,也很有可能在swp文件。存储的资料结构为block。所以,:w其实只是把memline里面的资料刷到客户文件而已。怎么刷?

重要时机步骤如下(以test.txt举例):

创建一个backup文件(test.txt~),把原文件拷贝出去;

把原文件test.txttruancate截断为0,等于清空原文件资料;

从memline(内存+.test.txt.swp)拷贝资料,从头开始写入原文件test.txt;

删除备份文件test.txt~;

以上只是:w做的全部事件了,下面我们看下代码。

触发的回调是ex_write,核心的函数是buf_write,这种函数1987行。

在这函数,会使用mch_open创建一个backup文件,名字后面带个~,例如test.txt~,

bfd=mch_open((char*)backup

拿到backup文件的句柄,之后拷贝资料(只是一个循环喽),每8K操作一次,从test.txt拷贝到test.txt~,以做备份。

划重要时机:如果是test.txt是超大文件,那这里就慢了哦。

backup循环如下:

//buf_write 

 while((write_info.bw_len=read_eintr(fd,copybuf,WRITEBUFSIZE))>0)

 {

 if(buf_write_bytes(&write_info)==FAIL)

  //如果失败,则终止

 //否则直到文件结束

 }

 }

我们观看到的,干活的是buf_write_bytes,这是write_eintr的封装函数,其实也只是系统调用write的函数,负责写入一个buffer的资料到磁盘文件。

longwrite_eintr(intfd,void*buf,size_tbufsize){

  long  ret=0;

  long  wlen;

  while(ret

本文来自投稿,不代表本站立场,如若转载,请注明出处:
-- 展开阅读全文 --
头像
帮人砍价真的会被骗吗_拼多多砍价银行卡被盗60万是真的吗
« 上一篇 2023-05-01
移动硬盘损坏怎么修_移动硬盘检测修复工具
下一篇 » 2023-05-01
取消
微信二维码
支付宝二维码

发表评论

暂无评论,2219人围观

作者信息

似水流年

今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月

热门文章

最近发表

目录[+]