Smiley face

我的征尘是星辰大海。。。

The dirt and dust from my pilgrimage forms oceans of stars...

-------当记忆的篇章变得零碎,当追忆的图片变得模糊,我们只能求助于数字存储的永恒的回忆

作者:黄教授

二〇二四


一月四日 等待变化等待机会

  1. 我发现ubuntu标准包的vscode似乎不太稳定,还是这个snap的好一些吧。我尝试要从源代码编译,但是遇到node.js的版本要求,这个东西我不熟悉,虽然看起来可以直接安装,但是多版本运行肯定麻烦。就放弃了。
    
    sudo snap install --classic code
    
    这里是ubuntu的源代码,难道想从源码编译吗?
  2. 阅读我以前的笔记发现了一个问题。难得发现了一个clang的继承类的问题。这里又遇到一个微妙的问题,就是当你有多重继承的时候,这里的public是要覆盖到所有的基类的,否则就是private了。 注意这里的class Virus : public T, public Ts... 第二个public,如果没有的话,那么除了第一个基类其他的就都是privately inherited了,这个是一个非常细微的地方!对于这个问题,GCC,还是MSVC都给出了出错信息,唯独clang错误的接受了,这个是比较罕见的。
  3. 关于c++的标准:
    1. 你在这里下载
    2. 编译时候首先,你还是要下载这个latex的编译工具。说明里要你安装必要的工具: 额外的是所谓的tex2html,这个不能由npm来安装。
    3. 始终都有编译出错的问题,我决定尝试大师的一个修正draft的repo
    4. 似乎安装haskell有些问题,这里说从2022年开始要这么安装:
      
      curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
      
  4. 需要重头学习制作ami

一月八日 等待变化等待机会

  1. 这个是配置openvpn的全部教程。其中这里是如何创建CA的步骤,我一直认为这个不是必须的,也许我是错的,不仅是浏览器出现警告那么简单吧?
  2. 这个设定openvpn的教程我始终无法成功。

一月十一日 等待变化等待机会

  1. 与其费力去创建vpn还是proxy,我直接的目的是编译c++标准的网页,所以,我直接部署一个aws/ec2的实例在上面试验就行了吧?距离我上一次密集使用aws已经过去整整十年了,可以说几乎完全的翻天覆地。
    1. 首先,登陆的方法都加强了,我之前被折磨的半死就是所谓的time-based-password系统,我现在是使用microsoft的authenticator来生成密码。这个美国手机简直太要命了,完全离不开。
    2. 找到官方的ubuntu/ami,这个是在所谓的ami catalog里免费的。
    3. 配置ubuntu当然是基本的,只是我不熟悉这些个npm/cabal,需要cabal update安装haskell的包,原本就是卡在这里的。
    4. 我使用的是便宜的micro instance,导致编译几乎停滞。现在可以在monitor里看到cpu的使用率一直在99%。
    最后我遇到内存不足的问题,但是使用了超大实例依然有和我本地类似的编译错误。于是我转而尝试ubuntu18.04的环境发现依赖的包都版本不支持,看来是要使用最新版的ubuntu才行。这时候我想起了另一位大师的做法,也许他的一些补丁修改可以吧?结果我又忘记要把依赖的子模块都clone,所以,只能这样子: git clone --recursive https://github.com/timsong-cpp/cppwp
  2. 随后我又遇到另一个老问题,就是github有了严格的认证制度,下载某些代码需要使用publickey,我只好去github生成我的 key,实际上是把我本地的pubkey拷贝到github,那么在我的instance里拷贝我的privatekey,这个当然是很冒险的。没有办 法。
  3. 我有点明白所谓的几个脚本实际上是编译c++几个不同标准部分的脚本,比如range,比如network,和具体的编译工具无关,于是我又回到 编译工具尝试使用stack build。这些名字对我来说完全不明白是什么,到底是haskell还是node.js,其实我也没兴趣也没能力搞明白。

一月十二日 等待变化等待机会

  1. 之前我设定的github的workaround又反过来害我了:
    
    url.https://github.com/.insteadof=git@github.com:
    url.https://gitclone.com/.insteadof=git://gitclone.com/
    url.https://.insteadof=ssh://
    url.https://.insteadof=git://
    http.sslverify=false
    
    赶紧全部取消掉!
    [url "https://github.com/"]
            insteadof = git@github.com:
    [url "https://gitclone.com/"]
            insteadof = git://gitclone.com/
    [url "https://"]
      insteadOf = ssh://
    [url "https://"]
      insteadOf = git://
    [http]
            sslVerify = false
    
  2. 我编译不过大师的cxxhtmlgen,就提了这么一个bug,实际上是想要获得相应的帮助,大师明确指出了方向,这个tex2html是在所谓的 mathjax-node-cli里的脚本。原本它就是编译的前提,是我自己不明白,一直以为npm已经解决了。我发现不如去尝试从mathjax-src来直接编译一下:
    
    git clone https://github.com/mathjax/MathJax-src.git mathjax-src
    cd mathjax-src
    npm run --silent compile
    npm run --silent make-components
    
    我现在都要把命令抄录下来因为中国政府不知道什么时候又把这些给屏蔽了,或许也不是中国屏蔽而是美国屏蔽了,总之,现在的世界很有可能是分裂的世界。
  3. 这里是遇到npm卡住的办法,试一下再说。
  4. 我不知道具体是怎么解决了,总之,我编译的mathjax-node-cli作为component需要一个mathjax-node-sre作 为前提,然后我对于怎么安装component实在是想不出来最后只好把mathjax-node-cli下的bin的脚本生生的拷贝软链接。比如
    
    cd /usr/local/bin
    sudo ln -s /path/to/mathjax-node-cli/bin/* .
    
    因为归根结柢编译时候需要执行tex2html作为node.js的外部命令。最后的确是编译了两千多个文件。
  5. 我打算把它们保存在这里:www.staroceans.org/cxx/index.html
  6. 如何列表文件不是什么扩展名的呢?比如不是.html的文件,我还真的不知道:ls | grep -v .html
  7. 然后我就发现了有几个.css文件,它的正确的mime-type是:application/x-pointplus
  8. 我又要拷贝一下这个mimetype表了。
    Suffixes applicable Media type and subtype(s)
    .3dm x-world/x-3dmf
    .3dmf x-world/x-3dmf
    .7z application/x-7z-compressed
    .a application/octet-stream
    .aab application/x-authorware-bin
    .aam application/x-authorware-map
    .aas application/x-authorware-seg
    .abc text/vnd.abc
    .acgi text/html
    .afl video/animaflex
    .ai application/postscript
    .aif audio/aiff
    .aif audio/x-aiff
    .aifc audio/aiff
    .aifc audio/x-aiff
    .aiff audio/aiff
    .aiff audio/x-aiff
    .aim application/x-aim
    .aip text/x-audiosoft-intra
    .ani application/x-navi-animation
    .aos application/x-nokia-9000-communicator-add-on-software
    .aps application/mime
    .arc application/octet-stream
    .arj application/arj
    .arj application/octet-stream
    .art image/x-jg
    .asf video/x-ms-asf
    .asm text/x-asm
    .asp text/asp
    .asx application/x-mplayer2
    .asx video/x-ms-asf
    .asx video/x-ms-asf-plugin
    .au audio/basic
    .au audio/x-au
    .avi application/x-troff-msvideo
    .avi video/avi
    .avi video/msvideo
    .avi video/x-msvideo
    .avs video/avs-video
    .bcpio application/x-bcpio
    .bin application/mac-binary
    .bin application/macbinary
    .bin application/octet-stream
    .bin application/x-binary
    .bin application/x-macbinary
    .bm image/bmp
    .bmp image/bmp
    .bmp image/x-windows-bmp
    .boo application/book
    .book application/book
    .boz application/x-bzip2
    .bsh application/x-bsh
    .bz application/x-bzip
    .bz2 application/x-bzip2
    .c text/plain
    .c text/x-c
    .c++ text/plain
    .cat application/vnd.ms-pki.seccat
    .cc text/plain
    .cc text/x-c
    .ccad application/clariscad
    .cco application/x-cocoa
    .cdf application/cdf
    .cdf application/x-cdf
    .cdf application/x-netcdf
    .cer application/pkix-cert
    .cer application/x-x509-ca-cert
    .cha application/x-chat
    .chat application/x-chat
    .class application/java
    .class application/java-byte-code
    .class application/x-java-class
    .com application/octet-stream
    .com text/plain
    .conf text/plain
    .cpio application/x-cpio
    .cpp text/x-c
    .cpt application/mac-compactpro
    .cpt application/x-compactpro
    .cpt application/x-cpt
    .crl application/pkcs-crl
    .crl application/pkix-crl
    .crt application/pkix-cert
    .crt application/x-x509-ca-cert
    .crt application/x-x509-user-cert
    .csh application/x-csh
    .csh text/x-script.csh
    .css application/x-pointplus
    .css text/css
    .csv text/csv
    .cxx text/plain
    .dcr application/x-director
    .deepv application/x-deepv
    .def text/plain
    .der application/x-x509-ca-cert
    .dif video/x-dv
    .dir application/x-director
    .dl video/dl
    .dl video/x-dl
    .doc application/msword
    .docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
    .dot application/msword
    .dp application/commonground
    .drw application/drafting
    .dump application/octet-stream
    .dv video/x-dv
    .dvi application/x-dvi
    .dwf drawing/x-dwf (old)
    .dwf model/vnd.dwf
    .dwg application/acad
    .dwg image/vnd.dwg
    .dwg image/x-dwg
    .dxf application/dxf
    .dxf image/vnd.dwg
    .dxf image/x-dwg
    .dxr application/x-director
    .el text/x-script.elisp
    .elc application/x-bytecode.elisp (compiled elisp)
    .elc application/x-elc
    .env application/x-envoy
    .eot application/vnd.ms-fontobject
    .eps application/postscript
    .es application/x-esrehber
    .etx text/x-setext
    .evy application/envoy
    .evy application/x-envoy
    .exe application/octet-stream
    .f text/plain
    .f text/x-fortran
    .f77 text/x-fortran
    .f90 text/plain
    .f90 text/x-fortran
    .fdf application/vnd.fdf
    .fif application/fractals
    .fif image/fif
    .flac audio/flac
    .fli video/fli
    .fli video/x-fli
    .flo image/florian
    .flx text/vnd.fmi.flexstor
    .fmf video/x-atomic3d-feature
    .for text/plain
    .for text/x-fortran
    .fpx image/vnd.fpx
    .fpx image/vnd.net-fpx
    .frl application/freeloader
    .funk audio/make
    .g text/plain
    .g3 image/g3fax
    .gif image/gif
    .gl video/gl
    .gl video/x-gl
    .gsd audio/x-gsm
    .gsm audio/x-gsm
    .gsp application/x-gsp
    .gss application/x-gss
    .gtar application/x-gtar
    .gz application/x-compressed
    .gz application/x-gzip
    .gzip application/x-gzip
    .gzip multipart/x-gzip
    .h text/plain
    .h text/x-h
    .hdf application/x-hdf
    .help application/x-helpfile
    .hgl application/vnd.hp-hpgl
    .hh text/plain
    .hh text/x-h
    .hlb text/x-script
    .hlp application/hlp
    .hlp application/x-helpfile
    .hlp application/x-winhelp
    .hpg application/vnd.hp-hpgl
    .hpgl application/vnd.hp-hpgl
    .hqx application/binhex
    .hqx application/binhex4
    .hqx application/mac-binhex
    .hqx application/mac-binhex40
    .hqx application/x-binhex40
    .hqx application/x-mac-binhex40
    .hta application/hta
    .htc text/x-component
    .htm text/html
    .html text/html
    .htmls text/html
    .htt text/webviewhtml
    .htx text/html
    .ice x-conference/x-cooltalk
    .ico image/x-icon
    .ics text/calendar
    .idc text/plain
    .ief image/ief
    .iefs image/ief
    .iges application/iges
    .iges model/iges
    .igs application/iges
    .igs model/iges
    .ima application/x-ima
    .imap application/x-httpd-imap
    .inf application/inf
    .ins application/x-internett-signup
    .ip application/x-ip2
    .isu video/x-isvideo
    .it audio/it
    .iv application/x-inventor
    .ivr i-world/i-vrml
    .ivy application/x-livescreen
    .jam audio/x-jam
    .jav text/plain
    .jav text/x-java-source
    .java text/plain
    .java text/x-java-source
    .jcm application/x-java-commerce
    .jfif image/jpeg
    .jfif image/pjpeg
    .jfif-tbnl image/jpeg
    .jpe image/jpeg
    .jpe image/pjpeg
    .jpeg image/jpeg
    .jpeg image/pjpeg
    .jpg image/jpeg
    .jpg image/pjpeg
    .jps image/x-jps
    .js application/x-javascript
    .js application/javascript
    .js application/ecmascript
    .js text/javascript
    .js text/ecmascript
    .json application/json
    .jut image/jutvision
    .kar audio/midi
    .kar music/x-karaoke
    .ksh application/x-ksh
    .ksh text/x-script.ksh
    .la audio/nspaudio
    .la audio/x-nspaudio
    .lam audio/x-liveaudio
    .latex application/x-latex
    .lha application/lha
    .lha application/octet-stream
    .lha application/x-lha
    .lhx application/octet-stream
    .list text/plain
    .lma audio/nspaudio
    .lma audio/x-nspaudio
    .log text/plain
    .lsp application/x-lisp
    .lsp text/x-script.lisp
    .lst text/plain
    .lsx text/x-la-asf
    .ltx application/x-latex
    .lzh application/octet-stream
    .lzh application/x-lzh
    .lzx application/lzx
    .lzx application/octet-stream
    .lzx application/x-lzx
    .m text/plain
    .m text/x-m
    .m1v video/mpeg
    .m2a audio/mpeg
    .m2v video/mpeg
    .m3u audio/x-mpequrl
    .man application/x-troff-man
    .map application/x-navimap
    .mar text/plain
    .mbd application/mbedlet
    .mc$ application/x-magic-cap-package-1.0
    .mcd application/mcad
    .mcd application/x-mathcad
    .mcf image/vasa
    .mcf text/mcf
    .mcp application/netmc
    .me application/x-troff-me
    .mht message/rfc822
    .mhtml message/rfc822
    .mid application/x-midi
    .mid audio/midi
    .mid audio/x-mid
    .mid audio/x-midi
    .mid music/crescendo
    .mid x-music/x-midi
    .midi application/x-midi
    .midi audio/midi
    .midi audio/x-mid
    .midi audio/x-midi
    .midi music/crescendo
    .midi x-music/x-midi
    .mif application/x-frame
    .mif application/x-mif
    .mime message/rfc822
    .mime www/mime
    .mjf audio/x-vnd.audioexplosion.mjuicemediafile
    .mjpg video/x-motion-jpeg
    .mka audio/x-matroska
    .mkv video/x-matroska
    .mm application/base64
    .mm application/x-meme
    .mme application/base64
    .mod audio/mod
    .mod audio/x-mod
    .moov video/quicktime
    .mov video/quicktime
    .movie video/x-sgi-movie
    .mp2 audio/mpeg
    .mp2 audio/x-mpeg
    .mp2 video/mpeg
    .mp2 video/x-mpeg
    .mp2 video/x-mpeq2a
    .mp3 audio/mpeg3
    .mp3 audio/x-mpeg-3
    .mp3 video/mpeg
    .mp3 video/x-mpeg
    .mp4 video/mp4
    .mpa audio/mpeg
    .mpa video/mpeg
    .mpc application/x-project
    .mpe video/mpeg
    .mpeg video/mpeg
    .mpg audio/mpeg
    .mpg video/mpeg
    .mpga audio/mpeg
    .mpp application/vnd.ms-project
    .mpt application/x-project
    .mpv application/x-project
    .mpx application/x-project
    .mrc application/marc
    .ms application/x-troff-ms
    .mv video/x-sgi-movie
    .my audio/make
    .mzz application/x-vnd.audioexplosion.mzz
    .nap image/naplps
    .naplps image/naplps
    .nc application/x-netcdf
    .ncm application/vnd.nokia.configuration-message
    .nif image/x-niff
    .niff image/x-niff
    .nix application/x-mix-transfer
    .nsc application/x-conference
    .nvd application/x-navidoc
    .o application/octet-stream
    .oda application/oda
    .ogg audio/ogg
    .ogg video/ogg
    .omc application/x-omc
    .omcd application/x-omcdatamaker
    .omcr application/x-omcregerator
    .otf font/otf
    .p text/x-pascal
    .p10 application/pkcs10
    .p10 application/x-pkcs10
    .p12 application/pkcs-12
    .p12 application/x-pkcs12
    .p7a application/x-pkcs7-signature
    .p7c application/pkcs7-mime
    .p7c application/x-pkcs7-mime
    .p7m application/pkcs7-mime
    .p7m application/x-pkcs7-mime
    .p7r application/x-pkcs7-certreqresp
    .p7s application/pkcs7-signature
    .part application/pro_eng
    .pas text/pascal
    .pbm image/x-portable-bitmap
    .pcl application/vnd.hp-pcl
    .pcl application/x-pcl
    .pct image/x-pict
    .pcx image/x-pcx
    .pdb chemical/x-pdb
    .pdf application/pdf
    .pfunk audio/make
    .pfunk audio/make.my.funk
    .pgm image/x-portable-graymap
    .pgm image/x-portable-greymap
    .pic image/pict
    .pict image/pict
    .pkg application/x-newton-compatible-pkg
    .pko application/vnd.ms-pki.pko
    .pl text/plain
    .pl text/x-script.perl
    .plx application/x-pixclscript
    .pm image/x-xpixmap
    .pm text/x-script.perl-module
    .pm4 application/x-pagemaker
    .pm5 application/x-pagemaker
    .png image/png
    .pnm application/x-portable-anymap
    .pnm image/x-portable-anymap
    .pot application/mspowerpoint
    .pot application/vnd.ms-powerpoint
    .pov model/x-pov
    .ppa application/vnd.ms-powerpoint
    .ppm image/x-portable-pixmap
    .pps application/mspowerpoint
    .pps application/vnd.ms-powerpoint
    .ppt application/mspowerpoint
    .ppt application/powerpoint
    .ppt application/vnd.ms-powerpoint
    .ppt application/x-mspowerpoint
    .pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
    .ppz application/mspowerpoint
    .pre application/x-freelance
    .prt application/pro_eng
    .ps application/postscript
    .psd application/octet-stream
    .pvu paleovu/x-pv
    .pwz application/vnd.ms-powerpoint
    .py text/x-script.phyton
    .pyc application/x-bytecode.python
    .qcp audio/vnd.qcelp
    .qd3 x-world/x-3dmf
    .qd3d x-world/x-3dmf
    .qif image/x-quicktime
    .qt video/quicktime
    .qtc video/x-qtc
    .qti image/x-quicktime
    .qtif image/x-quicktime
    .ra audio/x-pn-realaudio
    .ra audio/x-pn-realaudio-plugin
    .ra audio/x-realaudio
    .ram audio/x-pn-realaudio
    .ras application/x-cmu-raster
    .ras image/cmu-raster
    .ras image/x-cmu-raster
    .rast image/cmu-raster
    .rar application/vnd.rar
    .rexx text/x-script.rexx
    .rf image/vnd.rn-realflash
    .rgb image/x-rgb
    .rm application/vnd.rn-realmedia
    .rm audio/x-pn-realaudio
    .rmi audio/mid
    .rmm audio/x-pn-realaudio
    .rmp audio/x-pn-realaudio
    .rmp audio/x-pn-realaudio-plugin
    .rng application/ringing-tones
    .rng application/vnd.nokia.ringing-tone
    .rnx application/vnd.rn-realplayer
    .roff application/x-troff
    .rp image/vnd.rn-realpix
    .rpm audio/x-pn-realaudio-plugin
    .rt text/richtext
    .rt text/vnd.rn-realtext
    .rtf application/rtf
    .rtf application/x-rtf
    .rtf text/richtext
    .rtx application/rtf
    .rtx text/richtext
    .rv video/vnd.rn-realvideo
    .s text/x-asm
    .s3m audio/s3m
    .saveme application/octet-stream
    .sbk application/x-tbook
    .scm application/x-lotusscreencam
    .scm text/x-script.guile
    .scm text/x-script.scheme
    .scm video/x-scm
    .sdml text/plain
    .sdp application/sdp
    .sdp application/x-sdp
    .sdr application/sounder
    .sea application/sea
    .sea application/x-sea
    .set application/set
    .sgm text/sgml
    .sgm text/x-sgml
    .sgml text/sgml
    .sgml text/x-sgml
    .sh application/x-bsh
    .sh application/x-sh
    .sh application/x-shar
    .sh text/x-script.sh
    .shar application/x-bsh
    .shar application/x-shar
    .shtml text/html
    .shtml text/x-server-parsed-html
    .sid audio/x-psid
    .sit application/x-sit
    .sit application/x-stuffit
    .skd application/x-koan
    .skm application/x-koan
    .skp application/x-koan
    .skt application/x-koan
    .sl application/x-seelogo
    .smi application/smil
    .smil application/smil
    .snd audio/basic
    .snd audio/x-adpcm
    .sol application/solids
    .spc application/x-pkcs7-certificates
    .spc text/x-speech
    .spl application/futuresplash
    .spr application/x-sprite
    .sprite application/x-sprite
    .src application/x-wais-source
    .ssi text/x-server-parsed-html
    .ssm application/streamingmedia
    .sst application/vnd.ms-pki.certstore
    .step application/step
    .stl application/sla
    .stl application/vnd.ms-pki.stl
    .stl application/x-navistyle
    .stp application/step
    .sv4cpio application/x-sv4cpio
    .sv4crc application/x-sv4crc
    .svf image/vnd.dwg
    .svf image/x-dwg
    .svg image/svg+xml
    .svr application/x-world
    .svr x-world/x-svr
    .swf application/x-shockwave-flash
    .t application/x-troff
    .talk text/x-speech
    .tar application/x-tar
    .tbk application/toolbook
    .tbk application/x-tbook
    .tcl application/x-tcl
    .tcl text/x-script.tcl
    .tcsh text/x-script.tcsh
    .tex application/x-tex
    .texi application/x-texinfo
    .texinfo application/x-texinfo
    .text application/plain
    .text text/plain
    .tgz application/gnutar
    .tgz application/x-compressed
    .tif image/tiff
    .tif image/x-tiff
    .tiff image/tiff
    .tiff image/x-tiff
    .tr application/x-troff
    .ts video/mp2t
    .tsi audio/tsp-audio
    .tsp application/dsptype
    .tsp audio/tsplayer
    .tsv text/tab-separated-values
    .turbot image/florian
    .txt text/plain
    .uil text/x-uil
    .uni text/uri-list
    .unis text/uri-list
    .unv application/i-deas
    .uri text/uri-list
    .uris text/uri-list
    .ustar application/x-ustar
    .ustar multipart/x-ustar
    .uu application/octet-stream
    .uu text/x-uuencode
    .uue text/x-uuencode
    .vcd application/x-cdlink
    .vcs text/x-vcalendar
    .vda application/vda
    .vdo video/vdo
    .vew application/groupwise
    .viv video/vivo
    .viv video/vnd.vivo
    .vivo video/vivo
    .vivo video/vnd.vivo
    .vmd application/vocaltec-media-desc
    .vmf application/vocaltec-media-file
    .voc audio/voc
    .voc audio/x-voc
    .vos video/vosaic
    .vox audio/voxware
    .vqe audio/x-twinvq-plugin
    .vqf audio/x-twinvq
    .vql audio/x-twinvq-plugin
    .vrml application/x-vrml
    .vrml model/vrml
    .vrml x-world/x-vrml
    .vrt x-world/x-vrt
    .vsd application/x-visio
    .vst application/x-visio
    .vsw application/x-visio
    .w60 application/wordperfect6.0
    .w61 application/wordperfect6.1
    .w6w application/msword
    .wav audio/wav
    .wav audio/x-wav
    .wb1 application/x-qpro
    .wbmp image/vnd.wap.wbmp
    .web application/vnd.xara
    .webm video/webm
    .webp image/webp
    .wiz application/msword
    .wk1 application/x-123
    .wmf windows/metafile
    .wml text/vnd.wap.wml
    .wmlc application/vnd.wap.wmlc
    .wmls text/vnd.wap.wmlscript
    .wmlsc application/vnd.wap.wmlscriptc
    .word application/msword
    .woff font/woff
    .woff2 font/woff2
    .wp application/wordperfect
    .wp5 application/wordperfect
    .wp5 application/wordperfect6.0
    .wp6 application/wordperfect
    .wpd application/wordperfect
    .wpd application/x-wpwin
    .wq1 application/x-lotus
    .wri application/mswrite
    .wri application/x-wri
    .wrl application/x-world
    .wrl model/vrml
    .wrl x-world/x-vrml
    .wrz model/vrml
    .wrz x-world/x-vrml
    .wsc text/scriplet
    .wsrc application/x-wais-source
    .wtk application/x-wintalk
    .xbm image/x-xbitmap
    .xbm image/x-xbm
    .xbm image/xbm
    .xdr video/x-amt-demorun
    .xgz xgl/drawing
    .xif image/vnd.xiff
    .xl application/excel
    .xla application/excel
    .xla application/x-excel
    .xla application/x-msexcel
    .xlb application/excel
    .xlb application/vnd.ms-excel
    .xlb application/x-excel
    .xlc application/excel
    .xlc application/vnd.ms-excel
    .xlc application/x-excel
    .xld application/excel
    .xld application/x-excel
    .xlk application/excel
    .xlk application/x-excel
    .xll application/excel
    .xll application/vnd.ms-excel
    .xll application/x-excel
    .xlm application/excel
    .xlm application/vnd.ms-excel
    .xlm application/x-excel
    .xls application/excel
    .xls application/vnd.ms-excel
    .xls application/x-excel
    .xls application/x-msexcel
    .xlt application/excel
    .xlt application/x-excel
    .xlv application/excel
    .xlv application/x-excel
    .xlw application/excel
    .xlw application/vnd.ms-excel
    .xlw application/x-excel
    .xlw application/x-msexcel
    .xm audio/xm
    .xml application/xml
    .xml text/xml
    .xmz xgl/movie
    .xpix application/x-vnd.ls-xpix
    .xpm image/x-xpixmap
    .xpm image/xpm
    .x-png image/png
    .xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
    .xsr video/x-amt-showrun
    .xwd image/x-xwd
    .xwd image/x-xwindowdump
    .xyz chemical/x-pdb
    .yaml application/x-yaml
    .yml application/x-yaml
    .z application/x-compress
    .z application/x-compressed
    .zip application/x-compressed
    .zip application/x-zip-compressed
    .zip application/zip
    .zip multipart/x-zip
    .zoo application/octet-stream
    .zsh text/x-script.zsh
  9. 遇到了编译错误openFile: resource exhausted (Too many open files)我一开始并没有意识到这个是我系统默认文件打开总数是1024的关系。这个可以很容易解决: ulimit -n 5000 要是永远的改变需要修改这个文件:/etc/security/limits.conf nick hard nofile 5000

一月十三日 等待变化等待机会

  1. 我试图重现问题是怎么解决的,然后就明白了所谓的npm/component的安装。实际上是添加bin目录到bashrc,这个安装是基本的做法,我居然忘记了。
    1. 在aws上重开一个ubuntu的large的instance来实验,因为内存必须要大,我选择8G,而存储大小默认的8G不够,我选择了28G。
    2. 安装最最基本的工具git/npm:
      
      sudo apt-get install git npm cabal-install graphviz
      
    3. 安装Haskell,按照官方的安装指引
      
      curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
      
      这个安装过程要求我预先安装以下包:
      sudo apt-get install build-essential curl libffi-dev libffi8ubuntu1 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5
      这个似乎是有些多余,不过无大碍。因为他们似乎仅仅是屏幕显示相关的吧?
      我发现这个官方安装的版本会有冲突,要使用cabal update来安装haskell。
    4. 安装ubuntu的graphviz。注意不要搞错了,这个不是npm的!!!否则你就一直有dot找不到的问题。而所谓的node-dot和这个完全无关,我费了好大劲儿才明白。大师的文档写的一清二楚!在上一步完成了。
    5. 要安装一些npm的组件:
      npm install split mathjax-full mathjax-node-sre
    6. 下载mathjax-node-cli
      
      git clone https://github.com/mathjax/mathjax-node-cli/
      echo "export PATH=\"$PWD/mathjax-node-cli/bin:\$PATH\"" >> ~/.bashrc && source ~/.bashrc 
      
  2. 我提给大师的所谓的issue不是很正确,被驳回了。

一月十五日 等待变化等待机会

  1. 折腾了一个晚上终于才明白所有的问题都在于ec2有一个所谓的secuirty-group的概念我没有,这个就是防火墙的规则,而我没有为此打开port,所以,怎么都不可以通讯!
  2. 我完全不知道为什么能够成功,完全是突然之间,就可以了,在早上我出门之前还不行,回来也许和instance重启有关系吗?
    1. 首先是这个ami(ami-0bcd06f1209545cd6)是openvpn公司或者社区创建的,它通过了aws/ec2来收取的一个费用。
    2. 它应该属于所谓的BYOL(Bring Your Own Licence)的范畴。这个文档很详细。这里的费用是两重的,首先,是软件公司卖的的证书,两个连接免费,其次是aws的ec2 instance的钱,对于我目前使用t2.micro价格是$0.014/hour。另一个费用是存储的费用General Purpose SSD (gp2) $0.12 of GB of provisioned storage per month我现在使用的是8G,所以一个月应该是 8x$0.12=$0.96。所以,如果开一个月的话大约是11美元。我使用internet speed test,结果好几个没有结果,唯一的一个反映我的下载速度有500+M,这个速度的确是令人满意的。至少我看youtube非常的流畅应证了这一点。当 然这个vpn肯定有些古怪的地方因为我的android手机,
    3. 一个最具体的实证就是查询我的IP显示的的确是我的ec2/openvpn的ip,当然你能够上谷歌也已经证明了这一点。我的想法是这个是通过 DNS配合实现的,因为我之前就能够顺利连接我的vpn但是依旧不能访问谷歌的原因还是DNS的问题,因为这个和大公司使用vpn的情况不同,因为那个时 候其实问题相对简单,你要访问的是内网IP,或者是防火墙/gateway后面不可见的地址,毫无疑义的你目前的运营商提供的DNS服务不会有问题,而我 现在不是不能看到谷歌的IP,而是被GFW拦截了,这个不是在DNS出问题,那么你使用通常的运营商的路径就是死路一条。但是如何让我的OS知道我需要使用vpn自带的DNS服务呢?这个就是问题的核心,这个也是我遇到openvpn的文档里反复读到但是一直不得要领的地方:
      One major feature that is missing with the command line client is the ability to automatically implement DNS servers that are pushed by the VPN server. It is possible, but it requires you to install a DNS management program such as resolvconf or openresolv, and it may or may not clash with existing network management software in your OS. The idea here, however, is that you use a script that runs when the connection goes up, and when it goes down, that uses resolvconf or openresolv to implement the DNS servers for you. The reason why this client is not able to manage it completely by itself is mainly because in an operating system like Windows, Macintosh, Android, or iOS, there is already an established single method of handling DNS management. It is therefore easy for us to create a software client for those operating systems that already knows how to handle DNS. But Linux is available in so many variations and also supports different programs and methods of implementing DNS servers, and so it was only reasonable to leave built-in DNS support out of the OpenVPN program and instead to provide, where possible, a script that handles DNS implementation. Such a script could even be written by yourself to do whatever tasks are necessary to implement the DNS servers in your unique situation.
      我一直在想这个文档要表达什么意思呢?为什么要把DNS server推送给我们的client系统呢?
    4. 在aws/ec2上使用elastic ip是防止每次重启都要重新设置的关键,这个虽然很普通,但是也是我实践才意识到的,作为客户端获得的profile的配置是无法从ec2上自动探测它自 己的ip地址的(至少我目前还不知道简单的办法),因为都是内网地址,因此产生的证书不会每次重启都去自动更新,也许这里还有一个cluster的机制 吧,比如你创建的ec2是一个backup你当然不希望它改变?总之,要使用elastic ip才好。
    5. 这里的所谓的source-destination-check是aws的机制,和security-group不是一回事。

      If your VPN setup consists of a site-to-site setup between your cloud instances and your machines on-premises, ensure you disable source destination check protection on Amazon; otherwise, routing won’t function properly.

      Turn off source/destination checks:

      1. Right-click on the VPN instance
      2. Select Change Source/Dest.
      3. Check and make sure the status is Disabled.

      Source/destination checking can also block traffic if you want VPC data to go directly to the IP addresses of your VPN clients in the VPN client subnet. For that use case, turn off the check as well.

      这里所说的后一个use case,我还是不理解。总之,这个是我之前没有作的一个步骤,很可能是影响route的一个原因吧?
    6. 这里是重点,首先是为什么或者说是什么,因为这一点是vpn原理的一个核心,我之前始终不理解,作为vpnclient访问vpnserver是里所应当的,那么需要反过来吗?如果单单是网页浏览似乎没有必要吧?

      OpenVPN Access Server’s default routing uses network address translation (NAT). Traffic originating from the VPN clients appears to come from the local IP address of the Access Server with NAT, and this is simpler than setting up routing.

      However, when using NAT, your traffic from the VPC itself can’t directly access a VPN client as the NAT engine prevents direct contact. You must configure routing instead of NAT to allow direct access to a VPN client.

      那么不要NAT要routing,要怎么做呢?
      1. Sign in to the Admin Web UI.
      2. Click Configuration > VPN Settings.
      3. Scroll to the Routing section, where you can click Yes, using Routing.
      4. Configure your subnets for your network.
      这么作的结果是这样子的

      After setting up routing, the source IP address of packets coming from the VPN clients is kept intact, and direct access from the VPC network to the VPN client subnet is possible. However, because the VPC doesn’t automatically recognize the VPN subnet within the VPN instance, it doesn’t know how to send the return traffic back to the instance. To correct this problem, add a static route in the Amazon routing table for your VPC so that the return traffic flows properly. Refer to Amazon’s AWS VPC routing documentation: Route tables for your VPC (Amazon).

      最后一句实际上我还是看不懂,在ec2/vpc上的route难道aws已经自动作了吗?我看了半天也不理解。
    7. 在launch ec2的时候,你可以透过传递所谓的user data的方式设置一些openvpn的预设参数:
      Entering user data:

      1. During the steps above for creating an AMI, when you reach step 7, Advanced details, expand that section.
      2. Scroll down to the text field, User data.
      3. Enter your data for one or more of the available settings below. Ensure you enter each row as key1=value1, and don’t use quote keys or spaces on either side of the equal character.
      这里有两个我很感兴趣的部分:
      Key Description
      reroute_gw (boolean, default=0) If 1, clients route internet traffic through the VPN.
      reroute_dns (boolean, default=0) If 1, clients route DNS queries through the VPN. Note: If the VPC CIDR block is defined, it is made accessible to VPN clients via NAT.
    8. 目前遇到的另一个问题是在firefox可以使用,但是在chromium里似乎openvpn的配置不起作用,为什么?
    9. 在我本地如果使用nslookup看到的谷歌的ip和在openvpn上看到的是不一样的。这个当然是可以理解的,那么由此引发的不同的traceroute结果也是可以理解的,那么从两地达到同一个目的地的路线也是可以理解的。那么有什么结果可以总结的呢?
    10. 总而言之,有一个openvpn的设置是很重要的,就是要Should client Internet traffic be routed through the VPN?设定为Yes
    11. 看到另一个我不明白的地方,就是ubuntu似乎是使用所谓的resolvectl来解释域名吧?这个似乎不认同新创建的软设备如我们的tunnel,而且它解释的谷歌的地址和nslookup不一样不知道怎么回事现在所有resolvectl/dig/nslookup返回的结果都是一致的了。难道改变了什么?在我启动openvpn客户端后能够看到routing table的变化,那么这个应该不应该被chromium-browser设置呢?
      
      dig +short www.google.com
      nslookup www.google.com
      resolvectl query -4 www.google.com
      
      这三个命令得到的结果现在看来是一致的。
  3. 静夜思
    斜月三星伴,
    意马心猿牵。
    万里相思刻,
    灵台方寸间。
    

一月十八日 等待变化等待机会

  1. 我找到了一个制作或者说是复制现有的ami的过程,我对此还是不太相信,至少这个只是一半的流程。 我需要准备好我的证书和私钥,以及我的账号,然后调用ec2-ami-tools的命令:sudo ec2-bundle-vol --no-inherit -k private/key/path -c certificate/path -u my-aws-uid -d /dest/path
  2. 卡在qemu的bridge network上,我看这个也许有些希望。之前我已经可以使用ubuntu的安装iso来创建一个虚拟机了。
  3. 创建磁盘qemu-img create mydisk.img 10G然后就是运行安装iso,qemu-system-x86_64 -boot d -cdrom /dev/cdrom -m 2G -hda mydisk.img,安装之后就直接运行磁盘。
  4. 最后还是这个最最靠谱,因为,这个正如作者说的那样就是一个最最简单的方法,当然我之前不成功也许是某些操作或者之前的操作的干扰?
    1. sudo mkdir /etc/qemu; echo "allow virtbr0" | sudo tee /etc/qemu/bridge.conf
      但是我还是有些不解的是这个是qemu创建需要用到的吗?
    2. sudo chmod +s /usr/lib/qemu/qemu-bridge-helper
      我之前一直有acl的错误,我不知道这个是不是需要apparmor之类的pan的权限控制,总之,这个似乎在我七搞八搞重启服务之后解决了。
    3. 创建这个bridge分配ip是我当前的host机的,这个会不会有什么问题呢?有些博主是靠dhclient来作的,有些不太可靠。
      
      sudo brctl addbr virtbr0
      sudo brctl addif virtbr0 enp0s31f6
      sudo ip addr add 192.168.1.23/24 dev virtbr0
      sudo ip link set virtbr0 up
      
    4. sudo iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
      对于这个我始终是将信将疑,我刚刚还在担心结果网卡就挂了。
    5. 启动qemu的时候使用这个参数:
      qemu-system-x86_64 -boot d -m 2G -hda serverdisk.img -enable-kvm -net nic,model=virtio,macaddr=52:54:00:00:00:01 -net bridge,br=virtbr0
      
      那个mac地址无所谓的,只要不冲突都行。
    6. 我觉得我之前设立openvpn之所以能够成功的原因就在于routing-table里第一个解析必须要通过我的vpn才行:
      
      nick@nick-sager:~/ami$ route
      Kernel IP routing table
      Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
      0.0.0.0         172.27.232.1    128.0.0.0       UG    0      0        0 tun0
      default         192.168.1.1     0.0.0.0         UG    100    0        0 enp0s31f6
      ec2-54-67-3-66. 192.168.1.1     255.255.255.255 UGH   0      0        0 virtbr0
      128.0.0.0       172.27.232.1    128.0.0.0       UG    0      0        0 tun0
      link-local      0.0.0.0         255.255.0.0     U     1000   0        0 enp0s31f6
      172.27.232.0    0.0.0.0         255.255.248.0   U     0      0        0 tun0
      192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 virtbr0
      192.168.1.0     0.0.0.0         255.255.255.0   U     100    0        0 enp0s31f6
      192.168.1.0     0.0.0.0         255.255.255.0   U     425    0        0 virtbr0
      
      成也萧何,败也萧何,如今我要创建bridge似乎就在分配ip上有问题吧?
    7. 经过短暂的欢呼我遇到了地址冲突的问题,我不确定是不是我的虚拟机直接和我的笔记本在争夺控制权呢?

一月二十日 等待变化等待机会

  1. 我枉费心计想要达到的目的是如此的简单以至于没有人会想到这么的幼稚。当你运行虚拟机的时候,对于guest OS来说访问外界网络是一个基本的必须,而且早就作的妥妥的,而大多数所需要的是从host OS去访问guest OS,这个通常是不那么必须的,所以,才费了很大的功夫来设定bridge。而我的需求甚至于直接使用serial console就能够达到。总而言之,我再次审视我的qemu的虚拟机发现它可以很方便的访问host OS,比如它的ip是10.0.2.3,而网关是10.0.2.1,而我自己的笔记本就是hostOS就是10.0.2.2,这是一个多么简单的问题啊!
  2. 我使用ec2-upload-bundle总是遇到curl的cacert的错误,茫然不知所措,只是知道这个并非是我的问题,最后发现还是我安装默认的ubuntu的ec2-ami-tools的版本太低了,改成了直接安装最新版本才得到解决。
    
    	sudo apt-get update -y && sudo apt-get install -y ruby unzip
    wget https://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.zip
    sudo mkdir -p /usr/local/ec2
    sudo unzip ec2-ami-tools.zip -d /usr/local/ec2
    
    然后是环境设置在.bashrc的做法:
    
    export EC2_AMITOOL_HOME=/usr/local/ec2/ec2-ami-tools-1.5.19
    export PATH=$EC2_AMITOOL_HOME/bin:$PATH
    
    检验安装结果:ec2-ami-tools-version
  3. 有些问题是出奇的寻常,但是我确实脑子里模糊了。比如你要创建一个ami,那么它的image要存在哪里呢?很明显的你要附带的mount一个新 的volume,这不是很通常的想法吗?所以,要qemu-image去再创建一个磁盘,然后运行的时候把它作为-hdb的硬盘加载。可是文件系统没有格 式化,所以,这里要一步一步的创建:
    1. Determine Drive Information:
      sudo lshw -C disk
  4. Partition The Disk
    
    sudo parted /dev/sdb
    
    
  5. 1) Start parted as follows:

    sudo parted /dev/sdb

    2) Create a new GPT disklabel (aka partition table):

    (parted) mklabel gpt

    3) Set the default unit to TB:

    (parted) unit TB

    4) Create one partition occupying all the space on the drive. For a 4TB drive:

    (parted) mkpart
    Partition name?  []? primary
    File system type?  [ext2]? ext4
    Start? 0
    End? 4

    Alternatively, you can set the partition size as a percentage of the disk. To create a partition occupying all the space on the drive:

    (parted) mkpart
    Partition name?  []? primary
    File system type?  [ext2]? ext4
    Start? 0%
    End? 100%

    5) Check that the results are correct:

    (parted) print

    There should be one partition occupying the entire drive.

    6) Save and quit "parted":

    (parted) quit
  6. 但是我其实用fdisk比较多,而这个前提实际上并不是很清楚,这里写的就很清楚,如果我不在乎它只有一个partition,不在乎其他操作系统来使用,那么直接使用fdisk就可以创建磁盘文件系统了,因为我只是拿它作为一个临时存储而已。
    
    sudo fdisk /dev/sdb 
    
    然后直接 sudo mkfs -t ext4 /dev/sdb1 说来惭愧,这么多年来我始终对于分区表以及文件系统有着似是而非的认识,即便以前经常使用但依旧概念模糊,手法生疏,可耻啊。
  7. 我已经十几年没有正经使用aws了,结果就是cli必须要更新了。当然首先是要卸载ubuntu的官方的旧版本。
    
    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    unzip awscliv2.zip
    sudo ./aws/install
    
    配置又是一大堆的工作要作!先歇一下吧。
  8. 关于aws ec2的instance所对应的root device type也是一个很头疼的,如果我不是有一点点概念肯定就又卡壳了。
    Only the following instance types support an instance store volume as the root volume: C3, D2, G2, I2, M3, and R3.
    我发现实际上自己制作ebs-ami是一件很麻烦的事情,我决定放弃了。

一月二十二日 等待变化等待机会

  1. 我决定转移战场,因为实际上有需求的还是当初的stable-diffusion的项目。因为没有vpn而要修改github真的是很烦人的。那么
    1. git clone --recursive https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
    2. 直接运行./webui.sh会有Torch is not able to use GPU不能用的错误。于是我尝试先安装torch:
      
      pip install torch torchvision
      
    3. 以上并不能解决,似乎torch需要使用cuda来操纵gpu,那么安装cuda吧?
      sudo apt-get install nvidia-cuda-dev python3-pycuda
    4. 依旧解决不了,看看这个看行不行。 其中
      pip install git+https://github.com/crowsonkb/k-diffusion.git --prefer-binary
      看样子不能直接使用binary,去掉--prefer-binary重新编译。结果编译还是出错,再次重试。。。

      类似的

      pip install git+https://github.com/TencentARC/GFPGAN.git --prefer-binary
      去掉--prefer-binary重新编译。

      似乎这个module没有安装
      pip install pytorch_lightning
      而能够看到这个错误是直接运行python webui.py而不是运行那个webui.sh

      类似的要安装gradio

    5. 遇到sgm模块找不到只好
      
      git clone https://github.com/Stability-AI/generative-models.git repositories/generative-models
      
      这个似乎没有完成,因为我看到README.md里说了安装步骤:
      1. Clone the repo
        
        git clone https://github.com/Stability-AI/generative-models.git repositories/generative-models
        cd generative-models
        
      2. 
        python3 -m venv .pt2
        source .pt2/bin/activate
        pip3 install -r requirements/pt2.txt
        
      3. install sgm
        
        pip3 install .
        
      4. install sdata for training
        
        pip3 install -e git+https://github.com/Stability-AI/datapipelines.git@main#egg=sdata
        
      5. packaging:
        
        pip install hatch
        hatch build -t wheel
        
        然后
        pip install dist/*.whl
      6. 遇到了这个错误:ModuleNotFoundError: No module named 'pytorch_lightning.utilities.distributed' 这个看起来是一个笔误,把所谓的

        Tried something else, as well. there are a series of errors due to pytorch_lightning.utilities.distributed in

        1. /stable-diffusion-webui/repositories/stable-diffusion-stability-ai/ldm/models/diffusion/ddpm.py (Line: 20)
        2. /stable-diffusion-webui/extensions-builtin/LDSR/sd_hijack_ddpm_v1.py (Line: 17)

        In both the files, just change pytorch_lightning.utilities.distributed to pytorch_lightning.utilities.rank_zero at above stated lines. And the issues will be resolved.

        It worked for me. Might work for you as well.

      7. 对于ModuleNotFoundError: No module named 'taming'
    6. open_clip模块找不到需要pip install open_clip_torch

一月二十五日 等待变化等待机会

  1. 折腾了很久又被俗世打断。今天重新审视感觉没有安装好cuda是第一个问题。因此,这里先下载各种包吧? 而这里是所有相应的cuda包的列表。
    Table 5 Meta Packages Available for CUDA 12.3

    Meta Package

    Purpose

    cuda

    Installs all CUDA Toolkit and Driver packages. Handles upgrading to the next version of the cuda package when it’s released.

    cuda-12-3

    Installs all CUDA Toolkit and Driver packages. Remains at version 12.3 until an additional version of CUDA is installed.

    cuda-toolkit-12-3

    Installs all CUDA Toolkit packages required to develop CUDA applications. Does not include the driver.

    cuda-toolkit-12

    Installs all CUDA Toolkit packages required to develop applications. Will not upgrade beyond the 12.x series toolkits. Does not include the driver.

    cuda-toolkit

    Installs all CUDA Toolkit packages required to develop applications. Handles upgrading to the next 12.x version of CUDA when it’s released. Does not include the driver.

    cuda-tools-12-3

    Installs all CUDA command line and visual tools.

    cuda-runtime-12-3

    Installs all CUDA Toolkit packages required to run CUDA applications, as well as the Driver packages.

    cuda-compiler-12-3

    Installs all CUDA compiler packages.

    cuda-libraries-12-3

    Installs all runtime CUDA Library packages.

    cuda-libraries-dev-12-3

    Installs all development CUDA Library packages.

    cuda-drivers

    Installs all Driver packages. Handles upgrading to the next version of the Driver packages when they’re released.

  2. 但是我发现还是最好使用ubuntu官方的包,否则冲突是不可避免的。所以,先卸载旧的包。 sudo apt-get purge libnvidia-compute-510 libnvidia-compute-525 libnvidia-compute-525:i386 libnvidia-ml-dev 然后使用官方的所谓的p类型的包:aptitude search '~P cuda-'
  3. 遇到各种各样的奇怪的问题,时不时的openvpn不工作,另一个奇怪的问题就是ssh到aws的ubuntu为什么不能够正常的x11呢?似乎 不是openvpn的问题。并且我发现似乎我使用的免费的openvpn的配置的google等的网址居然使用了facebook的域名,或者说 PING www.google.com(edge-star-mini6-shv-01-vie1.facebook.com (2a03:2880:f107:83:face:b00c:0:25de)) 56 data bytes 这个让我有些毛骨悚然,也许这个openvpn免费的东西是在把我引流到某个广告引擎,甚至是黑客的过滤网站?然后启动web.sh的时候注意到一个 git的option居然没有--refetch,然后我才意识到开发者们使用的是高版本的git,我默认的ubuntu22.04的版本太老了。
  4. 然后我就发现是自己的愚蠢造成的问题,我之前发现了一个问题,就照着网络上的修改,改了一个东西,然后没有提交的状态下git无法正常的更新导致错误。 在repositories/stable-diffusion-stability-ai/ldm/models/diffusion/ddpm.py里有一个
    
    diff --git a/ldm/models/diffusion/ddpm.py b/ldm/models/diffusion/ddpm.py
    index bbedd04..ef0990e 100644
    --- a/ldm/models/diffusion/ddpm.py
    +++ b/ldm/models/diffusion/ddpm.py
    @@ -16,7 +16,7 @@ from contextlib import contextmanager
     from functools import partial
     from tqdm import tqdm
     from torchvision.utils import make_grid
    -from pytorch_lightning.utilities.distributed import rank_zero_only
    +from pytorch_lightning.utilities.rank_zero import rank_zero_only
     
     from ldm.util import log_txt_as_img, exists, default, ismap, isimage, mean_flat, count_params, instantiate_from_config
     from ldm.modules.ema import LitEma
    
    我只能希望新版本修正这个问题???
  5. 似乎安装是没有问题了,然后遇到了第一次运行需要checkpoint的问题。这里我需要下载所谓的.ckpt文件然后放在models/Stable-diffusion目录下。
  6. 我找到了这个指南,这里介绍了最流行的models。。
  7. 这是keywords的列表

    Medium

    Medium defines a category of artwork.

    keywordNote
    PortraitVery realistic drawings. Good to use with people.
    Digital paintingDigital art style.
    Concept artIllustration style, 2D.
    Ultra realistic illustrationDrawings that are very realistic. Good to use with people.
    Underwater portraitUse with people. Underwater. Hair floating.
    Underwater steampunkVery realistic drawings. Good to use with people.

    Style

    These keywords further refine the art style.

    keywordNote
    hyperrealisticIncreases details and resolution
    pop-artPop-art style
    Modernistvibrant color, high contrast
    art nouveauAdd ornaments and details, building style

    Artist

    Mentioning the artist in the prompt is a strong effect. Study their work and choose wisely.

    keywordNote
    John Collier19th century portrait painter. Add elegancy
    Stanley Artgerm LauGood to use with woman portrait, generate 19th delicate clothing, some impressionism
    Frida KahloQuite strong effect following Kahlo’s portrait style. Sometimes result in picture frame
    John Singer SargentGood to use with woman portraits, generate 19th delicate clothing, some impressionism
    Alphonse Mucha2D portrait painting in style of Alphonse Mucha

    Website

    Mentioning an art or photo site is a strong effect, probably because each site has its niche genre.

    keywordNote
    pixivJapanese anime style
    pixabayCommercial stock photo style
    artstationModern illustration, fantasy

    Resolution

    keywordNote
    unreal engineVery realistic and detailed 3D
    sharp focusIncrease resolution
    8kIncrease resolution, though can lead to it looking more fake. Makes the image more camera like and realistic
    vray3D rendering best for objects, landscape and building.

    Additional details

    Add specific details to your image.

    keywordNote
    dramaticshot from a low angle
    silkAdd silk to clothing
    expansiveMore open background, smaller subject
    low angle shotshot from low angle
    god rayssunlight breaking through the cloud
    psychedelicvivid color with distortion

    Color

    Add an additional color scheme to the image.

    keywordNote
    iridescent goldShinny gold
    silverSilver color
    vintagevintage effect

    Lighting

    keywordNote
    rim lightinglight on edge of an object
    cinematic lightingA generic term to improve contrast by using light
    crepuscular rayssunlight breaking through the cloud

一月二十七日 等待变化等待机会

  1. 不要还没学会走路就想跑!还是从第一步如何产生合适的prompt开始吧。
    1. Subject (required)
    2. Medium
    3. Style
    4. Artist
    5. Website
    6. Resolution
    7. Additional details
    8. Color
    9. Lighting
    10. 所谓的AIGC是画鬼最易,那么画真人才难。无怪话现代艺术都要走抽象派,印象派,逃避现实是最容易的解决方案。这里介绍了一个技巧就是可以使用名人的脸来作参考,当然这里要使用英文名字,拼音也行,比如我们可以创造一个范冰冰的脸孔:
      photo of young woman, [fan bingbing:0.96], highlight hair, sitting outside restaurant, wearing dress, rim lighting, studio lighting, looking at the camera, dslr, ultra quality, sharp focus, tack sharp, dof, film grain, Fujifilm XT3, crystal clear, 8K UHD, highly detailed glossy eyes, high detailed skin, skin pores
      我感觉AI是先产生了一个女人在酒吧的图片然后再换脸,这个可以似乎是我看到webui产生缩略图的步骤这么猜测的。这里是negative prompt
      disfigured, ugly, bad, immature, cartoon, anime, 3d, painting, b&w
      当然这里还可以把两个名人的名字串在一起搞出一个混合的脸。 但是这个完全取决于模型,我发现似乎prune的模型不再认识名人的脸了。
    11. 这个civitai.com有不少的模型,还有些似乎是使用别人的模型创建的模型,似乎就是一些prompt的集合?我不确定。总之这个领域是非常的兴旺繁荣的。
    12. 下载了一个名人的包,就是.pt的文件,按照指示,这个是要放在embedding目录下,然后当提示的关键字出现就可以了。

一月二十九日 等待变化等待机会

  1. 第一次录屏等于是学习总结如何使用inpaint来修图,这个以前在使用photoshop是一个很大的工程,如今对于一个门外汉只是几分钟的工作,而且超越了普通美工的水平了。
  2. 现在探索下一步,视频如何玩,我还是一无所知,面对ali的这个模型我甚至不知道要怎么安装,它不是针对初学者的。我想先尝试这个看上去更加成熟的模型
  3. 这里是UAE的一个基本介绍
    1. 首先,UAE是什么?Variational AutoEncoder就是:
      A variational autoencoder (VAE) is a technique used to improve the quality of AI generated images you create with the text-to-image model Stable Diffusion. VAE encodes the image into a latent space and then that latent space is decoded into a new, higher quality image.
      就是说它是一个提高图像质量的工具。什么是latent space呢?未知的?隐含的?让我想起了WarCraft里的咒语:From light to darkness. From darkness to light.
    2. 这里解释说latent space是一个a lower-dimensional representation of the image
      There are two main types of VAEs that can be used with Stable Diffusion: exponential moving average (EMA) and mean squared error (MSE). EMA is generally considered to be the better VAE for most applications, as it produces images that are sharper and more realistic. MSE can be used to produce images that are smoother and less noisy, but it may not be as realistic as images generated by EMA.
    3. 如何使用才是最实用的:

      To use VAE with Stable Diffusion, you will need to download a VAE model and place it in the stable-diffusion-webui/models/VAE directory. You can then select the VAE model that you want to use in the Settings > Stable Diffusion > SD VAE

    所以,我的理解就是VAE是一个后期提高画质的工具,但是我并不是直接使用,而是model或者说是checkpoint自动加载使用的,我只需要设置选择就可以了。所以,我之前看到阿里的txt2video并不是一个完全依靠VAE就能工作的。一定是辅助的。
  4. 偶然看到关于ssd常识的说明,我对于nvme是不是ssd始终不是很清楚。现在理解就是nvme是一个泛指的存储方式或者说协议?看来理解力有问题。我懒得读了。stable-diffusion的文档都读不过来。

一月三十日 等待变化等待机会

  1. 其实很多时候最难的是那些已经入门的人认为理所当然不言而喻的最最基本的问题。因为往往没有人会指出,或者不屑于指出,或者觉得说这些 babytalk太掉价。而我需要这类的基本指引。比如怎么安装一个extension呢?到底我直接把它科隆到extensions的目录下面就自动安 装了吗?不大可能的。后来我看到一个提示是在webui的extension上有一个install from URL,不知道这个是不是自动安装,但是至少我看到它是先科隆到repositories下,至于是否有通用的安装脚本我就不确定了。
  2. 我不知道extension是否成功安装,因为我的目的是text2video,这里我看大概有这么三条路径:
    1. AnimateDiff
      AnimateDiff is a text-to-video module for Stable Diffusion. It was trained by feeding short video clips to a motion model to learn how the next video frame should look like. Once this prior is learned, animateDiff injects the motion module to the noise predictor U-Net of a Stable Diffusion model to produce a video based on a text description.
      目前还不理解,先抄笔记再说。这里的结论是说视频的生成完全取决于模型,这个比较好理解,就是和文字图像没有本质区别的的方式,You can use AnimateDiff with any Stable Diffusion checkpoint model and LoRA. 安装步骤
      1. Start AUTOMATIC1111 Web-UI normally.
      2. Navigate to the Extension Page.
      3. Click the Install from URL tab.
      4. Enter the following URL in the URL for extension’s git repository field.
        https://github.com/continue-revolution/sd-webui-animatediff
      5. Wait for the confirmation message that the installation is complete.
      6. Download the AnimateDiff motion models and save it in stable-diffusion-webui > extensions > sd-webui-animatediff > model folder.

        Direct download link for v1.5 v2 motion model:

        https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v15_v2.ckpt

        Direct download link for v1.4 motion model:

        https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v14.ckpt

        Direct download link for v1.5 motion model:

        https://huggingface.co/guoyww/animatediff/resolve/main/mm_sd_v15.ckpt

        似乎这些是旧版本了。
      7. Restart Web-UI.
      从这看起来它仅仅是一个txt2img的一个拓展,比如怎样生成多幅图片的机制使用训练的模型,所以使用起来还是txt2img的界面。

      To use AnimateDiff in AUTOMATIC1111, navigate to the txt2img page. In the AnimateDiff section,

      • Enable AnimateDiff: Yes
      • Motion Module: There are two motion modules you can choose from. The v1.4 model creates more motion, but the v1.5 model creates clearer animations.

      Then write a prompt and a negative prompt as usual. For example

      prompt 1girl, looking at viewer, anime, cherry blossoms
      negative prompt disfigured, deformed, ugly
      似乎要配合这个动画的model才行? 这里是很多的动画的model 这里是使用指南因为我运行了一次后就在webui找不到了
    2. ModelScope
    3. Deforum
  3. 看来哪怕是最容易的video也要比image复杂上百倍。这里是基本的指引。
    1. 什么?

      AnimateDiff turns a text prompt into a video using a Stable Diffusion model. You can think of it as a slight generalization of text-to-image: Instead of generating an image, it generates a video.

    2. 怎么?这个就复杂了完全是另一篇的文章。我觉得核心的理解就是这个conditioning

      AnimateDiff uses a control module to influence a Stable Diffusion model. It is trained with a variety of short video clips. The control module conditions the image generation process to produce a series of images that look like the video clips it learns.

      我觉得另一个核心概念是controlNet:

      Like ControlNet, the control module of AnimateDiff can be used with ANY Stable Diffusion model. Currently, only Stable Diffusion v1.5 models are supported.

    3. 缺点或者说局限性: 简单:

      Since it follows the motion learned from the training data, it produces a generic motion that’s typically seen. It won’t produce a video that follows a detailed sequence of motions in the prompt.

      不可能有过多的创造想象的成分,不大可能造出没有见过的。
      The quality of motion is sensitive to the training data. It can’t animate exotic graphics that is not present in the training data.
    4. 窍门:
      1. Change the prompt during video generation. This technique is called prompt travel.
      2. Use a reference video with ControlNet.
    5. 这些都在以惊人的速度更新,之前的模型似乎已经过时了,作者这里有所有的新旧模型是否有冲突呢?
    6. 关于使用,作者建议使用这个模型cyberrealistic,但是这个似乎仅仅是一个prompt的模型?不,它是一个embedding。这里是安装指导
      Embedding, also called textual inversion, is an alternative way to control the style of your images in Stable Diffusion.
      就是说重定义?
      Embedding is the result of textual inversion, a method to define new keywords in a model without modifying it. The method has gained attention because its capable of injecting new styles or objects to a model with as few as 3 -5 sample images.
      就是说它不是模型,而是重定义旧的模型,这个工作量很小,仿佛思想钢印里打错一个真与假的符号就完全改变了信仰。
      The amazing thing about textual inversion is NOT the ability to add new styles or objects — other fine-tuning methods can do that as well or better. It is the fact that it can do so without changing the model.
      去哪里找呢?

      The go-to place to download embeddings is Civitai. Filter with textual inversion to view embeddings only.

      Hugging Face hosts the Stable Diffusion Concept Library, which is a repository of a large number of custom embeddings.

      总之,它是一些.bin文件要放在embedding目录下。它是使用文件名作为提示符号的。启动时候可以看到加载信息。
  4. 我折腾了半天也不知道为什么。我从webui里不能访问awailable extension: https://raw.githubusercontent.com/AUTOMATIC1111/stable-diffusion-webui-extensions/master/index.json 我尝试从命令行的确被拒绝了,我怀疑是vpn的问题,于是尝试curl/wget就发现同样的问题,使用nslookup raw.githubusercontent.com看到的是0.0.0.0,我怀疑这个是dns的设置问题。我尝试修改NetworkManager上的dns server ip。还安装bind9/dnsutil之类ubuntu的工具来手动修改。也许dns需要forward,总之过了好一会才有效果。这个和我启动webui.sh脚本应该是无关的。
  5. 然后安装extension重启遇到不兼容问题,这个解决了所有的问题:
    
    pip install --upgrade setuptools
    
    随后你运行
    
    pip install -r requirements.txt
    
    就看不到错误了。
  6. 始终看到什么TMalloc的问题,是要安装gperf库才行。
  7. 我终于开始明白一点我的UI的设置是在ui-config.json里定义的。但是这个似乎没有变化?同时大家普遍使用webui-user.sh来定制参数,一些设置都是为了防止不断升级带来的问题。
  8. 那么且把ui-config的问题放一边,不要太激进,从更容易的img2video来看看。这个实际上是一个更加成熟的步骤。就是我反复看到的所谓的svd。
    Stable Video Diffusion (SVD) Image-to-Video is a diffusion model that takes in a still image as a conditioning frame, and generates a video from it.
    所以,它的核心还是conditioning。我决定从webui直接安装这个不是所谓的extension,所以,不能安装,而且install from URL需要的是git不是huggingface这类的文件发布方式。
  9. 且慢,从源头来看这个是去年发行的这个generative-models来的。我要从源头看看。

    首先,安装就是很有意思。我感觉python和java一样非常复杂的运行环境,所以,使用所谓的虚拟环境很重要。

    1. 
      git clone https://github.com/Stability-AI/generative-models.git
      cd generative-models
      
    2. 
      # install required packages from pypi
      python3 -m venv .pt2
      source .pt2/bin/activate
      pip3 install -r requirements/pt2.txt
      
    3. pip3 install .
    4. 
      pip3 install -e git+https://github.com/Stability-AI/datapipelines.git@main#egg=sdata
      

    遇到fairscale安装总是失败的问题,那么就提前安装一下吧。实在是不行还有从源码安装的选项。
    
    git clone https://github.com/facebookresearch/fairscale.git
    cd fairscale
    pip install -r requirements.txt
    # -e signified dev mode since e stands for editable
    pip install -e .
    
    这里的packaging对于我是没有用的吧?我需要吗?我一点概念都没有。我是使用模型还是训练模型呢?
    
    pip install hatch
    hatch build -t wheel
    
    这里的inference是训练模型吗?
  10. 我这几天的折腾发现这个领域熟悉python比熟悉其他都来的重要,而这方面我又是几乎零起步。
  11. 这里又是另一个我不理解的东西,什么是streamlit,似乎是一个demo?demo什么呢?这里是文档
    Streamlit is an open-source Python library that makes it easy to create and share beautiful, custom web apps for machine learning and data science. In just a few minutes you can build and deploy powerful data apps. So let's get started!
    所以,它是一个python的库,可以作非常令人震惊的app应用,我看它的hello demo简直完全不知道它是怎么做出来的。这个是完全偏离了ai的部分,但是它非常的强大,以至于对于大量数据需要展示的应用的制作应该是非常的有用,我 不知道它是怎么作出那些漂亮的统计图表,简直就是行走的office应用。
  12. 什么是git-lfs呢?原理是什么呢?我要怎么使用呢?
  13. 今天是混乱的一天,我完全没有什么成就感,因为安装txt2video根本不能工作甚至于之前还能看到的ui-tab也消失了。完全不能理解。
  14. 有时候撤退是为了更好的进攻,我觉得我完全没有能力学习video,所以,先回到最基本的模型来吧。 Fine-tuned models
    Fine-tuning is a common technique in machine learning. It takes a model trained on a wide dataset and trains a bit more on a narrow dataset.
    简而言之,就是回锅肉。人们之所以要训练fine-tuned,就是因为原本的大模型广而不精。

    这个是SDXL模型

  15. 我终于找到我的问题原因了,在extension界面里有一个禁止所有extension的选项,我必须打开animatediff,不过随后我就遇到了致命的显卡显存不足的问题!这个就是我之前放弃的原因。显卡显存只有6G怎么办?有人建议优化
    export COMMANDLINE_ARGS="--opt-split-attention --opt-sub-quad-attention --lowvram"
    这个设在webui-user.sh里,看来最后一个应该是起作用的。
  16. 但是最后不能存成mp4或者gif,我看到--disable-safe-unpickle,但是作者说还是创建新的安全模型更好。这个方法,不知道行不行。 似乎是可以了,不过要记住生成的文件是在outputs目录下的一个Animatediff目录下。以下就是这个教程的结果。
  17. 稍微修改了一下prompt:
    ((best quality)), ((masterpiece)), ((realistic)), long highlighted hair,  (fan bingbing:0.95), Asian girl in red Chinese ancient armor, confident stance, high-resolution, living room, smiling, head tilted
    negative prompt依然是CyberRealistic_Negative-neg然后产生的这个结果: 而在img2img里选择这个motion module其实是相当关键的,因为在没有这个模型之前产生的图像变化很大,作出的视频还是gif都是巨大的跨越导致人眼看起来很不舒服。这里就是模仿范冰冰的视频和动画: 制作了一个简单的录频加以记录。

一月三十一日 等待变化等待机会

  1. 通过昨天的密集的学习应该是有些入门了,毕竟这个本来就是对于计算机小白都能掌握的,主要的用户人群实际上是美工和艺术家们。无论如何对于这个模型的入门介绍还是非常有价值的。而这个快速入门指导也是有意义的。
  2. 在开始其他两种视频途径之前,我想先学习一下各个模块,比如controlNet,这个我久闻大名,一直不明白。
    1. 什么?
      ControlNet is a neural network that controls image generation in Stable Diffusion by adding extra conditions.
      这个是原始论文的出处。我下载了一份备份
    2. 能做什么?
      • Specify human poses.
      • Copy the composition from another image.
      • Generate a similar image.
      • Turn a scribble into a professional image.
    3. 这个定义是更加详细的:

      ControlNet is a neural network model for controlling Stable Diffusion models. You can use ControlNet along with any Stable Diffusion models.

      而这里的核心又是conditioning,这里有一个更加深刻的定义:就是通过改变noise predictor,这个是什么我还不清楚,但是有个概念也好。
      The purpose of conditioning is to steer the noise predictor so that the predicted noise will give us what we want after subtracting from the image.
      我发现这个是整个工作机制的最好的介绍,但是我现在还没有能力一个个消化。
    4. ControlNet adds one more conditioning in addition to the text prompt. The extra conditioning can take many forms in ControlNet.
      ControlNet本身是一种额外的conditioning,是在原有的text prompt基础上的。作者举了两个实例来说明:
      Controlling image generation with (1) edge detection and (2) human pose detection.
    5. 第一种是edge detection,这个图非常的形象生动:
      ControlNet takes an additional input image and detects its outlines using the Canny edge detector. An image containing the detected edges is then saved as a control map. It is fed into the ControlNet model as an extra conditioning to the text prompt.
      能不能说controlNet需要图文并茂来给模型作提示,连说带比划
    6. 这个过程的更加专业的描述是
      The process of extracting specific information (edges in this case) from the input image is called annotation (in the research article) or preprocessing (in the ControlNet extension).
      图形轮廓是annotation,或者是脚注。而在controlNet内部是所谓的预处理。这些都是高级的概念,我听听就好。
    7. 在我看来第二种human pose detectionedge detection也许就是更加偏重于以人物为对象吧?
      Edge detection is not the only way an image can be preprocessed. Openpose is a fast human keypoint detection model that can extract human poses like positions of hands, legs, and head. See the example below.
      这里又提到了它内部使用的机制是一个什么openPose,人工智能的链条极其的长,任何一个环节都是无数人的心血劳动努力。
    8. 抄笔记本身就是学习的过程,抄一遍至少有一个印象:
      Below is the ControlNet workflow using OpenPose. Keypoints are extracted from the input image using OpenPose, and saved as a control map containing the positions of key points. It is then fed to Stable Diffusion as an extra conditioning together with the text prompt. Images are generated based on these two conditionings.
      我称之为精读 这里是一个从图像到语言,又从语言到图像的过程。又一次让我想起了:From light to darkness; From darkness to light;这个是符合马克思主义唯物辩证法关于人类认识世界改造世界的过程的一般论述的。就是从具体到抽象,再从抽象到具体。这个就是人眼识别的一般做法。 这里作者还给出了Openpose和Canny的区别:
      What’s the difference between using Canny edge detection and Openpose? The Canny edge detector extracts the edges of the subject and background alike. It tends to translate the scene more faithfully. You can see the dancing man became a woman, but the outline and hairstyle are preserved.
      Canny更加的忠实还原,或者说抽象程度低一些,而Openpose更加的触及图像的本质,更加的抽象。当然这个是有代价的,就是它更加的专注于人物的主要部分,而不是通用吧?
      OpenPose only detects human key points such as positions of the head, arms, etc. The image generation is more liberal but follows the original pose.
      这里作者的敏锐的观察力给你作更加细致的分析两者的结果
      The above example generated a woman jumping up with the left foot pointing sideways, different from the original image and the one in the Canny Edge example. The reason is that OpenPose’s keypoint detection does not specify the orientations of the feet.
    9. 安装步骤
      1. Navigate to the Extensions page.
      2. Select the Install from URL tab.
      3. Put the following URL in the URL for extension’s repository field.
        https://github.com/Mikubill/sd-webui-controlnet
      4. Click the Install button.
      5. Wait for the confirmation message saying the extension is installed.
      6. Restart AUTOMATIC1111.
      7. Visit the ControlNet models page.
      8. Download all model files (filename ending with .pth).

        (If you don’t want to download all of them, you can download the openpose and canny models for now, which are most commonly used.)

      9. Put the model file(s) in the ControlNet extension’s models directory.
        stable-diffusion-webui\extensions\sd-webui-controlnet\models
      10. Restart AUTOMATIC1111 webui.
      这个我好像没有看到,可能要等很久吧?
      If the extension is successfully installed, you will see a new collapsible section in the txt2img tab called ControlNet. It should be right above the Script drop-down menu.
  3. 关于这个T2I adaptor我不确定我是否需要。再说吧。
  4. 这里是使用的法门,也是最有用的部分,先下载这个指示图 然后运行过程中发现这个动作一直不前进
    Downloading: "https://huggingface.co/lllyasviel/Annotators/resolve/main/body_pose_model.pth" to /home/nick/workspace/stable-diffusion-webui/extensions/sd-webui-controlnet/annotator/downloads/openpose/body_pose_model.pth
    这里的解决方案还没有证实。也许是证书的问题,也许。。。吃饭了。
  5. 遇到一个打开huggingface.co的错误,说tls的certificated之类的。这个是完全和AI无关的问题。这个应该是最好的答案
    
    ex +'/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect huggingface.co:443) -scq >  huggingface.crt
    
    实际上,有很多更好的答案说到SNI和非SNI的处理,但是对于我来说,我甚至都懒得考虑certificate chain要取多少个的问题。总之,得到对方的证书是要使用的:
    
    sudo cp huggingface.crt /usr/local/share/ca-certificates/
    sudo update-ca-certificates
    
    然后,遇到了下载controlNet的组件的问题,比如
    https://huggingface.co/lllyasviel/Annotators/resolve/main/body_pose_model.pth
    这个位置不对了,需要手动到
    https://huggingface.co/lllyasviel/ControlNet-v1-1/tree/main
    去下载那些body_pose_model.pth, hand_pose_model.pthfacenet.pth,把它们拷贝到
    
    extensions/sd-webui-controlnet/annotator/downloads/openpos
    下去。 但是这一切做完我发现Openpose的人物畸形的很多,我的negative prompt也许不够多吧?我找到这些,结果还是不行,即便换成Canny的模型也是一样,而且,我也尝试换其他的基础模型,总之,效果让人很失望。
  6. 有一种可能是我选取的模型不对,比如这个是作者的公式
    PreprocessorModel
    depth_xxxxcontrol_xxxx_depth
    lineart_xxxxcontrol_xxxx_lineart
    openpose_xxxxcontrol_xxxx_openpose

二月一日 等待变化等待机会

  1. 这篇教程的关于openpose的描述是准确的。
  2. 这个dw_pose的论文我先保存一个拷贝。论文的好处在于它有很多引用是非常经典的学习途径,当然这个是对于专业研究者来说,对于门外汉来说知道标题甚至概述就可以拿出去唬人了。
  3. 其实选择一个好的模型是至关重要的,我看到模型也是互相引用或者说融合的吧,也有剔除等等。
    1. 这个Protogen提到了很多其他模型,我决定一个个来尝试一下,作者所说的keywords难道是各个融合的模型吗?比如(modelshoot style)?
    2. 比如这个看起来是模特的modelshoot
    3. 然后这个 photoreal也下载一下。
    4. openjourney
    5. robo-diffusion
    6. RPG模型出乎我的意料,它包含了好几个我都已经脸熟的好几个模型看来它不是模型提供者,只不过是概念提供者:Recaptioning, Planning, and Generating with Multimodal LLMs
    7. 这个sci-fi科幻还是很让人期待的。 这个是官方的咒语
      • Sci-Fi
      • caspian Sci-Fi
      • Star Citizen
      • Star Atlas
      • Spaceship
      • Render
    8. 这个SynthwavePunk自己的介绍就引出了另外两个它的来源模型:synthwave和punk,这里是原始的模型
    9. 这个就是Synthwave
    10. 这个是comic-diffusion 它说的六个艺术家是关键字吗?我对于训练还是一无所知无法理解。
      • charliebo artstyle
      • holliemengert artstyle
      • marioalberti artstyle
      • pepelarraz artstyle
      • andreasrocha artstyle
      • jamesdaly artstyle
    11. 这个是moistMix V2,还有V1的版本
    12. 我现在回过头来看,很多模型是在最基本的模型上变化而来的,我是否应该从头来看看这些鼻祖模型呢? 这里对于模型的介绍我还是要反复来理解:
      Custom checkpoint models are made with (1) additional training and (2) Dreambooth. They both start with a base model like Stable Diffusion v1.5 or XL.
      • Additional training is achieved by training a base model with an additional dataset you are interested in. For example, you can train the Stable Diffusion v1.5 with an additional dataset of vintage cars to bias the aesthetic of cars towards the vintage sub-genre.
      • Dreambooth, developed by Google, is a technique to inject custom subjects into text-to-image models. It works with as few as 3-5 custom images. You can take a few pictures of yourself and use Dreambooth to put yourself into the model. A model trained with Dreambooth requires a special keyword to condition the model.
      • The checkpoint model is not the only model type. We also have textual inversion (also called embedding), LoRA, LyCORIS, and hypernetwork.
    13. 这个是runwayml的stable-diffusion-v1-5,也许这个是最早的?
    14. 这个是stabilityAI公司的stable-diffusion-2-1
    15. 很多模型似乎不是官方发布的,比如这个看上去似乎是OpenDalle,然后我就找到了这个看上去是官方的。结果就发现模型都存放在这里我还是天真了,既然模型这么重要,当然不大可能都是免费的,这些官方的模型居然是要收费的。 不过这个模型的质量真的不错,这个图我很喜欢,用的是这个prompt: photo of young woman, Star Citizen, standing in front of a spaceship, staring forward, (Star Wars), face camera,(sci-fi style)
    16. 这个是微软的phi-2模型下载来试一试吧。
    17. 我发现在huggingface.co上找模型仅仅依靠关键字并不容易,至少不准确,谁都可以作模型。
    18. 我现在另一个苦恼就是那些咒语,我不知道每个模型所特有的关键字,否则什么模型都是差不多的。
  4. 这个是safetensors的格式。这个我还是看不太懂。这个有什么看不懂的呢?读出前8个byte就是头部的大小。
    
    $>od -N 8 -t d8 3dAnimationDiffusion_v10.safetensors
    0000000               154656
    0000010
    
    所以,头文件的大小是154656,我们可以打印到文件
    dd if=3dAnimationDiffusion_v10.safetensors of=/tmp/3dAnimationDiffusion_v10.txt bs=1 skip=8 count=154656
    文件里究竟是什么结构呢?头文件都是json的结构块。
    
    ...
    "model.diffusion_model.output_blocks.7.1.norm.weight":{"dtype":"F16","shape":[640],"data_offsets":[2049025648,2049026928]},"model.diffusion_model.output_blocks.7.1.proj_in.bias":{"dtype":"F16","shape":[640],"data_offsets":[2049026928,2049028208]},"model.diffusion_model.output_blocks.7.1.proj_in.weight":{"dtype":"F16","shape":[640,640,1,1],
    ...
    
    简言之: "tensor_name":{"dtype":"data type name", "shape":[num1,num2,...], "data_offsets":[begin,end]}
  5. 这里是一个所谓的negative prompt的东西,但是我还没有理解,难道图形也可以作为negative prompt?
    Many AI image generators, including Stable Diffusion, can use an image as a prompt to generate a similar image. On the other hand, we use text prompts to describe what we want and negative prompts to describe what we don’t want. How about a negative image prompt?
    还真的是这个作法!
  6. 我看到了ComfyUI,难道我应该尝试一下?它和webui有什么不同呢? 其实安装的开销不大,因为我已经有了好的环境不需要再次安装了。
    
    source ../stable-diffusion-webui/venv/bin/activate
    python main.py
    
    至于模型,我在models/checkpoints下建立软链接。
    
    ln -s ~/workspace/stable-diffusion-webui/models/Stable-diffusion/OpenDalleV1.1.safetensors .
    
    c 说不定我以后要在我的NAS上存储这些巨型模型吧?ComfyUI的确是非常绚丽,很棒!它给你看到整个流水线的流程!
  7. 昨天买的黑群晖到货了,看来我是小看了这些国内的年轻人,非常的有技术含量,因为那个引导u盘为什么要重启一次呢?从它的输出说是生成启动引导文件,给人一种动态产生的感觉。这个我的猜想是第一次启动得到真实的硬件信息或者驱动,第二次要适配群晖的硬件要求吧?这里的一些资料可以慢慢看吧?

二月二日 等待变化等待机会

  1. 这里总结webui和ConfyUI的对比。 首先,再明确一下模型的位置:
    model typemodel path
    Checkpointstable-diffusion-webui/models/Stable-diffusion
    VAEstable-diffusion-webui/models/VAE
    LoRAstable-diffusion-webui/models/Lora
    LyCORISstable-diffusion-webui/models/LyCORIS
    Embeddingsstable-diffusion-webui/embeddings
    Hypernetworksstable-diffusion-webui/hypernetworks
    Controlnetstable-diffusion-webui/ControlNet
    这里作者原来是想告诉我们一个webui和ComfyUI共享所有模型的方法: 编辑ComfyUI的extra_model_paths.yaml文件让它指向我们webUI的模型路径!这样子我连软链接都不用创建了!我一定要试一下!!!
  2. 如今还有人会被抄袭而苦恼吗?如果我看到了一幅画,我想模仿又不露痕迹,怎么办呢?很简单的,只要我使用img2img功能。

    原图

    模仿图

  3. 我有些看不懂,这个问题是需要img2txt,但是这个CLIP是否就做到了呢?我后来发现这个UI改变了很多,现在变成一个小小的button,我找了好久才找到。第一次运行CLIP它要下载很大的模型,所以,要耐心一些。
  4. 我运行CLIP但是初始化出了很多错误,我决定安装它作为extension来试一下。然后我遇到cdn-lfs.huggingface.co的certificate不对的问题,我怀疑这个和之前的问题类似。有一个时段我在想我是不是应该把对方的certificate chain都加入到我本机的信任的证书里。我发现有人和我有相似的问题。这是一个复杂的问题,复杂的问题没有简单的答案! 因为问题复杂,我连答案都看不 大懂。不过我的领悟是:我的问题和他的问题不太一样,他问的是为什么要certficate chain,而我要的是如果我信任了最末端的我还需要信任它的上级吗?或者说我因为某种原因降低了我的警惕,我需要这么大费周张的把所有的证书都下载一遍 吗?其实这个问题的复杂就在于我实际上还是不理解为什么证书是有效的。我的电脑里难道没有这个www.digicert.com的根证书吗?似乎还真的没有
    
    nick@nick-sager:~/Downloads$ awk -v cmd='openssl x509 -noout -subject' '
        /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt | grep DigiCert
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Assured ID Root CA
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Assured ID Root G2
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Assured ID Root G3
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root G2
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root G3
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
    subject=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Trusted Root G4
    subject=C = US, O = "DigiCert, Inc.", CN = DigiCert TLS ECC P384 Root G5
    subject=C = US, O = "DigiCert, Inc.", CN = DigiCert TLS RSA4096 Root G5
    
    撇开中间证书不提,cdn-lfs.huggingface.co的末端证书的确有些不是那么的对头:
    
    nick@nick-sager:~/Downloads$ openssl x509 -noout -subject -in cdn-lfs.huggingface.crt 
    subject=C = US, ST = California, L = Menlo Park, O = "Meta Platforms, Inc.", CN = *.facebook.com
    
    为了验证我的想法,我们先拿微软的bing来作一个对比:
    
    nick@nick-sager:~/Downloads$ ex +'/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect www.bing.com:443) -scq | openssl x509 -noout -subject
    depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root G2
    verify return:1
    depth=1 C = US, O = Microsoft Corporation, CN = Microsoft Azure TLS Issuing CA 02
    verify return:1
    depth=0 C = US, ST = WA, L = Redmond, O = Microsoft Corporation, CN = www.bing.com
    verify return:1
    DONE
    subject=C = US, ST = WA, L = Redmond, O = Microsoft Corporation, CN = www.bing.com
    
    以下都是我在openvpn服务器端作的测试,我在我本地无法得到访问,这个似乎也是一直困扰我的DNS或者是其他什么问题,我始终都在探索中。。。也不是完全不能只是要等的非常非常久 也就是说CN就是你的域名,这样子用户才能相信你。至于说多域名的访问或者说支持SNI,那个就复杂了。再举一个例子
    
    openvpnas@ip-172-31-35-59:~$ ex +'/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect www.google.com:443) -scq | openssl x509 -noout -subject
    depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1
    verify return:1
    depth=1 C = US, O = Google Trust Services LLC, CN = GTS CA 1C3
    verify return:1
    depth=0 CN = www.google.com
    verify return:1
    DONE
    subject=CN = www.google.com
    
    看看huggingface.co
    
    openvpnas@ip-172-31-35-59:~$ ex +'/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect huggingface.co:443) -scq | openssl x509 -noout -subject
    depth=2 C = US, O = Amazon, CN = Amazon Root CA 1
    verify return:1
    depth=1 C = US, O = Amazon, CN = Amazon RSA 2048 M01
    verify return:1
    depth=0 CN = huggingface.co
    verify return:1
    DONE
    subject=CN = huggingface.co
    
    如果是cdn-lfs.huggingface.co似乎也是正确的
    
    openvpnas@ip-172-31-35-59:~$ ex +'/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect cdn-lfs.huggingface.co:443) -scq | openssl x509 -noout -subject
    depth=2 C = US, O = Amazon, CN = Amazon Root CA 1
    verify return:1
    depth=1 C = US, O = Amazon, CN = Amazon RSA 2048 M01
    verify return:1
    depth=0 CN = cdn-lfs.huggingface.co
    verify return:1
    DONE
    subject=CN = cdn-lfs.huggingface.co
    
    看起来在openvpn的服务器端是没有问题的。那么证书是没有问题的吧?难道仅仅是连接非常的缓慢导致的问题?
  5. 紧接着上面的问题,就是我要怎么确定问题是不是DNS造成的呢? 我首先比较了我本地和VPN server上的DNS的表现: 为什么会造成这个区别呢?是地域不同所以自然的就不同吗? 这里的回答相当的全面。

    This is because after connecting to a VPN with vpnc, it puts a line in /etc/resolv.conf so it looks like:

    # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
    #     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
    nameserver 1.2.3.4
    nameserver 127.0.0.1
    search MyDomain
    
    我在openvpn上看到类似的:
    
    nameserver 127.0.0.53
    options edns0 trust-ad
    search us-west-1.compute.internal
    
    所以VPNC修改了DNS的设置。 这里DNS-over-TLS讲到的是另一个高度的问题,我还是看不明白。
    If true all connections to the server will be encrypted. Note that this mode requires a DNS server that supports DNS-over-TLS and has a valid certificate. If the hostname was specified in DNS= by using the format address#server_name it is used to validate its certificate and also to enable Server Name Indication (SNI) when opening a TLS connection. Otherwise the certificate is checked against the server's IP. If the DNS server does not support DNS-over-TLS all DNS requests will fail. When set to opportunistic DNS request are attempted to send encrypted with DNS-over-TLS. If the DNS server does not support TLS, DNS-over-TLS is disabled. Note that this mode makes DNS-over-TLS vulnerable to "downgrade" attacks, where an attacker might be able to trigger a downgrade to non-encrypted mode by synthesizing a response that suggests DNS-over-TLS was not supported. If set to false, DNS lookups are send over UDP. If set to default, uses the system default.
    这个systemd-resolved似乎是我要找的。我太累了。记录一下,随后再找。

二月三日 等待变化等待机会

  1. 很多时候最困难的事情就是你不知道它是什么问题。就是说解决问题的最主要困难是理解问题是什么!首先我就是不理解我的问题究竟是什么,是 DNS的问题没错,但是究竟是DNS的服务有问题还是多个设备的不同的DNS服务器的问题,还是说我必须什么访问都要通过openvpn的DNS服务?这 个似乎不合理,因为国内网很多有优化,我不可能穿越到外网再回来。但是你怎么区分什么网址需要什么DNS服务?我打算先从最稳妥的做起,就是安装 ubuntu官方的openvpn的套件:openvpn-systemd-resolved 这里也始终是我不理解的,究竟DNS的服务是通过哪一个?systemd-resolved还是resolvectl,这个文件/etc/resolv.conf还 是起作用的?那么127.0.0.53是做什么用的?而且DNS的解析哪怕你设置对了,在实际运行过程中哪一方的返回快最终决定了实际使用的结果,甚至于 有时候你设置了正确的可是因为缓存的缘故不能立刻看到正确的结果导致你对于正确的设置产生怀疑?种种问题的最根本问题还是我不理解问题是什么。连最基本的 概念都没有。
    这个是dig的结果
    
    nick@nick-sager:~/workspace/DS918$ dig www.google.com
    
    ; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> www.google.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55053
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 9
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 65494
    ;; QUESTION SECTION:
    ;www.google.com.			IN	A
    
    ;; ANSWER SECTION:
    www.google.com.		227	IN	A	74.86.3.208
    
    ;; AUTHORITY SECTION:
    google.com.		227	IN	NS	ns3.google.com.
    google.com.		227	IN	NS	ns2.google.com.
    google.com.		227	IN	NS	ns1.google.com.
    google.com.		227	IN	NS	ns4.google.com.
    
    ;; ADDITIONAL SECTION:
    ns2.google.com.		227	IN	AAAA	2001:4860:4802:34::a
    ns1.google.com.		227	IN	A	216.239.32.10
    ns1.google.com.		227	IN	AAAA	2001:4860:4802:32::a
    ns2.google.com.		227	IN	A	216.239.34.10
    ns4.google.com.		227	IN	AAAA	2001:4860:4802:38::a
    ns4.google.com.		227	IN	A	216.239.38.10
    ns3.google.com.		227	IN	A	216.239.36.10
    ns3.google.com.		227	IN	AAAA	2001:4860:4802:36::a
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
    ;; WHEN: Sat Feb 03 05:29:10 +08 2024
    ;; MSG SIZE  rcvd: 307
    
    那么从头学习基本概念就是必由之路!最基本的DNS的常识我始终是一知半解。
    Record TypeRecord DefinitionRecord Function
    A record The A record is the most important DNS record type. The "A" in A record stands for "address." An A record shows the IP address for a specific hostname or domain. The A record only supports IPV4 addresses. The main use of A record is for IP address lookup. Using an A record, a web browser is able to load a website using the domain name. As a result, we can access websites on the internet without knowing their IP addresses. Another use of A record is in the domain name system-based blackhole list (DNSBL). Here, the A record is used to block mail from known spam sources.
    AAAA record AAAA record, just like A record, point to the IP address for a domain. However, this DNS record type is different in the sense that it points to IPV6 addresses. IPV6 is an upgrade over IPV4 as it offers more IP addresses. As a result, IPV6 solves the issue of running out of unique IP addresses. Usage of the AAAA record for DNS resolution has great potential because it uses IPV6, which is an improvement over IPV4. Also, as the internet keeps growing and we're running out of IPV4 addresses, the potential for AAAA records is high. AAAA records are used to resolve a domain name to the newer IPV6 protocol address.
    CNAME record CNAME—or, in full, "canonical name"—is a DNS record that points a domain name (an alias) to another domain. In a CNAME record, the alias doesn't point to an IP address. And the domain name that the alias points to is the canonical name. For example, the subdomain ng.example.com can point to example.com using CNAME. A practical example for the use of CNAME records is running multiple subdomains for different purposes on the same server. For example, we can use ftp.example.com for file transfer protocol (FTP) and serve webpages via www.example.com. We can then use a CNAME record to point both subdomains to example.com. The main domain example.com then points to the server's IP address using an A record. It's also possible to point a CNAME to another CNAME. However, doing so is inefficient and can lead to slow load speed and poor user experience.
    NS record A nameserver (NS) record specifies the authoritative DNS server for a domain. In other words, the NS record helps point to where internet applications like a web browser can find the IP address for a domain name. Usually, multiple nameservers are specified for a domain. For example, these could look like ns1.examplehostingprovider.com and ns2.examplehostingprovider.com. If you've purchased a web hosting service or set up a simple website, you probably received an email with nameserver details. Those nameservers, in simple terms, connect your domain name to the actual server your site is hosted on. The nameserver contains other DNS records for the domain like an A record and MX record.
    MX record A mail exchange (MX) record, is a DNS record type that shows where emails for a domain should be routed to. In other words, an MX record makes it possible to direct emails to a mail server. You can have multiple MX records for a single domain name. And what this means is that you can have backup email servers. With an MX record, it's possible to hand off emails to a dedicated email server. For example, you can decide to leave all the trouble of setting up webmail on a server you own to a specialized email provider. This comes with many benefits, including custom email clients for reading and sending emails, and improved security and spam filters. Also, you can use a service like Site24x7 to monitor and verify issues with the mail server your MX records point to.
    SOA record SOA stands for "start of authority." It's an important DNS record type that stores admin information about a domain. This information includes the email address of the admin and when the domain was last updated.
    TXT record TXT stands for "text," and this record type lets the owner of a domain store text values in the DNS. Several services use this record to verify ownership of a domain.
    PTR record A pointer (PTR) record provides a domain name for reverse lookup. It's the opposite of an A record as it provides the domain name linked to an IP address instead of the IP address for a domain.
    SRV record SRV stands for service, obviously. Using this DNS record type, it's possible to store the IP address and port for specific services.
    CERT record CERT stands for certificate, obviously. This record type stores public keys certificates.
    DCHID DHCP configuration record This DNS record type stores information related to dynamic host configuration protocol (DHCP).
    DNAME The full meaning of DNAME is "delegation name." This record type works very similarly to CNAME; however, it points all the subdomains for the alias to the canonical domain name. That is, pointing the DNAME for secondsite.com to example.com will also apply to staff.secondsite.com and any other subdomain.
  2. 即便你对于基本的DNS的结构有了初步了解,那么系统如何使用它又是一个大大的领域。单单这个systemd-resolved服务就是多么复杂的一个体系。而且是否所有的应用都是使用它呢?它和NetworkManager的关系是什么呢?应该它是服务的提供者吧。它又可以透过什么DBUS之类的接口,这个又是一个我长久以来不理解的领域。D-BUS看了无数遍依旧不明白它是什么。
  3. 我发现了一个新途径来学习,就是这个官方的openvpn-systemd-resolved包里的说明文档有很多非常有针对性的部分!首先,文档说明了它的目的,似乎就是我要的结果:
    This is a helper script designed to integrate OpenVPN with the `systemd-resolved` service via DBus instead of trying to override `/etc/resolv.conf`, or manipulate `systemd-networkd` configuration files.
    它的工作原理揭示了它必须要配合openvpn服务起来的时候,我作为客户端是否也应该这样子呢?
    Since systemd-229, the `systemd-resolved` service has an API available via DBus which allows directly setting the DNS configuration for a link. This script makes use of `busctl` from systemd to send DBus messages to `systemd-resolved` to update the DNS for the link created by OpenVPN.
    安装部分ubuntu都不需要操心,使用前提也说明了:OpenVPN 2.1 or greater,iproute2, and have at least version 229 of systemd。而且我已经明确知道系统是运行systemd-resolved.service服务的。然后文档提到了NSS,这个又是一个全新的领域,难道Name Service Switch又会横插一杠子?怎么能够集中统一呢?我的初步理解是NSS定义了一个解析的顺序或者说优先级。总之我看不懂,先不要管了。接下来是关于stub的:
    The `systemd-resolved` service (since systemd-231) also listens on `127.0.0.53` via the `lo` interface, providing a stub resolver which any client can call to request DNS, whether or not it uses the system libraries to resolve DNS, and you no longer have to worry about trying to manage your `/etc/resolv.conf` file.
    然后解释的是结果吗?
    This set up can be installed by linking to `stub-resolv.conf`
    
    ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
    
    我发现系统已经是这样子了。我看的头疼死了。歇一歇吧。 这个是更容易阅读的版本。

二月四日 等待变化等待机会

  1. 我依旧不得要领。准本回过头来看看是不是我的openvpn Client端有什么遗漏。
  2. 我尝试使用openvpn官方的版本而不是ubuntu自带的版本。但是下载代码居然有问题,就是说我安装了gpg key,但是访问https的package repo一直不行。
  3. 然后我试图直接从源码编译,这个是源码的repo。 结果使用https的git又被openssl reset connection,我不明白这个是不是运营商根据法规禁止还是我自己的问题,总之很多时候是交织在一起。而糟糕的是单单下载这个源码是不够的因为它依 赖很多子项目,唯一简单的做法是在git submodule update,所以,我只能去注册一个用户把我的ssh-key放在网站里走ssh clone的办法:
    
    git clone --recursive git@codeberg.org:OpenVPN/openvpn3-linux.git
    
    编译倒是小问题。然后使用起来遇到大问题,因为完全不是我想象的,它的功能似乎是锦上添花的可有可无。我决定放弃。
  4. 我无意中发现ping www.google.com在ipv4下好像还是正确的。而且我再次去验证我的DNS理论发现也许根本没有问题,比如我比较两个网卡得到的DNS结果似乎是一致的。比如我的tun0的ip是172.27.232.49
    
    dig @172.27.232.49 -q www.google.com
    
    这个结果和我本地的网卡是一致的。 然后我决定禁止我的ipV6
    
    sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
    sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
    sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
    
    然后似乎就正常了。
  5. 终于可以暂时摆脱网络的困扰,可以重回正轨。现在我使用两个不同的模式从输入图像来得到text prompt,似乎DeepBooru得到的这个范冰冰好一些:
    
    1girl, 3d, bare shoulders, blurry, blurry background, blurry foreground, bokeh, cosplay photo, depth of field, hand on own face, head rest, lips, lipstick, long hair, looking at viewer, makeup, photo \(medium\), photo background, photorealistic, realistic, red lips, smile, solo, upper body
    
    依赖这个prompt,使用dreamshaper的模型,我得到的相当准确的图像,至少穿着打扮姿态是狠准的: CLIP得到的是更加的简单:
    
    a woman sitting at a table with a laptop computer in her hand and a brick wall behind her, with a yellow light behind her, phuoc quan, Du Qiong, a character portrait, private press
    
    当然反向再从txt2img是差得更远了。这种interrogate应该也是一种近似吧?
  6. 现在回到当初的这个教程。作者说这个是ComfyUI的custom-node。要学习怎么安装。先明确一下定义是什么
    The IPAdapter are very powerful models for image-to-image conditioning. Given a reference image you can do variations augmented by text prompt, controlnets and masks. Think of it as a 1-image lora.
    安装 这里有两个encoder需要下载
    IPAdapter also needs the image encoders. You need the CLIP-ViT-H-14-laion2B-s32B-b79K and CLIP-ViT-bigG-14-laion2B-39B-b160k image encoders, you may already have them. If you don't, download them but be careful because the file name is the same! Rename them to something easy to remember and place them in the ComfyUI/models/clip_vision/ directory.
    这个表看样子很重要,因为牵涉到不同的模型和image-encoder的组合
    SD v. IPadapter Img encoder Notes
    v1.5 ip-adapter_sd15 ViT-H Basic model, average strength
    v1.5 ip-adapter_sd15_light ViT-H Light model, very light impact
    v1.5 ip-adapter-plus_sd15 ViT-H Plus model, very strong
    v1.5 ip-adapter-plus-face_sd15 ViT-H Face model, use only for faces
    v1.5 ip-adapter-full-face_sd15 ViT-H Strongher face model, not necessarily better
    v1.5 ip-adapter_sd15_vit-G ViT-bigG Base model trained with a bigG encoder
    SDXL ip-adapter_sdxl ViT-bigG Base SDXL model, mostly deprecated
    SDXL ip-adapter_sdxl_vit-h ViT-H New base SDXL model
    SDXL ip-adapter-plus_sdxl_vit-h ViT-H SDXL plus model, stronger
    SDXL ip-adapter-plus-face_sdxl_vit-h ViT-H SDXL face model
  7. 至少我突然明白了一个尽人皆知的东西,就是ComfyUI是可以非常灵活或者说可靠的定义的,因为它是用一个json文件来描述它的UI,也就是说很通用。所以,当作者说参考这些范例我才能明白ComfyUI有多么的强大好用。当然作者的范例里有硬代码了输入文件名是一个遗漏。
  8. 我对于negative image prompt不太理解了,因为我使用图像本身作为negative image prompt,结果会怎么样子呢?我看不懂。还是先从原理学习一下吧。
    1. A brief history of negative prompts

      Initially, diffusion-based AI image generators could generate random, high-quality images. But there was no way to control what you generate. It just generates images that resembles the training data.
    2. 然后这里就是很重要的部分:
      Then, classifier-free guidance came into play. It hijacks the attention layers to inject the text embeddings to the sampling steps. The model is then trained with image and caption pairs. When generating an image, the model steers the images toward the prompt and away from the random images.
    3. 这里一个个来学习吧:原来CFG就是Classifier-Free Guidance的缩写。首先,什么是Classifier-Guidance:
      Classifier guidance is a way to incorporate image labels in diffusion models. You can use a label to guide the diffusion process.
      这样子举例就容易懂了就是训练时候加标签来分类。这里又引出了CFS这个常见的参数
    4. The classifier guidance scale is a parameter for controlling how closely should the diffusion process follow the label.
      这里我们记录一下引用的最原始的论文。这是拷贝
    5. With high classifier guidance, the images produced by the diffusion model would be biased toward the extreme or unambiguous examples. If you ask the model for a cat, it will return an image that is unambiguously a cat and nothing else.
      也就是说CFS高的话,AI会选择最无争议的图。
    6. The classifier guidance scale controls how closely the guidance is followed. In the figure above, the sampling on the right has a higher classifier guidance scale than the one in the middle. In practice, this scale value is simply the multiplier to the drift term toward the data with that label.
      这里的drift term toward the data with that labe指的是什么参数呢?
    7. 那么什么是Classifier-free guidance呢?首先,它的产生的原因就是它是什么。
      Although classifier guidance achieved record-breaking performance, it needs an extra model to provide that guidance. This has presented some difficulties in training.
      当然不希望反反复复去训练,能不能在模型的基础上改进一下就行了呢?
    8. Classifier-free guidance, in its authors’ terms, is a way to achieve “classifier guidance without a classifier”. Instead of using class labels and a separate model for guidance, they proposed to use image captions and train a conditional diffusion model, exactly like the one we discussed in text-to-image.
      我觉得这段话就是核心要深刻领会。首先是不用模型来再训练改变模型,靠的是什么?condition吗?还有uncondition。原论文概要里这句话也非常的重要:
      Classifier guidance combines the score estimate of a diffusion model with the gradient of an image classifier and thereby requires training an image classifier separate from the diffusion model. It also raises the question of whether guidance can be performed without a classifier.
      这里都是关键点。难怪作者说classifier guidance without a classifier
    9. 我觉得要先回顾一下什么是text-conditioning,才能从本质上理解怎么才能做到classifier guidance without a classifier
      Tokenizer first converts each word in the prompt to a number called a token. Each token is then converted to a 768-value vector called embedding. The embeddings are then processed by the text transformer and are ready to be consumed by the noise predictor.
    10. 从一个概念引出一群的概念,什么是CLIP呢?
      The CLIP model was proposed in Learning Transferable Visual Models From Natural Language Supervision .
      从标题猜测是从基本模型直接转用到图形吗?梗概很难懂。看实际的说明吧。
      CLIP is a multi-modal vision and language model. It can be used for image-text similarity and for zero-shot image classification. CLIP uses a ViT like transformer to get visual features and a causal language model to get the text features. Both the text and visual features are then projected to a latent space with identical dimension. The dot product between the projected image and text features is then used as a similar score.
      这是需要好好阅读的核心定义。为什么使用dot product呢?两个向量的点乘的数学含义是什么?
      The dot product, also called scalar product, is a measure of how closely two vectors align, in terms of the directions they point.
      ,的确,这个就是计算两个向量的相似度,所以,这个就是使用点乘的意思。
    11. To feed images to the Transformer encoder, each image is split into a sequence of fixed-size non-overlapping patches, which are then linearly embedded. A [CLS] token is added to serve as representation of an entire image.
      所以,这个就是我们怎么计算图像的数字签名或者说特征值吧,一个个grid或者patch的向量。当然还有总的
      CLS stands for classification and its there to represent sentence-level classification.
      这里来理解一下什么是CLS token。
      In order to better understand the role of [CLS] let's recall that BERT model has been trained on 2 main tasks:
      1. Masked language modeling: some random words are masked with [MASK] token, the model learns to predict those words during training. For that task we need the [MASK] token.
      2. Next sentence prediction: given 2 sentences, the model learns to predict if the 2nd sentence is the real sentence, which follows the 1st sentence. For this task, we need another token, output of which will tell us how likely the current sentence is the next sentence of the 1st sentence. And here comes the [CLS]. You can think about the output of [CLS] as a probability.
    12. 补习一下概念
      CLIP, which stands for Contrastive Language-Image Pre-training, is a model for telling you how well a given image and a given text caption fit together. In training, it tries to maximize the “cosine similarity” between correct image-caption vector pairs, and minimize the similarity scores between all incorrect pairs.
    13. 补习英文
      The adjective contrastive means "showing the difference between two things when you compare them" — like a contrastive analysis of American and British English. To contrast two things is to think about how they are different.

二月五日 等待变化等待机会

  1. 可以说每一步都是概念,What Are SOTA DNNs
    State-of-the-art (SOTA) Deep Neural Networks (DNNs) are the best models you can employ for a specific task. A DNN can earn the SOTA label based on its accuracy, speed, or any other relevant metric. However, in many computer vision domains, there exists a trade-off among these metrics. In other words, you might have a DNN that is very fast, but its accuracy falls short. Conversely, there are DNNs with impressive accuracy metrics that lack the necessary latency or throughput across various tasks, such as image classification and object detection. In these domains, a DNN will be deemed SOTA if it delivers an optimal trade-off between the relevant metrics.
    而这句话里有多少缩写单词我看不懂:
    The metrics we usually use to compare and evaluate DNNs are accuracy, precision, recall, F1-score, IoU, and mAP.
  2. 这里又是两个新的概念CNN和ViT:
    Convolutional Neural Networks (CNNs) and Vision Transformers (ViTs) are architectures commonly used in computer vision, each using a unique method of processing visual data.
    • CNNs have been the cornerstone for image processing tasks. They employ convolutional layers to systematically scan images, initially detecting basic features like edges and progressively identifying more intricate patterns deeper in the network. Due to their structured approach, CNNs have excelled in tasks such as image classification and object detection.
    • ViTs introduce a novel approach to image analysis. Originating from transformer architectures initially developed for natural language processing, ViTs segment images into fixed-size patches and process them as sequences, not grids. Their inherent attention mechanisms enable them to discern relationships between various patches, capturing context and offering an interpretation distinct from CNNs. This innovative perspective by ViTs has enriched the computer vision domain, igniting extensive research into synergies between them and CNNs.
  3. CNN的图像识别分类工作原理其实是人皆公知
    In essence, CNNs methodically sift through an image, detect hierarchical features using convolutions, distill essential information, and employ dense layers to draw conclusions about the image’s content.
    那么ViT呢?
    In a nutshell, ViTs deconstruct an image into numerical representations, infuse it with spatial context, and harness the transformer’s capabilities to evaluate and classify the visual data.
    我对于ViT是如何保持其空间感知能力感到不解,这个具体是什么意思
    ViTs incorporate a positional embedding to each patch embedding, ensuring the model retains spatial awareness of each segment’s origin within the image.
    什么叫做positional embedding?是其中某一个?
  4. 还是要先学习transformer的基本概念。
    A transformer is a deep learning architecture based on the multi-head attention mechanism, proposed in a 2017 paper "Attention Is All You Need".
    Attention这个词反复出现 这个流程似乎是很多文章里描述的,但是我依旧不理解。这篇论文保存一下。顺便说一下,这个人尽皆知的缩写的意思: generative pre-trained transformers (GPT)。论文是看不懂的,但是里面的名词是有印象的。这就是进步。
  5. 很多时候我不确定这个是论文的机制吗?
    Transformers provides APIs and tools to easily download and train state-of-the-art pretrained models. Using pretrained models can reduce your compute costs, carbon footprint, and save you the time and resources required to train a model from scratch.
    或者说我看到的论文是训练的机制,那么这里是使用GPT,机制当然是不同的。
  6. 理论往往并不能解答疑问,反而带来更多的疑问。也许只有实践能够在实践中回答疑问。
  7. 这段话的理解也是一个关键。什么叫做conditioned with negative prompt
    The idea was simple: Instead of steering away from a random image, you steer away from the images described by the negative prompt. Technically, you only need to replace the unconditioned latent image with the one that’s conditioned with the negative prompt.
    这幅图是很容易理解,但是什么叫做unconditional sampling with negative prompt?这里为什么是unconditional
  8. 这里又解释了一遍,我依然没有懂:
    The technique for enabling the negative prompt can be applied to images. We encode the negative image to an embedding and inject it into the sampling process of the “unconditioned” latent.
    因为这里的unconditioned latent我不明所以。不过我实验的结果并不满意,也许是模型使用的问题,也许是参数输入的问题。总之,我决定继续。
  9. 这个关于stable-diffusion的模型介绍还不错。虽然已经是一年多前的旧文,对于我来说还是学习的材料。
    ...this text encoder is a special Transformer language model (technically: the text encoder of a CLIP model). It takes the input text and outputs a list of numbers representing each word/token in the text (a vector per token).
    这个和我看的论文是相符合的。
    • Image information creator

      This component runs for multiple steps to generate image information. This is the steps parameter in Stable Diffusion interfaces and libraries which often defaults to 50 or 100. The image information creator works completely in the image information space (or latent space). The word “diffusion” describes what happens in this component. It is the step by step processing of information that leads to a high-quality image being generated in the end (by the next component, the image decoder).
    • Image Decoder

      The image decoder paints a picture from the information it got from the information creator. It runs only once at the end of the process to produce the final pixel image.
    这里是总的概述

    With this we come to see the three main components (each with its own neural network) that make up Stable Diffusion:

    • ClipText for text encoding.
      Input: text.
      Output: 77 token embeddings vectors, each in 768 dimensions.

    • UNet + Scheduler to gradually process/diffuse information in the information (latent) space.
      Input: text embeddings and a starting multi-dimensional array (structured lists of numbers, also called a tensor) made up of noise.
      Output: A processed information array

    • Autoencoder Decoder that paints the final image using the processed information array.
      Input: The processed information array (dimensions: (4,64,64))
      Output: The resulting image (dimensions: (3, 512, 512) which are (red/green/blue, width, height))

  10. 又要补习英语,这个tensor究竟要怎么理解,中文翻译是张量,可是我读这个定义感觉它就是影射,或者是一个高级函数?
    In mathematics, a tensor is an algebraic object that describes a multilinear relationship between sets of algebraic objects related to a vector space. Tensors may map between different objects such as vectors, scalars, and even other tensors. There are many types of tensors, including scalars and vectors (which are the simplest tensors), dual vectors, multilinear maps between vector spaces, and even some operations such as the dot product. Tensors are defined independent of any basis, although they are often referred to by their components in a basis related to a particular coordinate system; those components form an array, which can be thought of as a high-dimensional matrix.
  11. 这个是灵魂拷问:

    What is Diffusion Anyway?

    Diffusion is the process that takes place inside the pink “image information creator” component. Having the token embeddings that represent the input text, and a random starting image information array (these are also called latents), the process produces an information array that the image decoder uses to paint the final image.

    如果使用了这么长时间我不能回答这个问题,我就是白学了。 可以搞笑的回答,diffusion就像是在炒菜,把一堆输入的文字配合一系列的调味品炒出一锅菜。这个当然是向大妈大爷们解释的方式了。
  12. 好累啊。休息一下啊。

二月七日 等待变化等待机会

  1. 这篇启蒙文章确实非常的棒,不仅翔实,而且图文并茂,并且有大量的引用论文。这里就是一篇我认为挺重要的关于latent diffusion论文。对了论文的标题应该是很多人熟悉的:Departure to Latent Space
  2. 这个latent diffusion项目有一个所谓的model zoo,可以使用它的脚本下载。我现在对于DM和LDM的异同还一窍不通,不妨先下载来看看。放着吧。
  3. 再补习一下英语,到底latent space是什么定义呢? 这个是原文定义:
    A latent space, also known as a latent feature space or embedding space, is an embedding of a set of items within a manifold in which items resembling each other are positioned closer to one another. Position within the latent space can be viewed as being defined by a set of latent variables that emerge from the resemblances from the objects.
    说来惭愧,英文定义我也看的不是很明白。对照一些中文版本来更准确的理解吧:
    潜空间(Latent Space)也被称为潜特征空间嵌入空间,是一组元素在流形内的嵌入,相似的元素在潜空间内的距离较小。潜空间中的位置由一组潜变量定义,这些潜变量产生于元素间的相似性。
    这个概念之所以重要就是因为latent space并不一定和feature space有相同的dimension,通常是低维的,而降维打击在这里形成了所谓的数据压缩。这个形象非常的重要,因为只有理解了这一点才能理解为什么之前浙大的那篇论文他们能够通过平面图脑补三维模型,因为在模型的创建的根本意义就在于数据的压缩和提纯,否则模型如果是简单的客观世界的直接反映那就失去了建立模型的根本意义,没有对客观世界的数据压缩就不算机器学习,学习的过程就是一个降维打击的过程或者说数据压缩的过程。如果没有领悟这个最最浅显的基本道理,就不要再学习机器学习了,因为机器都比你懂学习!

    但是这个说法其实也有一种自相矛盾的悖论的感觉。本身我们看到的二维图像难道其本质是高维的吗?在图像识别的意义(semantical)层面肯定 是的。就连人们输入的text prompt看起来是一维的字符串在意义层面也要高维来解释。而latent space的一个做法是把它映射到二维图像,这个当然不是从二维到二维的简单映射,这个是我一时犯糊涂才会这么想。

    从一个空间到另一个空间的映射经过了从低维到高维,又从高维到低维的复杂的转换,这个也许就是transformer吧?总之像极了那句咒语:From light to darkness; From darkness, light!

    模型的另一重意义在于它的可复用性,否则也是没有意义的。也就是论文里说的:训练一次,反复使用。

    A notable advantage of this approach is that we need to train the universal autoencoding stage only once and can therefore reuse it for multiple DM trainings or to explore possibly completely different tasks.
  4. 明白了Markov Chain也就明白了DM为什么是一个概率模型,而这个在我看来就是一系列的条件概率,也许这个conditioning就是conditional probability的condition。
    A Markov process is a stochastic process that satisfies the Markov property[1] (sometimes characterized as "memorylessness"). In simpler terms, it is a process for which predictions can be made regarding future outcomes based solely on its present state and—most importantly—such predictions are just as good as the ones that could be made knowing the process's full history.

    在我看来Markov Chain的特点有一点点类比于Context-Free-Language(CFG),至少从上下文无关的 条件概率的角度来看,只不过CFG的概率只能是0或者100。而推而广之,也许世界上都是CFG,只不过有些Context Sensitive的语言的上下文相当的大,而且要打破某种递归循环导致的无限性。总而言之,condiitional和unconditional也许 也不过是某种准确性与性能可接受的准确度的妥协的结果,在概率模型下,只要准确度超过一定临界值它的上下文或者条件就可以放宽。

    那么概率模型是否注定不是一个准确的模型呢?人类的罗辑的无模糊性是否天生就和人类自然学习过程的概率模型不兼容?或者说机器学习模仿人类的学习过程反而 从一开始就背离了无含糊余地的罗辑推理模型。也许从概率模型进行归纳的过程从而最终上升到罗辑推理模型是一个本质的飞跃,而不是一个量变的简单积累。这个 是几十年后才会面对的问题,人类现在不需要操心。
  5. 那么与概率模型相对应的模型注意力机制模型(Attention-Model)。
    Machine learning-based attention is a mechanism which intuitively mimicks cognitive attention. It calculates "soft" weights for each word, more precisely for its embedding, in the context window. These weights can be computed either in parallel (such as in transformers) or sequentially (such as recurrent neural networks). "Soft" weights can change during each runtime, in contrast to "hard" weights, which are (pre-)trained and fine-tuned and remain frozen afterwards.
    是否可以通俗的理解就是赋予不同部分不同的重要性,事实上就是根据权值来取舍,这个是一种很粗暴的压缩方式,直接忽略。简单粗暴导致了质量的不稳定。因为 不明白注意力的规律,硬权值往往不可靠便改成了软权值,然而软权值像极了DM这种条件概率模型的方式,只不过怎么产生软权值永远是最核心的部分:怎么实现 才是最重要的,因为人人都明白要什么。

    Attention的机制实际上是模仿人类的语言理解过程,在《Yes, (Prime) Minister》里说普通人只能一次理解一个句子,因为长句子导致听众在听了后半截就已经忘了前半段。这个就是现代普通人的注意力机制,尤其是妇女它们 的注意力不能保持三秒。从这个角度来看Attention的机制可以看作是一个简单的上下文机制。线性递减。

  6. 反复看到这个概念词autoencoder
    An autoencoder is a type of artificial neural network used to learn efficient codings of unlabeled data (unsupervised learning). An autoencoder learns two functions: an encoding function that transforms the input data, and a decoding function that recreates the input data from the encoded representation. The autoencoder learns an efficient representation (encoding) for a set of data, typically for dimensionality reduction.
    然后数学部分我就看不懂了,只明白这一句话:An autoencoder, by itself, is simply a tuple of two functions.
  7. 很明显的,wiki太过于专业,很难懂因为严谨学术。这里有一个有趣的关于CLIP训练的方式。
    CLIP is trained on a dataset of images and their captions. In actuality, CLIP was trained on images crawled from the web along with their “alt” tags.
    这个是我没有想到的,原来利用了alttag。
    CLIP is a combination of an image encoder and a text encoder. Its training process can be simplified to thinking of taking an image and its caption. We encode them both with the image and text encoders respectively. We then compare the resulting embeddings using cosine similarity.
    这个所谓的cosine similarity应该指的就是计算text vector和image vector的dot product。
  8. 我想尝试一下这个latent diffusion,结果发现我的显卡显存不足。之前还遇到ldm版本不对的问题,我的解决方法似乎不太一样,我是使用python3而不是python命令来解决的:
    
    python3 scripts/knn2img.py  --prompt "a happy bear reading a newspaper, oil on canvas"
    
    但是使用小尺寸
    --W 256 --H 256
    GPU显存不足的问题依旧不能解决,这个帖子有很多建议可以去尝试。

二月八日 等待变化等待机会

  1. 我才突然意识到革命不是瞬间发生的,AI的火种是好多年的积累,那么这个大模型最初是在最容易突破的自然语言处理(NLP)发生的。所以,要读一下这个简介BERT。刚开了个头就被打断了。 这个是训练的两个步骤:半监督学习和监督学习过程。使用wiki作为学习资料看样子是非常有意义的,因为这个可以说是最最完整规范的文字图片材料集散地。视频和图像就不大有类似,也许Youtube?
  2. 我成了论文的收藏家,基本也除了前言看得懂其他都不懂。
    BERT is basically a trained Transformer Encoder stack.
    这个难道是我唯一看得懂的吗?看来我需要先理解它的基础transformer再强调一遍:CLS here stands for Classification.
  3. 这位大师有很多文章,不过依然太专业了我看不懂。

二月九日 等待变化等待机会

  1. 我放弃读论文的前言了,因为即使是梗概和简介部分也是挺不容易的。需要大量的知识储备,最大的作用也许是熟悉一些常见的名词,至少我现在理解什么是SOTA是什么东东了。所以,回过头来看也许这个一步步讲解也许是适合我的水平。我准备今天就集中注意力在这个简介上,至少它引入了一个个概念与步骤。
  2. 开宗明义关于stable-diffusion能做什么和它的模型的简介这部分我这几天算是基本明白了,可以通过了温故而知新,我还是发现很有意义与帮助的
    Stable Diffusion belongs to a class of deep learning models called diffusion models. They are generative models, meaning they are designed to generate new data similar to what they have seen in training. In the case of Stable Diffusion, the data are images.
    这里很有趣的是指出了名字的由来:Why is it called the diffusion model? Because its math looks very much like diffusion in physics.
  3. 魔鬼在细节,每当我庆幸自己学到了什么就发现我几乎还是一无所知,比如什么是forward diffusion?我似乎看到过这个描述的过程,但是没有这么讲解我真的没有理解。
    A forward diffusion process adds noise to a training image, gradually turning it into an uncharacteristic noise image. The forward process will turn any cat or dog image into a noise image. Eventually, you won’t be able to tell whether they are initially a dog or a cat. (This is important)
    为什么要在训练好的模型里添加噪音呢?我模糊记得论文提到了不同类型的噪音,但是这个用意我却没有理解,你把你清晰的印象掺沙子让你的印象模糊起来,这样子才能经受住未来的考验?彷佛令狐冲学习独孤九剑的 剑法之后,风轻扬大师问他忘记了多少,最后以至于完全忘记了才能无招胜有招,因为从具体到抽象的过程似乎就是一个逐渐遗忘或者说忽略细节存留根本的过程, 也许遗忘不是最好的提取方法,但是往往最本质的特征之所以是最根本的特征就在于它的抗干扰最强,所以,我们掺沙子就以为着我们希望我们的模型能够举一反三抓住模型的根本特征,而不是简单的猫有四条腿,狗有四条腿,所以,狗就是猫的形而上学式的学习。
  4. 如果我们的模型被噪音淹没而无法提取那么我们就是在作无用功,这reverse diffusion才是目的与关键。
    Technically, every diffusion process has two parts: (1) drift and (2) random motion. The reverse diffusion drifts towards either cat OR dog images but nothing in between. That’s why the result can either be a cat or a dog.
    这句话很关键,但是drift and random这两个是什么含义呢?它的效果我是理解了,这个逆向过程一定是一个明确的答案,是或者不是,已经没有概率的问题了,就是你的根本的特征值是被绛帷了,失去了一些额外的细节导致你可以抓住事物最根本的属性,而这个成为判断的最有力的证据,所以,你的答案是斩钉截铁的。
  5. 理解为什么和什么也许是最重要的,可是成功的源头在于怎么做!因为魔鬼依旧在细节,即便Most ones know the path, but only few can actually walk the path.
    To reverse the diffusion, we need to know how much noise is added to an image. The answer is teaching a neural network model to predict the noise added. It is called the noise predictor in Stable Diffusion. It is a U-Net model.
    这里的细节就是加噪音不是随便的,否则连小孩子都会掺沙子,关键是加噪音的时候要训练预测噪音,这一点就很高深了。
    1. Pick a training image, like a photo of a cat.
    2. Generate a random noise image.
    3. Corrupt the training image by adding this noisy image up to a certain number of steps.
    4. Teach the noise predictor to tell us how much noise was added. This is done by tuning its weights and showing it the correct answer.
    这个过程在我看来似乎是说我们人类的记忆遗忘并不是线性或者随机的,遗忘的过程是有模板特征的,如果我们能够学习怎么遗忘那么我们就能学会怎样回忆。如果 我们能够学会从零星的记忆碎片重新拼凑出原始的记忆,我们甚至于根本不需要记忆那么多的细节,这个就是压缩数据的高超的做法,对于有规律的数据我们用科学 的算法进行冗余数据归类式的无损压缩,但是对于无规律的记忆数据我们模仿记忆模糊的规律来复原最初的记忆从而起到了如梦境般的效果,似幻似真就是太虚幻境,也就是梦境,那么我们能不能把stable diffusion称作梦工厂呢?
  6. 这里我并行的要尝试安装anaconda,这个是具体的安装指南。我模糊记得去年我遇到conda总是有很多冲突,但愿不要把我安装的nvidia的驱动又搞乱了。
    
    If you'd prefer that conda's base environment not be activated on startup,
       run the following command when conda is activated:
    
    conda config --set auto_activate_base false
    
    You can undo this by running `conda init --reverse bash`?
    
    我想起来了,这个就是我讨厌conda的地方,当时它搞乱了系统我不得不卸载。看来我可以不用卸载而不用它。
    
    You have chosen to not have conda modify your shell scripts at all.
    To activate conda's base environment in your current shell session:
    
    eval "$(/home/nick/anaconda3/bin/conda shell.bash hook)" 
    
    To install conda's shell functions for easier access, first activate, then:
    
    conda init
    
    这个是更加稳妥的做法。就是说使用conda先运行conda init,它会改.bashrc,然后重启console,应该可以source .bashrc吧?难说?用完了可以conda init --reverse bash复原.bashrc。手动修改我感觉既麻烦也不好。anaconda的初始化下载进度条作的很好看!
    
    numpy-1.19.2         | 10 KB     | ############################################################# | 100% 
    ca-certificates-2023 | 126 KB    | ############################################################# | 100% 
    intel-openmp-2021.4. | 4.2 MB    | ############################################################# | 100% 
    mkl-service-2.4.0    | 59 KB     | ############################################################# | 100% 
    torchvision-0.8.1    | 17.9 MB   | ############################################################# | 100% 
    libffi-3.3           | 50 KB     | ############################################################# | 100% 
    wheel-0.41.2         | 108 KB    | ############################################################# | 100% 
    libdeflate-1.17      | 64 KB     | ############################################################# | 100% 
    pip-20.3.3           | 1.8 MB    | ############################################################# | 100% 
    libuv-1.44.2         | 864 KB    | ############################################################# | 100% 
    mkl-2021.4.0         | 142.6 MB  | ############################################################# | 100% 
    mkl_random-1.2.2     | 308 KB    | ############################################################# | 100% 
    cudatoolkit-11.0.221 | 622.9 MB  | #####################################################7        |  88% 
    mkl_fft-1.3.1        | 180 KB    | ############################################################# | 100% 
    typing_extensions-4. | 54 KB     | ############################################################# | 100% 
    pytorch-1.7.0        | 663.0 MB  | #############################################1                |  74% 
    python-3.8.5         | 49.3 MB   | ############################################################# | 100% 
    setuptools-68.2.2    | 948 KB    | ############################################################# | 100% 
    ninja-1.10.2         | 8 KB      | ############################################################# | 100% 
    xz-5.4.5             | 646 KB    | ############################################################# | 100% 
    numpy-base-1.19.2    | 10.1 MB   | ############################################################# | 100% 
     ... (more hidden) ...
    
    使用之前要初始化环境:
    
    conda env create -f environment.yaml
    conda activate ldm
    
    的确像我担心的那样子,conda的环境似乎不行,总是抱怨一些库没有安装。我还是不要冒险使用了。

二月十日 等待变化等待机会

  1. 至少我们现在知道stable-diffusion的的训练项目就是得到noise predictor的对于噪音的估计。
    After training, we have a noise predictor capable of estimating the noise added to an image.
    很多的技术核心都是在训练中,否则也不可能有这些年的突破。这里有关于sampler的文章,可以接下去阅读。
  2. 一个大转折是作者打得埋伏,这个实际上不是真的stable-diffusion,因为这个发生在image空间,它很慢需要大量的资源。对于512x512的图片如果是rgb三原色,那么纬度是怎么计算的?就是简单的512x512x3=786432。 这也难怪太大了,而之前提到相应的标签的语言的纬度要和图片一样才能进行点乘吧?所以,语言的纬度肯定不可能有这么大,那么图像的纬度要降低才行吧?所以,这里才引出了我昨天看的latent diffusion。这个是怎么做到的呢?
    Stable Diffusion is a latent diffusion model. Instead of operating in the high-dimensional image space, it first compresses the image into the latent space. The latent space is 48 times smaller so it reaps the benefit of crunching a lot fewer numbers. That’s why it’s a lot faster.
    就是说压缩了48倍,为什么是48?
  3. 这里的核心是另一个大神,Variational AutoEncoder(VAE)。只闻其名,不明其所以然。既然是autoencoder自然就包含了encoder和decoder。 这里的核心自然是latent space,它是一个什么样子的空间?
    So during training, instead of generating a noisy image, it generates a random tensor in latent space (latent noise). Instead of corrupting an image with noise, it corrupts the representation of the image in latent space with the latent noise. The reason for doing that is it is a lot faster since the latent space is smaller.
    核心依旧是latent-tensor,我的英文的理解始终卡在这些基本的概念上,tensor是向量的更高阶的物件吗?否则它怎么能够代替或者说压缩了 image space的向量呢?也许它就是图形里的所谓的特征值,就是你把图形的512x512x3这个纬度划分出一个个区块,比如一个区块全是空白自然没有必要罗 列那些纬度,这个和压缩是一个原理,只不过实现的方式是不一样吧?
  4. The image resolution is reflected in the size of the latent image tensor. The size of the latent image is 4x64x64 for 512×512 images only. It is 4x96x64 for a 768×512 portrait image. That’s why it takes longer and more VRAM to generate a larger image.
    读到这里我以为这个想法并没有那么难的想到,为什么是48?512/64=8,然后它的平方的3/4就是48,为什么是三通道的三原色改成了四通道?总之,我觉得一般人都能够想到用grid来代替最初的逐个像素,至少如同缩略图一样牺牲一些细节吧?
  5. 或许我很多年前就想到类似的embedding的概念,这个不是天上掉下来的,也不是什么灵光一线的神奇想法,但是具体实现就是最前沿的开拓者才有资格去做。当然这里最基本的问题是如果限制了图像的大小如何要产生大的图呢?这里有所谓的AI-Upscaler,这个也是下一步的阅读。另一个途径是image-to-image function for image upscaling看来大图也是可以拼起来的吧?或者不是拼,是某种放大?作者还提到了第三种途径就是使用大的训练的模型图SDXL模型
  6. 我在犹豫是不是有必要先过一遍SDXL呢?最后决定还是先继续把整个流程走完一遍再说。 因为这里的核心就是latent space,它是什么东西呢?这一句是提纲:Natural images are not random. The high dimensionality of images is artifactual.这里结论很重要,这里的压缩是无损的?
    Natural images can be readily compressed into the much smaller latent space without losing any information. This is called the manifold hypothesis in machine learning.
    第一句定义就让人哑口无言!
    The manifold hypothesis posits that many high-dimensional data sets that occur in the real world actually lie along low-dimensional latent manifolds inside that high-dimensional space.
    latent-manifold这是个什么东西?这个是embedding的定义:
    In mathematics, an embedding (or imbedding) is one instance of some mathematical structure contained within another instance, such as a group that is a subgroup.
    我对于拓扑学最最朴素的认识大概就是这个笑话:
    An often-repeated mathematical joke is that topologists cannot tell the difference between a coffee mug and a donut,[1] since a sufficiently pliable donut could be reshaped to the form of a coffee mug by creating a dimple and progressively enlarging it, while preserving the donut hole in the mug's handle. This illustrates that a coffee mug and a donut (torus) are homeomorphic.
  7. 很多的数学概念都是第一次接触,总是感觉自己的数学基础很差。只好绕过去。
    1. A random latent space matrix is generated.
    2. The noise predictor estimates the noise of the latent matrix.
    3. The estimated noise is then subtracted from the latent matrix.
    4. Steps 2 and 3 are repeated up to specific sampling steps.
    5. The decoder of VAE converts the latent matrix to the final image.
    这个过程堪比化腐朽为神奇,因为古人认为苍蝇蚊子是无中生有的,你能够从一堆噪音里经过提纯剥去噪音得到最终的隐藏的真相,这个简直就是奇迹。也许是从混乱到秩序的一个过程?
  8. VAE仅仅是用来提高眼睛和脸的吗?也许是人对于眼睛和脸的异常敏感我们需要额外的处理?默认的模型都是自带VAE的。但是有些模型说VAE-baked,就是说它需要自己的VAE文件,这一点我已经知道了。
    Stability AI released two variants of fine-tuned VAE decoders, EMA and MSE. (Exponential Moving Average and Mean Square Error are metrics for measuring how good the autoencoders are.)
    原来EMA-pruned是指的这个。
    EMA produces sharper images while MSE’s images are smoother.
    这个也许就够了。下载stabilityAI公司的EMA和MSE也许也是必要的。

    Download link for EMA VAE

    Download link for MSE VAE

    Compressing an image into the latent space does lose information since the original VAE did not recover the fine details. Instead, the VAE decoder is responsible for painting fine details.
    这一点还是很重要的,不要以为存在无损压缩这个事情。而VAE decoder是负责细节的。
  9. 这个是关于conditioning的非常精辟的描述:
    This is where conditioning comes in. The purpose of conditioning is to steer the noise predictor so that the predicted noise will give us what we want after subtracting from the image.
    这里先歇一下吧。

二月十一日 等待变化等待机会

  1. 对于text prompt的工作机制,我还是很意外的,我本来以为整个text prompt是转为一个embedding,但是似乎是每一个词都转为一个768-value的vector,这个768是什么来头?难道是 256x256x3代表了256边长的矩阵配合rgb三原色,我总是要它和图形的向量联系起来,否则怎么作点乘呢?
    Each token is then converted to a 768-value vector called embedding.
  2. The text prompt is first tokenized by a CLIP tokenizer. CLIP is a deep learning model developed by Open AI to produce text descriptions of any images. Stable Diffusion v1 uses CLIP’s tokenizer.
    这里的关键还是CLIP,我之前曾经读了一点它的介绍,结果还是忘了。
    CLIP (Contrastive Language-Image Pre-Training) is a neural network trained on a variety of (image, text) pairs. It can be instructed in natural language to predict the most relevant text snippet, given an image, without directly optimizing for the task, similarly to the zero-shot capabilities of GPT-2 and 3.
    这里让我吃惊的是它似乎是反过来的,就是给一幅图它去预测文字,当然这个确实是我在webui里使用的用途,但是难道它训练的时候不是反过来的吗?难道不是给文字训练匹配图像,或者是计算两个向量的点乘吗?使用是训练的逆过程? 这幅示意图是写实的吗?文字和图片的向量似乎是组合成了一个矩阵,难道要选择所有的组合对子?但是我上面的理解是每一个token都是一个embedding,如果理解不错的话这个是多少个token就是多少个向量要怎么。。。 至少我学习了CLIP的缩写,这个也许是唯一理解的东西。
    A tokenizer can only tokenize words it has seen during training.
    这里透露的是它纯粹就是一个字典,或者说就是一个记忆模型,难怪我当时使用哪吒的三头六臂作为输入,结果模型完全不理解。因为训练材料是事先有筛选的。
  3. 这里我决定先实践一下CLIP,这里的安装指南要求合适的CUDA版本,我都不知道我是怎么装的,装在哪里?这里的办法最好就是使用nvidia-smi来查找:
    
    nick@nick-sager:~/workspace$ nvidia-smi
    Sun Feb 11 05:05:06 2024       
    +---------------------------------------------------------------------------------------+
    | NVIDIA-SMI 545.23.08              Driver Version: 545.23.08    CUDA Version: 12.3     |
    |-----------------------------------------+----------------------+----------------------+
    | GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
    | Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
    |                                         |                      |               MIG M. |
    |=========================================+======================+======================|
    |   0  NVIDIA GeForce RTX 4050 ...    On  | 00000000:01:00.0 Off |                  N/A |
    | N/A   41C    P8               5W / 115W |    218MiB /  6141MiB |      0%      Default |
    |                                         |                      |                  N/A |
    +-----------------------------------------+----------------------+----------------------+
                                                                                             
    +---------------------------------------------------------------------------------------+
    | Processes:                                                                            |
    |  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
    |        ID   ID                                                             Usage      |
    |=======================================================================================|
    |    0   N/A  N/A    137163      G   /usr/lib/xorg/Xorg                            4MiB |
    |    0   N/A  N/A    186449      C   python3                                     192MiB |
    +---------------------------------------------------------------------------------------+
    
    所以,我的版本是CUDA Version: 12.3,我的GPU型号是NVIDIA GeForce RTX 4050
  4. 我从来也不愿意学习python,总觉得是小孩子的玩意,但是现在不得不使用,只好学习hello world之类的,我想知道CLIP究竟有哪些模型,所以:
    
    import torch
    import clip
    
    print("models:", clip.available_models())
    
    使用这个API,我得到的运行结果是
    
    models: ['RN50', 'RN101', 'RN50x4', 'RN50x16', 'RN50x64', 'ViT-B/32', 'ViT-B/16', 'ViT-L/14', 'ViT-L/14@336px']
    
    至少我可以 尝试比较一下这几个模型的优劣吧,使用这个测试代码似乎很容易,尽管我的python基础是0
    
    import torch
    import clip
    from PIL import Image
    
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model, preprocess = clip.load("ViT-B/32", device=device)
    
    image = preprocess(Image.open("CLIP.png")).unsqueeze(0).to(device)
    text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device)
    
    with torch.no_grad():
        image_features = model.encode_image(image)
        text_features = model.encode_text(text)
        
        logits_per_image, logits_per_text = model(image, text)
        probs = logits_per_image.softmax(dim=-1).cpu().numpy()
    
    print("Label probs:", probs)  # prints: [[0.9927937  0.00421068 0.00299572]]
    
    运行的结果如下:
    
    100%|████████████████████████████████████████| 338M/338M [07:53<00:00, 748kiB/s]
    Label probs: [[0.9927   0.004185 0.002968]]
    
    我不懂怎么下载模型,也不知道模型怎么保存,只好一个个模型尝试,就当是缓存了模型在本地。python的代码实际上是很容易理解的,基本猜出个八九不离十,它把这些模型都保存在本地的缓存出:~/.cache/clip/ 我决定统统下载这些模型
    
    _MODELS = {
        "RN50": "https://openaipublic.azureedge.net/clip/models/afeb0e10f9e5a86da6080e35cf09123aca3b358a0c3e3b6c78a7b63bc04b6762/RN50.pt",
        "RN101": "https://openaipublic.azureedge.net/clip/models/8fa8567bab74a42d41c5915025a8e4538c3bdbe8804a470a72f30b0d94fab599/RN101.pt",
        "RN50x4": "https://openaipublic.azureedge.net/clip/models/7e526bd135e493cef0776de27d5f42653e6b4c8bf9e0f653bb11773263205fdd/RN50x4.pt",
        "RN50x16": "https://openaipublic.azureedge.net/clip/models/52378b407f34354e150460fe41077663dd5b39c54cd0bfd2b27167a4a06ec9aa/RN50x16.pt",
        "RN50x64": "https://openaipublic.azureedge.net/clip/models/be1cfb55d75a9666199fb2206c106743da0f6468c9d327f3e0d0a543a9919d9c/RN50x64.pt",
        "ViT-B/32": "https://openaipublic.azureedge.net/clip/models/40d365715913c9da98579312b702a82c18be219cc2a73407c4526f58eba950af/ViT-B-32.pt",
        "ViT-B/16": "https://openaipublic.azureedge.net/clip/models/5806e77cd80f8b59890b7e101eabd078d9fb84e6937f9e85e4ecb61988df416f/ViT-B-16.pt",
        "ViT-L/14": "https://openaipublic.azureedge.net/clip/models/b8cca3fd41ae0c99ba7e8951adf17d267cdb84cd88be6f7c2e0eca1737a03836/ViT-L-14.pt",
        "ViT-L/14@336px": "https://openaipublic.azureedge.net/clip/models/3035c92b350959924f9f00213499208652fc7ea050643e8b385c2dac08641f02/ViT-L-14-336px.pt",
    }
    
    modela diagrama dog%a cat%
    RN500.9840.012980.003307
    RN1010.98930.006070.004726
    RN50x40.9720.018370.009384
    RN50x160.9050.084170.01087
    RN50x640.87740.094670.02798
    ViT-B/320.99270.0041850.002968
    ViT-B/160.76070.22840.01085
    ViT-L/140.94530.048550.005985
    ViT-L/14@336px0.9320.06290.00478
    除了这两个模型比较差以外其他似乎都还不错。对于这些API注解我觉得都是很初级的员工写的,反正开源的项目都是不挣钱的,比如这个怎么有乘以100呢?让我现在去理解这些算法还早的很,只不过我已经摸着了门径知道从哪里入手去找答案了。这就是我的进步。
  5. 总之,我们知道CLIP的确是有开放函数给你一幅图然后返回对应的text prompt的概率,也就是点乘的结果。不过这个能不能说给定多个图和一个text的结果呢?我不知道要怎么改代码,首先是这个图库很不错。
  6. 对于这个示例代码我不知道它下载的图片究竟是什么样子的,那么要怎么看看图片呢?我抄袭了一下这个代码。在原来代码基础上就是这么几行:
    
    import matplotlib.pyplot as plt
    
    # Prepare the inputs
    image, class_id = cifar100[3637]
    # we just show it with image loaded
    plt.imshow(image)
    plt.show()
    
    而对于普通的图片,本身就已经使用了这个库from PIL import Image 所以,可以直接调用它的显示函数。
    
    img = Image.open("my.png")
    img.show()
    
    我观察到另一个现象就是这些openAI的预训练模型非常的狭隘,就是说你给的text prompt超过两个词或者出现它不认识的token之类它就立刻崩溃了。它只能非常简单的识别诸如a man, a woman之类,但凡多一个形容词就不认识了。
  7. 我不妨尝试一下open-clip

二月十二日 等待变化等待机会

  1. 我对于clip的简单的实验得出一个简单的结论就是它还不是很稳定,比如,同样一幅图,让它测试看a cat, a dog, a diagram的标准表现的很不错,如果把a diagram换成a panda结果是毛和狗的比例都上升了,这个也许是有道理的,可是在我看来返回的总概率是100%本身这个做法就是强人所难,三个都不是怎么办?其次,就是如果我多几个text prompt,结果都不对了。
  2. open clip支持的模型多得多:
    
    import open_clip
    open_clip.list_pretrained()
    
    [('RN50', 'openai'), ('RN50', 'yfcc15m'), ('RN50', 'cc12m'), ('RN50-quickgelu', 'openai'), ('RN50-quickgelu', 'yfcc15m'), ('RN50-quickgelu', 'cc12m'), ('RN101', 'openai'), ('RN101', 'yfcc15m'), ('RN101-quickgelu', 'openai'), ('RN101-quickgelu', 'yfcc15m'), ('RN50x4', 'openai'), ('RN50x16', 'openai'), ('RN50x64', 'openai'), ('ViT-B-32', 'openai'), ('ViT-B-32', 'laion400m_e31'), ('ViT-B-32', 'laion400m_e32'), ('ViT-B-32', 'laion2b_e16'), ('ViT-B-32', 'laion2b_s34b_b79k'), ('ViT-B-32', 'datacomp_xl_s13b_b90k'), ('ViT-B-32', 'datacomp_m_s128m_b4k'), ('ViT-B-32', 'commonpool_m_clip_s128m_b4k'), ('ViT-B-32', 'commonpool_m_laion_s128m_b4k'), ('ViT-B-32', 'commonpool_m_image_s128m_b4k'), ('ViT-B-32', 'commonpool_m_text_s128m_b4k'), ('ViT-B-32', 'commonpool_m_basic_s128m_b4k'), ('ViT-B-32', 'commonpool_m_s128m_b4k'), ('ViT-B-32', 'datacomp_s_s13m_b4k'), ('ViT-B-32', 'commonpool_s_clip_s13m_b4k'), ('ViT-B-32', 'commonpool_s_laion_s13m_b4k'), ('ViT-B-32', 'commonpool_s_image_s13m_b4k'), ('ViT-B-32', 'commonpool_s_text_s13m_b4k'), ('ViT-B-32', 'commonpool_s_basic_s13m_b4k'), ('ViT-B-32', 'commonpool_s_s13m_b4k'), ('ViT-B-32-256', 'datacomp_s34b_b86k'), ('ViT-B-32-quickgelu', 'openai'), ('ViT-B-32-quickgelu', 'laion400m_e31'), ('ViT-B-32-quickgelu', 'laion400m_e32'), ('ViT-B-32-quickgelu', 'metaclip_400m'), ('ViT-B-32-quickgelu', 'metaclip_fullcc'), ('ViT-B-16', 'openai'), ('ViT-B-16', 'laion400m_e31'), ('ViT-B-16', 'laion400m_e32'), ('ViT-B-16', 'laion2b_s34b_b88k'), ('ViT-B-16', 'datacomp_xl_s13b_b90k'), ('ViT-B-16', 'datacomp_l_s1b_b8k'), ('ViT-B-16', 'commonpool_l_clip_s1b_b8k'), ('ViT-B-16', 'commonpool_l_laion_s1b_b8k'), ('ViT-B-16', 'commonpool_l_image_s1b_b8k'), ('ViT-B-16', 'commonpool_l_text_s1b_b8k'), ('ViT-B-16', 'commonpool_l_basic_s1b_b8k'), ('ViT-B-16', 'commonpool_l_s1b_b8k'), ('ViT-B-16', 'dfn2b'), ('ViT-B-16-quickgelu', 'metaclip_400m'), ('ViT-B-16-quickgelu', 'metaclip_fullcc'), ('ViT-B-16-plus-240', 'laion400m_e31'), ('ViT-B-16-plus-240', 'laion400m_e32'), ('ViT-L-14', 'openai'), ('ViT-L-14', 'laion400m_e31'), ('ViT-L-14', 'laion400m_e32'), ('ViT-L-14', 'laion2b_s32b_b82k'), ('ViT-L-14', 'datacomp_xl_s13b_b90k'), ('ViT-L-14', 'commonpool_xl_clip_s13b_b90k'), ('ViT-L-14', 'commonpool_xl_laion_s13b_b90k'), ('ViT-L-14', 'commonpool_xl_s13b_b90k'), ('ViT-L-14-quickgelu', 'metaclip_400m'), ('ViT-L-14-quickgelu', 'metaclip_fullcc'), ('ViT-L-14-quickgelu', 'dfn2b'), ('ViT-L-14-336', 'openai'), ('ViT-H-14', 'laion2b_s32b_b79k'), ('ViT-H-14-quickgelu', 'metaclip_fullcc'), ('ViT-H-14-quickgelu', 'dfn5b'), ('ViT-H-14-378-quickgelu', 'dfn5b'), ('ViT-g-14', 'laion2b_s12b_b42k'), ('ViT-g-14', 'laion2b_s34b_b88k'), ('ViT-bigG-14', 'laion2b_s39b_b160k'), ('roberta-ViT-B-32', 'laion2b_s12b_b32k'), ('xlm-roberta-base-ViT-B-32', 'laion5b_s13b_b90k'), ('xlm-roberta-large-ViT-H-14', 'frozen_laion5b_s13b_b90k'), ('convnext_base', 'laion400m_s13b_b51k'), ('convnext_base_w', 'laion2b_s13b_b82k'), ('convnext_base_w', 'laion2b_s13b_b82k_augreg'), ('convnext_base_w', 'laion_aesthetic_s13b_b82k'), ('convnext_base_w_320', 'laion_aesthetic_s13b_b82k'), ('convnext_base_w_320', 'laion_aesthetic_s13b_b82k_augreg'), ('convnext_large_d', 'laion2b_s26b_b102k_augreg'), ('convnext_large_d_320', 'laion2b_s29b_b131k_ft'), ('convnext_large_d_320', 'laion2b_s29b_b131k_ft_soup'), ('convnext_xxlarge', 'laion2b_s34b_b82k_augreg'), ('convnext_xxlarge', 'laion2b_s34b_b82k_augreg_rewind'), ('convnext_xxlarge', 'laion2b_s34b_b82k_augreg_soup'), ('coca_ViT-B-32', 'laion2b_s13b_b90k'), ('coca_ViT-B-32', 'mscoco_finetuned_laion2b_s13b_b90k'), ('coca_ViT-L-14', 'laion2b_s13b_b90k'), ('coca_ViT-L-14', 'mscoco_finetuned_laion2b_s13b_b90k'), ('EVA01-g-14', 'laion400m_s11b_b41k'), ('EVA01-g-14-plus', 'merged2b_s11b_b114k'), ('EVA02-B-16', 'merged2b_s8b_b131k'), ('EVA02-L-14', 'merged2b_s4b_b131k'), ('EVA02-L-14-336', 'merged2b_s6b_b61k'), ('EVA02-E-14', 'laion2b_s4b_b115k'), ('EVA02-E-14-plus', 'laion2b_s9b_b144k'), ('ViT-B-16-SigLIP', 'webli'), ('ViT-B-16-SigLIP-256', 'webli'), ('ViT-B-16-SigLIP-i18n-256', 'webli'), ('ViT-B-16-SigLIP-384', 'webli'), ('ViT-B-16-SigLIP-512', 'webli'), ('ViT-L-16-SigLIP-256', 'webli'), ('ViT-L-16-SigLIP-384', 'webli'), ('ViT-SO400M-14-SigLIP', 'webli'), ('ViT-SO400M-14-SigLIP-384', 'webli'), ('ViT-L-14-CLIPA', 'datacomp1b'), ('ViT-L-14-CLIPA-336', 'datacomp1b'), ('ViT-H-14-CLIPA', 'datacomp1b'), ('ViT-H-14-CLIPA-336', 'laion2b'), ('ViT-H-14-CLIPA-336', 'datacomp1b'), ('ViT-bigG-14-CLIPA', 'datacomp1b'), ('ViT-bigG-14-CLIPA-336', 'datacomp1b'), ('nllb-clip-base', 'v1'), ('nllb-clip-large', 'v1'), ('nllb-clip-base-siglip', 'v1'), ('nllb-clip-large-siglip', 'v1')]
    问题是这些模型和这个对不起来。也许是一大类的模型的规格吧?
    也许这些模型的规格是有用的吧,我决定存一个拷贝。
    1
    modelimage_sizeimage_widthtext_widthembed_dimmparamsimage_mparamstext_mparamsgflopsimage_gflopstext_gflops
    2
    ViT-S-32-alt22438425625643.2222.5920.633.562.291.27
    3
    ViT-S-3222438438438463.0922.6440.445.662.293.38
    4
    ViT-M-32-alt22451238438480.0739.6340.447.373.993.38
    5
    ViT-M-32224512512512103.1239.6963.439.953.995.96
    6
    ViT-S-16-alt22438425625642.421.7620.6310.479.21.27
    7
    ViT-S-1622438438438462.2621.8140.4412.589.23.38
    8
    ViT-B-32224768512512151.2887.8563.4314.788.825.96
    9
    ViT-B-32-quickgelu224768512512151.2887.8563.4314.788.825.96
    10
    convnext_tiny224768512102492.328.6163.6914.878.915.96
    11
    ViT-B-32-256256768512512151.2987.8663.4317.4611.55.96
    12
    RN50224645121024102.0138.3263.6918.1812.225.96
    13
    RN50-quickgelu224645121024102.0138.3263.6918.1812.225.96
    14
    ViT-M-16-alt22451238438478.9838.5340.4419.3615.983.38
    15
    ViT-M-16224512512512102.0238.5963.4321.9415.985.96
    16
    vit_relpos_medium_patch16_cls_224224768512512101.9438.5163.4321.9916.035.96
    17
    mt5-base-ViT-B-32224768512512365.7187.85277.8622.128.8213.3
    18
    convnext_small224768512512113.2849.8563.4323.3317.375.96
    19
    ViT-B-32-plus-256256896640640210.3119.1391.1624.8315.569.27
    20
    RN10122464512512119.6956.2663.4325.519.545.96
    21
    RN101-quickgelu22464512512119.6956.2663.4325.519.545.96
    22
    vit_medium_patch16_gap_256256768512512102.0438.6163.4327.121.145.96
    23
    coca_ViT-B-32224768512512253.5689.1663.4333.349.195.96
    24
    convnext_base224768512512151.5288.0963.4336.6730.715.96
    25
    swin_base_patch4_window7_224224768640640178.5687.491.1640.1330.869.27
    26
    ViT-B-16224768512512149.6286.1963.4341.0935.135.96
    27
    ViT-B-16-quickgelu224768512512149.6286.1963.4341.0935.135.96
    28
    EVA02-B-16224768512512149.6986.2663.4341.0935.135.96
    29
    ViT-B-16-SigLIP224768768768203.1692.88110.2746.4435.4211.02
    30
    convnext_base_w256768640640179.3988.2291.1649.3840.119.27
    31
    RN50x428880640640178.387.1491.1651.8242.569.27
    32
    coca_roberta-ViT-B-32224768768512420.3787.85124.4553.128.8213.12
    33
    ViT-B-16-plus224896640640208.35117.1991.1656.7547.499.27
    34
    ViT-B-16-SigLIP-256256768768768203.292.93110.2757.8446.8211.02
    35
    ViT-B-16-SigLIP-i18n-256256768768768370.6392.93277.757.8446.8211.02
    36
    ViT-B-16-plus-240240896640640208.38117.2191.1664.0354.769.27
    37
    convnext_base_w_320320768640640179.3988.2291.1671.9462.679.27
    38
    convnext_large224768768768321.06197.41123.6582.0268.7213.3
    39
    coca_base288768768512440.3486.4134.6699.0946.4713.3
    40
    roberta-ViT-B-32224768512512212.7287.85124.87105.878.8297.05
    41
    xlm-roberta-base-ViT-B-32224768512512366.1287.85278.27105.878.8297.05
    42
    convnext_large_d256768768768351.77199.77152.0107.589.7617.73
    43
    ViT-B-16-SigLIP-384384768768768203.4593.18110.27123.15112.1311.02
    44
    ViT-L-162241024768768427.74304.09123.65136.41123.1113.3
    45
    convnext_large_d_320320768768768351.77199.77152.0157.98140.2517.73
    46
    RN50x1638496768768290.98167.33123.65162.69149.3913.3
    47
    ViT-L-14-CLIPA2241024768768414.21303.96110.25167.5162.035.47
    48
    EVA02-L-14224768768768427.76304.11123.65175.3162.013.3
    49
    ViT-L-142241024768768427.62303.97123.65175.33162.0313.3
    50
    ViT-L-14-quickgelu2241024768768427.62303.97123.65175.33162.0313.3
    51
    convnext_xlarge25676810241024653.89350.25303.65198.38159.1439.24
    52
    ViT-L-16-SigLIP-25625676810241024652.15315.96336.19201.62162.5639.06
    53
    coca_ViT-L-142241024768768638.45306.72123.65214.52163.6413.3
    54
    ViT-B-16-SigLIP-512512768768768203.7993.52110.27227.26216.2411.02
    55
    ViT-SO400M-14-SigLIP22476811521152877.36427.68449.68233.54220.3513.19
    56
    ViT-L-14-2802801024768768427.76304.11123.65271.79258.4913.3
    57
    ViT-L-16-3203201024768768427.95304.3123.65271.93258.6313.3
    58
    ViT-H-16224128010241024986.26632.23354.03301.72254.6347.09
    59
    ViT-H-14-CLIPA224128010241024968.24632.07336.16354.02334.5919.43
    60
    nllb-clip-base224768512512501.8987.85414.04369.68.82360.78
    61
    ViT-H-14224128010241024986.11632.08354.03381.68334.5947.09
    62
    ViT-H-14-quickgelu224128010241024986.11632.08354.03381.68334.5947.09
    63
    ViT-L-14-CLIPA-3363361024768768414.54304.29110.25387.39381.925.47
    64
    EVA02-L-14-336336768768768428.08304.43123.65395.16381.8613.3
    65
    ViT-L-14-3363361024768768427.94304.29123.65395.22381.9213.3
    66
    ViT-L-16-SigLIP-38438476810241024652.48316.28336.19422.91383.8539.06
    67
    convnext_xxlarge256768102410241200.58846.54354.03443.03395.9447.09
    68
    nllb-clip-base-siglip384768512768507.4793.18414.3472.91112.13360.78
    69
    mt5-xl-ViT-H-14224128051210242306.75632.081674.68514.04334.59179.45
    70
    EVA01-g-1422476876810241136.441012.59123.85547.36534.0613.3
    71
    RN50x6444812810241024623.26420.38202.88552.65529.1123.55
    72
    EVA01-g-14-plus224768102410241366.621012.59354.03581.15534.0647.09
    73
    ViT-g-142241408102410241366.681012.65354.03581.15534.0647.09
    74
    convnext_xxlarge_320320768102410241200.58846.54354.03665.74618.6547.09
    75
    xlm-roberta-large-ViT-H-14224128051210241193.01632.08560.94671.01334.59336.42
    76
    ViT-SO400M-14-SigLIP-38438476811521152877.96428.23449.73723.48670.3553.13
    77
    ViT-H-14-CLIPA-336336128010241024968.64632.48336.16800.88781.4519.43
    78
    ViT-bigG-14-CLIPA2241664128012802517.221844.9672.321007.93967.540.44
    79
    ViT-H-14-378-quickgelu378128010241024986.71632.68354.031054.051006.9647.09
    80
    ViT-bigG-142241664128012802539.571844.91694.661065.36967.597.86
    81
    nllb-clip-large224128051210241399.22632.08767.141468.46334.591133.87
    82
    nllb-clip-large-siglip38476851211521195.5428.23767.271804.22670.351133.87
    83
    ViT-e-142241792128012804581.093807.72773.372091.451981.35110.1
    84
    ViT-bigG-14-CLIPA-3363361664128012802517.761845.44672.322271.582231.1540.44
    85
    EVA02-E-14224768102410244704.594350.56354.032311.422264.3347.09
    86
    EVA02-E-14-plus224768128010245044.894350.56694.332362.192264.3397.86
    从这个代码我猜想两个参数和clip是一样的就是模型名称
  3. 我从cdn-lfs.huggingface.co的ssh下载老是出错让我怀疑是否我有ssh-key没有setup的问题?问题这里检验我ssh访问应该是没有问题的
    
    ssh -T git@hf.co
    
    其实,我大概也能猜出来这个的确是伟光正网络的问题,但是我还是不太甘心。
  4. 遭遇了重大的挫折,就是我重启之后完全无法进入图形界面。按照经验肯定是nvidia的驱动在内核升级后的种种不兼容造成的。但是这里有几个问题:
    1. 首先,我希望进入ubuntu的grub启动菜单的高级模式以便修复。这个在bios模式下是shift按键,而uefi下是esc按键。而要进入我的笔记本的bios模式需要的是F2。这些我全都忘记了。
    2. 如果要固定显示启动的高级菜单,需要修改etc/default/grub里的
      
      #GRUB_TIMEOUT_STYLE=hidden
      GRUB_TIMEOUT=5
      
    3. 那么完全无法登陆很明显是显卡的初始化的问题吧?于是只能完全卸载NVIDIA的驱动,比如
      
      sudo apt-get purge "^nvidia-*"
      sudo apt-get purge "^libnvidia*" "^libcuda*"
      
      关于如何安装nvidia官方驱动,我以为我有笔记,结果书到用时方恨少,发现我居然没有!这里是下载的链接
    4. 但是官方的驱动并不是好的解决办法,因为安装需要编译内核模块,而这个混蛋居然要求编译器gcc-12,否则会有很多错误。我在ubuntu 22.04,手动安装gcc-12并不现实,因为不支持,除非你自己编译,这个时候我是在ubuntu的高级启动菜单里的root shell,怎么可能呢?所以,变成需要使用Ubuntu官方编译好的驱动。
      
      nick@nick-sager:~$ ubuntu-drivers devices
      == /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0 ==
      modalias : pci:v000010DEd000028A1sv00001558sd0000A650bc03sc00i00
      vendor   : NVIDIA Corporation
      driver   : nvidia-driver-535-server-open - distro non-free
      driver   : nvidia-driver-545-open - distro non-free
      driver   : nvidia-driver-525-open - distro non-free
      driver   : nvidia-driver-545 - third-party non-free
      driver   : nvidia-driver-525-server - distro non-free
      driver   : nvidia-driver-535 - third-party non-free
      driver   : nvidia-driver-525 - third-party non-free
      driver   : nvidia-driver-550 - third-party non-free recommended
      driver   : nvidia-driver-550-open - third-party non-free
      driver   : nvidia-driver-535-server - distro non-free
      driver   : nvidia-driver-530 - third-party non-free
      driver   : nvidia-driver-535-open - distro non-free
      driver   : xserver-xorg-video-nouveau - distro free builtin
      
      我选择安装最新的nvidia-driver-550驱动。
    5. 但是你以为着就算安成你就又错了,因为似乎没有人去更新grub,我模糊记得编译驱动会调用update-initramfs,但是似乎没有更新grub,update-grub2。总之,重启又是黑屏,这个时候已经不再是完全没有登陆界面,而是可以login,但是登录后就黑屏,这个说明只是桌面的问题。我可以使用ctl-alt-F4登陆来查看系统。
    6. 这个中间还有一个插曲就是我只好用手机下载安装程序,结果没有在存储界面安全弹出USB导致文件拷贝不全被安装程序的签名检查发现。总之,实在是太痛苦了。以后要怎么安装nvida的驱动呢?
  5. 我终于明白我从huggingface.co之类的下载预处理模型的代码调用的是huggingface_hub这个模块,而它可以在命令行工具里使用huggingface-cli,所以,我的问题可以在这个复现
    
    huggingface-cli download gpt2 config.json
    
    我决定按照这个指导来安装虚拟环境。实际上这个都是徒劳,看一下新闻就知道是为光正不知道为何把它封了,我觉得说不定是国内的AI公司在使坏。 这个大神使用镜像可能是唯一可行的。
    
    export HF_ENDPOINT=https://hf-mirror.com
    python -c "from huggingface_hub import model_info; print(model_info('gpt2'))"
    
    为了打印好看也许在浮点数后面加上.numpy()能有些帮助吧?
  6. 这个clip-retrieval也许是一个不错的工具。怎么用我还不清楚,但是看上去不错的。
  7. 这里有提到一个快速DNS的方案,我决定试一下。这里是官方的指引,我懒得看,就直接按照ubuntu的命令来吧:
    
    wget https://secure.nic.cz/files/knot-resolver/knot-resolver-release.deb
    sudo dpkg -i knot-resolver-release.deb
    sudo apt update
    sudo apt install -y knot-resolver
    sudo sh -c 'echo `hostname -I` `hostname` >> /etc/hosts'
    sudo sh -c 'echo nameserver 127.0.0.1 > /etc/resolv.conf'
    sudo systemctl stop systemd-resolved
    
    我因为已经有bind9了所以,不能立即启动knot,只有停掉它再启动knot之后修改配置又可以了。总之我是在云里雾里完全不知道在干什么。到底工作不工作也不知道。
  8. 我至今不会怎么在synology里设置nfs mount的权限,只好每次都login去修改/etc/exportfs里
    
    /volume1/photo  *(rw,async,no_wdelay,crossmnt,all_squash,insecure_locks,sec=sys,anonuid=1025,anongid=100)
    /volume1/music  *(rw,async,no_wdelay,crossmnt,all_squash,insecure_locks,sec=sys,anonuid=1025,anongid=100)
    /volume1/video  *(rw,async,no_wdelay,crossmnt,all_squash,insecure_locks,sec=sys,anonuid=1024,anongid=100)
    
    然后再exportfs -a去更新,否则我local mount权限不够。真是惭愧,我居然连最基本的nfs也没有搞明白,算是白在HDS待了这些年了。
  9. 这个看起来是不明觉厉的帖子,我现在始终搞不懂我的DNS是怎么回事。这个问题太复杂了,我记录下这个关于vpn的链接供以后查询吧。我实在是搞得焦头烂额。
  10. 我打算尝试一下这个img2dataset的mscoco例子:
    
    wget https://huggingface.co/datasets/ChristophSchuhmann/MS_COCO_2017_URL_TEXT/resolve/main/mscoco.parquet
    img2dataset --url_list mscoco.parquet --input_format "parquet"\
             --url_col "URL" --caption_col "TEXT" --output_format webdataset\
               --output_folder mscoco --processes_count 16 --thread_count 64 --image_size 256\
                 --enable_wandb False
    
    我遇到一些wandb的错误,索性就把它设为false。作者提到一个很酷的观察带宽的工具:bwm-ng 它下载的结果是下载了一系列的.parequest文件,这个应该是某种二进制的链接及其说明文件,依靠它再成功下载一个.tar文件,它包含了图片以及对应的说明文字.txt和结构化说明文件.json。这个以后可以作参考。

二月十三日 等待变化等待机会

  1. 似乎在nfs mount里tree不工作,那么如果仅仅想知道繁杂的目录结构的话,我们最简单的朋友find,就能做得很好:
    
    find . -type d  -not -path '*/\.*'
    
    这个是只显示目录,针对各个类型文件那就更容易的,比tree来的容易的多,只不过tree画图实在是个硬功夫!
  2. 感觉我的战线拉的太长了,到底我在解决vpn的问题还是在学习openClip的使用,抑或是stable diffusion的步骤,中间因为安装nvidia驱动导致的一系列显示和内核编译带来的问题?到底哪一个方向呢?也许还是要从基础开始,不然困扰我的 网络问题无时无刻都在,这个基础问题不彻底解决真的是永无宁日,但是我不明白的是为什么我使用了vpn还是被限制,照理说我已经跳出三界外,不在五行中, 难道是因为我的DNS/routing导致所谓的split DNS的问题,要么我全部都依赖vpn的DNS,一切都从海外,国内慢一些也 无妨?问题是这个问题是否有解?为什么防火墙能够阻挡我?我有没有可能使用ec2上的DNS解析?我到达ec2不也是要我本地的DNS指路才行吗?这个基 本的过程我始终不清楚,还有就是ec2上我为什么不能使用ssh+x11远程显示?
  3. 首先从最最容易的入手,那就是我自己本机的ssh-key代替/平替亚马逊的,这个本来是很容易的,只不过我担心有什么猫腻,专门查看sshd_config,后来才想到这个是ubuntu image提供的和亚马逊无关,而在/home/openvpnas/.ssh里的pubkey本来就是我给亚马逊的,所以,这个没有任何悬念,我本机的ssh-pubkey去concat还是并行设为authorized_keys2呢?这个我通过改变sshd_config的log level到DEBUG然后看/var/log/auth.log来看到sshd是两个文件都找一遍,虽然注解上说也许将来后者有可能放弃。总之这个问题解决了,我省的每次都要多加一个亚马逊的key。
  4. 第二个小问题也解决了,首先,我要明确xclock这种验证程序是工作的,那么其次使用firefox失败的错误X11 connection rejected because of wrong authentication.就是个别应用的,所以,这个帖子是解决办法。
    
    export XAUTHORITY=$HOME/.Xauthority 
    firefox
    
    当然这个仅仅是一个验证,因为带宽需要太大了,完全无法使用,这个还不如使用远程的rdp之类的。
  5. 那么当前的重点还是解决VPN的split DNS问题,或许仔细阅读这个是个入门。

二月十四日 等待变化等待机会

  1. 我的笔记本的第二个ssd的mount要记录一下:
    
    /dev/disk/by-uuid/9343f884-54dc-481f-a702-9a74ca0e025f /home/nick/workspace auto nosuid,nodev,nofail,x-gvfs-show,x-gvfs-name=fanxiang 0 0
    
    其中的选项nosuid,nodev,nofail,x-gvfs-show,x-gvfs-name=fanxiang值得重视。
  2. 早晨看到apparmor的配置文件/etc/apparmor.d/感觉这一套体系非常的复杂,希望我不要遇到这类问题。
  3. 我的感觉这篇博文是我寻找了很久的指引。最起码这个开宗明义的定义问题就是我想说却说不出来的东西:

    Let’s first define two distinct VPN use-cases:

    1. Corporate VPNs, i.e. VPNs that open access to a specific set of additional hosts. Only specific domains should be resolved via the VPN’s DNS servers, and everything that is not related to the company’s domain names should go to regular, non-VPN DNS instead.

    2. Privacy VPNs, i.e. VPNs that should be used for basically all DNS traffic, once they are up. If this type of VPN is used, any regular, non-VPN DNS servers should not get any traffic anymore.

    大多数在公司上班的人不认为VPN配置有什么问题的都是第一种在公司远程上班的,因为你的普通家用的DNS也不知道公司防火墙后面有什么,你毫不费力的转 向了VPN的DNS来询问,而我的情况是比以上两种都复杂的情况,明明家里普通的DNS都能找到的外网路径我不能使用,需要指明使用VPN的专项路径,同 时国内网络我又不想绕到国外去转一大圈,结果就是一个混乱的解决。也许我完全采用第二种就是放弃内网的访问效率就是一个简单的解决?
    这里接下去介绍的三种配置思路让我头疼不已,非常的难懂,因为我的网络知识非常的有限,对于search domain和routing domain的异同始终不能理解。也许看到最后的实作才能有所顿悟吧?

    Then, let’s briefly introduce three DNS routing concepts that software managing a network interface may configure.

    1. Search domains: these are traditional DNS configuration parameters and are used to suffix non-qualified domain names (i.e. single-label ones), to turn them into fully qualified domain names. Traditionally (before systemd-resolved.service), search domain names are attached to a system’s IP configuration as a whole, in systemd-resolved.service they are associated to individual interfaces instead, since they are typically acquired through some network associated concept, such as a DHCP, IPv6RA or PPP lease. Most importantly though: in systemd-resolved.service they are not just used to suffix single-label domain names, but also for routing domain name lookups: if a network interface has a search domain foo.com configured on it, then any lookups for names ending in .foo.com (or for foo.com itself) are preferably routed to the DNS servers configured on the same network interface.

    2. Routing domains: these are very similar to search domains, but are purely about DNS domain name lookup routing — they are not used for qualifying single-label domain names. When it comes to routing, assigning a routing domain to a network interface is identical to assigning a search domain to it.

      Why the need to have both concepts, i.e. search and routing domains? Mostly because in many cases the qualifying of single-label names is not desirable (as it has security implications), but needs to be supported for specific use-cases. Routing domains are a concept systemd-resolved.service introduced, while search domains are traditionally available and are part of DHCP/IPv6RA/PPP leases and thus universally supported. In many cases routing domains are probably the more appropriate concept, but not easily available, since they are not part of DHCP/IPv6RA/PPP.

      Routing domains for systemd-resolved.service are usually presented along with search domains in mostly the same way, but prefixed with ~ to differentiate them. i.e. ~foo.com is a configured routing domain, while foo.com would be a configured search domain.

      One routing domain is particularly interesting: ~. — the catch-all routing domain. (The dot domain . is how DNS denotes the “root” domain, i.e. the parent domain of all domains, but itself.) When used on an interface any DNS traffic is preferably routed to its DNS servers. (A search domain – i.e. . instead of ~. — would have the same effect, but given that it’s mostly pointless to suffix an unqualified domain with ., we generally declare it as a routing domain, not a search domain).

      Routing domains also have particular relevance when it comes to the reverse lookup DNS domains .in-addr.arpa and .ip6.arpa. An interface that has these (or sub-domains thereof) defined as routing domains, will be preferably used for doing reverse IP to domain name lookups. e.g. declaring ~168.192.in-addr.arpa on an interface means that all lookups to find the domain names for IPv4 addresses 192.168.x.y are preferably routed to it.

    3. The default-route boolean. This is a simple boolean value that may be set on an interface. If true (the default), any DNS lookups for which no matching routing or search domains are defined are routed to interfaces marked like this. If false then the DNS servers on this interface are not considered for routing lookups to except for the ones listed in the search/routing domain list. An interface that has no search/routing domain associated and also has this boolean off is not considered for any lookups.

    这里马上就暴露出了我的无知,我对于FQDN看了多少回也不知道它真正的含义:

    What is a fully qualified domain name (FQDN)?

    A fully qualified domain name (FQDN) is the complete address of an internet host or computer. It provides its exact location within the domain name system (DNS) by specifying the hostname, domain name and top-level domain (TLD). For example, for the domain name www.whatis.com, "www" is the hostname, "whatis" is the domain name and ".com" is the top-level domain.

    那么接下去的常识就是FQDN和URL的关系:
    An FQDN doesn't carry the TCP/IP protocol information -- such as Hypertext Transfer Protocol (HTTP) or Hypertext Transfer Protocol Secure (HTTPS) -- which is always used at the beginning of a URL. Therefore, adding the prefix http:// or https:// to the FQDN turns it into a full URL. Also, URLs can specify directory paths, file names and TCP port numbers, which FQDNs don't include.
    URL可以称之为FQDN的一个超集,它完全包含了并且还可以额外提供协议以及端口。更进一步就是A PQDN is part of an FQDN that isn't fully specified.

二月十五日 等待变化等待机会

  1. 这篇文章我读了但是依然不明所以然,我只能祈求使用实例来帮助理解,比如我首先要设置systemd-resolved的log
    
    sudo resolvectl log-level debug
    journalctl -f -u systemd-resolved.service
    dig www.google.com
    
    然后我就可以观察到DNS解析的每一步了。至少我现在观察到在chromium里为什么不能使用google搜索是因为它总是在问www.googleapis.com的解析,和www.google.com的解析结果不同,googleapis.com返回了更大uo的结果(我只保留了answer部分)
    
    nick@nick-sager:~$ dig www.googleapis.com
    
    ;; ANSWER SECTION:
    www.googleapis.com.	400	IN	A	172.217.160.106
    www.googleapis.com.	400	IN	A	172.217.163.42
    www.googleapis.com.	400	IN	A	142.251.42.234
    www.googleapis.com.	400	IN	A	142.251.43.10
    www.googleapis.com.	400	IN	A	172.217.160.74
    
    对比一下
    
    nick@nick-sager:~$ dig www.google.com
    
    ;; ANSWER SECTION:
    www.google.com.		600	IN	A	154.83.14.134
    
  2. 在我进一步作实验以前我想再读一下这篇文章。遥想了解现在,你必须先了解历史!

    Traditional DNS with nss-dns

    There are two important configuration files to discuss. The first is /etc/nsswitch.conf, which controls which NSS modules are invoked by glibc when performing name resolution.

    Next, let’s look at /etc/resolv.conf. This file contains a list of up to three DNS servers to use. The servers are attempted in order. If the first server in the list is broken, then the second server will be used. If the second server is broken, the third server will be used. If the third server is also broken, then everything fails, because no matter how many servers you list here, all except the first three are ignored.

    Traditional DNS Problems

    Traditional DNS is all well and good for a simple case like we had above, but turns out it’s really broken once you start adding VPNs to the mix.

    总而言之,它说到了split DNS的使用场景,我觉得是非常精准的契合我的情况,而这样的传统的DNS解决方案无法分清楚,因为从glibc模块的解决法就是第一个失败了找第二个, 然后第三个,最后失败。因为它不清楚这些有着不同意义的解析。但是作者讨论的是Fedora,也许和Ubuntu有些不同,我的是这样子的
    
    hosts:          files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns mymachines
    
    不过他这里讨论的NSS-DNS是另一个纬度的问题吧? 比如我的DNS是每一个网卡都有的吗?
    
    nick@nick-sager:~$ resolvectl status
    Global
           Protocols: -LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    resolv.conf mode: stub
    
    Link 2 (enp0s31f6)
        Current Scopes: DNS
             Protocols: +DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    Current DNS Server: fe80::1
           DNS Servers: 8.8.8.8 218.85.152.99 218.85.157.99 fe80::1%32544
    
    Link 3 (wlp109s0)
    Current Scopes: none
         Protocols: -DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    
    Link 9 (tun0)
    Current Scopes: none
         Protocols: -DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    
    我看不到我的VPN创建的tun0有解析的功能,这个在那里设置的?
    Although systemd-resolved supports several different modes for managing /etc/resolv.conf, the default mode, and the mode used in both Fedora and Ubuntu, is for /etc/resolv.conf to be a symlink to /run/systemd/resolve/stub-resolv.conf.
    这个结论还是很重要的,就是nss-dns只有在systemd-resolved不工作才起作用,而这个
    
    nick@nick-sager:/etc$ ll /run/systemd/resolve/stub-resolv.conf
    -rw-r--r-- 1 systemd-resolve systemd-resolve 920 Feb 12 18:27 /run/systemd/resolve/stub-resolv.conf
    nick@nick-sager:/etc$ ll /etc/resolv.conf 
    lrwxrwxrwx 1 root root 39 Mar 31  2023 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
    
    目前在ubuntu,的确是
    /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
    我的理解是这个stbu-resolve等价于127.0.0.53。
    systemd-resolved provides a local DNS stub listener on the IP addresses 127.0.0.53 and 127.0.0.54 on the local loopback interface. Programs issuing DNS requests directly, bypassing any local API may be directed to this stub, in order to connect them to systemd-resolved. Note however that it is strongly recommended that local programs use the glibc NSS or bus APIs instead (as described above), as various network resolution concepts (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped to the unicast DNS protocol.
    这个man page是非常的难懂,因为背景知识的缺失,我对于D-bus始终不理解

    A Word about Ubuntu

    Although Ubuntu has used systemd-resolved for four years now, it has not switched from nss-dns to nss-resolve, contrary to upstream recommendations. This means that on Ubuntu, glibc still reads /etc/resolv.conf, finds 127.0.0.53 listed there, and then makes an IP connection to systemd-resolved rather than talking to it via varlink or D-Bus, as occurs on Fedora. The practical effect is that, on Ubuntu, you can still manually edit /etc/resolv.conf and applications will respond to those changes, unlike Fedora.

  3. 这一段很重要因为我作为一个普通人也是有这种混淆,一提到routing,想到的都是IP routing,而不是这里的DNS routing

    IP Routing Domains, DNS Routing Domains, and DNS Search Domains: Oh My!

    systemd-resolved works with DNS routing domains and DNS search domains. A DNS routing domain determines only which DNS server your DNS query goes to.  It doesn’t determine where IP traffic goes to: that would be an IP routing domain. Normally, when people talk about “routing domains,” they probably mean IP routing domains, not DNS routing domains, so be careful not to confuse these two concepts.

    这里非常完美的回答了我另一个疑问domain search和domain routing的区别
    A DNS search domain is also different. When you query a name that is only a single labela domain without any dots — a search domain gets appended to your query.
    这段也是很重要的,这个tilde(~)是区别于search domain和routing domain的标志:
    In systemd-resolved, each DNS routing domain may or may not be used as a search domain. By default, systemd-resolved will add search domains for every configured routing domain that is not prefixed by a tilde. For example, ~example.com is a routing domain only, while example.com is both a routing domain and a search domain. There is also a global routing domain,  ~.
  4. 其实看到这里我已经大概明白了我应该怎么做,只是除了不完美,比如如果我直接修改/etc/resolve.conf设置我的routing domain为VPN的tun0为~.应该就是可以的,只不过我想能不能使用D-Bus的API来作,否则每次都要修改文件是很烦人。因为我的VPN不是自动开机启动的。 这里回答了开机脚本调用参数修改dns设置的方式。但是问题是我的openvpn的DNS我访问不了,它是亚马逊的云服务器,这个是不是有了更复杂的一层呢?
  5. 我读了这么多,但是我始终不确定要怎么做,甚至于我还是不敢肯定到底我的问题是不是能够解决。我决定不管如何尝试一下:
    
    sudo resolvectl flush-caches
    nick@nick-sager:~$ resolvectl domain tun0 '~.'
    nick@nick-sager:~$ resolvectl default-route tun0 true
    nick@nick-sager:~$ resolvectl dns tun0 8.8.8.8
    
    然后这个是结果
    
    nick@nick-sager:~$ resolvectl status
    Global
           Protocols: -LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    resolv.conf mode: stub
    
    Link 2 (enp0s31f6)
        Current Scopes: DNS
             Protocols: +DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    Current DNS Server: fe80::1
           DNS Servers: 8.8.8.8 218.85.152.99 218.85.157.99 fe80::1%21970
    
    Link 3 (wlp109s0)
    Current Scopes: none
         Protocols: -DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
    
    Link 11 (tun0)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
       DNS Servers: 8.8.8.8
        DNS Domain: ~.
    
    但是我依旧无法在命令行ping到谷歌和脸书,但是我现在chromium可以正常访问谷歌了,这个难道不是结果吗?然后我发现ping只能支持ipv4,就是说
    ping -4 www.google.com
    是可以的。 也许我可以自己加个脚本在openvpn启动的时候执行上述的命令,这样子就不是直接修改conf文件那么麻烦了。但是我确实是禁止了ipv6为什么不起作用呢?看来问题还是很复杂的。 但是不管怎样我至少解决我的困扰的huggingface-cli必须要设立镜像才能访问的问题。
    
    nick@nick-sager:~$ unset HF_ENDPOINT
    nick@nick-sager:~$ huggingface-cli download gpt2 config.json
    Consider using `hf_transfer` for faster downloads. This solution comes with some limitations. See https://huggingface.co/docs/huggingface_hub/hf_transfer for more details.
    /home/nick/.cache/huggingface/hub/models--gpt2/snapshots/11c5a3d5811f50298f278a704980280950aedb10/config.json
    nick@nick-sager:~$ 
    
    不过目前这些成果我还没有形成可靠的机制,如果重启电脑可能又要重新设置。我想还是要验证一下再说。不管怎么样还是有一点点的成就感的。让我比较一下我的openvpn上的解析和本地是否一样:
    
    openvpnas@ip-172-31-35-59:~$ resolvectl query www.google.com
    www.google.com: 142.250.189.164                -- link: eth0
                    2607:f8b0:4005:80e::2004       -- link: eth0
    
    而我的本地
    
    nick@nick-sager:~$ resolvectl query www.google.com
    www.google.com: 172.217.164.100                -- link: tun0
                    2607:f8b0:4005:80c::2004       -- link: tun0
    
    这个说明了什么呢?难道是我的概念从一开始就是错误的,根本不是DNS解析的问题?那么为什么现在可以工作了呢? 那么我们看看两者的route路径如何吧? 这个是本地的,注意的确是走到了VPN的IP172.27.232.1,单单是这一点就足够了吧。因为从后面的路径分岔是应该是在当地找最快的路径每次都不同而已?
    
    nick@nick-sager:~$ traceroute www.google.com
    traceroute to www.google.com (172.217.164.100), 30 hops max, 60 byte packets
     1  172.27.232.1 (172.27.232.1)  163.430 ms  164.283 ms  164.225 ms
     2  244.5.0.17 (244.5.0.17)  166.724 ms 244.5.0.151 (244.5.0.151)  164.419 ms 244.5.0.17 (244.5.0.17)  166.733 ms
     3  240.0.180.36 (240.0.180.36)  164.240 ms 100.65.17.96 (100.65.17.96)  175.723 ms 100.65.16.128 (100.65.16.128)  188.759 ms
     4  240.0.168.12 (240.0.168.12)  164.946 ms 100.66.8.226 (100.66.8.226)  184.148 ms 100.66.8.0 (100.66.8.0)  338.800 ms
     5  100.66.10.66 (100.66.10.66)  178.329 ms * *
     6  241.0.6.199 (241.0.6.199)  164.177 ms 72.14.197.18 (72.14.197.18)  164.947 ms 241.0.6.207 (241.0.6.207)  164.339 ms
     7  240.0.168.13 (240.0.168.13)  164.994 ms 240.0.168.15 (240.0.168.15)  165.019 ms  165.015 ms
     8  142.251.66.108 (142.251.66.108)  167.275 ms * 142.251.224.172 (142.251.224.172)  165.467 ms
     9  192.178.105.118 (192.178.105.118)  166.057 ms 209.85.252.251 (209.85.252.251)  166.081 ms  166.159 ms
    10  sfo03s18-in-f4.1e100.net (172.217.164.100)  165.434 ms 192.178.105.113 (192.178.105.113)  165.433 ms sfo03s18-in-f4.1e100.net (172.217.164.100)  164.973 ms
    
    而openvpn上呢?
    
    openvpnas@ip-172-31-35-59:~$ traceroute www.google.com
    traceroute to www.google.com (142.251.32.36), 30 hops max, 60 byte packets
     1  244.5.0.147 (244.5.0.147)  4.191 ms 216.182.237.205 (216.182.237.205)  7.062 ms 216.182.237.199 (216.182.237.199)  7.470 ms
     2  * 100.65.17.128 (100.65.17.128)  12.839 ms 100.65.19.160 (100.65.19.160)  21.472 ms
     3  240.0.168.12 (240.0.168.12)  0.996 ms 100.66.8.160 (100.66.8.160)  12.075 ms 100.66.8.148 (100.66.8.148)  18.355 ms
     4  * 100.66.10.130 (100.66.10.130)  13.099 ms 100.66.10.234 (100.66.10.234)  22.329 ms
     5  72.14.203.108 (72.14.203.108)  2.291 ms 241.0.6.199 (241.0.6.199)  0.300 ms 241.0.6.195 (241.0.6.195)  0.317 ms
     6  * 240.0.168.15 (240.0.168.15)  1.009 ms *
     7  142.251.228.80 (142.251.228.80)  1.485 ms 142.251.241.102 (142.251.241.102)  1.488 ms 142.251.224.172 (142.251.224.172)  1.502 ms
     8  192.178.105.98 (192.178.105.98)  1.464 ms 192.178.105.106 (192.178.105.106)  1.478 ms 142.251.224.179 (142.251.224.179)  1.440 ms
     9  * 192.178.105.95 (192.178.105.95)  1.562 ms sfo03s26-in-f4.1e100.net (142.251.32.36)  1.364 ms
    
    而最后两者殊涂同归到达域名sfo03s26-in-f4.1e100.net是最重要的,至于说具体IP不同那个是IP routing的问题吧?至于说为什么IPV6不工作,我想这个也许是别的设置的问题?我在openvpn上发现它似乎有ipv6的问题吧?
    
    openvpnas@ip-172-31-35-59:~$ traceroute -6 www.google.com
    traceroute to www.google.com (2607:f8b0:4005:812::2004), 30 hops max, 80 byte packets
    connect: Network is unreachable
    
  6. 也许我今天可以好好的休息一下,似乎困扰我的网络问题终于有了一个解决方案。
  7. 顺便说一下,获得FQDN是可以通过hostname -f

二月十六日 等待变化等待机会

  1. 在解决了一个大问题回到Stable-Diffusion之前通常应该休整一段时间,但是我觉得没必要。在选择深入了解openClip和继续概览stable-diffusion全貌之间我选择后者。
  2. 这个embedding是每一个token都独自拥有的。
    Stable diffusion v1 uses Open AI’s ViT-L/14 Clip model. Embedding is a 768-value vector. Each token has its own unique embedding vector. Embedding is fixed by the CLIP model, which is learned during training.
    对于SDv1这个古老的遗迹我觉得有必要熟悉起来。不了解历史是无法深刻领会现实的。
    Note: Stable Diffusion v1 is a general text-to-image diffusion model and therefore mirrors biases and (mis-)conceptions that are present in its training data. Details on the training procedure and data, as well as the intended use of the model can be found in the corresponding model card.
    保存一份相当不错的论文。我的理解就是团队发现语言模型的完善可以自动完善text-image的训练过程,难道语言和图像有着某种内在的联系?人们说一幅画包含了千言万语,这个不是没有道理的:
    
    画
    远看山有色,近听水无声。
    春去花还在,人来鸟不惊。
    
    对于每个token都有768-value的vector相联系,我以为虽然是上下文的标识,但也就是默认了文字和图像一样是高维的物件,那么它们在高维度上的契合也就是自然而然的,更何况人们判定文字和图像契合的点乘本身就是要求两者在纬度上的一致。
    We discover that large frozen language models trained only on text data are surprisingly very effective text encoders for text-to-image generation, and that scaling the size of frozen text encoder improves sample quality significantly more than scaling the size of image diffusion model.
    这篇论文的核心贡献!这里有一个排行榜,看来挺有用的,可以了解趋势。

二月十七日 等待变化等待机会

  1. 阅读这篇论文
    1. 传统的text-image模型通常是比较小的,因为这个数据的采集以及准确性的保证都是一个难题。结果另辟蹊径是提高语言模型的尺寸相对容易,毕竟不是 一个维度的东西。但是没想到结果却是有效的提高了对于text-image模型的准确识别率,这个是一个无心插柳柳成荫的结果。
    2. It thus becomes natural to explore both families of text encoders for the text-to-image task. Imagen explores pretrained text encoders: BERT, T5 and CLIP. For simplicity, we freeze the weights of these text encoders. Freezing has several advantages such as offline computation of embeddings, resulting in negligible computation or memory footprint during training of the text- to-image model.
      看起来我也应该学习使用现成的text-encoder,比如openClip就应该成为下一个目标任务。就是说我也可以在我的简陋条件下来训练?
    3. 什么是diffusion model呢?
      Diffusion models are a class of generative models that convert Gaussian noise into samples from a learned data distribution via an iterative denoising process. These models can be conditional, for example on class labels, text, or low-resolution images.
      补习数学:什么是Gaussian noise
      In signal processing theory, Gaussian noise, named after Carl Friedrich Gauss, is a kind of signal noise that has a probability density function (pdf) equal to that of the normal distribution (which is also known as the Gaussian distribution). In other words, the values that the noise can take are Gaussian-distributed.
      为什么是Gaussian noise,其实要理解它的来源。如果它是我们很普通的电子设备和传感器天然得到的,那么人脑也应该有类似的机制产生,那么。。。
      Principal sources of Gaussian noise in digital images arise during acquisition e.g. sensor noise caused by poor illumination and/or high temperature, and/or transmission e.g. electronic circuit noise.
      所以,降噪是一个还原图像的逆过程?难道说熵减少?充满了噪音的信号显然是宇宙中最普遍的现象,而人脑是训练可以降噪的,人类的神经系统肯定不可能如我们 的硅基电子设备来的精密,所以,人类的大脑天天都在和信号传输过程的噪音做斗争,因此,这个是人脑的天然的过程。那么降噪的方法呢?
      In digital image processing Gaussian noise can be reduced using a spatial filter, though when smoothing an image, an undesirable outcome may result in the blurring of fine-scaled image edges and details because they also correspond to blocked high frequencies. Conventional spatial filtering techniques for noise removal include: mean (convolution) filtering, median filtering and Gaussian smoothing.
    4. 让我们首先来理解什么是Spatial filter
      A spatial filter is an optical device which uses the principles of Fourier optics to alter the structure of a beam of light or other electromagnetic radiation, typically coherent laser light. Spatial filtering is commonly used to "clean up" the output of lasers, removing aberrations in the beam due to imperfect, dirty, or damaged optics, or due to variations in the laser gain medium itself.
      那么又要首先明白什么是Fourier optics
      Fourier optics is the study of classical optics using Fourier transforms (FTs), in which the waveform being considered is regarded as made up of a combination, or superposition, of plane waves. It has some parallels to the Huygens–Fresnel principle, in which the wavefront is regarded as being made up of a combination of spherical wavefronts (also called phasefronts) whose sum is the wavefront being studied. A key difference is that Fourier optics considers the plane waves to be natural modes of the propagation medium, as opposed to Huygens–Fresnel, where the spherical waves originate in the physical medium.
      这一段相当的深奥,结果维基百科很贴心的没有中文版本!TMD连越南语版本都有为什么没有中文版!难道中文里没有傅里叶光学?这里深奥的就是传播介质还是物理介质,波是能量自然会引起介质的运动,电磁波难道不是像微波炉一样的给介质加热传播能量吗?其核心的研究方法是傅里叶变换,这个难道是数字化作为研究的数学手段?总之它是一门科学。我需要的是具体的手段,而不是理论。
    5. 这里有一个很重要的概念是coherent laser light,我刚刚才意识到这个干扰是天生的,从同一个光源发出的固定频率光束天生就是干扰的来源:
      In physics, coherence expresses the potential for two waves to interfere. Two monochromatic beams from a single source always interfere. Physical sources are not strictly monochromatic: they may be partly coherent. Beams from different sources are mutually incoherent.
      就是说这个干扰原是天生的!不过我对于这句话就不大理解了:Beams from different sources are mutually incoherent.但是话说回来了,我需要了解或者理解这些信号领域的深奥概念吗?这是一个人穷尽一生都未必能够精通的概念领域。我需要的是理解为什么而不是怎么做。
    6. 我还是回到降噪的三种途径吧:
      Conventional spatial filtering techniques for noise removal include: mean (convolution) filtering, median filtering and Gaussian smoothing.
    7. 这些都很难懂,至少我觉得convolution比较难吧,你能写出真的函数来吗?
    8. 而相比之下median filter就很好理解了,就是去角化,把过于突出的极化部分拉平使得圆滑。
      The main idea of the median filter is to run through the signal entry by entry, replacing each entry with the median of neighboring entries.
      我觉得这个思路就是利用了正态分布的噪音特点,凡是超出周边范畴的就去除?也许不准确,不过算法是比较好理解的,有点interpolition的反向操作?
    9. 第三种Gaussian blur才是有技术含量的高大上:
      In image processing, a Gaussian blur (also known as Gaussian smoothing) is the result of blurring an image by a Gaussian function (named after mathematician and scientist Carl Friedrich Gauss).
      这个要怎么理解呢?能不能理解它是一种特殊的convolution呢?
    10. 吃了早饭我想这些都是作为下一步理解denoise的工具的储备知识。有个概念就好。另一个比较难以理解的是如何把64x64的基础图片扩展成256x256以及1024x1024而不失真?
      magen uses noise conditioning augmentation for both the super-resolution models. We find this to be a critical for generating high fidelity images.
      什么是noise conditioning augmentation
      Given a conditioning low-resolution image and augmentation level (a.k.a aug_level) (e.g., strength of Gaussian noise or blur), we corrupt the low-resolution image with the augmentation (corresponding to aug_level), and condition the diffusion model on aug_level. During training, aug_level is chosen randomly, while during inference, we sweep over its different values to find the best sample quality. In our case, we use Gaussian noise as a form of augmentation, and apply variance preserving Gaussian noise augmentation resembling the forward process used in diffusion model.
      这里能不能理解就是augmentation level=strength of Gaussian noise or blur?训练的时候是遍历所有的可能,也就是用随机数,而推理的时候则是反向的使用所有的可能性来找到最佳值。什么是diffusion model的forward process?看来我要重温一下基本的概念。
    11. 如何验证是非常重要的学习领域。这里提到使用COCO,我已经下载了。什么是FID呢?为什么CLIP score可以做检验?它是什么?
      Both FID and CLIP scores have limitations, for example FID is not fully aligned with perceptual quality, and CLIP is ineffective at counting.
      作者指出的这些缺陷是什么意思?这里人为鉴定的方法很有趣:
      1. To probe image quality, the rater is asked to select between the model generation and reference image using the question: “Which image is more photorealistic (looks more real)?”. We report the percentage of times raters choose model generations over reference images (the preference rate).
      2. To probe alignment, human raters are shown an image and a prompt and asked “Does the caption accurately describe the above image?”. They must respond with “yes”, “somewhat”, or “no”. These responses are scored as 100, 50, and 0, respectively. These ratings are obtained independently for model samples and reference images, and both are reported.
      第一个回答可信度也就是质量问题。第二个是准确度,这个很好理解,但是似乎作者对于COCO数据库原来的标题也不是很信赖,否则为什么要独立的验证它?难 道我理解错了?也许原本的训练库就不够贴合。注意,作者的模型是没有在COCO上训练过的,这一点很重要,不像很多是在训练库里训练出的模型再回炉去那么 它能证明什么?记忆力吗?我甚至怀疑这么大量的相类似的库难免有重叠,或者说相类似吧?这个是逃不掉的。人类总是类似的吧?这样子的训练就不是什么开创性 的了,而是比赛记忆力了。而作者的训练是一个纯粹的语言模型,只不过它借用了之前别人训练好的模型提高了质量,这里有着非常微妙的差别?
    12. 这里要学习一个概念Zero-shot learning
      Zero-shot learning (ZSL) is a problem setup in deep learning where, at test time, a learner observes samples from classes which were not observed during training, and needs to predict the class that they belong to.
      这个和我直觉是一样的,当然是要训练时候没有见过才行,否则就是考验记忆力了。这和人类学习中的举一反三有异曲同工之效。不过比我想象的要难的多,就是
      For example, given a set of images of animals to be classified, along with auxiliary textual descriptions of what animals look like, an artificial intelligence model which has been trained to recognize horses, but has never been given a zebra, can still recognize a zebra when it also knows that zebras look like striped horses.
      这里的反三不是简单的类比而是要上一个层次,是真正的要理解语言的描述,而不是平行类比。
    13. 我觉得这个截图是高度概括了这篇论文: 虽然是借鸡生蛋,但是很有独创性,而且我对于它的drawbench很感兴趣。这个是有人对于它的比较。这些prompt是特意挑选的吗?这个描述是自行车:
      A vehicle composed of two wheels held in a frame one behind the other, propelled by pedals and steered with handlebars attached to the front wheel.
      我觉得很多人类都未必能理解。
    14. 似乎我应该看看Imagen因为感觉DrawBench也许只是一个小工具?编译有问题,然后我就借用我的SD的虚拟环境:
      
      source ../stable-diffusion-webui/venv/bin/activate
      python setup.py install
      
    15. 什么是DDPM(Denoising Diffusion Probabilistic Model)?它和GAN有什么关系? 这个似乎才是官方的库?
      Generative adversarial networks (GANs) are an exciting recent innovation in machine learning. GANs are generative models: they create new data instances that resemble your training data.
      没什么关系,但是为什么当初这个东西和generative content有关系呢?在我看来这个是相当传统的人工智能的模式。
      A generative adversarial network (GAN) is a class of machine learning frameworks and a prominent framework for approaching generative AI. The concept was initially developed by Ian Goodfellow and his colleagues in June 2014. In a GAN, two neural networks contest with each other in the form of a zero-sum game, where one agent's gain is another agent's loss.
      这个训练方式是很有意思的,就是双方是博弈关系,你的训练对手给你的是错误的指引?
      The core idea of a GAN is based on the "indirect" training through the discriminator, another neural network that can tell how "realistic" the input seems, which itself is also being updated dynamically. This means that the generator is not trained to minimize the distance to a specific image, but rather to fool the discriminator. This enables the model to learn in an unsupervised manner.
      这样子岂不是越跑越偏吗?这个结果还能看吗?
  2. 完全偏离了方向,踏入了不可自拔的泥潭,因为我没有这方面的基础知识尤其是数学基础,这个领域非常的困难,而且我也缺乏基本的python的基础让这些实验更加的困难。我似乎应该回到高概括性的浏览熟悉层级上来。准备回到这个通览节点
  3. 我想把我的微信聊天记录备份到电脑,但是weixin.qq.com不提供Linux版本,于是我就是下载windows版本使用本地的wine的loader来运行安装,结果高级功能会crash,不过备份还是可以做得到的。

二月十八日 等待变化等待机会

  1. 不要指望学习无上的内功心法,那个需要极高的数学基础和大量的AI的技术储备知识,对于连基本的论文都看不懂的门外汉来说是水中捞月。但 是如果仅仅是学习武功的招式,运用法门,那么哪怕是一个零基础的门外汉也不是什么大问题,只要下一些功夫,因为武功心法是给内功修炼者看的,但是练功口诀 却是给普通练习者学习用的。
  2. 这个就是你需要了解的细节,对于普通使用者似乎没有必要了解各个环节,但是正如修车和开车的关系一样,普通驾驶者满足于正常驾驶,这个在普通道路 上可能都没有问题,但是对于崎岖山路,又或是一部二手老爷车,经常在道路上维修也许就需要了解基本的机械结构,实施普通的维修保养,尤其是车子本身就是拼 凑出的多方零件来源的话。
    The embedding needs to be further processed by the text transformer before feeding into the noise predictor. The transformer is like a universal adapter for conditioning.
    这里点出了conditioning要在什么节点发力。而embedding如何成为latent space的向量恐怕不是那么简单的。这里就是语言模型的关键,否则单单靠词频上下文产生的tokenizer不可能有这么强大吧?
  3. In this case, its input is text embedding vectors, but it could as well be something else like class labels, images, and depth maps. The transformer not only further processes the data but also provides a mechanism to include different conditioning modalities.
    这里点出了一个关键点,text-prompt最后是映射到了什么?它是等同于image-text-pair里的caption部分?显然不是,但是分 类是这些caption的一部分功能,所以,如果简简单单就是class label也许是一个shortcut,但是我不清楚它和内部的embedding是什么关系,也许class-label仅仅是一个比较特殊的 text-prompt,正像很多人口中的咒语一样,但是这一点看起来也很像是conditioning,也许两者本来就是一样的东西。只是实际使用起来 我感觉这些keywords的位置似乎没有关系,这个在向量生成里说不通了。也许我的观察是不对的,但是它的特殊性是一定的。很prompt专门加各种挂 号引号,仅仅是为了强调这个要单独tokenize吗?还是要作为class-label?这个是今后要回答的问题。另一方面,img2img的机制显然 和text2img的不一样,但是似乎它们使用了同样的模型,这里难道说text-prompt最后的embedding转化结果就是image的 latent space vector?这个也是需要进一步理解的。到时候水到渠成,瓜熟蒂落。这里的插曲是depth image。作 者专门有一篇教程来利用它做一些高难度的动作,这个似乎看上去像是controlNet异曲同工之效,所有的text-prompt都是某种 conditioning,问题是如何才是最有效的,动作指引edge detection以及其他做法从直觉上看是给text-image过程一个类似模板的东西,就是说在Gaussian noise下原始的信号是什么?你给的越是贴近目标那么降噪的偏差就越小,这个是不言而喻的。原理人人都明白,但是魔鬼都在细节。 Knowing the path is totally diffferent from actually walking the path.很多人不屑于具体的操作法门,认为那是不重要的实作,然而练拳不练功,到头一场空,作为战场格斗的士兵的基本职能并不是你自己以为的武林宗师一样要你在观众席上作不负责任的指指点点。
  4. 作者要用这个浪漫场景来生成两个男人摔跤的图片,我想实践看看效果如何。
    treat depth-to-image as an enhanced version of image-to-image. They can be used in exactly the same way — given an image and a text prompt, it will generate a new image.
    用普通的img2img产生的就是和图片人体形体基本无关的摔跤场景。那么depth-to-image在哪里呢?不过这里让我亲身感到如果denoise调的较小的话,那么产生的图片就和原来的基本一致,仿佛就是这个量是一个变形参数一样。
  5. 这里才是关键的怎么做之前的是什么
    In depth-to-image, Stable Diffusion similarly takes an image and a prompt as inputs. The model first estimates the depth map of the input image using MIDaS, an AI model developed in 2019 for estimating monocular depth perception (that is estimating depth from a single view). The depth map is then used by Stable Diffusion as an extra conditioning to image generation.
    一言以蔽之:an extra conditioning
    In other words, depth-to-image uses three conditionings to generate a new image:
    1. text prompt
    2. original image
    3. depth map
    但是这里的foreground-object和background是什么呢?
    Equipped with the depth map, the model has some knowledge of the three-dimensional composition of the scene. Image generations of foreground objects and the background can be separated.
  6. 作者似乎是使用这个网站来生成depth-image。
  7. 在国内就是时时刻刻被网络所困扰,我发现我昨天以为成功的设置又都失效了,似乎tun0这个自己更新了?总之,resolvectl status可以看到原来的设置消失了。我又要重复一下,以后看能不能用脚本运行,不过这些需要sudo权利,是否在D-Bus那里可以使用user权利?
  8. 回到问题,设置midas。我下载了它的模型以及配置文件,按照作者的步骤重复了一遍,从摔跤的动作来看的确是保全了原来图像的形态,但是人物形 象让人惨不忍睹,这个和模型的质量有关吧?或许没有添加相关的negative prompt?总之,并不是令人满意。作者又说了一些应用场景,比如换脸的时候如果要保持原图的形态,那么denoise就不能很大,结果不会太好。使用 depth-image模型就可以自由调整denoise参数任意变化。不过我感觉效果也不像作者的结果那么理想,也许还有别的参数作者没有提到?或者我 的模型配置问题?总之,这个是一个有意义的尝试。
  9. 顺便说一下,如果使用国内的DNS解析raw.githubusercontent.com,结果可能就是0.0.0.0,这个也许是DNS的cache也许是人为设置障碍,总之这个是正确的解析:
    
    $ nslookup raw.githubusercontent.com
    Server:		127.0.0.53
    Address:	127.0.0.53#53
    
    Non-authoritative answer:
    Name:	raw.githubusercontent.com
    Address: 185.199.111.133
    Name:	raw.githubusercontent.com
    Address: 185.199.109.133
    Name:	raw.githubusercontent.com
    Address: 185.199.110.133
    Name:	raw.githubusercontent.com
    Address: 185.199.108.133
    Name:	raw.githubusercontent.com
    Address: 2606:50c0:8000::154
    Name:	raw.githubusercontent.com
    Address: 2606:50c0:8001::154
    Name:	raw.githubusercontent.com
    Address: 2606:50c0:8002::154
    Name:	raw.githubusercontent.com
    Address: 2606:50c0:8003::154
    
  10. 这些当然都是很好的应用场景。不过我对于这个模型的质量还是将信将疑。比如我可以使用img2txt的方式先获得相当准确的prompt,例如对于La La Land我使用Interrogate DeepBooru获得的相当详细的prompt:
    
    1boy, 1girl, brown hair, city, city lights, dancing, dress, facial hair, fireworks, high heels, necktie, night, outdoors, outstretched arm, outstretched arms, outstretched hand, pants, road, shoes, short hair, sky, spread arms, street, tree, water, yellow dress
    
    然后再txt2img得到的结果也不算很差劲儿 应该说卡通化可以掩盖大部分的缺陷,这个也许是目前的一个解决办法。
  11. 按照从易到难的程度,text,audio,video,所以,我想看看这个whisper项目,这个也是OpenAI的开源项目吧?有开源吗?
    
    git clone --recursive https://github.com/openai/whisper
    cd whisper
    pip install .	
    whisper --model "medium" --output_format srt --verbose True --task transcribe --language Mandarin --output_dir ./output output.mp3
    ffmpeg -i input.mp4 -vf subtitles=./output/output.srt ./output.mp4
    
    效果只能说还可以,因为有些专有名字我们人类可以很容易识别,但是模型不行,此外,有些我也听不出的部分机器就乱翻了。
  12. 这个cross attention我反复看到过,但是不理解它的意义:
    The output of the text transformer is used multiple times by the noise predictor throughout the U-Net. The U-Net consumes it by a cross-attention mechanism. That’s where the prompt meets the image.
    它是在U-Net里?被noise predictor使用的机制?
    A side note: Hypernetwork, a technique to fine-tune Stable Diffusion models, hijacks the cross-attention network to insert styles. LoRA models modify the weights of the cross-attention module to change styles. The fact that modifying this module alone can fine-tune a Stabe Diffusion model tells you how important this module is.
    这就意味着我需要了解LoRA
    LoRA (Low-Rank Adaptation) is a training technique for fine-tuning Stable Diffusion models.
    也就是说训练模型是费时费力的,修改模型也是费时费力,更加麻烦的是模型文件的大小经常是好几个G,那么TI比较小,但是功能有限,而LoRA大小只有几百兆比较适中。
    LoRA applies small changes to the most critical part of Stable Diffusion models: The cross-attention layers. It is the part of the model where the image and the prompt meet.
  13. 这幅图描述了整个流程: 这幅图是从这篇stable diffusion paper里来的。再看看论文作者的关于latent space的描述:
    Compared to the high-dimensional pixel space, this space is more suitable for likelihood-based generative models, as they can now
    1. focus on the important, semantic bits of the data and
    2. train in a lower dimensional, computationally much more efficient space.
    这是diffusion model的数学描述:
    Diffusion Models are probabilistic models designed to learn a data distribution p(x) by gradually denoising a nor- mally distributed variable, which corresponds to learning the reverse process of a fixed Markov Chain of length T . For image synthesis, the most successful models rely on a reweighted variant of the variational lower bound on p(x), which mirrors denoising score-matching. These models can be interpreted as an equally weighted sequence of denoising autoencoders εΘ (xt , t); t = 1 . . . T , which are trained to predict a denoised variant of their input xt , where xt is a noisy version of the input x. The corre- sponding objective can be simplified to L DM = E x,ε∼N (0,1),t [ ||ε - ε Θ ( x t ,t) || 2 2 ]
    我费了好大的力气去写这个公式,其实根本就不理解数学符号的意义,但是从描述大概能够猜出来可能的大概意思。就是说。。。
  14. 这里是关于LoRA的论文,而关于它的诀窍就是
    The weights of a cross-attention layer are arranged in matrices. A LoRA model fine-tunes a model by adding its weights to these matrices. The trick of LoRA is breaking a matrix into two smaller (low-rank) matrices.
    因为LoRA就是weight matrix。而我现在的直觉就是计算机里最普通的关于压缩的小技巧,针对matrix里大量的0,完全可以舍弃掉而只储存那些有数值的部分。这个是任何一个人都能想得到的方法。
    ...the learned over-parametrized models in fact reside on a low intrinsic dimension. We hypothesize that the change in weights during model adaptation also has a low “intrinsic rank”...LoRA allows us to train some dense layers in a neural network indirectly by optimizing rank decomposition matrices of the dense layers’ change during adaptation instead, while keeping the pre-trained weights froze
    这里有LoRA的源代码, 在其说明文字里解释的就更清楚了,它不是shipment的节约,因为你还是需要原始的模型,但是训练的时候你节约了,因为有很多的参数不需要训练。那么 我认为运行期模型需要的资源也相对减少了。所以,你可以在原来模型基础上训练自己的模型,而且更省力。这个专业的说法叫做fine-tuning。 这里是LoRA的优点
    • A pre-trained model can be shared and used to build many small LoRA modules for dif- ferent tasks. We can freeze the shared model and efficiently switch tasks by replacing the matrices A and B in Figure 1, reducing the storage requirement and task-switching over- head significantly.
    • LoRA makes training more efficient and lowers the hardware barrier to entry by up to 3 times when using adaptive optimizers since we do not need to calculate the gradients or maintain the optimizer states for most parameters. Instead, we only optimize the injected, much smaller low-rank matrices.
    • Our simple linear design allows us to merge the trainable matrices with the frozen weights when deployed, introducing no inference latency compared to a fully fine-tuned model, by construction.
    • LoRA is orthogonal to many prior methods and can be combined with many of them, such as prefix-tuning.
    简而言之,训练效率提高,部署有优势(假定通用模型已经部署,只是分发fine-tuned的LoRA模型),运用时候没有额外成本,方法独立可以和其他方法并用。就是说它是训练方便,使用没有区别。
  15. 补习一下基本概念:
    An autoregressive language model is a type of Machine Learning model that uses autoregressive techniques to predict the next word in a sequence of words based on the words that have come before it.
    可以说现在通常所说的模型都是AutoRegressive language model。
  16. 我的幼稚在于只能理解都是0的matrix部分,就是linearly dependent的部分,而真正的可以忽略的还包含了那些非独立的向量部分
    In linear algebra, the rank of a matrix A is the dimension of the vector space generated (or spanned) by its columns. This corresponds to the maximal number of linearly independent columns of A.
    这么看起来矩阵其实可以分别计算它的行与列的rank,而最大值代表了矩阵的rank。A fundamental result in linear algebra is that the column rank and the row rank are always equal.
    The column rank of A is the dimension of the column space of A, while the row rank of A is the dimension of the row space of A.
    而这个matrix的rank只有2,因为column1和column2的线性组合是column3。 [ 1 0 1 0 1 1 0 1 1 ]
  17. 这里是html里的math部分,很多复杂公式需要用到它。
  18. 这里的解释我就立刻明白为什么decomposition rank等同于matrix factorization了。

    Decomposition rank

    The rank of A is the smallest integer k such that A can be factored as A = C R, where C is an m × k matrix and R is a k × n matrix. In fact, for all integers k, the following are equivalent:
    1. the column rank of A is less than or equal to k,
    2. there exist k columns c1 , … , ck of size m such that every column of A is a linear combination of c1 , … , ck,
    3. there exist an m × k matrix C and a k × n matrix R such that A = C R (when k is the rank, this is a rank factorization of A),
    4. there exist k rows r1 , … , rk of size n such that every row of A is a linear combination of r1 , … , rk,
    5. the row rank of A is less than or equal to k.
    Indeed, the following equivalences are obvious: ( 1 ) ⇔ ( 2 ) ⇔ ( 3 ) ⇔ ( 4 ) ⇔ ( 5 ) . For example, to prove (3) from (2), take C to be the matrix whose columns are c1 , … , ck from (2). To prove (2) from (3), take c1 , … , ck to be the columns of C.
    从这就可以看出来我之前的直觉也没有错,如果部分的matrix是0,那当然可以舍弃,其实这个是从化简后的row/column echelon form的角度来看问题,直觉能看到0却没有看到线性相关,当然线性相关意味着可以被化简为0。所以,最深奥的理论往往来自于最肤浅的直觉。
  19. 从一篇好的论文能够牵扯到一堆的好的论文,这就是好的论文的意义。花花轿子人抬人。这篇论文据说有谈到GPT的架构。
    In this work we propose the Transformer, a model architecture eschewing recurrence and instead relying entirely on an attention mechanism to draw global dependencies between input and output. The Transformer allows for significantly more parallelization and can reach a new state of the art in translation quality after being trained for as little as twelve hours on eight P100 GPUs.
    要明白这里的关键就是attention。而要明白作者提出的cross attention首先要明白原先的Self-attention
    Self-attention, sometimes called intra-attention is an attention mechanism relating different positions of a single sequence in order to compute a representation of the sequence. Self-attention has been used successfully in a variety of tasks including reading comprehension, abstractive summarization, textual entailment and learning task-independent sentence representations.
  20. 这里既要补习英语也要补习概念
    Transduction (machine learning), the process of directly drawing conclusions about new data from previous data, without constructing a model
    而这一段解释非常的深奥:
    In logic, statistical inference, and supervised learning, transduction or transductive inference is reasoning from observed, specific (training) cases to specific (test) cases. In contrast, induction is reasoning from observed training cases to general rules, which are then applied to the test cases. The distinction is most interesting in cases where the predictions of the transductive model are not achievable by any inductive model. Note that this is caused by transductive inference on different test sets producing mutually inconsistent predictions.
    这些概念的语言版本有莫名其妙的少数族裔语言,比如亚美尼亚,波斯,俄语,可是居然没有汉语! 就是说要解决逻辑推理苏格拉底是人类需要从具体到一般的归纳总结能力吗?
    induction requires solving a more general problem (inferring a function) before solving a more specific problem. When solving a problem of interest, do not solve a more general problem as an intermediate step. Try to get the answer that you really need but not a more general one.
    这的确是一个很深奥的问题,这个quora回答的大致和wiki相似,不过更加的形象生动一些。 这个抽象的概括总结一开始把人吓死了:

    Inductive learning is nothing but the principle behind the supervised machine learning algorithms where a model tries to build a relationship between the feature variables and target variable by examining the hidden patterns in the train data. Although the model is exposed to a restricted scope of the training data, the learning of the model will be according to a generic nature of data such that it can predict the value of any data point from an unlabelled dataset (test dataset). This kind of learning is termed inductive learning. It is to be noted here that the model is not exposed to the test data during the learning phase and is only provided with the training data for the learning purpose.p

    In transductive learning, both training and testing data set is exposed to the model in the learning phase itself. The model tries to find any information about the pattern in the combined dataset (training + testing) and later uses this information for predicting the values of the unlabelled testing data points.

    其实如果这么解释就好懂的多了,inductive是一种严格的supervised learning模式,它有着优越的地方在于它的学习数据被严格筛选过,就是说学习数据都有标记;而与之相对应的transductive则没有那种奢侈 可以事先把学习数据严格标记,只有部分标记,也不存在inductive那样可以用同样严格筛选过的实验数据来检验。粗略的看就是 transductive就是时间紧任务重来不及准备充分就让你去边学边练。
    • Inductive learning trains the model with labeled data points and tries to predict the label of unlabeled data points. However, transductive learning trains the entire data set and tries to predict the label of unlabeled data points.
    • In inductive learning, if a new unlabelled data point is getting introduced then we can use the already trained model for the prediction. However, in transductive learning, we may need to retrain the entire model.
    • Transductive learning is computationally expensive than inductive learning.
  21. 我今天也不学了,机器学习,我也学习。累死了。

二月十九日 等待变化等待机会

  1. 我以为革命发生在Departure to Latent Space。不要仅仅以为它是某种压缩技术的革命,量变引起的 质变往往在它发生的时候被大多数人所忽视。因为它从内在触及了图形识别的本质,图形本身的意义部分(semantic),和图形可识别部分 (perceptual)是两个物件,而压缩的本质就是最大限度保留semantics而忽略所谓的imperceptual的部分。这篇stable-diffusion-paper读多少遍都不为过,它太重要了。

    Departure to Latent Space

    Our approach starts with the analysis of already trained diffusion models in pixel space.
    这里说的是作者出发地。几乎所有的革命性发现发明都是建立在前人的成果基础上的,所以,站在巨人的肩膀上。
    As with any likelihood-based model, learning can be roughly divided into two stages: First is a perceptual compression stage which removes high-frequency details but still learns little semantic variation. In the second stage, the actual generative model learns the semantic and conceptual composition of the data (semantic compression).
    1. 这个是极其精炼的总结,短短的文字蕴藏了几乎无限的信息。首先定义对象是likelihood-based model,这个背后的含义就是巨大的,其实最基本的概念的建立有时候花的时间是超出想象的长,现在这些模型的本质是什么?或者说它里面究竟存了些什么呢?我没有看过,没有感性认识。我没有具体实践过。但是提纲挈领的断言它是一个概率模型就是一锤定音的。
    2. 第一阶段在做什么?perceptual compression的具体含义是什么?什么又是perceptual的内容呢?或者说什么是high-frequency details?怎么界定频率?怎么界定细节?为什么说learns little semantic variation?首先什么是semantic variation?它怎么学?体现学习的结果是什么?甚至于什么是variation?我反复看到但是始终无法理解它的含义。中文一言以蔽之就是变化,可是在数学上它是有特定含义的吧?我在线性代数的定义看到
      A variation is a relation between a set of values of one variable and a set of values of other variables.
      这个在我看来更像是映射的定义? 这里的定义似乎更加的详细:
      In problems relating to two or more variables, it is seen that the value of a variable changes with the change in the value ( or values ) of the related variable (or variables). Suppose a train running at a uniform speed of v km./h. travels a distance of d km. in t hours. Obviously, if t remains unchanged then v increases or decreases according as d increases or decreases. But if d remains unchanged, then v decreases or increases according as t increases or decreases. This shows that the change in the value of a variable may be accompanied differently with the change in the values of related variables. Such relationship with regards to the change in the value of a variable when the values of the related variables change, is termed as variation.
      在概率论里我们定义随机变量的前提是有一个所谓的随机过程,所以,我们可以相类似的定义一个相关变量的数值变化而引起的一个变化过程,我们关注的是相关变量数值变化的对应关系。我想中文的所谓的简而言之的变化似乎遗漏了巨大的特定数学含义吧?
    3. 为什么说semantic and conceptual composition of the data就是semantic compression?什么地方体现了压缩呢?semantic和conceptual composition of data分别指的是什么?
  2. 线性代数的继续学习一旦中断就被忘记了。以后吧。原因是我以为从根本上来说LoRA是一个改进,而目前要了解最根本的原理比了解改进来的重要的多。并且这个都是如何实现改进的细节,先放一放吧。
  3. 我如果看完论文依然不能回答这些问题那就是失败。作者写了他们研究的动机:
    We thus aim to first find a perceptually equivalent, but computationally more suitable space, in which we will train diffusion models for high-resolution image synthesis.
    要记住这个就是所谓的latent space的由来,就是从识别的角度来看是等价的但是计算量小的多的空间。
  4. we train an autoencoder which provides a lower-dimensional (and thereby efficient) representational space which is perceptually equivalent to the data space.
    这个部分回答了之前的问题,lower-dimensional representational space当然是目的,只有降低维度才能降低计算量,从向量的角度来看当然就是减少其中的元素。矩阵就复杂一些,LoRA的就是一种比较高级的降低维度的技巧。但是怎么能够做到perceptually equivalent呢?关键是这里。
  5. Importantly, and in contrast to previous work, we do not need to rely on excessive spatial compression, as we train DMs in the learned latent space, which exhibits better scaling properties with respect to the spatial dimensionality. The reduced complexity also provides efficient image generation from the latent space with a single network pass. We dub the resulting model class Latent Diffusion Models (LDMs).
    这个是十分重要的总结,就是在实现手段上一开始就否定了方法的本质不是所谓的空间压缩,这个是太低级了?因为传统的压缩是欺骗眼睛,当然眼睛背后是用来识别的大脑。我的理解就好像说普通的视频压缩对于大脑来说还是无损的压缩,故而压缩的力度还不高,而从generative的角度来看不需要这么高保真,可以失去更多的细节只留下概念性的部分?这些是我的猜测,在后面的阅读看看对不对?同时最重要的是这里点出了模型的名称LDM
  6. A notable advantage of this approach is that we need to train the universal autoencoding stage only once and can therefore reuse it for multiple DM trainings or to explore possibly completely different tasks
    这里点出了模型的可复用性让我有些吃惊,难道这不就是模型的意义吗?能够复用,否则为什么叫做模型呢?难道之前的所谓的模型是不可复用的?或者说之前的智能学习还没有建立可复用的模型?让我们再次学习autoencoder的定义
    An autoencoder is a type of artificial neural network used to learn efficient codings of unlabeled data (unsupervised learning). An autoencoder learns two functions: an encoding function that transforms the input data, and a decoding function that recreates the input data from the encoded representation. The autoencoder learns an efficient representation (encoding) for a set of data, typically for dimensionality reduction.
    所以,这个是在unsupervised learning阶段的分类标签学习过程,目的是建立降维打击手段得到的输入数据的表达方式,同时能够完满的逆过程把输入数据再用降维后的表达来还原。这个就是一个高级的识别过程,因为真正的认知不是简单的像素级的细节比对,也不是简单的猫有四条腿,狗有四条腿,所以猫就是狗,狗就是猫的简单特征推理。总之,真正的识别必然是去除大量不重要细节的抓主要矛盾的压缩,这个压缩的概念不同于数据的压缩而是在高维度到低维度的压缩,是概念性的提取特征,是不依赖于各种各样不同品种的狗的特征细节而能够一眼望去肯定的回答什么是一只狗的概念性压缩后的特征提取。我以为这个是非常的高级的智能学习步骤,因为几乎所有的智能过程都充斥着对象的识别与认知,而这个最最基本的功能是其他高级能力的基础。
  7. 进一步补习autoencoder的定义

    An autoencoder is defined by the following components:

    Two sets: the space of decoded messages X; the space of encoded messages Z. Almost always, both X and Z are Euclidean spaces, that is, X = Rm , Z = Rn for some m , n .

    Two parametrized families of functions: the encoder family Eϕ : X → Z, parametrized by ϕ ; the decoder family Dθ : Z → X , parametrized by θ .

    For any x ∈ X , we usually write z = Eϕ ( x ) , and refer to it as the code, the latent variable, latent representation, latent vector, etc. Conversely, for any z ∈ Z , we usually write x ′ = Dθ ( z ) , and refer to it as the (decoded) message.

    Usually, both the encoder and the decoder are defined as multilayer perceptrons. For example, a one-layer-MLP encoder Eϕ is:

    E ϕ ( x ) = σ ( W x + b )

    where σ is an element-wise activation function such as a sigmoid function or a rectified linear unit, W is a matrix called "weight", and b is a vector called "bias".

    这些是数学的严格定义,还有关于如何检验autoencoder的质量牵扯出了一个Gradient descent的概念。
  8. we design an architecture that connects transformers to the DM’s UNet backbone and enables arbitrary types of token-based conditioning mechanisms
    作者的设计的架构是什么意思?UNet的定义是什么?

二月二十一日 等待变化等待机会

  1. 论文作者自己宣称的贡献实际上是非常重要的部分,是理解这篇论文重要性的核心部分:
    1. In contrast to purely transformer-based approaches, our method scales more graceful to higher dimensional data and can thus (a) work on a compression level which provides more faithful and detailed reconstructions than previous work (see Fig. 1) and (b) can be efficiently applied to high-resolution synthesis of megapixel images. 这里的关键词是压缩方式的有效性,能够忠实还原是压缩的基本要求,压缩效率更高是其存在意义的基础。 压缩效果的比较是硬碰硬的指标,不论你的算法有多么高明,压缩率有多高,但是一旦你的还原质量不够,那几乎就立刻被判了死刑了。 这幅图是阅读理解的关键,而其中的含义是非常深刻的,我至今还是不能完全掌握。这里要先补课学习gradient的概念,一言以蔽之,
      A gradient is a derivative of a function that has more than one input variable.
      这个是它的定义,但是要真正理解它还需要理解它的作用:
      The gradient is the generalization of the derivative to multivariate functions. It captures the local slope of the function, allowing us to predict the effect of taking a small step from a point in any direction.
      说到底是给你一个工具来判断那个方向变化最快从而给你一个获取极值的方向。这个当然是局部优化的概念,但是通常简单有效。
    2. We achieve competitive performance on multiple tasks (unconditional image synthesis, inpainting, stochastic super-resolution) and datasets while significantly lowering computational costs. Compared to pixel-based diffusion approaches, we also significantly decrease inference costs.
      它的压缩效果是和谁相比?当然是和传统的像素空间,那里充斥了大量的无意义的细节,对于保持semantic并无多大帮助,这个也是引入latent space的最核心原因,这个也就是目前AGC能够流行起来的最根本原因。量变引发的质变,不要简单的认为一个压缩算法就不能带来革命,当前的人工智能大 多数想法并不是什么绝对的新鲜概念,很多都是几十年前就有了,只不过当时的算力不足以支撑而不被看好,今天的想法也不是前人想不到只是认为当时的现实做不 到而不被人看好而已。
    3. We show that, in contrast to previous work which learns both an encoder/decoder architecture and a score-based prior simultaneously, our approach does not require a delicate weighting of reconstruction and generative abilities. This ensures extremely faithful reconstructions and requires very little regularization of the latent space.
      这里的意义也是非常重大的,这里也许就是模型的本质意义,能够轻易的复用是一个模型的最根本的意义,不论多么复杂的训练过程,模型一旦完成,可以很简单的 重复使用达到它的逆过程的还原而无需做过多的调整配置修改。这个是架构革命的重大创新,我的觉得这个意义是仅次于压缩的大贡献。
    4. We find that for densely conditioned tasks such as super-resolution, inpainting and semantic synthesis, our model can be applied in a convolutional fashion and render large, consistent images of ∼ 10242 px.
      它的应用场景也是其重大贡献,否则一个无用的发明不论多么精巧高明都是无意义的玩具而已,对于实际工作需要高强度的扩展这种最基本的需求的满足是足以夸耀的贡献!
    5. Moreover, we design a general-purpose conditioning mechanism based on cross-attention, enabling multi-modal training. We use it to train class-conditional, text-to-image and layout-to-image models.
      这里是非常的高级的功能,这个cross-attention是有一个核心的贡献,而它能够让多个模型同时训练更是超出我目前能够理解的范畴,这个需要在随后的阅读里深刻理解领会。难道可以把controlNet之类的形体指示,或者深度图之类的作为提示条件来综合训练?这个也是开创性的贡献。
    6. Finally, we release pretrained latent diffusion and autoencoding models at https://github.com/CompVis/latent-diffusion which might be reusable for a various tasks besides training of DMs
      还有什么共享更高尚的贡献呢?一个伟大的发明创造如果不是为了人类的共同福祉而共享,那么它的意义终将是有限的。更何况它的用途并不限于训练模型,这个更加令人神往。
  2. 所谓站在巨人的肩膀上,如果对于前人的工作不明白其优劣处,那谈何创新改进?非常多的人包括我自己都是在现有前人工作都不甚了了的情况下就空谈什 么新方向,殊不知别人很可能已经走过或者其它方法早已解决了你所谓的不足只是你不知道而已。这里的总结言简意赅,我需要很多的理解:
    1. The high dimensional nature of images presents distinct challenges to generative modeling.
      这里开宗明义说出了问题和挑战,这才引出了各个方向方法的孰优孰劣。
    2. 惜秦皇汉武,略输文采;
      Generative Adversarial Networks (GAN) allow for efficient sampling of high resolution images with good perceptual quality, but are difficult to optimize and struggle to capture the full data distribution.
      GAN虽然不错,但是很难优化,而且不能掌握全部的数据分布。
    3. 唐宗宋祖,稍逊风骚;
      Variational autoencoders (VAE) and flow-based models enable efficient synthesis of high resolution images, but sample quality is not on par with GANs.
      VAE在我看来和作者的过程有着非常相似的部分,这个是需要格外注意理解的。
      Variational autoencoders are often associated with the autoencoder model because of its architectural affinity, but with significant differences in the goal and mathematical formulation. Variational autoencoders are probabilistic generative models that require neural networks as only a part of their overall structure.
      VAE似乎就是autoencoder,但是又不同?很复杂。
      The neural network components are typically referred to as the encoder and decoder for the first and second component respectively. The first neural network maps the input variable to a latent space that corresponds to the parameters of a variational distribution. In this way, the encoder can produce multiple different samples that all come from the same distribution. The decoder has the opposite function, which is to map from the latent space to the input space, in order to produce or generate data points. Both networks are typically trained together with the usage of the reparameterization trick, although the variance of the noise model can be learned separately.
      就是说VAE只是利用了autoencoder架构的encoder/decoder而已?很难理解其中的微妙区别。这里有大量的概率数学部分,照例我准 本忽略,因为我的感觉实际上VAE的核心还是要假设prio和posterior的概率分布模型,这个直接关系到还原噪音的效果,到底是正态分布还是波努 力分布这似乎要根据具体的现实吧?总之,我看起来很吃力。暂时先放一放吧。
    4. 一代天骄,成吉思汗,只识弯弓射大雕。
      While autoregressive models (ARM) achieve strong performance in density estimation, computationally demanding architectures and a sequential sampling process limit them to low resolution images. Because pixel based representations of images contain barely perceptible, high-frequency details, maximum-likelihood training spends a disproportionate amount of capacity on modeling them, resulting in long training times. To scale to higher resolutions, several two-stage approaches use ARMs to model a compressed latent image space instead of raw pixels.
      说老实话,我根本不明白autoregressive (AR) modelVariational autoencoders (VAE)到底有什么不同?这里的回答比较清楚

      Autoregressive models are basically modeling a time series, or a random process. They can be used in VAEs as well, which is what happens in the case of text, the decoder models p(x|z) in an autoregressive way, i.e the current word to be predicted is dependent on the previously predicted words.

      Variational Autoencoders are a general representation learning and generative modeling framework, they try to model your data, by learning a latent variable representation p(z|x) and then generate p(x|z). They use variational inference to estimate these distributions accurately. i.e they assume a general class of distributions and then use an optimization scheme to find parameters that allow them to match the target distribution well.

      The idea behind normalizing flows is that given a simple distribution, you can perform invertible transformations on them to get more complex distributions, if you can compute the log probabilities of these transformed distributions efficiently, then basically you can perform variational inference with more complex distributions, which might help.

      Looking at the definitions it is clear that all of them are interconnected, you can use normalizing flows to improve the class of distributions you are using in a VAE, you can use an autoregressive decoder to generate p(x|z) if your data is sequential.

      能不能理解,虽然autoregressive指的是泛泛的概率模型,但是VAE更加具体是一个和时间相关的随机过程,而flow-based model更像是一个原则性的假设你能够根据逆过程计算原本的概率分布,这个似乎是encoder/decoder的理论基础?总之,它们是非常紧密联系 的,这是我唯一能够确定的。
    5. 俱往矣,数风流人物,还看今朝。 作者的细节描述是很复杂的我基本上看不懂,只有一点似乎可以明白一点,就是因为训练是在低维度的latent space,所以,大大降低了计算需求,这个公式有一个细小的变化。
      L DM = E ε(x),ε∼N (0,1),t [ ||ε - ε Θ ( x t ,t) || 2 2 ]
      就是说从x变成ε(x)是大大减少了计算强度。
    这里补习一下概念Fréchet inception distance (FID)
    The Fréchet inception distance (FID) is a metric used to assess the quality of images created by a generative model, like a generative adversarial network (GAN). Unlike the earlier inception score (IS), which evaluates only the distribution of generated images, the FID compares the distribution of generated images with the distribution of a set of real images ("ground truth").
  3. 看论文非常的难,只能由浅入深,逐步反复,我想目前还是忽略实现细节,领会论文的贡献和原理吧。它所引用的大量文献是接下来阅读的一个方向,否则 这样子看论文是对作者的不尊重:你能够在没有了解引用的论文的基础上就明白这篇论文岂不是说你比作者更强吗?这是多么的荒谬而且不严肃。先休息一下吧。
  4. 看到老爸发来的《枕上诗书》非常优美:
    千人同尝不同味,
    万人同道不同心。
    有人理解我之幸,
    无人理解我独行。
    知我者慰我心忧,
    不知我者谓我何求?
    世间万物皆可有,
    唯有懂字最难求。
    

二月二十二日 等待变化等待机会

  1. 顺便记录一个视频,算是说Sora的比较靠谱的.
  2. 这篇Attention is all you need论文也是一片非常重要的论文。这个应该是所谓的Transformer的最早提出的。
    A transformer is a deep learning architecture based on the multi-head attention mechanism, proposed in a 2017 paper "Attention Is All You Need". It has no recurrent units, and thus requires less training time than previous recurrent neural architectures, such as long short-term memory (LSTM), and its later variation has been prevalently adopted for training large language models on large (language) datasets, such as the Wikipedia corpus and Common Crawl. Input text is split into n-grams encoded as tokens and each token is converted into a vector via looking up from a word embedding table. At each layer, each token is then contextualized within the scope of the context window with other (unmasked) tokens via a parallel multi-head attention mechanism allowing the signal for key tokens to be amplified and less important tokens to be diminished. The transformer paper, published in 2017, is based on the softmax-based attention mechanism proposed by Bahdanau et. al. in 2014 for machine translation, and the Fast Weight Controller, similar to a transformer, proposed in 1992.
    所以,这里究竟有多少概念需要学习呢?
    1. 首先Transformer它是一个架构而不是一个模型,因为模型这个中文词有时候会被理解为某种架构的含义。它不是数据模型,而是针对RNN的 一种变革。而且它是attention机制。当然这个定义也是不妥的,因为一个架构必然对应某种类型的数据存储格式,所以,Transformer也必然 有其对应的数据模型,只不过,所谓的模型只有当它的训练结果能够轻而易举的复用作为检验或者应用的时候才能称之为模型,否则就是一些内部存储的原始数据而 已。
    2. 要补习的基础知识太多了,什么是Outer Product呢?它的现实意义是什么呢?不同于寻找两个向量方向差异的点乘,外乘代表了两个向量空间的融合或者是交锋,因为结果是一个矩阵,它代表了两个向量在每一个维度上的比较。
      Given two vectors of size m × 1 and n × 1 respectively, u = [ u1 u2 . . . um ] , v = [ v1 v2 . . . vn ] their outer product, denoted u ⊗ v , is defined as the m × n matrix A obtained by multiplying each element of u by each element of v : u v = A = [ u1 v1 u1 v2 . . . u1 vn u2 v1 u2 v2 . . . u2 vn . . . . . . . . . . .. um v1 um v2 . . . um vn ] Or, in index notation: ( u v ) i j = u i v j
    3. 这里是Mathtml的官方网页,至少我看是比较靠谱?花这么多时间去学习简单的mathtml实际上也是一种加深记忆的过程。
    4. 那么什么是RNN呢?这里的一个解释定义似乎是比较准确了。
      A recurrent neural network (RNN) is a type of artificial neural network which uses sequential data or time series data. These deep learning algorithms are commonly used for ordinal or temporal problems, such as language translation, natural language processing (nlp), speech recognition, and image captioning; they are incorporated into popular applications such as Siri, voice search, and Google Translate. Like feedforward and convolutional neural networks (CNNs), recurrent neural networks utilize training data to learn. They are distinguished by their “memory” as they take information from prior inputs to influence the current input and output. While traditional deep neural networks assume that inputs and outputs are independent of each other, the output of recurrent neural networks depend on the prior elements within the sequence. While future events would also be helpful in determining the output of a given sequence, unidirectional recurrent neural networks cannot account for these events in their predictions.
      这里要注意的是结果也能再影响下一次的输入结果,似乎预示着某种动态的模型?也许这个就是其中提到的时间敏感性的意思?wiki的解释一般更加的权威,而且把CNN放到一起来对比是更加的精准全面。
      A recurrent neural network (RNN) is one of the two broad types of artificial neural network, characterized by direction of the flow of information between its layers. In contrast to the uni-directional feedforward neural network, it is a bi-directional artificial neural network, meaning that it allows the output from some nodes to affect subsequent input to the same nodes. Their ability to use internal state (memory) to process arbitrary sequences of inputs makes them applicable to tasks such as unsegmented, connected handwriting recognition or speech recognition. The term "recurrent neural network" is used to refer to the class of networks with an infinite impulse response, whereas "convolutional neural network" refers to the class of finite impulse response. Both classes of networks exhibit temporal dynamic behavior. A finite impulse recurrent network is a directed acyclic graph that can be unrolled and replaced with a strictly feedforward neural network, while an infinite impulse recurrent network is a directed cyclic graph that can not be unrolled.
      流动是双向的?反复看到所谓的Long short-term memory
      Long short-term memory (LSTM)[1] network is a recurrent neural network (RNN), aimed to deal with the vanishing gradient problem present in traditional RNNs. Its relative insensitivity to gap length is its advantage over other RNNs, hidden Markov models and other sequence learning methods. It aims to provide a short-term memory for RNN that can last thousands of timesteps, thus "long short-term memory".
  3. 我学习这些基本概念的意义目的在哪里?显然不是为了学术研究,是为了帮助理解用途?照理说这个目的性非常容易满足,可是关键是到什么程度?能不能 把各个相关联的概念串起来理解也许就是检验我的学习效果的标准吧?只不过缺失的链条与基础太多了,任何一句阅读都往往碰到一大堆的似曾相识,甚至完全陌生 的概念与名词。这个也是大多数人在这个领域遇到的困难吧?毕竟这些是积累了几十年的研究成果演化而来的,很多人为此付出毕生的努力才前进一小步,而我等贩 夫俗子想要在短时间内站在巨人的肩膀上那是痴心妄想。
  4. 针对最近很火爆的Sora的现象级热评,我希望我也能看到它的真实的内在机制,但是没有基础只能人云亦云。我觉得还是要深刻深入理解到底什么是transformer的机制,只有从最根本的基础开始才有可能理解发展的现状和方向。

二月二十三日 等待变化等待机会

  1. 这篇论文开宗明义的介绍之前的模式的核心问题运用了高度的概括和抽象,这个是对于初学者来说很难深刻理解的,但是这种提纲挈领式的总结也是最好的见面礼,因为我读了几遍定义也不如这一段话来的更加的一针见血。
    Recurrent models typically factor computation along the symbol positions of the input and output sequences. Aligning the positions to steps in computation time, they generate a sequence of hidden states ht , as a function of the previous hidden state ht−1 and the input for position t. This inherently sequential nature precludes parallelization within training examples, which becomes critical at longer sequence lengths, as memory constraints limit batching across examples.
    它点出了attention不,应该说是recurrent机制,因为attention是这篇论文提出的,而这个上下 文敏感机制在我看来被说成是attention机制是非常恰如其分的,在《Yes,Minister》里普通选民对于政治家的演讲的注意力就只有三秒,一 旦把一个观点分在两句话里讲的话,听众就会忘记上一句是什么意思,这个就是attention的核心思想,因为人们一次性输入语言的处理的带宽是有限甚至 是固定的,导致超过这个时长或者带宽就必须忘记,这个在recurrent/attention实现里有着非常生动的体现,所以,attention并不 是完全摒弃recurrent模式,而是为了效率把输入性的依赖剔除了,这个是它的天才的做法,当然也是非常自然的做法,因为为了能够并行处理。机制,或者还不如说是recurrent模式本身固有的机制:当前状态是关于当前输入和之前的状态的函数。这个模式本身本来是对于表达上下文敏感性过程的一个很好的方案,但是它固有的上下文依赖性导致对于计算资源成为瓶颈:内存是有限的,并行计算要求摆脱过多的依赖性。
  2. 那么作者的最重要的贡献是什么呢?
    Transformer, a model architecture eschewing recurrence and instead relying entirely on an attention mechanism to draw global dependencies between input and output.
  3. 但是还是要补习概念,要理解RNN就要先明白CNN做了什么,以及能做什么?
    Convolutional neural network (CNN) is a regularized type of feed-forward neural network that learns feature engineering by itself via filters (or kernel) optimization. Vanishing gradients and exploding gradients, seen during backpropagation in earlier neural networks, are prevented by using regularized weights over fewer connections.
    这里的关键字是什么呢?依赖filter优化。而为什么它是convolution机制呢?我之前一直不太理解,现在有些明 白这个是模仿人类五官尤其是视觉作为传感器采集外界光能量刺激的机制,所谓的convolution计算的是光通量,这就是理解的核心,因为积分机制原本 就是研究过程累积的数学方式,那么这个方案它的前提就是和平面以及时序紧密关联的,这是纯粹的依靠视觉来理解人类识别的路径。而RNN则至少是更加的抽象 了一层,因为不论你的眼睛作为传感器采集的具体光信号的总和要怎样存储,最终抽象出来的信号提取,或者是特征提取都是向量来存储,因为即便是向量空间也不 过是一族向量,在本质上不需要强加的空间强相关性,因为在向量空间里的basis是唯一能够识别的基础向量,它们的顺序似乎是不重要的,我以为这个才是处 理提纯过的本征信号。那么作为一般性的思想,CNN要去除空间干扰,RNN去除时序敏感性,这些都是对于一个过程的信号总量采集之后的特征提取方式不同而 已。
  4. 模式是否一定必须是Feed-Forward Neural Network呢? 这个是反映了人类神经元系统的特征还是说仅仅因为当时对于处理反馈机制的资源不足的体现而化简模型呢?我直觉以为不应该过于固化某种固定机制,人类神经元 或者自然形成的机制应该是通过巨大时间维度筛选出来的,而筛选过程应该有尝试多种机制,唯一存活的标准是有效性和稳定性以及发展性。如果一个机制不能还原 抽象或者说提取的特征回到本来的客观现实,那么就无法验证抽象的可靠性,那么它就是无效或者说无用的;同样的如果还原过程的不稳定和不可靠是一个质量衡量 标准;那么发展性要求的是这个抽象还原机制有自我提高的机制,也就是学习性,能够随着训练数量的增长而提高有效性与可靠性,这个正增长机制不论多么微不足道随着训练数量的增加它一定能够达到某种质的飞跃,也就是增长或者学习能力的几何级数的增加。这个是正反馈的机制。
  5. 如果要对于人类的学习能力有更深刻的理解的话应该要去研究人脑机制。这个真的是无穷无尽的海洋啊。
  6. 很容易就被带偏的原因是每一句都是大量的概念,很多都是闻所未闻,当然这也取决于到底能够领会多少,学习的目的和目标是什么?在不确定不明确的情 况下,四处遨游也是一种吸收养分水分的最好的模式,重要的往往被反复强调与引用,那么在多篇论文反复强调引用的东西往往是重要的。而重要的东西往往是可以 被反复灌输的,所以,这个机制是自然而然的过滤或者说自然反馈加强的机制。
  7. 神经网络为什么是一个天然的计算机制呢?它的智能表达机制来源于哪里呢?这个图解说明是非常好容易理解什么是人类识别的模式,至少是我们现在认为的机制吧? 如果自然界的确能够被细分为一个个所谓的feature或者说特征,那么识别特征就是识别对象的简单组合。而神经网络基于光通量来过滤所谓的杂音之后的坚 强的连接自然而然的成为下一次识别或者说还原的快车道。自然往往选择最简单可靠的路径,人们总结为奥卡姆剃刀原则,因为精锐所以锋利,从哪里来就回哪里 去:From light to darkness; From darkness, light!既然光信号经过损耗杂音干扰后存留的信号肯定是本征信号,那么反复出现的就必然是本征信号。我们需要解决的往往是怎么最大限度的模拟信号的损 失而又能够尽可能的还原,这里说是还原还不如说是经过检验可以接受就算成功。人类的反馈外界客观世界的唯一标准就是有用,画画像不像是由谁决定的?是通过 比较来决定的,这个图灵实验是一个原理,没有什么数学方法来定义这个复制是否可信,只能用使用者能否发现区别来作为检验的唯一标准:实践是检验真理的唯一 标准,人如果是唯一的实践者,人就是检验的唯一标准。如果学习的机器是唯一的实践者,那么让机器来做机器的检验者吧。
  8. 想要理解神经网络需要先理解为什么其他方案失败了。Perceptron可 能是任何普通人都能够理解的最理想的也是最简单的方案,如果这个机制能行,人工智能也许真的明天就能来临,因为我们的目前的计算机架构本身就是最典型的 Perceptron,只要你能够被输入到计算机那么一定是被数学严格定义的,那么就是可以做到二元可识别的。能够识别自然能够被联系,能够被联系自然能 够被反馈,那么认识世界,改造世界的闭圈循环就形成了。这个是理想。 我 以为这个对于客观复杂世界作出机械刻板一比一复刻的思路是过于天真的,因为现实是资源永远是有限的宝贵的,而自然界的信息几乎是无穷的,以有限的处理能力 硬抗无限的输入信息是徒劳的。人类智能进化史就是压缩还原的不断自我平衡,去粗取精,去伪存真的真正目的是自然处理能力的不足。同样的,现在人类发明的种 种算法也是现实的计算能力与客观处理的要求的之间的矛盾的某种妥协产物,如果有一天在虚拟现实里实现真正的Perceptron我也不会感到惊奇,但是不 是现在。
  9. 为什么不能处理XOR呢?看来神经网络的可计算性是经过了严格的数学证明了,这个正像计算机架构归根结底是图灵机一样,那么它的可计算性也是经过了严格的数学证明的。
  10. 累了。休息吧。

二月二十四日 等待变化等待机会

  1. 如果你自己坚信了,那么很多时候别人也相信了。这在以前被称之为信念,很多时候是一种从众心理,因为人不可能处处时时都亲自实践,那么间 接学习就是一个明智的选择,也就是说我们常常会不自觉的相信我们的同类的经验。久而久之,经验被传递了,尽管没有被亲自验证,以至于很多的传说传统就是这 么来的。
  2. 单单理解Transformer就需要理解这篇论文,我看了三天还在看引言:
    ...the number of operations required to relate signals from two arbitrary input or output positions grows in the distance between positions...This makes it more difficult to learn dependencies between distant positions. In the Transformer this is reduced to a constant number of operations, albeit at the cost of reduced effective resolution due to averaging attention-weighted positions, an effect we counteract with Multi-Head Attention...
    这里说的是什么呢?就是核心的问题是位置。如果没有位置,那么一切都是非常简单的,我的理解位置是核心因为上下文敏感本质上就是位置敏感,而所谓的上下文 那更加是一个资源与准确性平衡的结果,目前的概率模型是类似于条件概率,那么多大的前置条件要纳入条件呢?无限大的上下文敏感自然是无限,可是可能吗?所 以,要引入上下文的一个类似于活动窗口(attention),但是一词多义就是很典型的上下文敏感,要解决这个问题依靠的是什么办法呢?
    Self-attention, sometimes called intra-attention is an attention mechanism relating different positions of a single sequence in order to compute a representation of the sequence.
    这里的所谓的self-attention就是把上下文限制在输入?为什么引入上一次的输出也作为上下文的一部分呢?我感觉这个想法是非常的聪明的,上一 次的结果是上一次上下文的结果但是从attention的角度来看它会作为一个潜在的变量影响到下一次的结果,这个本身就是上下文,而且是非常聪明的把几 乎无限的前置输入做了一个限制,也就是把上一次的活动窗口的结果作为前置输入,这个是很自然的。从数学上看像是递归函数,很优美的一个表达。
  3. The Transformer is the first transduction model relying entirely on self-attention to compute representations of its input and output without using sequence-aligned RNNs or convolution.
    我始终对于transduction model很在意,因为我对于它很敬畏,维基百科说它的每一次变动都要重新训练,这个不知道是不是我理解错误了?这里点出了Transformer的本质是抛弃了顺序相关的传统做法。
  4. 这里是关于模式的高度概括:
    Most competitive neural sequence transduction models have an encoder-decoder structure. Here, the encoder maps an input sequence of symbol representations (x1 , ..., xn ) to a sequence of continuous representations z = (z1 , ..., zn ). Given z, the decoder then generates an output sequence (y1 , ..., ym ) of symbols one element at a time. At each step the model is auto-regressive, consuming the previously generated symbols as additional input when generating the next.
    重温一下transformer model的架构。 最后一句最重要,上一次产生的符号会作为额外的输入。这个思想就是解决无限上下文难题的核心。但是所有的秘密也都在于这里。
  5. 魔鬼在细节的原因在与大多数人对于宏观抽象的为什么以及什么都能理解,但是到了怎么做就触礁沉没了。因为归根到底前者是为了方便理解而降低了复杂度的高度 概括,你即便没有完全理解也不一定会被人看出来。可是一旦到了具体实作阶段就再也来不得半点模糊,因为每一步的理解可能都是下一步的输入,这个正好就是 Transformer的模型的要义,而机器学习和人类的学习本质上是一回事,结合自己的学习过程就能理解它的核心:
    Encoder: The encoder is composed of a stack of N = 6 identical layers. Each layer has two sub-layers. The first is a multi-head self-attention mechanism, and the second is a simple, position-wise fully connected feed-forward network. We employ a residual connection around each of the two sub-layers, followed by layer normalization. That is, the output of each sub-layer is LayerNorm(x + Sublayer(x)), where Sublayer(x) is the function implemented by the sub-layer itself. To facilitate these residual connections, all sub-layers in the model, as well as the embedding layers, produce outputs of dimension dmodel = 512.
    这里字字珠玑,信息量非常的大,因为它肯定是非常的复杂,否则也不可能有那么神奇的效果。只能慢慢的消化了。这个还仅仅是encoder。
  6. Decoder: The decoder is also composed of a stack of N = 6 identical layers. In addition to the two sub-layers in each encoder layer, the decoder inserts a third sub-layer, which performs multi-head attention over the output of the encoder stack. Similar to the encoder, we employ residual connections around each of the sub-layers, followed by layer normalization. We also modify the self-attention sub-layer in the decoder stack to prevent positions from attending to subsequent positions. This masking, combined with fact that the output embeddings are offset by one position, ensures that the predictions for position i can depend only on the known outputs at positions less than i.
    这个比encoder更难懂,而且为什么说六层是identical呢?结合架构图我很难理解六层在哪里?
  7. Attention是什么呢?
    An attention function can be described as mapping a query and a set of key-value pairs to an output, where the query, keys, values, and output are all vectors. The output is computed as a weighted sum of the values, where the weight assigned to each value is computed by a compatibility function of the query with the corresponding key.
    这里的定义非常的复杂,我只能说现在终于正式看到了QKV这个定义了。在架构图上看到过它们,不理解是什么,现在看来这个是一个函数,函数 的值是一个相当复杂的就在于说这里的weight是什么角色和用意呢?因为如果没有这个weight简单的query/key pair几乎是任何学过计算机的人都能明白的,几乎就是传统的数据库查询的原理,可是有了这个weight据说还要和query/key关联,那么这么做 的用意是什么呢?
  8. 再次重温一下softmax的概念,虽然我觉得用脚趾头都能理解了,但是很容易模糊。
    The softmax function, also known as softargmax or normalized exponential function, converts a vector of K real numbers into a probability distribution of K possible outcomes.
    单单使用看起来是很简单的,但是它的真正含义却很深奥
    It is a generalization of the logistic function to multiple dimensions, and used in multinomial logistic regression.
    这里为什么是Logistic function?它的最早引入是和预测人口增长的:
    The initial stage of growth is approximately exponential (geometric); then, as saturation begins, the growth slows to linear (arithmetic), and at maturity, growth stops.
    这个似乎是一种人们对于事物发展的一般规律的描述,任何一个新事物或者新刺激,在一开始的时候增长都是几何级数的,随着时间的推移,增长变缓慢了成为线性增长,最后停滞了。我喜欢它的标准公式:
    f ( x ) = 1 1 + e -1 = e x e x + 1
    后面这个形式更加的直观:一个以指数增长的事物它的可持续增长的概率是怎么样子的呢?很明显的一个国家的GDP增长就是这个典型,在初期指数增长的确很 快,但是增长越大基数越大意味着后续增长的动力越来越相对变弱,最后高增长变成低增长以至于零增长甚至倒退。我觉得选取这个概率模型来表示信号处理强度是 恰如其分的,一个信号刺激的烈度逐步减退也是如此的,至于为什么是指数我的理解可以看作是维度的增加,这个在图形里是显而易见的,曝光的过程就是面积的增 大,自然而然的维度增大,当然就是指数的增长,这个从convolution的概念就是一个体现。
  9. Scaled Dot-Product Attention:The input consists of queries and keys of dimension dk , and values of dimension dv . We compute the dot products of the query with all keys, divide each by dk , and apply a softmax function to obtain the weights on the values.
    这个是原理 真正的跨越是并行计算,而这其中的道理却又出奇的简单,看起来伟大的跳跃都只不过是平时散步的时候把脚尖颠起来走一走而已。就是说原本是一个个向量的计算,现在换成了矩阵,那么可以利用计算机的矩阵计算优化一个个单独的向量计算,尤其是高维度的矩阵。
    In practice, we compute the attention function on a set of queries simultaneously, packed together into a matrix Q. The keys and values are also packed together into matrices K and V . We compute the matrix of outputs as: Attention ( Q,K,V ) = softmax ( Q K T d k ) V
  10. 后面的Muti-Head Attention机制就更加的复杂了。先休息一下吧。我以为我看不懂细节的原因在于我不明白新的实现是对于旧机制的改进而我连旧的机制都不明白,所以,应该从旧的机制来学习。
  11. 那么是不是应该把著名的论文都先浏览一下有个概念再说呢?我感觉这个领域里著名的论文就如同经典著作一样必须熟读,因为大部头的古老的教科书式的书籍很多都是很多年前的著作,和时代离得有些远了。这里是21篇最多引用的论文,我先来看看。我感觉这个标准似乎不好,因为单单的引用有时候不能完全反映它的重要性。
    1. Deep Residual Learning for Image Recognition
    2. ImageNet Classification with Deep Convolutional Neural Networks
    3. ADAM: A METHOD FOR STOCHASTIC OPTIMIZATION
    4. Random Forests
    5. VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION
    6. Support-Vector Networks
    7. LIBSVM: A Library for Support Vector Machines
    8. Scikit-learn: Machine Learning in Python
    9. Deep learning
    10. Gradient based learning applied to document recognition
    11. Latent Dirichlet Allocation
    12. Generative Adversarial Nets
    13. Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
    14. Going Deeper with Convolutions
    15. ImageNet: A Large-Scale Hierarchical Image Database
    16. TensorFlow: A system for large-scale machine learning
    17. MapReduce: Simplified Data Processing on Large Clusters
    18. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
    19. Dropout: A Simple Way to Prevent Neural Networks from Overfitting
    20. Bagging Predictors
    21. ImageNet Large Scale Visual Recognition Challenge
  12. 补习一个基本概念overfitting,这个我之前确实没有看懂。
    Overfitting is an undesirable machine learning behavior that occurs when the machine learning model gives accurate predictions for training data but not for new data. When data scientists use machine learning models for making predictions, they first train the model on a known data set. Then, based on this information, the model tries to predict outcomes for new data sets. An overfit model can give inaccurate predictions and cannot perform well for all types of new data.
    简而言之,就是测试不尽如人意。原因吗?
    • The training data size is too small and does not contain enough data samples to accurately represent all possible input data values.
    • The training data contains large amounts of irrelevant information, called noisy data.
    • The model trains for too long on a single sample set of data.
    • The model complexity is high, so it learns the noise within the training data.
    这里甚至有对于单个训练数据过长时间训练的问题!这个真的是有趣。总而言之,只能归罪于训练数据质量不高,否则你还能说是训练过程有错误?所以是单个数据重复训练?
  13. 这里是另一个概念的学习ensemble learning
    Ensemble learning is a machine learning technique that enhances accuracy and resilience in forecasting by merging predictions from multiple models. It aims to mitigate errors or biases that may exist in individual models by leveraging the collective intelligence of the ensemble.
    三个臭皮匠,顶个诸葛亮。
  14. 什么是Support vector machine呢?
    In machine learning, support vector machines (SVMs, also support vector networks[1]) are supervised max-margin models with associated learning algorithms that analyze data for classification and regression analysis.

    In addition to performing linear classification, SVMs can efficiently perform a non-linear classification using what is called the kernel trick, implicitly mapping their inputs into high-dimensional feature spaces. SVMs can also be used for regression tasks, where the objective becomes ϵ-sensitive.

    如果这里看的云里雾里,这里的例子就很清楚了
    In the case of support vector machines, a data point is viewed as a p-dimensional vector (a list of p numbers), and we want to know whether we can separate such points with a ( p − 1 ) -dimensional hyperplane. This is called a linear classifier.
    简而言之,降维!分类!
    H1 does not separate the classes. H2 does, but only with a small margin. H3 separates them with the maximal margin.
  15. 这个ReLU(rectified linear unit)概念是很明显的三极管的开关一类功能。
    In the context of artificial neural networks, the rectifier or ReLU (rectified linear unit) activation function f ( x ) = x + | x | 2 = { x if x > 0 0 otherwise
  16. 我觉得可能只有这篇Deep Learning论文值得仔细阅读。还有其他Lecun的论文。 Text Understanding from ScratchLearning in High Dimension Always Amounts to ExtrapolationEMP-SSL: TOWARDS SELF-SUPERVISED LEARNING IN ONE TRAINING EPOCH

    不过我感觉这个也不是好办法,因为似乎Yann LeCun后来在很多论文上署名是一种背书的意义。大家应该都争先恐后把他拉在论文的署名最后,而他也很随和乐意。


二月二十五日 等待变化等待机会

  1. 昨天下载了很多论文,今天要上传,找了一个前天的文件做参照:
    
    for file in $(find stable-diffusion/*.pdf -cnewer stable-diffusion/1706.03762.pdf); do name=$(basename $file) && s3cmd put --mime-type='application/pdf' stable-diffusion/$name s3://www.staroceans.org/stable-diffusion/$name; done
    
  2. 这篇论文标题很有意思:Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs。 这篇论文有趣的是它提出了一个问题,就是CLIP模型可能固有的缺陷,就是它的encoding结果对于某些特征是无视的,或者说盲点。而发现这些盲点是 一个不容易的过程,作者提出了一个实践的方法,就是寻找一个纯视觉训练的模型,这是因为作者认为这些盲点可能是语言大模型带给视觉模型的,所以,找到纯视 觉模型能够看出区别而CLIP不能的样本,这个思路是很好的,因为这个是多模型所带来的问题。这篇论文的好的地方是它指示了一些别的论文能够得到的开源模 型能够进行实验的方向。
  3. 我昨天翻看那些十几年前的convolution方法的论文模糊记得我十几年前也翻看过,所以,和今天的做法可能是有了很多的不同了,究竟怎么从image-space到latent-space的跳跃的呢?这个我依然不明所以然。
  4. 所以要理解之前的CLIP的问题,首先要明白CLIP的由来。Learning Transferable Visual Models From Natural Language Supervision
  5. 先补习一下概念Zero-Shot Transfer 我怎么觉得这个就是transduction的定义呢?
    Zero-shot learning (ZSL) is a problem setup in deep learning where, at test time, a learner observes samples from classes which were not observed during training, and needs to predict the class that they belong to. Zero-shot methods generally work by associating observed and non-observed classes through some form of auxiliary information, which encodes observable distinguishing properties of objects. For example, given a set of images of animals to be classified, along with auxiliary textual descriptions of what animals look like, an artificial intelligence model which has been trained to recognize horses, but has never been given a zebra, can still recognize a zebra when it also knows that zebras look like striped horses. This problem is widely studied in computer vision, natural language processing, and machine perception.
    这个描述的是学习过程的设置,但是它要解决的问题的本质是transduction。2008n年提出的时候乘坐dataless classification
    In computer vision, zero-shot learning models learned parameters for seen classes along with their class representations and rely on representational similarity among class labels so that, during inference, instances can be classified into new classes.
    这怎么看都是类比,就是举一反三。
    Unlike standard generalization in machine learning, where classifiers are expected to correctly classify new samples to classes they have already observed during training, in ZSL, no samples from the classes have been given during training the classifier. It can therefore be viewed as an extreme case of domain adaptation.
    核心要义就是要分类的是绝对没有训练过的类别。可以理解为移花接木式的运用已经学习的分类来做新的分类,这个半主动学习如果成功,机器学习当然可以半自动学习了,那效率自然大为提高了。
    • Learning with attributes: classes are accompanied by pre-defined structured description. For example, for bird descriptions, this could include "red head", "long beak". These attributes are often organized in a structured compositional way, and taking that structure into account improves learning.While this approach was used mostly in computer vision, there are some examples for it also in natural language processing.
    • Learning from textual description. As pointed out above, this has been the key direction pursued in natural language processing. Here class labels are taken to have a meaning and are often augmented with definitions or free-text natural-language description. This could include for example a wikipedia description of the class.
    • Class-class similarity. Here, classes are embedded in a continuous space. a zero-shot classifier can predict that a sample corresponds to some position in that space, and the nearest embedded class is used as a predicted class, even if no such samples were observed during training.
    应该是最后的class-class similarity是我理解的CLIP的机制吧?

二月二十六日 等待变化等待机会

  1. 这篇Deep Learning论文依然是很难的,尽管它看起来更像是一个全揽,对于历史的总结,可是我还是缺乏基本的概念掌握。对于十几年前的convolution的体系这里是一点点的领会:
    1. First, in array data such as images, local groups of values are often highly correlated, forming distinctive local motifs that are easily detected.
    2. Second, the local statistics of images and other signals are invariant to location. In other words, if a motif can appear in one part of the image, it could appear anywhere, hence the idea of units at different locations sharing the same weights and detecting the same pattern in different parts of the array. Mathematically, the filtering operation performed by a feature map is a discrete convolution, hence the name.
    所以,这个就是当年这个思路成功的根本原因,在人类视觉识别过程中,小块的图像的识别,或者说pattern的识别肯定是最最核心的部分,而这个是依赖于 这种特征的大量的出现在训练过程,并且它的出现的方位却是随机无规律的,这个是显而易见的,人眼如同摄像机,在不断变换方位角度对于同一个对象的审视自然 会在不同的方位得到同一影像特征块。至于说这个印象块要多小这个倒是一个细节问题,太大太小都是一个问题。
  2. 之前对于反复提到的pooling layer一直不理解它的作用,这里有一个解读:
    Although the role of the convolutional layer is to detect local conjunctions of features from the previous layer, the role of the pooling layer is to merge semantically similar features into one.
    在很多软件应用层里有去重,而这里我们希望发现重复并且反复引用它,这正好相压缩算法寻找重复的字符串一样,某种意义上说学习就是一个寻找最大限度压缩的 办法。否则不是因为记忆有限,为什么要学习?直接记忆就好了。这正如同监控录像一眼,如果存储是无限的,而分析查询时间没有限制的,就直接机械录像好了。
  3. 我现在也是先低强度的学习记忆,因为记忆力实在是太差了,只能完全照抄。
  4. 神经网络本身就能够自动组合:
    Deep neural networks exploit the property that many natural signals are compositional hierarchies, in which higher-level features are obtained by composing lower-level ones.

二月二十九日 等待变化等待机会

  1. 休息了两天,一来是俗事,二来换换脑筋。我实在是越来越糊涂,需要重新来纵览一下大图景。偶尔看老电影发现有些下砸的老电影居然已经重新修复成了高清,这个是一个很成熟的领域了。不妨学习一下
  2. 一早上在折腾一个无聊的问题,就是使用webui上传文件如果是nfs的文件夹总是报权限错,最好的解决当然是在nfsserver端创建的 uid和我的本地是一样的,可是synology没有usermod,而且新用户都是从1025开始。我很惭愧的是这个基本概念我始终不清楚,为什么我在 server的exports里指定了anonuid结果client端反而不能读了。但是这一切瞎折腾的最根本的原因是我想实验upscaler,但是 效果不好。人脸变形了,所以,这个实验是没有结果的失败。

三月一日 等待变化等待机会

  1. 关于upscaler的实验暂时告一段落,因为效果并不好,这个的确是有专业成分的,不是随便就可以做好的。
  2. 继续读LeCun2015的论文,这个是一个巡礼式的总结,他提到使用RNN来给CNN来提供标题以便作为训练?至少我是这么理解的,也许机器学 习可以形成某种互助关系,取长补短,因为训练材料的收集整理本身就是一个巨大的挑战,在自主学习中这个是至关重要的。他举了一个例子,在这幅图, RNN能够识别出woman throwing frisbee,我使用Interrogate CLIP得到:
    a woman and a child playing frisbee in a field of grass with trees in the background and a river running through the grass, promotional image, Elizabeth Durack, a stock photo, heidelberg school
    似乎更加的详细,如果使用Interrogate DeepBooru得到的更加的详细几乎是驴唇不对马嘴!
    3d, 4girls, audience, aurora, ball, baseball, bicycle, blur censor, blurry, blurry background, blurry foreground, bokeh, building, camera, caution tape, cellphone picture, chain-link fence, christmas tree, chromatic aberration, concert, cosplay photo, day, depth of field, electric fan, fence, field, figure, film grain, focused, garden, glowstick, graffiti, grass, gyaru, gym, hammock, hands on own head, holding ball, holding phone, in the face, jungle, kicking, kogal, leaf, leggings, looking at viewer, male focus, messy hair, motion blur, multiple girls, on grass, outdoors, park, path, people, phone screen, photo \(medium\), photo \(object\), photo background, photo inset, photorealistic, pool, poster \(object\), pov, pov hands, rainbow, recording, reference inset, selfie, shadow, shiny pokemon, shorts, shouji, shrine, sketchbook, soccer, soccer ball, solo focus, sport, stadium, storefront, taking picture, tanaka mamimi, tanzaku, tatami, tennis, throwing, timestamp, tree shade, unconventional media, viewfinder, volleyball

三月三日 等待变化等待机会

  1. 今天偶然看到这个stable-diffusion-2模型决定试一下,发现一直有这个Cannot import ClipProcessor的错误,发现需要先更新transformaers这个模块:
    pip install --upgradie transformers
    pip install --upgradie torch
    
    当然了不要忘记每次都要重新设定tun0的DNS设置,因为我发现Ubuntu似乎会定期刷新这些设置。
  2. 似乎还要安装CUDA驱动

三月五日 等待变化等待机会

  1. 启动笔记本登陆时间无限长。查看journalctl发现启动时候calendar去搜索。这里介绍了三种方法来禁止gnome-software在启动时候就更新,我发现第一种方式我无法修改文件,而且似乎是snap安装的calendar,第二种key似乎也不对。尝试第三种屏蔽packagekit服务:
    
    sudo systemctl mask packagekit.service
    
    等一下重启看看行不行。
  2. 看到类似的错误:
    
    nick@nick-sager:~$ grep --include=*.conf -rnw '/' -e "nvidia-drm" 2>/dev/null
    /etc/modprobe.d/nvidia-graphics-drivers-kms.conf:3:options nvidia-drm modeset=1
    /usr/lib/modprobe.d/nvidia-kms.conf:3:options nvidia-drm modeset=1
    /usr/share/X11/xorg.conf.d/10-nvidia.conf:3:    MatchDriver "nvidia-drm"
    /usr/src/nvidia-550.40.07/dkms.conf:12:BUILT_MODULE_NAME[2]="nvidia-drm"
    
    我尝试禁止了modeset。但是这个是否是黑屏的问题还是延迟的问题,就是说谁是问题,还是都是问题?我看journalctl的时间戳也是不明所以然,似乎都是也都不是。可能昨晚看的时候睡着了吧?
  3. 看到openAI的这个tiktoken,我一开始以为和抖音有关系,其实只是个名字,更主要的是token这个词,不过我对于它声称BPE(Byte Pair Encoding)感觉很神奇:
    1. It's reversible and lossless, so you can convert tokens back into the original text
    2. It works on arbitrary text, even text that is not in the tokeniser's training data
    3. It compresses the text: the token sequence is shorter than the bytes corresponding to the original text. On average, in practice, each token corresponds to about 4 bytes.
    4. It attempts to let the model see common subwords. For instance, "ing" is a common subword in English, so BPE encodings will often split "encoding" into tokens like "encod" and "ing" (instead of e.g. "enc" and "oding"). Because the model will then see the "ing" token again and again in different contexts, it helps models generalise and better understand grammar.
    要编译它需要先安装rust:
    
    sudo apt install rust-all
    pip install .
    
  4. 我无意中看到calendar使用我的gmail创建的一个卷才打开发现在设置里有online account里这个是可以创建一个虚拟的云端的数据盘的,应该是这个问题,我需要把禁止掉,因为没有办法连接到gmail所以才会死等。
  5. eog原本不是原生支持webp格式的,这里说要添加这个包:webp-pixbuf-loader,不过这个已经在ubuntu的官方库里了。
  6. 原本我是想AI可以帮助破解CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) ,但是这个目前应该是非常的困难的吧? 因为这个目的就是防止机器行为,这个应该是简单的OCR程序难以胜任的。即便训练可能也不行吧?
  7. 如果要学习openAI的库这个是一个入门,可是这个还是让我摸不着头脑,恐怕python的基本常识是我的问题。
  8. 如果需要清理journalctl的log,需要
    
    sudo journalctl --rotate
    sudo journalctl --vacuum-time=1s
    
  9. 我发现geoclue这个服务可能也是一个因素,就把它mask了。此外这个盲人需要的函数xbrlapi我也卸载了。然后启动就飞快了。
  10. 这一次debug长进了不少,就是我发现我的journal居然有将近4G,这个无论如何都是一个隐患,我记得以前遇到过磁盘被耗光的问题。而发 现gmail这个云端数据来源无疑是避免了将来的问题,还有geoclue这样子的泄漏行踪的服务都是一些麻烦,不过也许有些网站需要?总而言之,这个比 我之前遇到黑屏就认为是nvidia驱动造成的而一股脑卸载驱动要进步的多了。不过nvidia驱动搞坏的问题比这个严重的多,我现在仅仅是 desktop不能快速进入的痛苦,而不是彻底黑屏的无奈。
  11. 等我回来就看这个,也许比较适合我目前的状态。还有一个就是CNN包含的内容非常的多,也许很值得学习。毕竟它是现在的Transformer的上一代。

三月七日 等待变化等待机会

  1. 这个总结比我的理解要好的多:
    • ClipText for text encoding.
      Input: text.
      Output: 77 token embeddings vectors, each in 768 dimensions.

    • UNet + Scheduler to gradually process/diffuse information in the information (latent) space.
      Input: text embeddings and a starting multi-dimensional array (structured lists of numbers, also called a tensor) made up of noise.
      Output: A processed information array

    • Autoencoder Decoder that paints the final image using the processed information array.
      Input: The processed information array (dimensions: (4,64,64))
      Output: The resulting image (dimensions: (3, 512, 512) which are (red/green/blue, width, height))

  2. 魔鬼都藏在细节,你从概念上说都理解训练是一个去除噪音的能力的训练,但是具体的过程才是关键,我模模糊糊有一个forward diffusion的名字的印象,但是具体是什么才是最要紧的步骤:
    1. 先是制作训练样品 就是给原始图片依次添加噪音这个过程在我看来是模仿信号传输过程的衰减与人类记忆存储里的自然存储失效的随机过程
    2. 真正训练的是识别图像吗?不!如果是这样子的话,那么和前一代的图像空间的复杂程度岂不是一样了吗?训练的是降噪!因为这个好像是一个大数据量的叠加增量部分,这个δ的数据量小的多! 这里是把事先添加的噪音量作为预测的对象,相当于训练一个降噪器,那么这个和图像的性质似乎是无关,也就是说不同的图片在latent space的数据对于噪音的敏感度也许是更高一阶的函数,往往高维度的信息的函数也要变化率慢于低阶函数吧?这里预测与修正的都是噪音部分,而我们对于图 像空间的理解是肤浅的,可是对于噪音空间目前可以说是完全可以先简化为一个标准模型,就是说为什么可以选在常见的正态分布等等,因为磁盘失效,信号衰减在 没有人为干预的情况下就是一个标准的随机过程,而且可以简化为白噪音一样。所以,这个预测容易的多!

三月八日 等待变化等待机会

  1. 这一篇论文的数学公式太多了,根本看不懂,我的概率部分太薄弱了,主要是数学表达式方面缺失。
  2. 最主要的问题是我对于正向反向还是搞不明白。当然能够明白核心的巨大进步在引进latent space这个世人皆知的原理也可以满足了吧?
    Now the forward diffusion process is done on the compressed latents. The slices of noise are of noise applied to those latents, not to the pixel image. And so the noise predictor is actually trained to predict noise in the compressed representation (the latent space).
    这个原理图当然是容易理解的,可是具体步骤却依然不甚了了 这幅图是怎么和这个反复看到的流程图联系起来的呢?我以为最最核心的也是最最复杂的细节就是在这里 因为其他部分都是普通人都能理解的原理部分,而所有的改进与数学算法就在这个核心部分,而恰恰是这个核心部分看不懂!论文里的数学部分太多了,而这个仅仅是一个部分,更主要的是训练是一个逆过程,当你把大部分的数据进行了有损压缩之后再还原的时候肯定是要有很多假设条件的吧?应该是训练的时候有些假设条件啊,作者似乎是说有些参数是不可能学习的,究竟正态分布高斯噪音的哪些参数是不可能学习的?这个依据是什么?是不可能还是理想化? 其实正太分布和高斯分布是一回事。
  3. Their importance is partly due to the central limit theorem. It states that, under some conditions, the average of many samples (observations) of a random variable with finite mean and variance is itself a random variable—whose distribution converges to a normal distribution as the number of samples increases. Therefore, physical quantities that are expected to be the sum of many independent processes, such as measurement errors, often have distributions that are nearly normal.
    其实我需要熟记得是标准的正态分布
    The simplest case of a normal distribution is known as the standard normal distribution or unit normal distribution. This is a special case when μ = 0 and σ = 1, and it is described by this probability density function (or density): φ ( z ) = e - z 2 / 2
    我现在可以熟练的使用mathtml这个本身是一个加深对于公式记忆的好方法。 我似乎发现了一个wiki的笔误,就是高斯分布是标准正态分布的一个变形,那是当σ(variance)的平方等于½而不是σ本身: σ = 1 2 只有这样子才是高斯分布 φ ( z ) = e - z 2 π 到这里我们可以明白其实如果假定分布符合正太分布,那么核心就是发现σ或者说variance。明白了这一点就好理解多了,至于说Markov链的前件后件之类的概念我感觉就是纯粹的数学计算吧?这个我不确定,不过今天的学习到此为止了。

三月九日 等待变化等待机会

  1. 其实从最最基本的训练入手得到的是最直观的感觉。这个word2vector的深入浅出的讲解是非常的好的。细节就在于最简单的概念之后,在我买完油条后再来看吧。这个好像就是所谓的N-gram
  2. 这里是word2vector的论文这个是改进最根本的源头是另一篇。看论文还是有些吃力尽管这篇论文相对来说比较容易,但是问题是我对于几个架构的细节不清楚,所以一到复杂度分析部分就不知所以然了。不过这个是否是我需要目前理解的细节呢?不如还是从论文讲解的这种博客入手。
  3. 这篇博客提到的这篇去噪音的论文我一看就头疼,权且下载吧。
  4. 这篇博客有一个好的地方是引文都有下载链接这篇论文《A Neural Probabilistic Language Model》这篇论文的好处是它给出了一个训练算法的细节也许学习这个过程是很有意义的?似乎是这个领域的最先的先驱?我看明白了大部分,应该说绝大部分人都能看的懂的部分,但是到了关键的实现细节的解说我就迷糊了,作者的视频并没有什么帮助,因为基本上都是概述而已。不过他的频道倒是可以作为平常泛泛浏览。
    这个是它的频道视频

三月十日 等待变化等待机会

  1. 为什么是语言大模型?没有想过这个问题的肯定是不够资格的:
    Language models have a huge advantage over most other machine learning models. That advantage is that we are able to train them on running text – which we have an abundance of. Think of all the books, articles, Wikipedia content, and other forms of text data we have lying around. Contrast this with a lot of other machine learning models which need hand-crafted features and specially-collected data.
  2. 其实,这些都是最最基本的常识,我没有想过那说明我没有这个基础。首先就是理解 joint distribution
    A joint distribution is a probability distribution having two or more independent random variables. In a joint distribution, each random variable will still have its own probability distribution, expected value, variance, and standard deviation. In addition, probabilities will exist for ordered pair values of the random variables. Furthermore, the strength of any relationship between the two variables can be measured.
    而作为joint distribution的两个随机变量这个是基本的公式: 我喜欢这个标准化的公式的一个原因是作者有现成的mathtml代码:
    Suppose a joint distribution of the random variables X and Y are given in table form, so that PXY(X=x,Y=y), typically abbreviated as PXY(x,y), is given for each pair (x,y), of random variables. As with all discrete distributions, two requirements must hold for each pair (x,y): 0 P X Y ( x , y ) 1 all  x all  y P X Y ( x , y ) = 1
    而这个所谓的marginal probabilities我一开始被吓了一跳,后来才回想起这个是很普通的概念:
    Then the marginal probabilities PX(X=x) and PY(Y=y), the expected values E(X) and E(Y), and the variances Var(X) and Var(Y) can be found by the following formulas. P X ( X = x ) = all  y P X Y ( x , y ) P Y ( Y = y ) = all  x P X Y ( x , y ) E ( X ) = all  x x P X ( x ) E ( Y ) = all  y y P Y ( y ) V a r ( X ) = all  x x 2 P X ( x ) ( E ( X ) ) 2 V a r ( Y ) = all  y y 2 P Y ( y ) ( E ( Y ) ) 2
    之所以重温这个概念是因为这篇很重要的论文要解决的所谓的curse of dimensionality,而这个核心的难题curse of dimensionality就在于要把一组词作为一个joint distribution来训练的天文数字的组合:
    For example, if one wants to model the joint distribution of 10 consecutive words in a natural language with a vocabulary V of size 100,000, there are potentially 10000010 − 1 = 1050 − 1 free parameters.
    这个就是基本的问题,如果没有理解问题根本谈不上寻找解决问题的方法!
  3. 很多基础的概念我没有学习过,比如这个non-parametric density estimation
    The models we saw in the previous chapters share a common root: all of them are parametric. This means that they assume a certain structure on the regression function m, which is controlled by parameters185. If this assumption truly holds, then parametric methods are the best approach for estimating m. But in practice it is rarely the case where parametric methods work out-of-the-box, and several tricks are needed in order to expand their degree of flexibility in a case-by-case basis. Avoiding this nuisance is the strongest point of nonparametric methods: they do not assume major hard-to-satisfy hypotheses on the regression function, but just minimal assumptions, which makes them directly employable. Their weak points are that they usually are more computationally demanding and are harder to interpret.
    这个是一个概率推理的范畴,我之前基础很薄弱。具体的公式开头看明白了,后面就丢了,就比如这个density的问题,本来以为很简单,但是只看懂了开头。
  4. 但是至少今天我明白了几个基本的概念,或者说动机:为什么是语言大模型?因为训练材料的充足容易,所以,它是最最基本的入手。而之前我们理解了为什么是多个大模型联动?这是另一个机器学习的微妙之处,这个可以看作自主学习的一个基本能力:transduct可以不用在训练中实际出现过,那么推而广之,训练图像识别是否是否可以从文字识别的高成功率来推动?一个强大的语言模型最后联合一个普通的图像标题模型训练居然成为更高准确率的方式,这些都是革命性的。而这篇论文的核心要解决的是这个curse of dimentionality难道不会出现在别的领域吗?作者的思路是什么?不是简单的N-gram的机械的有序词组n- grams with n up to 5 (i.e. 4 words of context) have been reported, though, but due to data scarcity, most predictions are made with a much shorter context.(这个是作者指出的问题实质:N-gram其实没有普遍性,四字成语不是大多数语言的特征),那是什么?前人的工作缺陷在哪里?
    First, it is not taking into account contexts farther than 1 or 2 words, second it is not taking into account the “similarity” between words.
    对,就是不能机械的固定几个N-gram,而且要考虑相似性,也就是要能够触类旁通,举一反三。但是这个是多么的难啊,如果自主学习做得到我们还发愁吗?
  5. 作者说这个是他们的核心思想:
    1. associate with each word in the vocabulary a distributed word feature vector (a real-valued vector in Rm),
    2. express the joint probability function of word sequences in terms of the feature vectors of these words in the sequence, and
    3. learn simultaneously the word feature vectors and the parameters of that probability function.
    我的理解就是这里的word feature vector就是现在人们常说的embedding吧?这里是作者的核心思想解释:
    The feature vector represents different aspects of the word: each word is associated with a point in a vector space. The number of features (e.g. m =30, 60 or 100 in the experiments) is much smaller than the size of the vocabulary (e.g. 17,000).
    这个是两个思考方式的碰撞:通常人们把一组词看作是一个有机的整体,比如一个有十万词汇的词库里每十个有序词组就是一个整体,可是现实中有那么多吗?中文 的四字成语也许是它的例证,可是大多数语言里没有这个整齐划一的结构实体,反而是硬性的机械的这个处理方式出现了难以克服的维度诅咒;那么换种思考,也许 确实有这个潜在的实体,可是能不能把这个十个词扩展到三十,五十?不敢,因为十个就很大了何况三十五十?而这里就是出彩的思考,就是每一个词也许都有这么 一个出现在几十个词的上下文的属性,那么用什么来表达呢?太具体的不一定有,可以用向量来表达,其实有的词很活跃,它说不定三十个维度还不够,可是很多词 三十个维度绰绰有余,但是不管怎么样,我们得到了一个统一。这个计算空间立刻从指数级降低到了线性的空间了。这个真的是高明。
  6. 我有大量的概率论知识要温习与学习,有一些是熟知的但是名词不熟悉的,比如这个难道不就是通常所理解的条件概率吗?

三月十一日 等待变化等待机会

  1. probability mass function其实很简单在于:
    A probability mass function differs from a probability density function (PDF) in that the latter is associated with continuous rather than discrete random variables. A PDF must be integrated over an interval to yield a probability.
    仅仅因为它是离散的概率密度函数而已。而使用Log Likelihood Function的原因是因为我们可以把乘法变加法,其次画图也更容易一些。
  2. 这是关于likelihood statistics的讲稿,学习一下是很有意义的。
  3. 我现在回过头来温习我的笔记感觉大有裨益,首先是很多的概念定义的摘抄是非常值得的,因为概念往往需要反复阅读与理解,一次两次多次,很多时候即 便是理解了也会忘记需要反复加深记忆。其次,很多概念是融会贯通才能真正领会,尤其是很多概念实际上是建立其他更复杂概念的基石,你学习这些基本概念的目 的是为了学习掌握更复杂更高级概念的必由之路,所以,有时候在当时是仅仅能够掌握基本概念而无瑕真正集中注意力在当前的高级概念,顾此失彼,模模糊糊,而 复习的过程是一个真正的好整以暇融会贯通的机会。更何况很多提纲挈领只有在扫清了外围据点之后发起的总攻才能发现,而这个总攻过程往往又会再次发现很多以 前没有攻击到跟前而看不到的隐蔽火力点。这个突击过程往往不是一蹴而就,反复拉锯是不能避免的。在这种反复拉锯过程中往往很多之前在另外一个方向的疑团也 能触类旁通式的理解了,这个本身就是一个学习过程,与其说机器学习是一个模仿人类学习的过程还不如说它是一个精炼科学化系统化规律化的过程,学习过程本身 就是有一个客观规律的性质,只不过在人类自己无法概念化之前把它神话为某种智慧生物特有的过程而已,哪有什么独一无二?只不过没有见过就以为世间独有!人 类掌握机器学习的过程就是人类自我解放的过程,它的最终也许是人类自身的替代结局,但是对于整个文明一定是一次跨越式的革命演化。站在整个宇宙文明的角度 来看人类自我革命是一个自然而不可阻挡的必然进程,一切企图阻挡历史车轮前进的螳臂挡车必然是被历史的车轮碾为齑粉!

三月十二日 等待变化等待机会

  1. 有很多深奥的理论是需要在以后深入学习中才能理解,比如这里
    ... the probability function is a smooth function of these feature values, a small change in the features will induce a small change in the probability.
    作者是在这里解释为什么相似的字词能够获益于其算法。可是在我看来这个是很深奥的概率理论。比如阅读这个讲演是很有长进的: i = 1 n 1 2 π exp 就比如这里的是数学上的连乘的符号,而不能简单的用π来代表。至少不好看。其实我内心是理解了likelihood function的只是嘴上说不出,它本身不能看作是概率分布,但是它是相关的,因为是看作在一个带参数的概率分布观察到的实际的随机变量的值之后人们用来推测的这些值再次发生的一个可能性,这个似乎也是字面的理解,可能性和概率在中文有什么不同呢?这个不是数学语言。
    The likelihood function is not a probability distribution.
    • It does not transform like a probability distribution.
    • Normalization is not defined.
    可是这些空洞的概念对于我有什么意义呢?我觉得我会用到的是求它的最大值的log,后者是因为取对数转化为加法可以简化计算量,前者是因为研究正态分布时候倒退它的参数在最大值点可以得到mean。
  2. 这里再强调一下这个w3的mathml的核心入口
  3. 这里是真正的Likelihood function的定义
    The likelihood function (often simply called the likelihood) is the joint probability mass (or probability density) of observed data viewed as a function of the parameters of a statistical model. Intuitively, the likelihood function L ( θ x ) is the probability of observing data x assuming θ is the actual parameter.
    这里要注意的是在统计学里parameter是有独特含义的,说白了就是关于随机过程的mean和standard deviation。因为研究一个概率模型最基本的就是这两样东西,它们描述了一个模型的基本样貌。而这里的概率模型其实反而是一个更加虚拟的假设,是一 个数学的假设公式化的推测。那么人们在模型未建立之前的观察得到的实际数据成为一种经验值来作为推测的随机过程再次发生的可能性,这就是这个 likelihood function的本意。
  4. 之所以我们关心Maximum likelihood estimation是因为我们研究的目的就是要试图建立概率模型,而最基本的就是它的参数,因为基本上不管什么样的模型,几乎必然的它的最大值都在它的mean的附近或者就是重合的,我实在想不出是否不重合的可能性?这个当然是不重合了,这个仅仅是正态分布的特例。只不过AI的噪音模型大多数都是假定为normal distribution而已。maximum被称作mode,大概是吧?
    In statistics, maximum likelihood estimation (MLE) is a method of estimating the parameters of an assumed probability distribution, given some observed data. This is achieved by maximizing a likelihood function so that, under the assumed statistical model, the observed data is most probable. The point in the parameter space that maximizes the likelihood function is called the maximum likelihood estimate. The logic of maximum likelihood is both intuitive and flexible, and as such the method has become a dominant means of statistical inference.
    这里是多么清楚的解释和定义啊!一目了然的,根本不需要多余的话!它就是一种inference,而且是做了很合理的假设。
  5. 在学习了这些基础知识后我们再来读这段话
    The probability function is expressed as a product of conditional probabilities of the next word given the previous ones, (e.g. using a multilayer neural network to predict the next word given the previous ones, in the experiments). This function has parameters that can be iteratively tuned in order to maximize the log-likelihood of the training data or a regularized criterion, e.g. by adding a weight decay penalty. The feature vectors associated with each word are learned, but they could be initialized using prior knowledge of semantic features.
    这段文字可谓是字字珠玑,它描述了整个过程,说到底这个Markov链它就是一个条件概率的结果,所以,我们所谓的预测下一个字就是当初观察到的实际发生 的随机变量实际值来倒推概率模型,而反复微调迭代取得概率最大值实际上也是在摸索寻找假设的概率模型的参数,因为我们已经假设了正态分布。这里weight decay penalty作者的注解是Like in ridge regression, the squared norm of the parameters is penalized. 这里又要补课:
    Ridge regression is a statistical regularization technique. It corrects for overfitting on training data in machine learning models.

    Ridge regression—also known as L2 regularization—is one of several types of regularization for linear regression models. Regularization is a statistical method to reduce errors caused by overfitting on training data. Ridge regression specifically corrects for multicollinearity in regression analysis. This is useful when developing machine learning models that have a large number of parameters, particularly if those parameters also have high weights.

    这里提到的multicollinearity指的是
    Multicollinearity denotes when independent variables in a linear regression equation are correlated. Multicollinear variables can negatively affect model predictions on unseen data. Several regularization techniques can detect and fix multicollinearity.
    我感觉这个就是自相矛盾的,因为我印象中所谓的independent variable本来就是彼此的发生概率是独立不受影响的,而说它们是correlated就是自定义的错误,当然也许是我们现实中理想的独立并不实际存在,只是影响因素可以忽略而已吧?看来我把概念混淆了,这里的independent和dependen variable并不是独立概率里的Independent Random Variables的概念
    In the simple stochastic linear model

    yi = a + bxi + ei

    the term yi is the ith value of the dependent variable and xi is the ith value of the independent variable. The term ei is known as the "error" and contains the variability of the dependent variable not explained by the independent variable.
    这个概念其实是更加泛泛的数学概念
  6. 再补课linear regression model
    A linear regression model describes the relationship between a dependent variable, y, and one or more independent variables, X. The dependent variable is also called the response variable. Independent variables are also called explanatory or predictor variables. Continuous predictor variables are also called covariates, and categorical predictor variables are also called factors. The matrix X of observations on predictor variables is usually called the design matrix.

    A multiple linear regression model is

    yi01Xi12Xi2+⋯+βpXipi, i=1,⋯,n,

    where
    • n is the number of observations.
    • yi is the ith response.
    • βk is the kth coefficient, where β0 is the constant term in the model. Sometimes, design matrices might include information about the constant term. However, fitlm or stepwiselm by default includes a constant term in the model, so you must not enter a column of 1s into your design matrix X.
    • Xij is the ith observation on the jth predictor variable, j = 1, ..., p.
    • εi is the ith noise term, that is, random error.
  7. 这篇论文读的好辛苦啊,几乎每一个字都是需要补课,我看引言就看了两三天。不过我觉得是值得的,因为它是几乎所有的基础,尤其是背后的统计学的理论基础是必不可少的。只是要消化吸收还需要很多时间和反复。
  8. 这里有一个中西方的语言的误导,或许是我自己没有概念,这里的Regression是有一个统计学上的特定含义的:
    Regression analysis, a statistical technique for estimating the relationships among variables. There are several types of regression:
    看了这些我才有些开窍,现实世界里我们从现象来推测事物运行的本质规律就是一个归纳的过程,有些时候我们为了简化或者我们已经有证据支持某些模型,所以,我们可以用很少的实验数据来勾勒出概率模型公式。与此相对的是Nonparametric
    Nonparametric regression is a category of regression analysis in which the predictor does not take a predetermined form but is constructed according to information derived from the data. That is, no parametric form is assumed for the relationship between predictors and dependent variable. Nonparametric regression requires larger sample sizes than regression based on parametric models because the data must supply the model structure as well as the model estimates.
    这里的不完全列表把我都看吐了。
  9. 感觉太累了。休息吧。我想去大自然里奔跑!

三月十三日 等待变化等待机会

  1. 我现在开始明白所谓的线性回归(Linear Regression)是一个非常复杂的课题,其中有大量的概念与理论要学 习,而它背后的这些根本原因其实是更加的深奥,而这一切都是基于很多的关于一个观察的现象中假定有多少个独立变量,有多少个依赖变量,而它们直接的关系又 是如何,这个简直就像是要靠单单的观察一个复杂的多齿轮精密的钟表来推理它的齿轮间转动规律一般。单单学习这个领域就可能穷尽一个人的一生,因为这个彷佛 是一个数学的逆向工程,大自然写了一个复杂的函数然后把这个函数作为概率密度函数让你依靠仍色子来猜出这个函数,这个简直就是在破解上帝设置的大自然的密码!
  2. 我的线性代数是白学了因为我居然不知道Eigenvalue和EigenVector的数学含义!
    In linear algebra, it is often important to know which vectors have their directions unchanged by a given linear transformation. An eigenvector (/ˈaɪɡən-/ EYE-gən-) or characteristic vector is such a vector. Thus an eigenvector v of a linear transformation T is scaled by a constant factor λ when the linear transformation is applied to it: T v = λ v. The corresponding eigenvalue, characteristic value, or characteristic root is the multiplying factor λ.
    而它的几何意义其实更加的有用和直观
    Geometrically, vectors are multi-dimensional quantities with magnitude and direction, often pictured as arrows. A linear transformation rotates, stretches, or shears the vectors upon which it acts. Its eigenvectors are those vectors that are only stretched, with no rotation or shear. The corresponding eigenvalue is the factor by which an eigenvector is stretched or squished. If the eigenvalue is negative, the eigenvector's direction is reversed.
    很显然的这个对于线性变换肯定是非常的有针对性的意义的特殊指向,简直就是线性变换的变换方向,由此而知其发力的方向,那么它的意义能不大吗?它的数学表达是这样子的:
    If T is a linear transformation from a vector space V over a field F into itself and v is a nonzero vector in V, then v is an eigenvector of T if T(v) is a scalar multiple of v. This can be written as

    T ( v ) = λ v , (in Matrix language, it is Au = λu where A is the matrix representation of T and u is the coordinate vector of v. )

    where λ is a scalar in F, known as the eigenvalue, characteristic value, or characteristic root associated with v.
  3. 结果就是我才意识到之所以统计和线性代数有这么密切的关系是因为大量的统计学的公式往往是大量的观察数据的结果,而要找出其规律又要求助于线性变换的大量的应用,所以,线性代数是一个具体的数学工具是统计数据抽象化的数学结果以及如何通过结果寻找规律的手段。结果就是我又发现线性代数的基础又要重新温习学习,很多概念或者忘记了,或者根本没有接触过。
  4. 一个矩阵的是什么几何意义呢?我似乎完全没有印象!居然是两个向量组成的平行四边形的面积!作为三维向量那就是体积了。代数在几何空间的意义往往是更加的直观与重大。
  5. 我似乎完全忘记了这个kernel的定义。
  6. 补课补的头疼啊。
  7. 我把向量的叉乘和外乘搞混了。cross product依旧是一个向量是和相乘的两个向量垂直的法线向量。现在回过头来看实际上和线性回归大有渊源你可以看作是一组dependent variable对于一个线性方程关系的n次的实验,因为这个变量是这个线性关系的n次的系数。

三月十四日 等待变化等待机会

  1. 学习必要的基础数学告一段落,因为我的目的还是读这篇论文,而基础永远是不够的,够用就好,而且我目前仅仅是帮助理解,远没有到实现级别的细节。所以,回过头来看
    The idea of using neural networks for language modeling is not new either (e.g. Miikkulainen and Dyer, 1991). In contrast, here we push this idea to a large scale, and concentrate on learning a statistical model of the distribution of word sequences, rather than learning the role of words in a sentence.
    这段话其实很关键,作者有划重点,我认为这肯定是作者要强调的核心思想。在肯定前人的成就基础上的,并不是完全的新思想,只是把前人的想法放大。但是也有创新的,就是这个我还没有理解的是distribution of word sequences,那么什么是role of words in a sentence?这个要好好体会。而前人的思想是each word is associated deterministically or probabilistically with a discrete class, and words in the same class are similar in some respect.作者的不同在于:
    In the model proposed here, instead of characterizing the similarity with a discrete random or deterministic variable (which corresponds to a soft or hard partition of the set of words), we use a continuous real-vector for each word, i.e. a learned distributed feature vector, to represent similarity between words.
    这篇论文的核心要点就是这里learned distributed feature vector。我们要搞明白的它是什么,它为什么可以,要怎么做。
  2. 向量空间来表达也不是什么新鲜主意,但是作者强调他们的创新是
    An important difference is that here we look for a representation for words that is helpful in representing compactly the probability distribution of word sequences from natural language text.
    所以,我们要理解这里的表达不是单个词而是一组词,这个和N-gram要取得的效果目的是类似的,但是是聪明的解脱了维度诅咒(curse of dimentionality)的 一个好方法,把一个词库里几何级数的组合概率改为了某种线性的一维,代价是每一个单词是有远远大于通常人们做N-gram的维度,据我所知只有不到5维。 这个就是一个非常的强悍的地方。因为对于词组这样子的有序序列实际上也是很难的一个模型,大量的语言现象其实是无序的,或者说是不值得有序,汉语中四字一 组的成语是大多数语言中罕见的,那么结果就是要构造常用词组的模型是非常困难或者说模糊的,我想这里的向量空间是一个足够模糊的方式,甚至于好像人类 DNA序列一样的看起来模糊的。这个比喻似乎很不恰当,DNA也许像程序代码一样的有特殊含义,可是人类自然语言似乎没有那么严格的语法。
  3. 这里的一处细节我始终没有理解:
    Experiments suggest that learning jointly the representation (word features) and the model is very useful.
    这里的一鱼两吃是什么意思?到底学的是什么?建立的是什么?这个是理解的真正核心,我能回答这个问题这篇论文才算理解了。作者接下来的描述我同样的不理解
    We tried (unsuccessfully) using as fixed word features for each word w the first principal components of the co-occurrence frequencies of w with the words occurring in text around the occurrence of w.
    为什么是unsuccessfully?难道这个是另一个方向的尝试?我曾经预想过fixed word features不大可能,那么不是fixed,哪能怎么办?还能是动态的?或者意思是我的英文理解有问题,这里指的是单词相对于上下文的不同而不同?那这个和N-gram有什么区别?我越看越糊涂了。
  4. 最终语言的描述是受限于读者如我一样的理解能力,尤其是第二语言更是如此,只有数学语言才是精确的,所以,这个摘抄是非常非常必要的理解过程:
    The training set is a sequence w1 · · · wT of words wt ∈ V , where the vocabulary V is a large but finite set. The objective is to learn a good model f ( w t ,..., w t−n+1 ) = P ˆ ( w t | w t-1 1 ) , in the sense that it gives high out-of-sample likelihood. Below, we report the geometric average of 1 P ˆ ( w t | w t-1 1 ) , also known as perplexity, which is also the exponential of the average negative log-likelihood
    这里先科普一下perplexity
    In information theory, perplexity is a measure of uncertainty in the value of a sample from a discrete probability distribution. The larger the perplexity, the less likely it is that an observer can guess the value which will be drawn from the distribution.
    简而言之就是复杂度或者难易程度,如果一个考公务员的特殊试卷只有对和错两个选择,那么白痴也能得的满分几率几乎就是50%,这个让一个白痴AI去随机猜对的可能是很大的,算不得本事。只有选择空间很大才方显英雄本色。
  5. 这里补习一下各种数学符号
    Symbol Name Date of earliest use First author to use
    horizontal bar for division 14th century (approx.) Nicole Oresme
    +
    plus sign 1360 (approx.), abbreviation for Latin et resembling the plus sign Nicole Oresme
    minus sign 1489 (first appearance of minus sign, and also first appearance of plus sign in print) Johannes Widmann
    radical symbol (for square root) 1525 (without the vinculum above the radicand) Christoff Rudolff
    (...)
    parentheses (for precedence grouping) 1544 (in handwritten notes) Michael Stifel
    (...)
    parentheses (for precedence grouping) 1556 Niccolò Tartaglia
    =
    equals sign 1557 Robert Recorde
    .
    decimal separator 1593 Christopher Clavius
    ×
    multiplication sign 1618 William Oughtred
    ±
    plus–minus sign 1628 William Oughtred
    proportion sign 1628 William Oughtred
    x n
    radical symbol (for nth root) 1629 Albert Girard
    <
    >
    strict inequality signs (less-than sign and greater-than sign) 1631 Thomas Harriot
    xy
     
    superscript notation (for exponentiation) 1636 (using Roman numerals as superscripts) James Hume
    x
     
    Use of the letter x for an independent variable or unknown value. See History of algebra: The symbol x. 1637[2] René Descartes (La Géométrie)
    xy
     
    superscript notation (for exponentiation) 1637 (in the modern form) René Descartes (La Géométrie)
    √ ̅
    radical symbol (for square root) 1637 (with the vinculum above the radicand) René Descartes (La Géométrie)
    %
    percent sign 1650 (approx.) unknown
    infinity sign 1655 John Wallis
    ÷
    division sign (a repurposed obelus variant) 1659 Johann Rahn


    unstrict inequality signs (less-than or equals to sign and greater-than or equals to sign) 1670 (with the horizontal bar over the inequality sign, rather than below it) John Wallis
    integral sign 1675 Gottfried Leibniz
    d
    differential sign 1675 Gottfried Leibniz
    :
    colon (for division) 1684 (deriving from use of colon to denote fractions, dating back to 1633) Gottfried Leibniz
    ·
    middle dot (for multiplication) 1698 (perhaps deriving from a much earlier use of middle dot to separate juxtaposed numbers) Gottfried Leibniz
    division slash (a.k.a. solidus) 1718 (deriving from horizontal fraction bar, invented by Abu Bakr al-Hassar in the 12th century) Thomas Twining


    unstrict inequality signs (less-than or equals to sign and greater-than or equals to sign) 1734 (with double horizontal bar below the inequality sign) Pierre Bouguer
    x
    prime symbol (for derivative) 1748 Leonhard Euler
    Σ
    summation symbol 1755 Leonhard Euler
    proportionality sign 1768 William Emerson
    partial differential sign (a.k.a. curly d or Jacobi's delta) 1770 Marquis de Condorcet
    identity sign (for congruence relation) 1801 (first appearance in print; used previously in personal writings of Gauss) Carl Friedrich Gauss
    !
    factorial 1808 Christian Kramp
    [x]
    integral part (a.k.a. floor) 1808 Carl Friedrich Gauss
    Π
    product symbol 1812 Carl Friedrich Gauss

    set inclusion signs (subset of, superset of) 1817 Joseph Gergonne
    |...|
    absolute value notation 1841 Karl Weierstrass
    |...|
    determinant of a matrix 1841 Arthur Cayley
    ‖...‖
    matrix notation 1843[3] Arthur Cayley
    nabla symbol (for vector differential) 1846 (previously used by Hamilton as a general-purpose operator sign) William Rowan Hamilton

    intersection

    union
    1888 Giuseppe Peano

    set inclusion signs (subset of, superset of) 1890 Ernst Schröder
    aleph symbol (for transfinite cardinal numbers) 1893 Georg Cantor
    membership sign (is an element of) 1894 Giuseppe Peano
    O
    Big O Notation 1894 Paul Bachmann
    {...}
    braces, a.k.a. curly brackets (for set notation) 1895 Georg Cantor
    Blackboard bold capital N (for natural numbers set) 1895 Giuseppe Peano
    Blackboard bold capital Q (for rational numbers set) 1895 Giuseppe Peano
    existential quantifier (there exists) 1897 Giuseppe Peano
    ·
    middle dot (for dot product) 1902 J. Willard Gibbs
    ×
    multiplication sign (for cross product) 1902 J. Willard Gibbs
    logical disjunction (a.k.a. OR) 1906 Bertrand Russell
    (...)
    matrix notation 1909[3] Maxime Bôcher
    [...]
     
    matrix notation 1909[3] Gerhard Kowalewski
    contour integral sign 1917 Arnold Sommerfeld
    Blackboard bold capital Z (for integer numbers set) 1930 Edmund Landau
    universal quantifier (for all) 1935 Gerhard Gentzen
    arrow (for function notation) 1936 (to denote images of specific elements) Øystein Ore
    empty set sign 1939 André Weil / Nicolas Bourbaki[4]
    Blackboard bold capital C (for complex numbers set) 1939 Nathan Jacobson
    arrow (for function notation) 1940 (in the present form of f: XY) Witold Hurewicz
    end of proof sign (a.k.a. tombstone) 1950[5] Paul Halmos
    x x
    greatest integer ≤x (a.k.a. floor)

    smallest integer ≥x (a.k.a. ceiling)
    1962[6] Kenneth E. Iverson
    inequality sign (not equal to) unknown Leonhard Euler
    然后我发现这个表的数学符号更全一些。而我为了找这个Hat notation花了差不多一个小时,我印象中学校里经常用,但是就是叫不出名字来:而这个是在统计学里有特定含义的,不同于以前逻辑或者其他领域的含义。
    In statistics, a circumflex (ˆ), called a "hat", is used to denote an estimator or an estimated value. For example, in the context of errors and residuals, the "hat" over the letter ε ^ indicates an observable estimate (the residuals) of an unobservable quantity called ε (the statistical errors).
  6. 在统计学里这个概念是相当的重要,因为我觉得直觉是不足以意识到这个概念,就是说它们是两个不同的概念,但是又是紧密相联系的:
    In statistics and optimization, errors and residuals are two closely related and easily confused measures of the deviation of an observed value of an element of a statistical sample from its "true value" (not necessarily observable). The error of an observation is the deviation of the observed value from the true value of a quantity of interest (for example, a population mean). The residual is the difference between the observed value and the estimated value of the quantity of interest (for example, a sample mean). The distinction is most important in regression analysis, where the concepts are sometimes called the regression errors and regression residuals and where they lead to the concept of studentized residuals. In econometrics, "errors" are also called disturbances.
    都是偏差可是原因不同。这个是中文的名词对照:误差(error)和残差(residual)。 而Population Mean在中文里叫做总体平均值,相对应的Sample Mean叫做样本平均值。这些在中文里意思似乎很清楚,但是会误导。它们真正的数学或者说统计学上的意义是深奥的。
    In statistical inference, a subset of the population (a statistical sample) is chosen to represent the population in a statistical analysis. Moreover, the statistical sample must be unbiased and accurately model the population (every unit of the population has an equal chance of selection). The ratio of the size of this statistical sample to the size of the population is called a sampling fraction. It is then possible to estimate the population parameters using the appropriate sample statistics.
    这个是statistical inference的基本出发点,而这些概念也许在descriptive statistic的角度来看就是官僚主义的正常节奏,可是在推本逐源的探索过程就是大学问。
    A statistical error (or disturbance) is the amount by which an observation differs from its expected value, the latter being based on the whole population from which the statistical unit was chosen randomly.
    而与之相对的是
    A residual (or fitting deviation), on the other hand, is an observable estimate of the unobservable statistical error.
    看起来两者的来源是截然不同的。前者是不可避免的因为你的取样总是小于总体样本,如果不是的话根本就不用研究统计学直接去暴力调查所有样本就可以了吗?统 计学就是要少花钱多办事才对,见微知卓,管中窥豹。所以,这个是不可避免的,除非是理想的概率分布严格的发生,可是上帝会掷色子吗?上帝的色子是绝对均匀 的吗?即便是,我们能够有幸在少数样本里正好得到准确的概率分布的样本吗?

    后者是有些主观因素在起作用,能否观察到是一个问题,选取合适的样本也是一个问题,就是人为去除噪音这个需要人的信念支持,或者说有主观成分了。

    The sample mean is the average of the values of a variable in a sample, which is the sum of those values divided by the number of values.

    总而言之,两者非常的相似,甚至在实际工作中是一回事,但是理论上能否避免则有不同,前者是不可能,后者是可能。
  7. 为什么要花这么多时间来理解这些枯燥的东西呢?原因是人工智能说到底是依靠statistical inference的方法和理论来探索其概率分布来作为模型以便应用。那么这个探索的方法和过程就是大有学问了。 归根结底是这个神经网络的模型的数学表达式的准确理解是什么?这里的下标突然冒出一个未定义的n,那么是不是说 f ( w t ,..., w t - n + 1 ) = P ˆ ( w t | w t-1 1 ) 这里的n是当前context下n个单词的词组概率?总之,我的理解是这里的n不是一个变量而是一个常量。在接下去注意这个细节。而这里接下去都是硬核,完全是最最至关重要的定义与实现细节。我感觉今天已经疲乏了,明日再战。
  8. 这个是最复杂的部分,先从模型定义的公式开始:这里是所有的拉丁字母在数学上的应用
    We decompose the function f ( w t ,..., w t - n + 1 ) = P ˆ ( w t | w t-1 1 ) in two parts:
    1. A mapping C from any element i of V to a real vector C(i) ∈ ℝm. It represents the distributed feature vectors associated with each word in the vocabulary. In practice, C is represented by a|V| × m matrix of free parameters.
    2. The probability function over words, expressed with C: a function g maps an input sequence of feature vectors for words in context, (C(wt−n+1), ...,C(wt−1)), to a conditional probability distribution over words in V for the next word wt . The output of g is a vector whose i-th element estimates the probability P ˆ ( w t | w t-1 1 ) as in Figure 1.

      f (i, wt−1, ... , wt−n+1) = g(i,C(wt−1), ...,C(wt−n+1))

    我先把定义抄了一遍,文字部分还比较清楚,但是这个架构图就非常的复杂,需要花至少一两天来理解。

三月十五日 等待变化等待机会

  1. 有大量的数学基础要补习,比如对于group我原来是有概念的,只是忘记了,要温习一下。
    A group is a non-empty set G together with a binary operation on G, here denoted " ⋅ ", that combines any two elements a and b of G to form an element of G , denoted a ⋅ b, such that the following three requirements, known as group axioms, are satisfied:
    • Associativity

      For all a , b, c in G , one has ( a ⋅ b ) ⋅ c = a ⋅ ( b ⋅ c ) .
    • Identity element

      There exists an element e in G such that, for every a in G , one has e ⋅ a = a and a ⋅ e = a . Such an element is unique. It is called the identity element (or sometimes neutral element) of the group.
    • Inverse element

      For each a in G , there exists an element b in G such that a ⋅ b = e and b ⋅ a = e , where e is the identity element. For each a , the element b is unique; it is called the inverse of a and is commonly denoted a-1 .
  2. 数学上有一个metric space的概念:
    In mathematics, a metric space is a set together with a notion of distance between its elements, usually called points. The distance is measured by a function called a metric or distance function.
    为什么我们需要这个概念呢?原因是我们需要distance-preserving transformation的概念,这个是线性变换的一个特殊变换,它的严格的数学定义是显而易见的:这里有一个小的技巧我花了快一个小时才找到,就是数学上定义集合的blackboard bold字体我不想使用unicode就是用mathvariant来定义字体,这里使用Double-struck就可以取得这个效果。
    Let X and Y be metric spaces with metrics (e.g., distances) dX and dY . A map f:XY is called an isometry or distance preserving map if for any a,bX one has d X ( a , b ) = d Y ( f ( a ) , f ( b ) )
  3. 这里是数学的所谓的BlackBoard Bold的html表达,有时候你都不知道要怎么去搜索这些东西。
  4. 这个网站的数学符号比较全
    MathML Symbol HTML Entity Hex Code Description
    - &minus; &#x2212; To specify subtraction
    × &times; &#x00d7; To specify multiplication
    ÷ &divide; &#x00f7; To specify division
    &ne; &#x2260; To specify not equals
    &asymp; &#x2248; To specify approximately equals
    < &lt; &#x003c; To specify less than
    &le; &#x2264; To specify less than or equals
    > &gt; &#x003e; To specify greater than
    &ge; &#x2265; To specify greater than or equal
    ± &plusmn; &#x00b1; To specify plus or minus
    &prop; &#x221d; To specify proportional to
    &sum; &#x2211; To specify summation
    &prod; &#x220f; To specify product
    &lfloor; &#x230a; To specify left floor
    &rfloor; &#x230b; To specify right floor
    &lceil; &#x2308; To specify left ceiling
    &rceil; &#x2309; To specify right ceiling
    &hellip; &#x2026; To specify horizontal ellipsis
    &vellip; &#x22ee; To specify vertical ellipsis
    &ctdot; &#x22ef; To specify midline horizontal ellipsis
    &utdot; &#x22f0; To specify diagonal ellipsis
    &dtdot; &#x22f1; To specify downright diagonal ellipsis
    ° &deg; &#x00b0; To specify degrees
    &ang; &#x2220; To specify angle
    &angmsd; &#x2221; To specify measured angle
    &angrt; &#x221f; To specify right angle
    &vangrt; &#x299c; To specify right angle with square
    &lrtri; &#x22bf; To specify right triangle
    &cir; &#x25cb; To specify circle
    &xutri; &#x25b3; To specify triangle
    &squ; &#x25a1; To specify square
    &fltns; &#x25b1; To specify parallelogram
    &spar; &#x2225; To specify parallel
    &npar; &#x2226; To specify not parallel
    &perp; &#x22a5; To specify perpendicular
    &cong; &#x2245; To specify congruent
    &rarr; &#x2192; To specify ray (used with <mover>)
    &harr; &#x2194; To specify line (used with <mover>)
    - (n/a) &#x002d; To specify line segment (used with <mover>)
    &prime; &#x2032; Prime (1st derivative)
    &prime; &#x2033; Double prime (2nd derivative)
    &tprime; &#x2034; Triple prime (3nd derivative)
    &part; &#x2202; To specify partial differential
    δ &delta; &#x0394; To specify increment
    &del; &#x2207; To specify gradient
    &int; &#x222b; To specify integral
    &int; &#x222c; To specify double integral
    &tint; &#x222d; To specify triple integral
    &qint; &#x2a0c; To specify quadruple integral
    &conint; &#x222e; To specify contour integral
    &cwconint; &#x2232; To specify clockwise contour integral
    &awconint; &#x2233; To specify anticlockwise contour integral
    &conint; &#x222f; To specify surface integral
    &cconint; &#x2230; To specify volume integral
    &infin; &#x221e; To specify infinity
    &sdot; &#x22c5; To specify dot product
    &cross; &#x2a2f; To specify cross product
    &vert; &#x2016; To specify norm (magnitude) bars
    &lang; &#x27e8; To specify left angle bracket
    &rang; &#x27e9; To specify right angle bracket
    &compfn; &#x2218; To specify function composition
    &rarr; &#x2192; To specify general function mapping
    &mapsto; &#x21a6; To specify concrete function mapping
    ı &imath; &#x0131; To specify dotless i
    ȷ &jmath; &#x0237; To specify dotless j
    &applyfunction; &af; &#x2061; It is used to specify function application
    &invisibletimes; &it; &#x2062; It is used to specify invisible multiplication
    &invisiblecomma; &ic; &#x2063; It is used to specify invisible separator
    ¬ &not; &#x00ac; To specify negation
    &and; &#x2227; To specify logical conjunction
    &or; &#x2228; To specify logical disjunction
    &veebar; &#x22bb; To specify exclusive disjunction
    &forall; &#x2200; To specify universal quantification
    &exist; &#x2203; To specify existential quantification
    &rarr; &#x21d2; To specify material implication
    &harr; &#x21d4; To specify material equivalence
    &emptysmallsquare; &#x25fb; To specify necessarily
    &loz; &#x25ca; To specify possibly
    &vdash; &#x22a2; To specify provable
    &vdash; &#x22a8; To specify entails
    &there4; &#x2234; To specify therefore
    &empty; &#x2205; To specify the empty set
    &isin; &#x2208; To specify the member of set
    &notin; &#x2209; It specifies not a member of set
    &sube; &#x2286; To specify a subset
    &nsube; &#x2288; To specify not a subset
    &sub; &#x2282; To specify a strict subset
    &nsub; &#x2284; To specify not a strict subset
    &supe; &#x2287; To specify a superset
    &nsupe; &#x2289; To specify not a superset
    &sup; &#x2283; To specify strict superset
    &nsup; &#x2285; To specify not a strict superset
    &cap; &#x2229; To specify intersection
    &cup; &#x222a; To specify union
    &ssetmn; &#x2216; To specify complement
    Capital Letter (C) Small Letter (S) Entities(C) Entities(S) Hex Codes(C) Hex Codes(S)
    Α α &alpha; &alpha; &#x0391; &#x03b1;
    Β β &beta; &beta; &#x0392; &#x03b2;
    Γ γ &gamma; &gamma; &#x0393; &#x03b3;
    Δ δ &delta; &delta; &#x0394; &#x03b4;
    Ε ε &epsilon; &epsilon; &#x0395; &#x03b5;
    Ζ ζ &zeta; &zeta; &#x0396; &#x03b6;
    Η η &eta; &eta; &#x0397; &#x03b7;
    Θ θ &theta; &theta; &#x0398; &#x03b8;
    Ι ι &iota; &iota; &#x0399; &#x03b9;
    Κ κ &kappa; &kappa; &#x039a; &#x03ba;
    Λ λ &lambda; &lambda; &#x039b; &#x03bb;
    Μ μ &mu; &mu; &#x039c; &#x03bc;
    Ν ν &nu; &nu; &#x039d; &#x03bd;
    Ξ ξ &xi; &xi; &#x039e; &#x03be;
    Ο ο &omicron; &omicron; &#x039f; &#x03bf;
    Π π &pi; &pi; &#x03a0; &#x03c0;
    Ρ ρ &rho; &rho; &#x03a1; &#x03c1;
    Σ σ &sigma; &sigma; &#x03a3; &#x03c3;
    Τ τ &tau; &tau; &#x03a4; &#x03c4;
    Υ υ &upsilon; &upsilon; &#x03a5; &#x03c5;
    Φ φ &phi; &phi; &#x03a6; &#x03c6;
    Χ χ &chi; &chi; &#x03a7; &#x03c7;
    Ψ ψ &psi; &psi; &#x03a8; &#x03c8;
    Ω ω &omega; &omega; &#x03a9; &#x03c9;
  5. 我发现我完全低估了这篇论文的难度,它完完全全的超过了我的范畴,我需要从更加基本的论文出发,也许作者二十年前的论文可以作为一个容易入门的吧? Taking on the curse of dimensionality in joint distributions using neural networks

三月十六日 等待变化等待机会

  1. 感觉高等数学根没有学过一样的苍白无力,我想从头补习数学,记忆繁琐的公式也许可以从学习表达式来反复练习:不过我还是取巧从这里拷贝了Taylor Series,这个做法实在是矛盾,不过我把原来的unicode字符改成了无需记忆的可识别的名称,并且去掉了无效的mlabeledtr
    Let f(x) have derivatives of all orders at x=c. n = 0 f ( n ) ( c ) n ! ( x c ) n .
    那么简单的Maclaurin Series也就是当c=0时的特殊形式我打算手写一下:
    n = 0 f ( n ) ( 0 ) n ! x n
    手写公式要写的漂亮要注意挂号不要扩张mo的属性是stretchy="false"
    e n = n = 0 x n n !
    这个是自然指数的泰勒展开,也是最常用的。而这个所谓的几何级数我很惭愧似乎没有印象:
    n = 0 a r n = a 1 r provided  | r | < 1
    它的更加简化的模式
    1 1 + x = n = 0 ( 1 ) n x n 1 1 x = n = 0 x n 1 x = n = 0 ( 1 ) n ( x 1 ) n
  2. 这里是三角函数的泰勒级数:
    cos x = n = 0 ( 1 ) n x 2 n ( 2 n ) ! sin x = n = 0 ( 1 ) n x 2 n + 1 ( 2 n + 1 ) !
  3. 自然对数的泰勒展开也很重要:
    ln x = n = 0 ( 1 ) n 1 ( x 1 ) n n ln ( 1 + x ) = n = 1 ( 1 ) n + 1 x n n

三月十七日 等待变化等待机会

  1. 无意中找到一本不错的电子书,主要是很多数学概念的图解。我之前对于泰勒展开式百思不得其解,而这里非常直观清楚的解释了它的由来,而这层窗户纸一旦捅破就是如此的简单!我现在看来几乎所有的人都能够想到对于多项式如果想要求解那么最直观的就是依次求导数得到它的各个阶的项的系数,这个是多么朴素的想法啊,可是我居然没有想到!当然这里需要的是有级数收敛的概念其实这个还是误解,唯一需要的是函数在该点要无限可导,这个是微积分的核心思想,我始终没有建立起来结果就是对于微积分中可以抓住主要项而忽略次要项不得要领,其实这个是需要求极限来证明是否可以忽略次要项的。

    Derivation of the Formula for the Coefficients of a Power Series.

    One way of finding the coefficients is using Taylor's theorem, derived as follows: Given this polynomial series, f ( z ) = n = 0 c n ( z a ) n (7.1.1) = c 0 + c 1 ( z a ) + c 2 ( z a ) 2 + c 3 ( z a ) 3 + We evaluate both sides of equation above at the point z=a, to obtain: (7.2.1) f ( a ) = c 0 + c 1 ( a a ) + c 2 ( a a ) 2 + c 3 ( a a ) 3 + Since all of the terms, except the first, on the right hand side of are zero, the equation simplifies to: (7.2.2) c 0 = f ( a ) To find the next coefficient, c 1 , we first differentiate (7.2.3) f ( z ) = c 1 + 2 c 2 ( z a ) + 3 c 3 ( z a ) 2 + We then evaluate it at z=a to obtain: (7.2.4) c 1 = f ( a ) We continue to differentiate equation above and then evaluate at z=a, reordering the equation as necessary f ( z ) = 2 c 2 + ( 3 ) ( 2 ) ( z a ) + c 2 = 1 2 f ( a ) The nth coefficient is given by (7.2.5) c n = 1 n ! d n d z n f ( z ) | z = a (7.2.6) = 1 n ! f ( n ) ( a ) By plugging these values of the coefficients into equation , we obtain the following form of the power series: (7.2.7) f ( z ) = n = 0 1 n ! d n d z n f ( a ) ( z a ) n
  2. 这个是一个非常好的关于泰勒展开式求解的视频

三月十八日 等待变化等待机会

  1. 这个真的精髓之言。
    Why should you care about power series? One reason is because they allow us to approximate functions at a point to any desired accuracy.
    这是一个非常的好的直观的工具,它帮助我非常的信服的演示了泰勒展开式来模拟函数的威力和准确度。比如我仅仅使用了最高项11次方就可以很好的拟合正弦曲线了。
    
    x=var('x')
    f(x)=sin(x)
    p1(x)=x
    p2(x)=-x^3/6
    p3(x)=x^5/120
    p4(x)=-x^7/5040
    p5(x)=x^9/362880
    p6(x)=-x^11/39916800
    s(x)=p1(x)+p2(x)+p3(x)+p4(x)+p5(x)
    S=plot(s(x),(x,-5,5),ymin=-2,ymax=2,color=Color('blue'))
    F=plot(f(x),(x,-5,5),ymin=-2,ymax=2,color=Color('red'))
    P1=plot(p1(x),(x,-5,5),ymin=-2,ymax=2,color=Color('green'))
    P2=plot(p2(x),(x,-5,5),ymin=-2,ymax=2,color=Color('green'))
    P3=plot(p3(x),(x,-5,5),ymin=-2,ymax=2,color=Color('green'))
    P4=plot(p4(x),(x,-5,5),ymin=-2,ymax=2,color=Color('green'))
    P5=plot(p5(x),(x,-5,5),ymin=-2,ymax=2,color=Color('green'))
    P6=plot(p6(x),(x,-5,5),ymin=-2,ymax=2,color=Color('green'))
    F+P1+P2+P3+P4+P5+P6+S
    
    针对课后练习题要求解任意的点拟合曲线,我一开始就糊涂了,我还以为这个Maclaurin Series式的公式能够任意的求解,后来看了之前的教程才明白我只能利用泰勒展开式的定义一步一步的求解。 于是这个是求解在函数f=sin(x)当x=π/2的拟合曲线
    
    x=var('x')
    f(x)=sin(x)
    p1(x)=1
    p2(x)=-(x-3.14/2)^2/2
    p3(x)=(x-3.14/2)^4/24
    p4(x)=-(x-3.14/2)^6/720
    p5(x)=(x-3.14/2)^8/40320
    p6(x)=-(x-3.14/2)^10/362880
    s(x)=p1(x)+p2(x)+p3(x)+p4(x)+p5(x)
    S=plot(s(x),(x,-3,7),ymin=-2,ymax=2,color=Color('blue'))
    F=plot(f(x),(x,-3,7),ymin=-2,ymax=2,color=Color('red'))
    P1=plot(p1(x),(x,-3,7),ymin=-2,ymax=2,color=Color('green'))
    P2=plot(p2(x),(x,-3,7),ymin=-2,ymax=2,color=Color('green'))
    P3=plot(p3(x),(x,-3,7),ymin=-2,ymax=2,color=Color('green'))
    P4=plot(p4(x),(x,-3,7),ymin=-2,ymax=2,color=Color('green'))
    P5=plot(p5(x),(x,-3,7),ymin=-2,ymax=2,color=Color('green'))
    P6=plot(p6(x),(x,-3,7),ymin=-2,ymax=2,color=Color('green'))
    F+P1+P2+P3+P4+P5+P6+S
    
    结果是相当的令人满意: 注意红色和蓝色曲线在x=π/2的周围拟合度非常的高。泰勒级数真的是诚不我欺也!我似乎是第一次对于这种微积分的逼近思想有了信服的认识,泰勒多项式虽然是一种近似,但是泰勒级数却是数学上的严格的相等,因为无穷大的逼近就是真的相等而不是有限项的近似。这里的这个工具可以让你选择任意的函数来具象化拟合曲线。非常棒!

三月二十日 等待变化等待机会

  1. 关于泰拉展开的定理的基础是这么一个定理
    Theorem 76 states that the error between a function f(x) and its nth--degree Taylor polynomial pn(x) is Rn(x),where (8.8.7) | R n ( x ) | max | f ( n + 1 ) ( z ) | ( n + 1 ) ! | ( x c ) ( n + 1 ) | If Rn(x) goes to 0 for each x in an interval I as n approaches infinity, we conclude that the function is equal to its Taylor series expansion.
    这里的结论是怎么跳跃的我没有搞明白,因为每一个Rn(x)都趋近于0怎么就能够得到它们的加总也趋近于0呢?就是这个结论是怎么来的呢?
    Let f(x) have derivatives of all orders at x=c,let Rn(x) be as stated in Theorem 76, and let I be an interval on which the Taylor series of f(x) converges. If lim n R n ( x ) = 0 for all x in I,then (8.8.8) f ( x ) = n = 0 f ( n ) ( c ) n ! ( x c ) n    on  I .
    这个就是泰勒定理吧也就是泰勒展开的最最标准的公式吧?

三月二十二日 等待变化等待机会

  1. 这里是Sage的手册,我想学习一些语法使用这个工具来作图有一些直观的感受函数曲线变化。这个是Oregon State大学的在线书非常的好,这些才是美国大学教育的强项。而使用sagemath从安装开始。
  2. sage的颜色很难看,这里说设置成linux好一些:
    
    nick@nick-sager:~$ cat ~/.sage/init.sage 
    %colors Linux
    

三月二十三日 等待变化等待机会

  1. 搜集论文Improving Language Understanding by Generative Pre-Training。现在读起来就有一点感觉了,如果是一个月以前来读的话我可能每一句话都要碰到钉子,经过一个时期的学习,我感觉至少我有一种been there before的感觉,就是说即便不知道准确的含义,但是混了个脸熟的好处就是知道去哪里找,或者说知道它大概的重要性或者方向。
  2. 复习一下逻辑学的三段论
    A premise or premiss is a proposition—a true or false declarative statement—used in an argument to prove the truth of another proposition called the conclusion. Arguments consist of a set of premises and a conclusion.
    这里就是三段论
    Aristotle held that any logical argument could be reduced to two premises and a conclusion.
    很多中文的名词和英文对起来是一个学习的过程。这个既是中文教育的缺点也是一种独特优势。
  3. 这个主意非 常的好(Aligning Books and Movies: Towards Story-like Visual Explanations by Watching Movies and Reading Books),它是openAI那篇著名的GPT论文的一个实验素材的来源。
  4. GPT的论文大体上是理解了,但是一触及具体的实验方法就立刻露馅了,因为每一个步骤的细节可能都是前人积累很可能几十上百年的人类成果,单单从 最高层来理解都是一个很困难的事情。但是这个也正说明了我的成果,我目前能够在忽略细节的基础上了解一篇论文的主旨,这就足够了。
  5. 有必要这里重新温习一下GPT的概念
    Generative pre-trained transformers (GPT) are a type of large language model (LLM) and a prominent framework for generative artificial intelligence. They are artificial neural networks that are used in natural language processing tasks. GPTs are based on the transformer architecture, pre-trained on large data sets of unlabelled text, and able to generate novel human-like content. As of 2023, most LLMs have these characteristics and are sometimes referred to broadly as GPTs.
    这里有必要在重新引述一下Transformer的概念。它的核心是引入了所谓的attention机制去除了RNN从而减少了训练时间。这些在名词上我知道,但是真正的含义我依然不清楚,比如attention机制是怎么回事我始终搞不懂,这些看样子只有在实现细节才能接触到。那么基于这个概念,GPT论文的 核心是什么呢?首先使用Transformer依靠自主学习来使用大量未标记的语言素材来训练大语言模型,然后在不对模型作大调整的前提下来针对其他领域 做微调和优化。这个可以称之为本主动学习,因为之前的领域是完全的自主的未分类的学习样本。这个好处是可以充分利用资源,否则人工标记分类是不值得在第一 步去做的,人工指导应该放在第二步的特定目标领域去做。这个仿佛是一个培养徒弟的过程,一开始在少林寺里练了七年的挑水做饭的自学是没有师傅指点的,只有 等徒弟动心忍性开始领悟的时候师傅才开始点播他。从这一点来看,GPT实际上解决了一个入门的问题,就是我们有大量的训练资料但是不知道要怎么给 模型,而如果解决了语言模型那么大量的下一阶段的专项学习就可以事半功倍了。就好比操作系统的成熟与发展都在于一个shell的完善能够让我们在这个平台 上自由驰骋。想象看互联网上有着近乎无限的信息可以用来训练,但是如何入手是一个难题,更难的是训练的结果如何转化运用。而GPT开创的正是这个。

三月二十四日 等待变化等待机会

  1. sage是一个很好的概率分布函数的工具,我使用它来加深对于概率曲线的熟悉。
    1. 首先创建四条正态分布
      
      sage: T1 = RealDistribution('gaussian',0.3)
      sage: T2 = RealDistribution('gaussian',1)
      sage: T3 = RealDistribution('gaussian',2)
      sage: T4 = RealDistribution('gaussian',3)
      
    2. 对于四条曲线我分贝设定了不同的颜色
      
      sage: P1=plot(T1, xmin=-5, xmax=5, color="red")
      sage: P2=plot(T2, xmin=-5, xmax=5, color="yellow")
      sage: P3=plot(T3, xmin=-5, xmax=5, color="blue")
      sage: P4=plot(T4, xmin=-5, xmax=5, color="brown")
      
    3. 作图的时候使用Graphics这个函数
      
      sage: g=Graphics()
      sage: g+=P1
      sage: g+=P2
      sage: g+=P3
      sage: g+=P4
      sage: g.show()
      
    4. 假如我想看这些曲线的累积概率曲线
      
      sage: C1=T1.cum_distribution_function
      sage: C2=T2.cum_distribution_function
      sage: C3=T3.cum_distribution_function
      sage: C4=T4.cum_distribution_function
      sage: g=Graphics()
      sage: g+=plot(C1, color="red")
      sage: g+=plot(C2, color="blue")
      sage: g+=plot(C3, color="black")
      sage: g+=plot(C4, color="brown")
      sage: g.show()
      
    5. 我对于我的作图有些怀疑,在比较这个标准的图之后我才意识到,我提供的参数是σ的平方,所以,我要改成σ的平方根来作图:
      
      sage: T1.set_distribution("gaussian", sqrt(0.2))
      sage: T2.set_distribution("gaussian", sqrt(1))
      sage: T3.set_distribution("gaussian", sqrt(5.0))
      sage: T4.set_distribution("gaussian", sqrt(0.5))
      sage: P1=plot(T1, xmin=-5, xmax=5, color="red")
      sage: P2=plot(T2, xmin=-5, xmax=5, color="yellow")
      sage: P3=plot(T3, xmin=-5, xmax=5, color="blue")
      sage: P4=plot(T4, xmin=-5, xmax=5, color="brown")
      sage: g=Graphics()
      sage: g+=P1
      sage: g+=P2
      sage: g+=P3
      sage: g+=P4
      sage: g.show()
      
      现在就和wiki的结果图对上了。
    结论就是gaussian分布的参数是方差的σ而不是它的平方。这个本来是显而易见的我不知道为什么我会糊涂了。

三月二十六日 等待变化等待机会

  1. 有必要学习tensor algebra
    In mathematics, the tensor algebra of a vector space V, denoted T(V) or T•(V), is the algebra of tensors on V (of any rank) with multiplication being the tensor product. It is the free algebra on V, in the sense of being left adjoint to the forgetful functor from algebras to vector spaces: it is the "most general" algebra containing V, in the sense of the corresponding universal property.
    这个定义非常的难懂,我先要明白Tensor Product的定义:
    In mathematics, the tensor product V ⊗ W of two vector spaces V and W (over the same field) is a vector space to which is associated a bilinear map V × W → V ⊗ W that maps a pair ( v , w ) , v ∈ V , w ∈ W to an element of V ⊗ W denoted v ⊗ w .
    那么什么是tensor呢?
    In mathematics, a tensor is an algebraic object that describes a multilinear relationship between sets of algebraic objects related to a vector space. Tensors may map between different objects such as vectors, scalars, and even other tensors. There are many types of tensors, including scalars and vectors (which are the simplest tensors), dual vectors, multilinear maps between vector spaces, and even some operations such as the dot product. Tensors are defined independent of any basis, although they are often referred to by their components in a basis related to a particular coordinate system; those components form an array, which can be thought of as a high-dimensional matrix.

三月二十八日 等待变化等待机会

  1. 我一直觉得tensor product很眼熟,就是想不起来在哪里看到过类似的,直到说这个Cartesian Product才意识到我的潜意识里一直在想这个集合的操作。
    In mathematics, specifically set theory, the Cartesian product of two sets A and B, denoted A × B, is the set of all ordered pairs (a, b) where a is in A and b is in B. In terms of set-builder notation, that is A × B = { ( a , b ) ∣ a ∈ A and b ∈ B } .
    我看的头疼,最后这个简单的说明就解决了我的基本的需求:对于这样两个矩阵 它们的tensor product是什么呢?
  2. 意外的收获是第一次意识到svg原来就是xml的格式。这样子把svg嵌入html其实没有多少的节约。反正都是文字。
  3. 如果你不知道这个所谓的Einstein notation,你可能真的要把脑袋想破也不明白为什么这个等式可以成立! 明明右边看起来像是一个项和∑不相干,其实这个就是爱因斯坦的写法,居然省略了∑

三月二十九日 等待变化等待机会

  1. 工欲善其事,必先利其器。因为很多的数学符号常常的困扰我,不会读也不明白其含义。这里有一个很多数学符号的说明。我觉得相当的不错,因为有很多是不常见的。但是其中的 Hat ˆ 是有另外的意义,通常是单位向量,不知道为什么作者没有列出来,可见这个领域是非常的不规范的。数学家的严谨在于他们的思想,反而在符号上有很大的随意性,从使用各种字母符号就知道是一种约定成俗
  2. 这里是一个非常的典型的对比mathjax和mathml的例子,效果当然是很震撼的。不过我的理解是mathjax是要依赖于脚本来解析吧?而mathml是xml的形式,应该是所谓的自定义不需要额外的资源?
  1. 重新阅读这篇关于使用GPT作自然语言处理的论文。 作者首先列举了一些难题:textual entailment, question answering, semantic similarity assessment, and document classification。这些任何一项都是要穷经皓首才能解决的难题。单单理解问题就是超过普通人想象的困难。
  2. 作者反复提到一个词:discriminatively trained models。这个是什么意思呢?是指的很多训练者在模型转为非训练域作应用的时候必须要再针对使用目标做特化或者优化?还是有一点点通用模型的意味?总而言之,我的理解这篇论文的核心是这个词:generative pre-training,就是GPT的前两个字母,至于最后一个字母TTransformer是之前就已经解决了的现成的模型架构。所以,作者要解决的是如何运用这个模型架构。用作者的话就是训练的时候使用的数据是unlabelled,之后针对具体的运用做微调就是discriminative fine-tuning
    we make use of task-aware input transformations during fine-tuning to achieve effective transfer while requiring minimal changes to the model architecture.
    什么是task-aware input transformations?这个需要再理解。
  3. 第一句话往往是作者最想表达的思想:
    The ability to learn effectively from raw text is crucial to alleviating the dependence on supervised learning in natural language processing (NLP).
    机器学习什么是最重要的?当然是自主学习。什么是最困难的?当然是训练。什么是训练最需要的?当然是训练素材的取得。那么放着广阔无垠的互联网海洋知识库 不能使用而只能在海滩边建一个小小的人工水池来模拟学习游泳是不是机器训练的痛点?怎么才能让机器自由的在互联网里畅游而随后能够自然而然的积累经验?这 个就是机器学习最紧要的地方。
  4. 作者提到的两个难点我居然以前没有注意到!
    1. First, it is unclear what type of optimization objectives are most effective at learning text representations that are useful for transfer.
    2. Second, there is no consensus on the most effective way to transfer these learned representations to the target task.
    这两个问题是有某种相似性或者联系性的。学以致用是每个人的目标,只有实践才是检验真理的唯一标准。那么在学习中要带着问题和目标去学,这个是被广泛接受 的好方式。但是对于机器学习的训练阶段却是不可取的,因为你不能彻底沦为南翔技校学习挖掘机式的培训,换言之,很多时候训练的通用性决定了训练成果的价 值。训练时候的某种定向优化彷佛是现行教育里的定向代培,导致了模型固化的偏废。一个方面的优化意味着其他方面的弱化。这个固然是优化的本来意义,但是如 果伤筋动骨的大的调整就失去了培训的意义。就是说一事一训的成本太高。第二个问题实际上是第一个问题的进一步深入,就是假如我们要举一反三的能力要怎么 做?这个问题实际上比第一个问题还重要。一事一训固然是成本问题,举一反三是能力建设!前者只是成本问题,而后者是无价的。理解了问题的核心就理解了问题的重要性,这个是理解过程的一体两面。
  5. 论文的要义是什么?
    ...we explore a semi-supervised approach for language understanding tasks using a combination of unsupervised pre-training and supervised fine-tuning. Our goal is to learn a universal representation that transfers with little adaptation to a wide range of tasks.
    就是说这个是半自主学习领域的一种尝试,预训练是自主式的学习,而微调是监督学习。而训练的范畴是语言学习,目标则是不限于语言的更广泛的运用,需要很小 的适配。这一点我是在第一次读论文所没有意识到的。训练的领域是语言,应用的领域却不一定,这才是巨大的跃进!也就是突破,举一反三才是能力建设。
  6. 为什么是语言?马克思恩格斯认为语言不仅仅是智慧的工具也是智慧的结晶。也许通过学习语言是一条通向智慧的快车道,甚至是唯一的通道。
    We employ a two-stage training procedure. First, we use a language modeling objective on the unlabeled data to learn the initial parameters of a neural network model. Subsequently, we adapt these parameters to a target task using the corresponding supervised objective.
    也许一个能够掌握语言的头脑才是有智能的潜力的头脑,所以,学习必须要先从学习语言开始。语言学习所掌握的参数作调整后可以运用到其他领域。
  7. 作者使用了Transformer的架构来训练,那么回过头来我们随后再去回味为什么是Transformer而不是其他的模型。
    This model choice provides us with a more structured memory for handling long-term dependencies in text, compared to alternatives like recurrent networks, resulting in robust transfer performance across diverse tasks.
    其实回想一下那篇Transformer的论文的标题是什么?是Attention Is All You Need。这个标题在我一开始 的时候觉得很突兀,为什么人工智能和Attention杠上了?现在才开始明白,这一切的一切,包括GPT都是在解决一个记忆的问题,这些模型都是人类记 忆的某种实现形式。而记忆是有长期短期的,上下文是短期记忆,也就是attention的实质。使用RNN到底不好在哪里呢?我现在并不能知道具体 Transformer的multihead attention机制的原理是什么,可是一言以蔽之,肯定是多快好省。这个连YesPrimeMinister里的白痴都能理解的,不用说普通人。一定 的,否则为什么要进步。声称是AI的机理究竟是什么?是记忆再现?还是知识存储方式的本质揭示,不管如何,知识本身就是记忆的一种表现形式,去粗取精,拨 云见日是知识的提取,也是记忆的优化。能够智能的处理记忆本身就是智能。当然这个是悖论,也是费话。应该说在不失去本质特征前提下的记忆优化是一种智能提 取。总之,压缩就是某种智能也是智能的必要,否则就没有智能。
  8. 作者总结说自己的工作主要是自然语言的处理(NLP),而大多数着眼于词语或者短句层级,而作者要瞄准更高的层面:These approaches, however, mainly transfer word-level information, whereas we aim to capture higher-level semantics.。当然更近的是很多工作已经开始在句子层级上的研究了。我觉得这个也是顺理成章的,因为计算量自然就上去了,没有现代的硬件水平支撑,以前想做也困难。作者还提到了更加类似的前人的工作,但作者的改进在于使用Transformer的机制而不是LSTM 被限制了预测的能力在短期。还有其他的就是训练与应用之间需要更大的调整。以后我也找这几篇论文来翻翻。
  9. 作者始终提到训练的目标的问题,这个是回归的目标吗?在自主训练添加额外目标是什么意思?Adding auxiliary unsupervised training objectives is an alternative form of semi-supervised learning. 到底什么是Unsupervised Learning
    Unsupervised learning is a method in machine learning where, in contrast to supervised learning, algorithms learn patterns exclusively from unlabeled data. The hope is that through mimicry, which is an important mode of learning in people, the machine is forced to build a concise representation of its world and then generate imaginative content from it.
    为什么提到这个进化论的概念mimicry? 我以为也许是模拟的意思,这个就是英语霸权主义的主要表现形式,我无法完全肯定我的理解。总而言之,训练材料无标记是核心,自主建立某种模式是结果和目 标,因此,建立什么目标还是自主学习必要设定的参数。学习是要有目的的。关键是怎么输入这个预设的目标?理解的关键是这个词的含义:language modeling objective。目标要怎么描述?模型的内涵是什么?语言在这里是什么角色?语言即是表达的工具也是内容本身。用语言训练语言模型是语言训练的目标?简单的词语需要大量的理解。很困难。
  10. 我这种笔记学习本身也是一种学习的过程的学习。学习可以有很多种方法,达到的目的也许只有一个,但是检验结果的方法却有几乎无数种,应用固然是目的也是检验的手段。我现在缺乏的正是这种检验与运用的手段。
  11. 什么是语言模型?其实说起来很高大上,现在看起来其实很简单,就是这么个人人都能理解的公式:虽然这个是看似很熟悉的语言模型公式,但是一个细节要指出这里是对于条件概率的对数,这一点要明白是我们为了简化乘法计算而使用对数变为加总,因为贝叶斯条件概率是连乘,这个是常识,但是细节是关键的!
    Given an unsupervised corpus of tokens U = {u1 , . . . , un }, we use a standard language modeling objective to maximize the following likelihood: L 1 ( U ) = i log P ( u i | u i-k ,..., u i-1 ; Θ ) where k is the size of the context window, and the conditional probability P is modeled using a neural network with parameters Θ. These parameters are trained using stochastic gradient descent.
    这里要再复习一下likelihood的定义, 作者针对标准Transformer做了一些修改,这个公式太复杂了,我就偷懒截图如下:
  12. 再次复习一下Softmax函数
    In words, the softmax applies the standard exponential function to each element zi of the input vector z (consisting of K real numbers), and normalizes these values by dividing by the sum of all these exponentials. The normalization ensures that the sum of the components of the output vector σ(z) is 1. The term "softmax" derives from the amplifying effects of the exponential on any maxima in the input vector.

三月三十一日 等待变化等待机会

  1. 预训练之后的微调非常像是在实践检验训练的语言模型,当然这个是我的直觉,因为这个是使用标记的数据来校验吧? 可是这里有些细节还不明白,比如这个所谓的final transformer block's activation hlm究竟是何方神圣?而那个神秘的parameter Wy又是从哪里来的?
    We additionally found that including language modeling as an auxiliary objective to the fine-tuning helped learning by
    1. improving generalization of the supervised model, and
    2. accelerating convergence.
    这里为什么说including language modeling as an auxiliary objective?这里看起来 的确像是在训练一个语言模型,为什么说是auxiliary?难道说我们的本意不是在训练语言模型?或者说这些新的标记的数据和原本的训练数据是风马牛不 相及?原则上自主学习的确是要求训练数据和检验数据不能重叠,问题是这个fine-tuning算不算是在不同领域的训练,或者是同领域的校验?我始终不 明白GPT的机制是怎么样子的,难道使用和训练数据集完全无关的互联网得到的图片和它的alt属性的文字作为一个图形文字对可以作为微调吗?这个如果是对的,在我看来,这个只是利用的GPT语言模型对于语言的模型的有效性来更加准确的理解那些作为标记的alt文字,并不代表GPT模型对于图形的更深入理解。图形归图形,文字归文字,说到底GPT是根据输入的prompt的文字来联系配对的图形和机器学习的图形理解是两码事吧?除非我对于这部分有重大的理解偏差。但是这个部分应该是所谓的GPT的Generative的部分。总之,这些不明白的地方才是GPT的关键部分。
  2. 作者的解释回答了一部分的疑问,就是这个是一个一体两面的模型,不能放弃预训练建立的自主的语言模型,但是也要兼顾微调时候指导的新语言模型,所以,这里的这个联合目标是核心的目标
    Specifically, we optimize the following objective (with weight λ):

    L3(C) = L2(C) + λ ∗ L1(C)

  3. 人类的语言的确是和人类的智能是同步发展的,我个人习惯于自问自答式的学习方式,因为语言不但是知识的载体,也是其组织结构的依托,皮之不存毛之 焉附?没有语言的加持很可能人类就不能进化到智慧生物阶段。刘慈欣在《乡村教师》里从高级宇宙智慧体的视角来嘲笑人类这种带宽不超过几十几百个字节每秒的 通讯方式,这个在未来的硅基智慧看来是落后原始的,但是这个是传播方式,归根结底知识的组织形式还是要依赖于生物体最容易接触的通讯方式,而串流的字节流 似乎是最好的方式。当然《Arrival》里的外星文明使用的是高维度的通讯方式complex written language of the aliens, consisting of palindromic phrases written with circular symbols,也许是接近甚至超过图形化的多角度多层面通讯肯定要在高度进化以后才能想象的到,那时候肯定不会是现在的人类了,也许将来的硅基生命体更加适合。 这里是电影《Arrival》脚本的github repo,真的是不可思议。 这里是解说外星语言文字,和听天书毫无二致。我对于国内书法界的歪风邪气深恶痛绝,简直是糟蹋老祖宗的宝贝,如果它们这群渣滓真的要创新,干脆去写外星书法去吧。
  4. 作者介绍了微调的流程,这幅图我依然似懂非懂。 也许原理上有一个概念,但是作者并没有回答额外的训练参数Wy是怎么来的。作者的解说让我感到有些不可思议,难道人工智能就是这样子实现的吗?虽说熟读唐诗三百首不会作诗也会吟是一种通用的人类学习方法,但是这个真的只能模仿啊。比如pre-trained model was trained on contiguous sequences of text,那么对于微调来说,分类 (classification)是不需要专门改动的,而对于文字理解或者说问答(question answering or textual entailment),它的输入是一对句子。我镇的难以想象AI就是这么练成的,一个对于阅读了大量素材之后就能侃侃而谈式的为人类指点迷津?
    • Textual entailment: For entailment tasks, we concatenate the premise p and hypothesis h token sequences, with a delimiter token ($) in between.
    • Similarity For similarity tasks, there is no inherent ordering of the two sentences being compared. To reflect this, we modify the input sequence to contain both possible sentence orderings (with a delimiter in between) and process each independently to produce two sequence representations hlm which are added element-wise before being fed into the linear output layer.
    • Question Answering and Commonsense Reasoning For these tasks, we are given a context document z, a question q, and a set of possible answers {ak }. We concatenate the document context and question with each possible answer, adding a delimiter token in between to get [z; q; $; ak ]. Each of these sequences are processed independently with our model and then normalized via a softmax layer to produce an output distribution over possible answers.
    这个看到了吧!AI是怎么训练的?就是背答案,对于多重选择是最典型的,就是把每一个答案选择连同原文和问题一个个的单独输入看看条件概率是不是最大。这个你不能说不对,但是总觉得似乎难以置信。
  5. 这个是论文训练的数据bookcorpus,似乎最近OpenAI的头在记者访问中被问到授权数据的问题跟这个有关系吧?这个是下载bookcorpus的链接。顺便复习一下perplexity的概念,它越大越难猜对。
  6. 然后这里又遇到最最核心的细节,就是模型的细节:12-layer decoder-only transformer with masked self-attention heads (768 dimensional states and 12 attention heads)。这里每一个字都很重要。我首先还是要去理解decoder的层数是什么意思,从概念上当然好像是神经网络,但是我觉得肯定不是,因为神经网络的层数是非常大的。而反反复复出现的maskedunmasked让我摸不着头脑,至于说self-attention就更是Transformer的精髓所在。所以一遇到真正的干货就卡壳了。看样子我需要回过头来读Transformer论文。今天就到这里吧?

四月一日 等待变化等待机会

  1. 第一个概念是BLEU(bilingual evaluation understudy)
    BLEU (bilingual evaluation understudy) is an algorithm for evaluating the quality of text which has been machine-translated from one natural language to another. Quality is considered to be the correspondence between a machine's output and that of a human: "the closer a machine translation is to a professional human translation, the better it is" – this is the central idea behind BLEU. Invented at IBM in 2001, BLEU was one of the first metrics to claim a high correlation with human judgements of quality, and remains one of the most popular automated and inexpensive metrics.
    这个东西实在是平淡无奇,为什么IBM会和平淡无奇经常的挂钩呢?一个纯粹要靠和主观人为的结果来比较的算法能够自动化吗?也许部分吧?这个本质上和图灵实验一样的是一种非常的别扭的定义方式。这个和之前看到的universal property相 比就落后了一大街,一个事物固然可以以它的构成方法来定义,但是更为高明或者有普遍意义的是以它的固有的属性来定义。目前AI这个领域之所以充斥了太多这 种只能简单的凭借其生成方式来定义的现象归根结底还是因为我们对于它的本质属性把握的缺失,一个工业标准制定的如同手工艺制品的鉴定水平,无法批量化客观 化。好像鉴宝大会的专家系统一样。
  2. 复习一下AttentionDefinition。要学习掌握Attention先要理解为什么,也就是它的前任之一:RNN。简而言之,RNN是一种模仿人类的记忆的模式,它的输入是带有时间信息的,而这个其实是合理的,并且类似递归效应,结果会对下一次的结果有影响,这个也有人类记忆的影子。这个并非不好,但是这篇GPT-paper要 解决的并非要否定这一点,主要是针对输入和输出元素位置的敏感信息部分,作为这个RNN记忆模型天然的就是含有时间信息,那么位置敏感是自然的,这样子对 于联系输入输出里元素它们之间的距离成为天然屏障。这个是它的缺点。现代人对于信息洪流普遍能够处理的注意力带宽就是三秒,短期记忆限制了人类的联系能 力,政客们针对于此有很多的把戏。
  3. 什么是attention呢?这个本来是认知领域的概念:
    Attention is the concentration of awareness on some phenomenon to the exclusion of other stimuli. It is a process of selectively concentrating on a discrete aspect of information, whether considered subjective or objective.
    在机器学习里它的定义是这样子的:
    Machine learning-based attention is a mechanism which intuitively mimics cognitive attention. It calculates "soft" weights for each word, more precisely for its embedding, in the context window. These weights can be computed either in parallel (such as in transformers) or sequentially (such as recurrent neural networks). "Soft" weights can change during each runtime, in contrast to "hard" weights, which are (pre-)trained and fine-tuned and remain frozen afterwards.
    所以,这个是一个影响word embedding的各个值的过程。也就是换言之,word embedding里包含了全部的信息,上下文窗口范围内的重点划线词字是靠它的向量的转动来体现的。
  4. RNN有固有的难题,Attention某种程度上解决了Attention allows the calculation of the hidden representation of a token equal access to any part of a sentence directly, rather than only through the previous hidden state. RNN的问题是什么?the weaknesses of leveraging information from the hidden outputs。为什么?favor more recent information contained in words at the end of a sentence,, 这个本来就是RNN的地层逻辑,时间敏感性,哪怕是同一个句子,也是后面的词优先前面的词,这个是误解的,本来是它的优势,因为人类的记忆就是如此,甚至 很多电子电路也是如此,取决于你要多大的缓冲区,好像网络设备一样有这个窗口期开多大的难题一样,资源是有限的,时间延迟也是一种资源有限的体现。总之, attention从某种程度解决一定也是付出代价,它自己说:at the cost of reduced effective resolution due to averaging attention-weighted positions,这里的averaging attention-weighted positions是什么意思我还不懂,但是结果是明白的:reduced to a constant number of operations 估计是一个常量级别的查询动作?但是它也有后续补偿机制:counteract with Multi-Head Attention,所以,这个是以后阅读的重点理解部分。
  5. 这里要特别强调self-attention
    Self-attention, sometimes called intra-attention is an attention mechanism relating different positions of a single sequence in order to compute a representation of the sequence.
    可以理解为在当前句子里或者是窗口期内?总之目的是对冲位置敏感性。位置不是不敏感,而是在当前句子里不应该成为障碍。尤其是长句子。其实我需要补课word embedding的细节才能真正理解这其中的关键。
  6. 这里有一个实践word embedding的工具。还是要实践!这里谈到的依靠直觉来设计word embedding的向量非常的有启发性,值得深入思考理解!等吃完早饭再来看过。

四月二日 等待变化等待机会

  1. 实践也许是最好的老师,因为很多人善于从观察反馈中学习。我感觉纸上得来终觉浅是有道理的就在于很多理论的宣讲是有类似于 attention机制的取舍的,因为要强调一些重点就有意无意忽视一些细节。成败虽然在于细节,但是失败也往往是挂一漏万的过于关注局部,只见树木不见 森林。
  2. 对于设计一个字词的表达的数据结构就能够考验出一个人对于知识技能的把我。如果我们设计的目的在于存储与查找这种单一的目的,那么自然关注的是搜 索效率,可是在word embedding的范畴里,我们希望达到另一个目的,就是字和字之间的相似度,甚至我们希望一词多义,那么传统的index是无法满足的,因为 index说到底还是一维的线性关系,一个字在不同的上下文有不同的意思这个要怎么表达呢?传统的数据结构是苍白无力的。一定是多维度的,但是一言以蔽 之,多维度为什么又要是浮点数,为什么整数的向量表达不能满足呢?这个team有一个架构keras也许能够帮助理解具体的细节。tensorflow的embedding也讲了一些细节,不过是假定你不是小白的细节。我决定跟随这个tensorflow的tutorial一 个核心的要点是我们不仅仅是要表达如何定位一个词在词库中的地位,更加重要的是要表达这个词和它有紧密联系的词的关系程度。一词多义是一个特殊意义的关 系,更加普遍的是有上下文经常出现在一起的词之间的紧密关系,这个关系的表达才是数据结构的设计核心。而查找单个词在整个词库的位置则是一个相对不重要的 方面。
  3. 仔细体会这个教程,究竟为什么和怎么设计word embedding。先感受一下视觉的震撼!看看这个embedding projector

四月三日 等待变化等待机会

  1. 这个word embedding教程值得深入反复学习。
  2. 一直有一个numa的错误,我的笔记本怎么会有numa呢?但是似乎tensorboard运行的时候认为这个是一个错误。看看这个能不能修复这个问题。
    
    sudo echo 0 | sudo tee -a /sys/bus/pci/devices/0000\:01\:00.0/numa_node
    
    但是似乎这个并不是源头错误,而是tensorRT没有安装的关系。这个是为了安装运行tensorboard,它并不是必须的,但是我想看看效果。
  3. 这里的missing feature instruction的解说让我十分的费解,究竟这个是对于CPU做优化吗?这里是github的源代码,看起来是很简单,但是用意是什么我有些糊涂了。我于是检查我自己的笔记本电脑的cpu信息:
    
    nick@nick-sager:/tmp$ sudo lshw -C cpu
      *-cpu                     
           description: CPU
           product: 13th Gen Intel(R) Core(TM) i9-13900HX
           vendor: Intel Corp.
           physical id: 4
           bus info: cpu@0
           version: 6.183.1
           serial: To Be Filled By O.E.M.
           slot: U29
           size: 1905MHz
           capacity: 5200MHz
           width: 64 bits
           clock: 100MHz
           capabilities: lm fpu fpu_exception wp vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp x86-64 constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pku ospke waitpkg gfni vaes vpclmulqdq rdpid movdiri movdir64b fsrm md_clear serialize arch_lbr ibt flush_l1d arch_capabilities cpufreq
           configuration: cores=24 enabledcores=24 microcode=285 threads=32
    
    所以,现在我才理解了,就是在编译tensor库的时候,如果编译者的系统没有支持avx/avx2的cpu feature,或者编译者有意识的屏蔽掉这两个feature,故意去除这两个预定义的宏__AVX__,__AVX2__,我记得这个应该是安装gcc之类的预定义吧?似乎是在编译gcc的时候你需要设置的,或者gcc编译的时候自动识别?如果我是从源码编译的时候吧?,总而言之,编译者没有设置那么库就会自动收集这两个运行期的系统cpu feature,然后报警让你重新编译来优化。
  4. 但是我安装了tensorrt似乎还是报错说找不到,看看这里的如何安装GPU 首先我要明确我的GPU型号:
    
    ick@nick-sager:/tmp$ lspci | grep -i nvidia
    0000:01:00.0 VGA compatible controller: NVIDIA Corporation AD107M [GeForce RTX 4050 Max-Q / Mobile] (rev a1)
    0000:01:00.1 Audio device: NVIDIA Corporation Device 22be (rev a1)
    
    tensorflow官方指引nvidia官方安装网站。我对于这个有些发憷,因为似乎这个和ubuntu的官方有冲突?不过我已经安装了550.40.07,这个是nvidia-smi的结果,所以,我只需要安装tensorflow的GPU版本就可以了。
    
    pip install tensorflow[and-cuda]
    
    而我以前是安装的默认的cpu版本:
    
    pip install tensorflow
    
    这个应该才是问题。
  5. 前两天一个小问题是find的regex选项,它的*是前面一个字符的若干重复而不能空泛的单独定义,所以,任意字符需要定义为.*,这一点是一个教训。
  6. 这里是两个试金石
    
    Verify the CPU setup:
    
    python3 -c "import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([1000, 1000])))"
    
    If a tensor is returned, you've installed TensorFlow successfully.
    
    Verify the GPU setup:
    
    python3 -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"
    
    If a list of GPU devices is returned, you've installed TensorFlow successfully.
    
    我的GPU返回是空,我决定冒险升级我的驱动。这个是非常的冒险,因为很有可能又把内核版本搞乱,因为nvidia的驱动需要动态内核编译,非常的烦人, 难怪当年Linus对于黄仁勋竖中指。不过我现在是通过ubuntu官方的更新包,它自动的在编译内核驱动,也许好一些吧?ubuntu要我订阅什么 pro,虽然是免费的还是让我惴惴不安。现在重启看看。
  7. 问题还是一样,所以我怀疑我没有安装基本的库。似乎cuDNN SDK我没有安装?

四月四日 等待变化等待机会

  1. 我的系统应该是出现了混乱,只好把tensorflow先卸载再安装。我觉得我的问题是python找不到这个tensorRT,但是我 的ubuntu系统的包是已经安装过了,那么应该是python的接口没有安装吧?总之,我看到nvidia把这个软件生态搞得越来越封闭是不太高兴的, 这个要安装tensorRT究竟是怎么个东西我是懒得理解了。pip不提供搜索功能,所以,只能到网上搜索。我对于搜索的结果感到十分的困惑,现在的python和当年的java虚拟环境一样的混乱。
  2. 如果机器学习是绕不开tensorflow的话,那么熟悉甚至编译应该是一个必修课。
    1. 这里是tensorflow编译指南
    2. 一些必要安装环境:
      
      sudo apt install python3-dev python3-pip
      
    3. 然后是安装Bazel,指南说是使用Bazelisk来安装Bazel。这个我还在实验中。我想起来之前编译c++标准文档的node.js里用到过,但是不理解。
    4. 谷歌这些人搞得GO就是复杂,当然我是赞成的,没有版本管理的任何工具都是一个噩梦。就是说Bazelisk管理着Bazel的版本。所以,这个回答了我的一个疑问,就是Bazelisk的版本似乎没有人在意了,因为它就是一个管家而已我们关心的是干活的Bazel版本对了就行了。
    5. 就是说Bazel的版本号的控制是依赖这个机制。
    6. 我对于安装Bazel的机制依然感到困惑,甚至于它的功能依然感到不可理解,究竟它是一个make的替代吗?为什么这么多平台居然没有ubuntu的单独支持,难道是debian的普通版本就可以了?
    7. 果然还是要读wiki的定义,
      Bazel (/ˈbeɪzəl/[3]) is a free and open-source software tool used for the automation of building and testing software.[2] Google uses the build tool Blaze internally[4] and released an open-sourced port of the Blaze tool as Bazel, named as an anagram of Blaze.[5] Bazel was first released in March 2015 and was in beta status by September 2015.[6] Version 1.0 was released in October 2019.

      Similar to build tools like Make, Apache Ant, and Apache Maven,[2][5] Bazel builds software applications from source code using rules. Rules and macros are created in the Starlark language (previously called Skylark),[8] a dialect of Python.[5] There are built-in rules for building software written in Java, Kotlin, Scala, C, C++, Go, Python, Rust, JavaScript, Objective-C, and bash scripts.[5][6] Bazel can produce software application packages suitable for deployment for the Android and iOS operating systems.

      这个是一个核心的特点:
      it creates a new directory and fills it with symlinks to the explicit input dependencies for the rule
      ,就是把include的头文件全部清晰的表达为软链接,这个的确是一个好方法。
    8. Bazel对于编译的各个软件使用严格的版本控制,不依赖于时间戳,而是用文件的数字签名,而对于bazel本身的版本又加了一个Bazelisk来控制,谷歌这些人确实是狠。
  3. 我的openVPN一直叫着要我输入这个,我决定把这个auth-user-pass注释掉,因为我不想让它自动运行,也没有设定为startup的服务。
  4. 编译tensorflow的确给了很严格的控制它的能力,这个是我第一次接触GPU的Compute Capability
    The compute capability of a device is represented by a version number, also sometimes called its “SM version”. This version number identifies the features supported by the GPU hardware and is used by applications at runtime to determine which hardware features and/or instructions are available on the present GPU.

    The compute capability comprises a major revision number X and a minor revision number Y and is denoted by X.Y.

    我一开始还以为是类似于CPU feature一样的一些名词,原来是用版本数字代替,这个好。我的显卡的能力是: GeForce RTX 4050 8.9
    GeForce RTX 4050 Laptop GPU
    NVIDIA CUDA Cores

    2560

    Boost Clock

    1605 - 2370 MHz

    Memory Size

    6 GB

    Memory Type

    GDDR6

  5. tensorflow的默认编译器是clang,这个看样子是逼我安装了,我之前还犹豫要不要用GCC,看来还是不要折腾了。
    
    sudo apt-get update && sudo apt-get install -y llvm-17 clang-17
    
    但是很显然的我的ubuntu官方不可能有这么高的版本,为什么要这么新的版本啊?The current supported version is LLVM/Clang 17. 看样子只能照着做了:
    
    wget https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.2/clang+llvm-17.0.2-x86_64-linux-gnu-ubuntu-22.04.tar.xz
    tar -xvf clang+llvm-17.0.2-x86_64-linux-gnu-ubuntu-22.04.tar.xz
    
    不过平心而论clang确实比GCC要略胜一筹,我对于clang的稳定性还是比较信任的。但是居然clang是这样子安装的?我对于这个原始的做法总是惴惴不安。
    
    cp -r clang+llvm-17.0.2-x86_64-linux-gnu-ubuntu-22.04/* /usr
    
    不过结果似乎也是不用操心,反正clang的版本都是覆盖。
    nick@nick-sager:~/Downloads$ clang --version clang version 17.0.2 (https://github.com/llvm/llvm-project b2417f51dbbd7435eb3aaf203de24de6754da50e) Target: x86_64-unknown-linux-gnu Thread model: posix InstalledDir: /usr/bin
  6. 核心的难点还是在GPU相关的驱动软件的支持上

    The following NVIDIA® software are only required for GPU support.

    这几个都是nvidia的东西,很讨厌的。从nvidia-smi我可以知道我的GPUdriver版本是够了:NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4
    1. toolkit我是使用ubuntu官方的cuda-toolkit-12-3
    2. 这里需要理解cuda有11和12两个平台,看样子这个也是一个复杂的领域,我使用ubuntu的官方的安装包cudnn9-cuda-12-3应该和nvidia的差不多吧?不过我吃不透ubuntu自己的安装脚本:nvidia-cudnn,似乎的版本是cuda11的,那么我还是安装nvidia的吧:
      
      wget https://developer.download.nvidia.com/compute/cudnn/9.0.0/local_installers/cudnn-local-repo-ubuntu2204-9.0.0_1.0-1_amd64.deb
      sudo dpkg -i cudnn-local-repo-ubuntu2204-9.0.0_1.0-1_amd64.deb
      sudo cp /var/cudnn-local-repo-ubuntu2204-9.0.0/cudnn-*-keyring.gpg /usr/share/keyrings/
      sudo apt-get update
      sudo apt-get -y install cudnn
      
      不过看到这里我似乎觉得我直接安装cudnn就可以了。没必要安装localrepo吧?这里先明确一下概念:
      The NVIDIA CUDA Deep Neural Network library (cuDNN) is a GPU-accelerated library of primitives for deep neural networks. cuDNN provides highly tuned implementations for standard routines such as forward and backward convolution, attention, matmul, pooling, and normalization.
      所以,关键字是GPU-accelerated library就是GPU加速,但是这个几乎就是费话,否则我干嘛要装你呢?只不过是针对deep neural networks。这里提到的几个标准很重要!
    3. 其实我一直有问题的是tensorRT这个可选的库,一直不理解,看看这个文档吧:
      NVIDIA® TensorRT™ is an SDK for optimizing trained deep learning models to enable high-performance inference. TensorRT contains a deep learning inference optimizer for trained deep learning models, and a runtime for execution. After you have trained your deep learning model in a framework of your choice, TensorRT enables you to run it with higher throughput and lower latency.
      所以,这里是关于deep learning inference,这个具体是什么呢?
    4. 关于tensorRT的安装,这里的指南我还是不确定,因为ubuntu的官方安装包tensorrt据说是c++/python的接口都齐全了,为什么我还是需要再次使用pip来安装呢?我只能理解是不同的路径的殊涂同归?不过编译所谓的
      
      python3 -m pip install --pre --upgrade tensorrt
      
      这里说是
      The above pip command will pull in all the required CUDA libraries in Python wheel format from PyPI because they are dependencies of the TensorRT Python wheel. Also, it will upgrade tensorrt to the latest version if you had a previous version installed.
      在我看来这个所谓的dependency是编译期的并非运行期吧?

四月六日 等待变化等待机会

  1. 在编译tensorflow的configure里你看到有一系列的选项,这个在.bazelrc里有详细的选项,不过我还是按照最基本的configure的流程。这里遇到bazel的安装的问题,因为版本6.5是必须的。这里是专门针对ubuntu的指引。
    
    sudo apt install apt-transport-https curl gnupg -y
    curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor >bazel-archive-keyring.gpg
    sudo mv bazel-archive-keyring.gpg /usr/share/keyrings
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/bazel-archive-keyring.gpg] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
    
    这个是一个比较典型的通用流程,添加gpg key看来是一个趋势了,应该是可以过滤掉大多数的无谓地下载请求吧。使用官方的安装包我比较放心。这里有一个插曲,就是ubuntu有一个所谓的bazel-bootstrap的包,我之前不明所以,就安装了,我的猜测是类似于官方的下载安装脚本之类的。这个和使用现在的apt安装有冲突必须删除。 这个jdk的安装环境似乎是必须的,否则bazel去下载什么jvm又会失败:
    
    sudo apt install default-jdk
    
  2. 我感觉问题其实很多,也许应该尝试一下docker编译的方式。
    1. 安装docker
      
      sudo apt install gnome-terminal
      # Add Docker's official GPG key:
      sudo apt-get update
      sudo apt-get install ca-certificates curl
      sudo install -m 0755 -d /etc/apt/keyrings
      sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
      sudo chmod a+r /etc/apt/keyrings/docker.asc
      
      # Add the repository to Apt sources:
      echo \
        "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
        $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
        sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      sudo apt-get update
      
      然后是安装最新版本
      
      sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
      
      测试docker:
      
      sudo docker run hello-world
      
    2. 接下来是安装nvidia的container的支持,这个又是一个long story!
      
      curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
        && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
          sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
          sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
      
      然后是安装包
      
      sudo apt-get update
      sudo apt-get install -y nvidia-container-toolkit
      
    3. 然后我们需要下载tensorflow的docker image。这里我才意识到我的docker是必须root accessed,这样子感觉有什么设置的问题?
      
      docker pull tensorflow/tensorflow                     # latest stable release
      docker pull tensorflow/tensorflow:devel-gpu           # nightly dev release w/ GPU support
      docker pull tensorflow/tensorflow:latest-gpu-jupyter  # latest release w/ GPU support and Jupyter
      
      就是说安装和运行docker都要sudo,那么这个是否有危险呢?
    4. 运行docker在bash里的话这个命令可以map到我自己的用户比较好
      
      sudo docker run -u $(id -u):$(id -g) -it tensorflow/tensorflow bash
      
    5. 这个时候我才意识到我使用的是cpu-only的docker,需要这个gpu docker
      
      docker pull tensorflow/tensorflow:devel-gpu
      docker run --gpus all -it -w /tensorflow -v $PWD:/mnt -e HOST_PERMS="$(id -u):$(id -g)" \
          tensorflow/tensorflow:devel-gpu bash
      git pull  # within the container, download the latest source code
      
      这个总是报错,我只能去除掉--gpus all这个选项,并且,这个docker是默认root环境的,我无法用映射成我自己的用户名的方式。应该是安全的吧?我的理解是使用root的方式mount的话会有一些垃圾清理的问题,并不一定算是系统的威胁吧?
      
      ________                               _______________                
      ___  __/__________________________________  ____/__  /________      __
      __  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
      _  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ / 
      /_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/
      
      
      WARNING: You are running this container as root, which can cause new files in
      mounted volumes to be created as the root user on your host machine.
      
      To avoid this, run the container by specifying your user's userid:
      
      $ docker run -u $(id -u):$(id -g) args...
      
      我是喜欢这个tensorflow的logo。
    6. 在docker里更新git repo遇到这个错误
      
      error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err 8)
      fatal: the remote end hung up unexpectedly
      fatal: early EOF
      fatal: index-pack failed
      
      这里
      
      git config --global http.version HTTP/1.1
      
      还有说是增加buffer
      
      git config --global http.postBuffer 157286400
      
    7. 另一种想法就是这个image是不让你再更新的了?也许直接编译
      
      bazel build //tensorflow/tools/pip_package:wheel --repo_env=WHEEL_NAME=tensorflow --config=cuda --config=opt
      chown $HOST_PERMS bazel-bin/tensorflow/tools/pip_package/wheel_house/tensorflow-version-tags.whl
      
    8. 所以,在docker环境下还是要先安装jdk
    总而言之,使用docker是一个很复杂的过程,不使用docker是一个更加无尽头的复杂的过程!这个是一个大趋势!我一向对于虚拟机有抵触,因为尤其 是使用GPU这类硬件加速的需求下使用虚拟机无异于缘木求鱼,但是docker应该是一个例外,这个从内核隔绝来实现的虚拟技术应该是损耗最小的虚拟技 术,甚至于我觉得从原理来看是最好的解决办法。所以,无论是snap还是docker应该是我进一步需要增强的方向。
  3. 我似乎终于开始明白一些浅显的道理,就是配置tensorflow的环境其实很麻烦,一方面最最基本的硬件驱动是逃不掉的,这个只要你需要使用 nvidia的gpu是无论如何必须的前提,但是另一方面nvidia的一整套的软件库如cuda/dnn/tensorrt之类是很麻烦的,那么如果你 使用tensorflow的docker的image的话,这些软件库的配置就省心了,那么在这样的环境下编译tensorflow就简单很多了,这个是 我的理解,这个是说的很明白的道理,但是我之前是无论如何无法理解的。当然这个还是我的猜想,要实际去验证才明确。
  4. 这个关于docker的安全指导值得我随后学习。
  5. 这个是docker入门教育,以前公司花钱让你学都学不进去,现在有些后悔了。
    Docker is a set of platform as a service (PaaS) products that use OS-level virtualization to deliver software in packages called containers.[4] The service has both free and premium tiers. The software that hosts the containers is called Docker Engine.[5] It was first released in 2013 and is developed by Docker, Inc.
    我原来认为docker就是cgroup,看来是错误的认识看来也不错
    Because all of the containers share the services of a single operating system kernel, they use fewer resources than virtual machines
    When running on Linux, Docker uses the resource isolation features of the Linux kernel (such as cgroups and kernel namespaces) and a union-capable file system (such as OverlayFS)[10] to allow containers to run within a single Linux instance, avoiding the overhead of starting and maintaining virtual machines.
  6. 对于我的docker的问题,这里重启docker似乎可以解决,
    
    sudo apt install -y nvidia-docker2
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    
    因为我之前总是遇到docker: error response from daemon: could not select device driver "" with capabilities: [[gpu]].的错误。
  7. 我编译的时候遇到更多的错误,使用clang总是遇到各种各样的头文件的问题,比如cstddef.h之类的,我于是转而使用gcc,但是遇到 /usr/include/x86_64-linux-gnu/NvUtils.h找不到的问题,我找了一下才意识到这个是我安装的标准的tensorrt-dev库的问题:
    dpkg -L libnvinfer-dev 
    这个是看不到头文件的,我不知道这个是否是早期的包?那么看看官方的做法吧。 不过还是要先理解一下什么是tensorRT?
    The core of NVIDIA® TensorRT™ is a C++ library that facilitates high-performance inference on NVIDIA graphics processing units (GPUs). TensorRT takes a trained network, which consists of a network definition and a set of trained parameters, and produces a highly optimized runtime engine that performs inference for that network.
    难道是把模型也包含在里面了吗?
  8. 今天有些累了,焦头烂额。

四月七日 等待变化等待机会

  1. 我还是不太理解tensorRT到底是个什么东西。
    TensorRT provides APIs via C++ and Python that help to express deep learning models via the Network Definition API or load a pre-defined model via the ONNX parser that allow TensorRT to optimize and run them on an NVIDIA GPU. TensorRT applies graph optimizations, layer fusions, among other optimizations, while also finding the fastest implementation of that model leveraging a diverse collection of highly optimized kernels. TensorRT also supplies a runtime that you can use to execute this network on all of NVIDIA’s GPU’s from the NVIDIA Volta™ generation onwards.
    难道就是你可以操纵模型?似乎是说你给我模型参数我给你生成模型或者直接调现成的模型?这个Open Neural Network Exchange (ONNX) parser是一个关键的概念:
    The Open Neural Network Exchange (ONNX) is an open-source artificial intelligence ecosystem of technology companies and research organizations that establish open standards for representing machine learning algorithms and software tools to promote innovation and collaboration in the AI sector. ONNX is available on GitHub. ONNX was originally named Toffee and was developed by the PyTorch team at Facebook.
    它具体是什么呢?
    ONNX provides definitions of an extensible computation graph model, built-in operators and standard data types, focused on inferencing (evaluation). Each computation dataflow graph is a list of nodes that form an acyclic graph. Nodes have inputs and outputs. Each node is a call to an operator. Metadata documents the graph. Built-in operators are to be available on each ONNX-supporting framework.
    基本是三样:操作流程,操作运算的定义,操作数据的定义。这个是万变不离其宗的。
  2. 这里有一个AI的相关软件的比较,了解一下。
  3. 我对于ubuntu官方的安装包感到有些困惑,是否那些动态库都安装了呢?我现在才理解了一点,原来nvidia让你下载一个所谓的本地安装包,而这个本地安装包是众多的安装包的集合:
    
    nick@nick-sager:~/Downloads/NVidia$ dpkg -L nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8
    /.
    /etc
    /etc/apt
    /etc/apt/sources.list.d
    /etc/apt/sources.list.d/nv-tensorrt-local-ubuntu2204-10.0.0-cuda-11.8.list
    /usr
    /usr/share
    /usr/share/doc
    /usr/share/doc/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8
    /usr/share/doc/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/changelog.Debian.gz
    /var
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/2B368663.pub
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/InRelease
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/Local.md5
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/Local.md5.gpg
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/Packages
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/Packages.gz
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/Release
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/Release.gpg
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-bin_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-dispatch-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-dispatch10_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-headers-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-headers-plugin-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-lean-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-lean10_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-plugin-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-plugin10_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-samples_10.0.0.6-1+cuda11.8_all.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-vc-plugin-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer-vc-plugin10_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvinfer10_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvonnxparsers-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/libnvonnxparsers10_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/nv-tensorrt-local-2B368663-keyring.gpg
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/onnx-graphsurgeon_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/python3-libnvinfer-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/python3-libnvinfer-dispatch_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/python3-libnvinfer-lean_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/python3-libnvinfer_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/tensorrt-dev_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/tensorrt-libs_10.0.0.6-1+cuda11.8_amd64.deb
    /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/tensorrt_10.0.0.6-1+cuda11.8_amd64.deb
    
    而这里最关键的是它增添了一个source_list文件
    
    nick@nick-sager:~/Downloads/NVidia$ cat /etc/apt/sources.list.d/nv-tensorrt-local-ubuntu2204-10.0.0-cuda-11.8.list
    deb [signed-by=/usr/share/keyrings/nv-tensorrt-local-2B368663-keyring.gpg] file:///var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8 /
    
    所以,我现在可以一个个的去安装这些本地的包了。难怪!然后我一股脑的安装了所有的包:
    
    sudo apt install /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/*.deb
    

四月八日 等待变化等待机会

  1. 什么是知识的储备呢?就是说一些本来应该是早就解决的问题不应该在多少年的使用中司空见惯而不去细究导致在随后的某一个行动中导致卡壳的疑问。这些都是早就该解决的问题:什么是deb文件?
    1. 首先它是一个压缩文件
      
      file /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/tensorrt-libs_10.0.0.6-1+cuda11.8_amd64.deb
      /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/tensorrt-libs_10.0.0.6-1+cuda11.8_amd64.deb: Debian binary package (format 2.0), with control.tar.xz, data compression xz
      
    2. 怎么解压缩呢?我像当然的人为xz就用xz来解压缩,但是不行,需要
      
      ar x /var/nv-tensorrt-local-repo-ubuntu2204-10.0.0-cuda-11.8/tensorrt-libs_10.0.0.6-1+cuda11.8_amd64.deb
      
      我们会好无例外的得到这么几个文件:control.tar.xz data.tar.xz debian-binary _gpgbuilder
    3. 这个解开的control包含什么内容呢?
      
      Package: tensorrt-libs
      Source: tensorrt
      Version: 10.0.0.6-1+cuda11.8
      Architecture: amd64
      Maintainer: cudatools <cudatools@nvidia.com>
      Installed-Size: 8
      Depends: libnvinfer10 (= 10.0.0.6-1+cuda11.8), libnvinfer-lean10 (= 10.0.0.6-1+cuda11.8), libnvinfer-plugin10 (= 10.0.0.6-1+cuda11.8), libnvinfer-vc-plugin10 (= 10.0.0.6-1+cuda11.8), libnvinfer-dispatch10 (= 10.0.0.6-1+cuda11.8), libnvonnxparsers10 (= 10.0.0.6-1+cuda11.8)
      Section: multiverse/devel
      Priority: optional
      Description: Meta package for TensorRT runtime libraries
       Meta package for TensorRT runtime libraries.
      
      这里就是我不明白的地方了,这个是NVidia的众多的子package的一个,那么它是无名的吗?它们有一个共同的Packages文件里倒是列表了这些关系,问题是我怎么安装呢?。但是最根本的问题是这些头文件包里并没有给我惹麻烦的NvUtils.h的问题。
    且慢,我昨天遇到的问题是两个完全不同的问题,我现在才有些理解了:gcc遇到的问题看来是tensorflow已经在新版本不支持了,因为有大量的包的 安装或者什么原因,所以,我编译的时候遇到这个NvUtils.h头文件找不到。但是clang的问题根本就是clang自己安装的问题,比如说我任意编 译一个包含c++头文件#include <string>这样子的文件就会报出来找不到的问题,这个我以前是知道的,后来忘记了!但是我现在看到的似乎都不是我以前的解决 方法,而且似乎都不行! 比如我们看到gcc的搜索路径是
    
    #include "..." search starts here:
    #include <...> search starts here:
     /usr/include/c++/11
     /usr/include/x86_64-linux-gnu/c++/11
     /usr/include/c++/11/backward
     /usr/lib/gcc/x86_64-linux-gnu/11/include
     /usr/local/include
     /usr/include/x86_64-linux-gnu
     /usr/include
    End of search list.
    
    而clang的搜索路径漏掉了第一个
    
    #include "..." search starts here:
    #include <...> search starts here:
     /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++
     /usr/lib/llvm-15/lib/clang/15.0.7/include
     /usr/local/include
     /usr/include/x86_64-linux-gnu
     /usr/include
    End of search list.
    
    我隐约记得我原来是遇到这个问题,看来没有记录下来,现在要记下来!就是clang无法找到最基本的c++的头文件,因为它们并不是普通的头文件,而是一种高级的没有后缀名的头文件,比如string这个文件,对于gcc来说它是定义在 /usr/include/c++/11/string 这里还有一个关于#pragma GCC system_header的解释就是压制警告信息。
    The header files declaring interfaces to the operating system and runtime libraries often cannot be written in strictly conforming C. Therefore, GCC gives code found in system headers special treatment. All warnings, other than those generated by ‘#warning’ (see Diagnostics), are suppressed while GCC is processing a system header.
    而这个文件和clang找到的完全不一样这个需要clang添加参数-stdlib=libc++,而它是这个/usr/bin/../include/c++/v1/string 就是说目前我的系统不支持-stdlib=libstdc++,至少这个是由编译clang的时候决定的。gcc的这个string文件应该是所谓的stdc++一个头文件,它有一大堆的所谓的bits目录下的实现头文件。而与之相对比的libc++的是一个直接使用模板实现的完全的实现。
  2. 我当初在读gcc/clang文档的时候就没有搞懂这两者的区别。原来原因竟然是这么个狗皮许可证问题

    libstdc++ is the GNU c++ standard library implementation.

    libc++ is the LLVM/clang c++ standard library implementation.

    Even when compiling with clang, libstdc++ (gnu) is often used (on Linux).

    A main reason libc++ (clang) exists is that libstdc++ (gnu) is GPL and so Apple can't ship it, so you can think of libc++ as the non-GPL libstdc++.

  3. 我现在终于知道为什么,至少是部分知道,clang的问题了:
    1. clang的libstdc++的搜索路径不全,比如gcc的搜索路径是 /usr/include/c++/11 有一个版本号11,而当我使用clang -stdlib=libstdc++的时候得到的是
      
      nick@nick-sager:~$ realpath /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++
      /usr/include/c++
      
      而这里有个问题就是
      
      nick@nick-sager:~$ ls /usr/include/c++
      11  v1
      
      就是说也许是我的gcc-12的安装有些问题,或者是clang查询到的路径有问题,这里必须指定11,因为v1是libc++的实现,它们都在 /usr/include/c++是等效的实现,那么clang是如何获得gcc的libstdc++的路径的呢?
    2. 我找到以前的笔记,我曾经是直接使用clang的cc1来查看它的路径搜索的,不过这个-cc1似乎是没有更多的帮助,还是直接使用clang -c -v /tmp/test.cpp看到更多的细节
      
      (in-process)
       "/usr/bin/clang-17" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name test.cpp -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/home/nick/diabloforum -resource-dir /usr/lib/clang/17 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++ -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/x86_64-linux-gnu -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/backward -internal-isystem /usr/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir=/home/nick/diabloforum -ferror-limit 19 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -fcolor-diagnostics -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o test.o -x c++ /tmp/test.cpp
      
      这里都是这些-internal-isystem得到的路径,这里 所以,这个-internal-isystem是谁加给cc1才是最最需要搞清楚的。但是似乎这个clang的内部文档没有很简单的透露这个,这个部分从来就不是一个很标准的代码,因为往往和一些平台相关的很无理的实现。
    但是这个问题真的很难吗?我发现要么是ubuntu把包的名字改掉了,我发现它应该是libstdc++-12-dev。所 以,如果我们默认clang是使用libc++而这个是混蛋的许可证问题苹果作出的决定,那么就要默认使用gcc的自带的libstdc++的话,就会遇 到究竟采用哪一个gcc的版本,clang会挑选最高的版本,问题是这个对于gcc来说是自带的默认的,而clang需要额外的安装这个开发包,比如 gcc-12版本的开发包就是libstdc++-12-dev!!!真的是瞎折腾!
  4. 这里有一个令人敬畏的帖子我需要仔细阅读。就是详细的编译clang的步骤,看来我又要回头来自己编译clang了吗?
  5. clang的问题解决了,我几乎就是身心疲惫,然后又遇到了最早的问题,就是gcc更早暴露的问题:
    
    cp: cannot stat '/usr/include/x86_64-linux-gnu/NvUtils.h': No such file or directory
    
    所以,这个才是根源?这个是大家都知道的问题了。
    
    $ dpkg -S NvInferPlugin.h
    libnvinfer-plugin-dev: /usr/include/x86_64-linux-gnu/NvInferPlugin.h
    $ sudo apt install libnvinfer-plugin-dev
    
    眼花了,根本是不同的文件! 我能找到最接近的问题应该是这里。我太累了!

四月九日 等待变化等待机会

  1. 偶然搜到Linux From Scratch,这个真是令人怀念的岁月啊,什么时候重温一下旧时的梦想?
  2. 我冷静了一下理了一下思路发现必须要踏踏实实学习一些基础知识,而熟悉掌握一些应用tensorflow是一个不错的选择,尤其先从它的应用着手是正确的选择,不要一开始就好高骛远,这个大量的论文应用模型就是一个很好的入手。
  3. tensorflow是有官方模型相联系的。这个是安装指南 我目前是安装了2.16版本,
    
    pip3 install tf-models-official==2.16
    
    小的release没有相应的模型版本号,就是说2.16.0是没有必要的,只要2.16就可以了。
  4. 这个不明觉厉的介绍让我看的晕晕乎乎。它的目的是什么呢?似乎它是这个标准文档的镜像
    BERT (Pre-training of Deep Bidirectional Transformers for Language Understanding) introduced the method of pre-training language representations on a large text corpus and then using that model for downstream NLP tasks.
    就是说BERT可以预训练语言模型,这个应该是我目前应该熟悉的部分。
    The nlp.networks.BertEncoder class implements the Transformer-based encoder as described in BERT paper. It includes the embedding lookups and transformer layers (nlp.layers.TransformerEncoderBlock), but not the masked language model or classification task networks.
    这个不就是我现在读论文感到有些不明所以然的部分吗?就是这个预训练的语言模型在Transformer的架构里作为一个encoder的角色出现来实现word embedding生成查找的功能,至于后面的mask部分我还是不明白,至少目前读的论文没有介绍到?
  5. 我开始有点感觉这个Model Garden for TensorFlow。架构和模型是紧密相联系的,工具怎么使用确实是一个大学问。
  6. 我之前没有下载过BER论文吗?
  7. 补课一个概念General Language Understanding Evaluation (GLUE) ,而这个wiki的大表非常的让我印象深刻!目前人工智能的发展是多么的兴旺与广泛啊!而这里仅仅是所谓的datasets这个是一个什么概念呢?
  8. 偶然搜到这个debootstrap ubuntu的帖子,它提到它的源头是这里这里。而官方的帮助我很久以前读过再复习一下。
  9. 我准备做一个实验,同时也是把一些命令写下来,用过无数次却都忘记了!
    1. 创建一个5G的文件。
      
      dd if=/dev/zero of=ubuntu-disk bs=1M count=5000
      
    2. 把它做成ext3文件系统
      
      mkfs -t ext3 ubuntu-disk 
      
    3. mount这个文件系统
      
      mkdir mnt
      sudo mount -o loop ubuntu-disk mnt/
      
    4. 使用debootstrap来创建ubuntu 22.04,参考部分这里的做法,其实大侠的问题很复杂,我根本看不大懂,因为他似乎要加密的安装?
      
      sudo apt install debootstrap
      sudo debootstrap focaljammy ./mnt https://mirror.leaseweb.com/ubuntu/
      
      这里后来我才发现我拷贝粘贴别人的命令安装的是20.04而不是22.04因为代号应该是jammy而不是focal。怎么办?升级吧反正是实验,因为是arch-chroot不用担心搞坏母系统。我看到很多人都是用这个do-release-upgrade来升级的。这个是在arch-chroot里面看到的arch-chroot的mount的环境,我估计我如果使用纯粹的chroot就要做这些个bind之类的,很麻烦。
      
      root@nick-sager:/# mount
      /home/nick/workspace/debootstrap/ubuntu-disk on / type ext3 (rw,relatime)
      proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
      sys on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
      efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)
      udev on /dev type devtmpfs (rw,nosuid,relatime,size=32733676k,nr_inodes=8183419,mode=755,inode64)
      devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
      shm on /dev/shm type tmpfs (rw,nosuid,nodev,relatime,inode64)
      tmpfs on /run type tmpfs (rw,nosuid,nodev,noexec,relatime,size=6554832k,mode=755,inode64)
      tmp on /tmp type tmpfs (rw,nosuid,nodev,inode64)
      tmpfs on /run/systemd/resolve/stub-resolv.conf type tmpfs (rw,nosuid,nodev,noexec,relatime,size=6554832k,mode=755,inode64)
      
    5. 然后我决定使用这个比通常的chroot高级的
      
      sudo arch-chroot ./mnt
      
    6. 我觉得第一件事情应该是设置时区语言等等,汉字是zh_CN.utf8。我怎么知道我当前的键盘是怎样的呢?这个好像是安装系统第一件需要人为设置的,因为无法自动识别。所以,这个文件记录的是我当前的键盘/etc/default/keyboard
      
      dpkg-reconfigure tzdata
      dpkg-reconfigure locales
      dpkg-reconfigure keyboard-configuration
      
    7. 启动如何安装呢?
      
      apt install grub-efi-amd64
      
      但是这里我是不敢安装bootloader,因为怕搞坏我的系统,显然这个是不能启动的:
      
      qemu-system-x86_64 -hda ubuntu-disk -m 2G
      
      所以,这里就是一个悖论,bootloader是实际媒介的启动,如何在虚拟的介质安装呢?我没有实际的分区来安装啊?其实这个不仅仅是一个好奇的想法,事实上是有实际用途的,这里解说的就是不贸然升级系统的解决方案。从这里也看出来如果你使用老式的chroot你要自己手动的bind这些设备/dev,/dev/pts,proc,sysfs
      
      sudo mount --bind /dev /mnt/installer/dev
      sudo mount --bind /dev/pts /mnt/installer/dev/pts
      sudo mount -t proc proc /mnt/installer/proc
      sudo mount -t sysfs sys /mnt/installer/sys
      sudo chroot /mnt/installer
      
      从这里看出来arch-chroot就都替你做了。
    8. 而安装内核,可以指定需要安装的ubuntu对应的版本
      
      apt install --no-install-recommends \
        linux-{,image-,headers-}generic-hwe-22.04 \
        linux-firmware initramfs-tools efibootmgr
      
      否则安装一般性的grub-pc linux-image是被问什么版本的,因为linux-image是一个虚拟的包。这里的这位大侠做的似乎是我需要的。不过我还是需要进一步的实验。总之我是完全的老糊涂了,第一步需要先把虚拟盘bootable
      
      fdisk mnt
      
      需要创建新的分区n,然后设置dos bootable。这里有极大可能是我要寻找的但是我非常的担心这个危险的操作,肯定要确保完全理解的前提下才能尝试,否则就是大灾难。总之原理是对的,就是要安装grub在一个loop device上,但是其中的细节至关重要我还没有掌握。
    9. 我确实是老糊涂了,我之前直接把我的image就是ext3方式mount在了mnt目录,那么我一直却觉得我是loop mount的,比如mount -t ext3 ubuntu-disk mnt,可是这个对于chroot是足够但是我现在需要安装grub的话,那就必须使用loop device
      
      sudo mount -o loop ubuntu-disk mnt
      
      可以事先使用losetup -f来发现第一个空闲的loop number,但是这个总是不十分稳妥的,还是事后查看来确定loop device。
      
      losetup -l | grep ubuntu-disk | cut -d' ' -f1
      
      这位大师是专业的,非常的严谨有各种各样的检验,而且考虑的是更加复杂的多个分区的情况,对于我仅仅是玩一下而已。
    10. 但是grub target有这么多选择
      --target=TARGET

      install GRUB for TARGET platform [default=x86_64-efi]; available targets: arm-coreboot, arm-efi, arm-uboot, arm64-efi, i386-coreboot, i386-efi, i386-ieee1275, i386-multiboot, i386-pc, i386-qemu, i386-xen, i386-xen_pvh, ia64-efi, loongarch64-efi, mips-arc, mips-qemu_mips, mipsel-arc, mipsel-loongson, mipsel-qemu_mips, powerpc-ieee1275, riscv32-efi, riscv64-efi, sparc64-ieee1275, x86_64-efi, x86_64-xen

      我最后尝试使用古老的i386-pc,但是遇到grub拒绝,于是只能硬来
      
      sudo grub-install --target=i386-pc --force --boot-directory=mnt/boot /dev/$(losetup -l | grep ubuntu-disk | cut -d' ' -f1)
      
    11. 然后我们来检验一下
      
      qemu-system-x86_64 -hda ubuntu-disk -m 2G
      
      不行!彻底的失败!这个是不对的!不能启动!而且我的鼠标被困住了,需要CTRL+ALT+G,因为我是GTK的前端。为了这个所谓的键盘特效,我才意识到<kbd>根本是一个仅仅表意的tag,需要自己设定css style,我特意加了一个box-shadow: 10px 5px 5px black;
    12. 我终于有一点点头绪了,就是我要事先创建EFI的partition,之前我一直拒绝想要使用古老的i386-pc,但是似乎很多毛病,也许需要ext2?太累了。
    13. 中午吃饭前活动了一下脑子有些清醒了,实际上所谓的EFI分区就是说我们要把UEFI的那些可以boot的binary文件放在那里,为什么呢? 我以为是因为这个是通用的,也就是说windows/linux等等都要能够识别的文件系统,为了牵制只能选择windows的,所以,只是为了存放那些 binary而已,并不是为了启动的其他原因,那么我就使用fdisk把之前的image文件硬硬的切割了一块下来,这个当然是非常的冒险,好在这个是实 验而且是一个文件也没有什么大不了的,估计大部分都是空的,然后n(new)了一个FAT32/Win95(0b)的分区。这些做完了我就 遇到我之前反复遇到的问题,我一直不知道有什么好的办法,就是说对于这个mulitiple的partition的image,正常的loop mount不能识别,最早我是借助于手动计算偏移非常的麻烦,现在才知道可以直接使用losetup的-Pf选项:
      
      sudo losetup -Pf ubuntu-disk
      $ ll $(losetup -l | grep ubuntu-disk| cut -d' ' -f1)*
      brw-rw---- 1 root disk   7, 9 Apr 10 11:38 /dev/loop9
      brw-rw---- 1 root disk 259, 7 Apr 10 11:38 /dev/loop9p1
      brw-rw---- 1 root disk 259, 8 Apr 10 11:38 /dev/loop9p2
      

四月十一日 等待变化等待机会

  1. 我参考以前的image,发现它是这样子的
    
    nick@nick-sager:~/ami$ sudo fdisk -l serverdisk.img 
    Disk serverdisk.img: 10 GiB, 10737418240 bytes, 20971520 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: gpt
    Disk identifier: 49FC2B67-59F5-4FCC-B556-CC70715F2FCE
    
    Device          Start      End  Sectors Size Type
    serverdisk.img1  2048     4095     2048   1M BIOS boot
    serverdisk.img2  4096 20969471 20965376  10G Linux filesystem
    
    这里的BIOS boot据说并不是文件系统,而是所谓的
    A BIOS boot partition doesn't contain a filesystem; it's just a place to put some GRUB code that on an MBR disk would've been located immediately after the boot sector, before the start of the first partition. On a GPT disk, that area is used by the (larger) partition table and isn't available for bootloader code, so the bootloader code goes in a small partition instead.
    这个是我依稀记得的。让我看看实际的结果吧:使用fdisk选择第一个partition然后使用i查看信息:
    
             Device: serverdisk.img1
              Start: 2048
                End: 4095
            Sectors: 2048
               Size: 1M
               Type: BIOS boot
          Type-UUID: 21686148-6449-6E6F-744E-656564454649
               UUID: F9AA3449-71BC-4D24-959B-D1DE8D0F8C5B
    
    注意它的起始依然是2048*512=1024*1024=1M,我依稀记得GPT的启动部分是兼容MBR的吧?MBR是512能被包含在这个1M里?
  2. 所以,我重新制作我的image:
    1. 第一步依然是
      dd if=/dev/zero of=ubuntu-disk bs=1M count=5000
    2. 然后使用fdisk制作两个分区:第一个是代号4的BIOS boot,大小为1M;第二个才是其余的全部。整个使用GPT分区方式。
    3. 使用losetup -Pf来mount
      
      sudo losetup -Pf --show ubuntu-disk
      
      这样子可以获得loop的号码,查看可以看到有两个partiton的mount。第二个是我们需要的。
    4. 针对第二个partition制作文件系统:
      
      dev=$(losetup -l | grep ubuntu-disk | cut -d' ' -f1)
      sudo mke2fs -t ext4 ${dev}p2
      
      相应的对于第一个BIOS boot分区我做成vfat
      
      dev=$(losetup -l | grep ubuntu-disk | cut -d' ' -f1)
      sudo mkfs.vfat -F 32 ${dev}p1
      
    5. 创建mount point并且mount
      
      mkdir -p linux efi
      dev=$(losetup -l | grep ubuntu-disk | cut -d' ' -f1)
      sudo mount -t ext4 ${dev}p2 linux/
      sudo mount -t vfat ${dev}p1 efi
      
    6. 然后就是debootstrap来创建ubuntu,这里我使用官方的网站速度快得多。
      
      sudo debootstrap jammy linux/
      
    7. 如何配置呢?我这里参考一下这位大侠的做法,重要的是网络的设置部分,首先我是arch-chroot去安装必要的工具,就容易的多了。不过这里的网络设置似乎是没有用的,因为我的虚拟机似乎是不能够直接使用网卡的吧?所以,这里是一个接下去需要理解的部分。
      
      apt install vim
      apt update && apt upgrade -y
      apt install -y --no-install-recommends \
        linux-{,image-,headers-}generic linux-firmware \
        initramfs-tools cryptsetup{,-initramfs} efibootmgr
      dpkg-reconfigure tzdata
      dpkg-reconfigure locales
      dpkg-reconfigure keyboard-configuration
      echo "nick-emu" > /etc/hostname
      touch /etc/systemd/network/ethernet.network
      echo "[Match]
      Name=enp0s31f6
      [Network]
      DHCP=yes
      " > /etc/systemd/network/ethernet.network
      
      然后是安装grub,不要os-probe安装其他的系统。我这里选择使用grub-pc也就是i386纯粹为了简单。也就是说GPT是兼容MBR的启动的吧?所以,这个领域是我之前一直概念模糊的地方。
      
      apt install grub-pc
      echo "GRUB_DISABLE_OS_PROBER=false" >> /etc/default/grub
      dev=$(losetup -l | grep ubuntu-disk | cut -d' ' -f1)
      grub-install ${dev}
      
    8. 不要忘了设置登陆密码passwd
    9. 最后就是唤起虚拟机
      
      qemu-system-x86_64 -hda ubuntu-disk -m 4G
      
      这里我不用sudo就是为了安全,但是不知道这个会不会有什么其他的问题?网卡?看起来网络不是虚拟机本身的问题而是在qemu的外设配置吧?
    总而言之,总算第一步完成了,可以顺利制作一个虚拟磁盘并且启动了。
  3. 之前使用的是grub-pc创建的i386-pc目标,这个做法纯粹的是古老的模式,不是现在的UEFI,其实,压根没有必要创建单独的vfat分区。我打算重新做一个带有efi分区的支持UEFI启动的磁盘:
    1. 县创建5G的磁盘文件
      dd if=/dev/zero of=ubutnu-efi-disk bs=1M count=5000
    2. 我想尝试制作一个脚本来制作分区,保存这个手动得到脚本 这个是所有的partition type的表。
      
      $ sfdisk ubuntu-efi-disk > ubuntu-efi.script
      $ cat ubuntu-efi.script
      label: gpt
      label-id: 77615560-1E8B-724E-9415-2E9E25D1A687
      device: ubuntu-efi-disk
      unit: sectors
      first-lba: 2048
      last-lba: 10239966
      sector-size: 512
      
      ubuntu-efi-disk1 : start=        2048, size=     1048576, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=B97AF9B8-AF27-784D-AA31-6C3A9CA9D2CD
      ubuntu-efi-disk2 : start=     1050624, size=     9189343, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=F5E033F5-DE64-CB4C-8899-E2AC39CE0935
      
      如果再次使用的话就是
      
      sfdisk ubuntu-efi-disk < ubuntu-efi.script
      
      但是这么做有些太危险了。主要是其中的uuid不应该被复用。。。
      
      fdisk -l ubuntu-efi-disk
      Disk ubuntu-efi-disk: 4.88 GiB, 5242880000 bytes, 10240000 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: gpt
      Disk identifier: 77615560-1E8B-724E-9415-2E9E25D1A687
      
      Device             Start      End Sectors  Size Type
      ubuntu-efi-disk1    2048  1050623 1048576  512M EFI System
      ubuntu-efi-disk2 1050624 10239966 9189343  4.4G Linux filesystem
      
      总而言之,这个是结果。
    3. 然后依然使用losetup来loop mount这个image,这里--show记录下了loop设备。
      
      sudo losetup -Pf --show ubuntu-efi-disk
      
    4. 分别创建vfat和ext4文件系统
      
      dev=$(losetup -l | grep ubuntu-efi-disk | cut -d' ' -f1)
      sudo mkfs.vfat -F 32 ${dev}p1
      sudo mke2fs -t ext4 ${dev}p2
      
    5. 分别把两个分区都mount到efi和linux再去安装系统
      
      mkdir -p linux efi
      dev=$(losetup -l | grep ubuntu-efi-disk | cut -d' ' -f1)
      sudo mount -t vfat ${dev}p1 efi
      sudo mount -t ext4 ${dev}p2 linux
      
    6. 然后重复使用debootstrap创建ubuntu22.04
      
      sudo debootstrap jammy linux
      
      安装内核,因为debootstrap仅仅是帮你创建了linux的文件系统,必要的配置我觉得都可以省略因为反正boot以后也不能访问硬件?我应该尝试在qemu里配置?
    7. 这个就是我以前记忆中的debootstrap里的后半段,就是chroot去配置的部分,以前不理解为什么文件系统都有了还要chroot去安 装,其实理论上debootstrap也可以把内核安装,可是这个根本就是多余,这个是linux的灵活之处,大多数人肯定是要定制化的。所以吃力不讨 好。以前不知道有arch-chroot的好非常的辛苦。
      sudo arch-chroot linux
      配置安装内核和昨天是一样的。
      
      apt install --no-install-recommends \
        linux-{,image-,headers-}generic-hwe-22.04 \
        linux-firmware initramfs-tools efibootmgr
      apt install grub-efi
      
    8. 这里我们将对这个磁盘镜像安装efi-grub,这里的efi实际上是一个efi的分区的mount point
      
      sudo grub-install --target=x86_64-efi --boot-directory=linux/boot --efi-directory=efi ${dev}
      
      结果是怎么样子的呢?
      这里是boot/grub目录下安装的情况
      
      $ sudo tree linux/boot/grub
      locales-launch: Data of en_US locale not found, generating, please wait...
      linux/boot/grub
      ├── fonts
      │   └── unicode.pf2
      ├── grubenv
      ├── locale
      │   ├── en_AU.mo
      │   ├── en_CA.mo
      │   ├── en_GB.mo
      │   ├── en@quot.mo
      │   └── zh_CN.mo
      └── x86_64-efi
          ├── acpi.mod
          ├── adler32.mod
          ├── affs.mod
          ├── afs.mod
          ├── afsplitter.mod
          ├── ahci.mod
          ├── all_video.mod
          ├── aout.mod
          ├── appleldr.mod
          ├── archelp.mod
          ├── ata.mod
          ├── at_keyboard.mod
          ├── backtrace.mod
          ├── bfs.mod
          ├── bitmap.mod
          ├── bitmap_scale.mod
          ├── blocklist.mod
          ├── boot.mod
          ├── bsd.mod
          ├── bswap_test.mod
          ├── btrfs.mod
          ├── bufio.mod
          ├── cat.mod
          ├── cbfs.mod
          ├── cbls.mod
          ├── cbmemc.mod
          ├── cbtable.mod
          ├── cbtime.mod
          ├── chain.mod
          ├── cmdline_cat_test.mod
          ├── cmp.mod
          ├── cmp_test.mod
          ├── command.lst
          ├── configfile.mod
          ├── core.efi
          ├── cpio_be.mod
          ├── cpio.mod
          ├── cpuid.mod
          ├── crc64.mod
          ├── cryptodisk.mod
          ├── crypto.lst
          ├── crypto.mod
          ├── cs5536.mod
          ├── ctz_test.mod
          ├── datehook.mod
          ├── date.mod
          ├── datetime.mod
          ├── diskfilter.mod
          ├── disk.mod
          ├── div.mod
          ├── div_test.mod
          ├── dm_nv.mod
          ├── echo.mod
          ├── efifwsetup.mod
          ├── efi_gop.mod
          ├── efinet.mod
          ├── efi_uga.mod
          ├── ehci.mod
          ├── elf.mod
          ├── eval.mod
          ├── exfat.mod
          ├── exfctest.mod
          ├── ext2.mod
          ├── extcmd.mod
          ├── f2fs.mod
          ├── fat.mod
          ├── file.mod
          ├── fixvideo.mod
          ├── font.mod
          ├── fshelp.mod
          ├── fs.lst
          ├── functional_test.mod
          ├── gcry_arcfour.mod
          ├── gcry_blowfish.mod
          ├── gcry_camellia.mod
          ├── gcry_cast5.mod
          ├── gcry_crc.mod
          ├── gcry_des.mod
          ├── gcry_dsa.mod
          ├── gcry_idea.mod
          ├── gcry_md4.mod
          ├── gcry_md5.mod
          ├── gcry_rfc2268.mod
          ├── gcry_rijndael.mod
          ├── gcry_rmd160.mod
          ├── gcry_rsa.mod
          ├── gcry_seed.mod
          ├── gcry_serpent.mod
          ├── gcry_sha1.mod
          ├── gcry_sha256.mod
          ├── gcry_sha512.mod
          ├── gcry_tiger.mod
          ├── gcry_twofish.mod
          ├── gcry_whirlpool.mod
          ├── geli.mod
          ├── gettext.mod
          ├── gfxmenu.mod
          ├── gfxterm_background.mod
          ├── gfxterm_menu.mod
          ├── gfxterm.mod
          ├── gptsync.mod
          ├── grub.efi
          ├── gzio.mod
          ├── halt.mod
          ├── hashsum.mod
          ├── hdparm.mod
          ├── hello.mod
          ├── help.mod
          ├── hexdump.mod
          ├── hfs.mod
          ├── hfspluscomp.mod
          ├── hfsplus.mod
          ├── http.mod
          ├── iorw.mod
          ├── iso9660.mod
          ├── jfs.mod
          ├── jpeg.mod
          ├── json.mod
          ├── keylayouts.mod
          ├── keystatus.mod
          ├── ldm.mod
          ├── legacycfg.mod
          ├── legacy_password_test.mod
          ├── linux16.mod
          ├── linuxefi.mod
          ├── linux.mod
          ├── loadbios.mod
          ├── load.cfg
          ├── loadenv.mod
          ├── loopback.mod
          ├── lsacpi.mod
          ├── lsefimmap.mod
          ├── lsefi.mod
          ├── lsefisystab.mod
          ├── lsmmap.mod
          ├── ls.mod
          ├── lspci.mod
          ├── lssal.mod
          ├── luks2.mod
          ├── luks.mod
          ├── lvm.mod
          ├── lzopio.mod
          ├── macbless.mod
          ├── macho.mod
          ├── mdraid09_be.mod
          ├── mdraid09.mod
          ├── mdraid1x.mod
          ├── memdisk.mod
          ├── memrw.mod
          ├── minicmd.mod
          ├── minix2_be.mod
          ├── minix2.mod
          ├── minix3_be.mod
          ├── minix3.mod
          ├── minix_be.mod
          ├── minix.mod
          ├── mmap.mod
          ├── moddep.lst
          ├── modinfo.sh
          ├── morse.mod
          ├── mpi.mod
          ├── msdospart.mod
          ├── mul_test.mod
          ├── multiboot2.mod
          ├── multiboot.mod
          ├── nativedisk.mod
          ├── net.mod
          ├── newc.mod
          ├── nilfs2.mod
          ├── normal.mod
          ├── ntfscomp.mod
          ├── ntfs.mod
          ├── odc.mod
          ├── offsetio.mod
          ├── ohci.mod
          ├── part_acorn.mod
          ├── part_amiga.mod
          ├── part_apple.mod
          ├── part_bsd.mod
          ├── part_dfly.mod
          ├── part_dvh.mod
          ├── part_gpt.mod
          ├── partmap.lst
          ├── part_msdos.mod
          ├── part_plan.mod
          ├── part_sun.mod
          ├── part_sunpc.mod
          ├── parttool.lst
          ├── parttool.mod
          ├── password.mod
          ├── password_pbkdf2.mod
          ├── pata.mod
          ├── pbkdf2.mod
          ├── pbkdf2_test.mod
          ├── pcidump.mod
          ├── pgp.mod
          ├── play.mod
          ├── png.mod
          ├── priority_queue.mod
          ├── probe.mod
          ├── procfs.mod
          ├── progress.mod
          ├── raid5rec.mod
          ├── raid6rec.mod
          ├── random.mod
          ├── rdmsr.mod
          ├── read.mod
          ├── reboot.mod
          ├── regexp.mod
          ├── reiserfs.mod
          ├── relocator.mod
          ├── romfs.mod
          ├── scsi.mod
          ├── search_fs_file.mod
          ├── search_fs_uuid.mod
          ├── search_label.mod
          ├── search.mod
          ├── serial.mod
          ├── setjmp.mod
          ├── setjmp_test.mod
          ├── setpci.mod
          ├── sfs.mod
          ├── shift_test.mod
          ├── signature_test.mod
          ├── sleep.mod
          ├── sleep_test.mod
          ├── smbios.mod
          ├── spkmodem.mod
          ├── squash4.mod
          ├── strtoull_test.mod
          ├── syslinuxcfg.mod
          ├── tar.mod
          ├── terminal.lst
          ├── terminal.mod
          ├── terminfo.mod
          ├── test_blockarg.mod
          ├── testload.mod
          ├── test.mod
          ├── testspeed.mod
          ├── tftp.mod
          ├── tga.mod
          ├── time.mod
          ├── tpm.mod
          ├── trig.mod
          ├── tr.mod
          ├── true.mod
          ├── udf.mod
          ├── ufs1_be.mod
          ├── ufs1.mod
          ├── ufs2.mod
          ├── uhci.mod
          ├── usb_keyboard.mod
          ├── usb.mod
          ├── usbms.mod
          ├── usbserial_common.mod
          ├── usbserial_ftdi.mod
          ├── usbserial_pl2303.mod
          ├── usbserial_usbdebug.mod
          ├── usbtest.mod
          ├── video_bochs.mod
          ├── video_cirrus.mod
          ├── video_colors.mod
          ├── video_fb.mod
          ├── videoinfo.mod
          ├── video.lst
          ├── video.mod
          ├── videotest_checksum.mod
          ├── videotest.mod
          ├── wrmsr.mod
          ├── xfs.mod
          ├── xnu.mod
          ├── xnu_uuid.mod
          ├── xnu_uuid_test.mod
          ├── xzio.mod
          ├── zfscrypt.mod
          ├── zfsinfo.mod
          ├── zfs.mod
          └── zstd.mod
      
      3 directories, 285 files
      
      而efi分区下的结果就简单的多了
      
      $ sudo tree efi/
      efi/
      └── EFI
          ├── BOOT
          │   ├── BOOTX64.EFI
          │   ├── fbx64.efi
          │   └── mmx64.efi
          └── ubuntu
              ├── BOOTX64.CSV
              ├── grub.cfg
              ├── grubx64.efi
              ├── mmx64.efi
              └── shimx64.efi
      
      3 directories, 8 files
      
      这里要明白一点,就是grub仅仅是安装和设置,真正起作用配置启动是要在update-grub脚本执行的时候,现在先把os-probe禁止掉。这里我才意识到我还是要在镜像里安装grub,否则怎么执行update-grub呢?
      
      $ sudo arch-chroot linux
      $ apt install grub-common
      $ echo "GRUB_DISABLE_OS_PROBER=true"  >> /etc/default/grub
      
    9. 然后我们来检验一下是否能够启动
      
      qemu-system-x86_64 -hda ubuntu-efi-disk -m 4G
      
      失败!!!看来有什么地方做的不对!我尝试在fdisk里进入所谓的hybrid的MBR子菜单toggle boot flag,也不行。 虽然失败了,但是我发现有一个更加安全的做法,就是在arch-chroot里安装grub后执行grub-install之前,把efi的loop mount到/boot/efi目录,这个是默认的目录,就可以了。但是无论如何efi的制作应该是有什么关节没有打通。
  4. 总而言之,目前我只能制作i386的MBR或者GPT的磁盘镜像文件。
  5. 我回过头来总结发现其实之前的做法有很多问题
    1. 我如果是简单的MBR的启动完全不必创建多余的分区,也就是说不论GPT/MBR,我就只创建一个Linux分区,看来我是错的,因为如果我没有这个BIOS boot分区的话安装grub会报错this GPT partition label contains no BIOS Boot Partition; embedding won't be possible这里有着非常的详细的解释。而这个wiiki正是我这两天最需要阅读的部分。这个官方的称法是BIOS-based boot
      The BIOS boot partition is a partition on a data storage device that GNU GRUB uses on legacy BIOS-based personal computers in order to boot an operating system, when the actual boot device contains a GUID Partition Table (GPT). Such a layout is sometimes referred to as BIOS/GPT boot.
      这里解释了为什么GPT需要这个partition
      A BIOS boot partition is needed on GPT-partitioned storage devices to hold the second stages of GRUB. On traditional MBR-partitioned devices, the disk sectors immediately following the first are usually unused, as the partitioning scheme does not designate them for any special purpose and partitioning tools avoid them for alignment purposes. On GPT-based devices, the sectors hold the actual partition table, necessitating the use of an extra partition. On MBR-partitioned disks, boot loaders are usually implemented so the portion of their code stored within the MBR, which cannot hold more than 512 bytes, operates as a first stage that serves primarily to load a more sophisticated second stage, which is, for example, capable of reading and loading an operating system kernel from a file system.
      简而言之,MBR不需要的原因就是它的loader必须是小于512可以放在MBR结构里的,而GPT则不同,因为兼容512-byte-MBR的后面是真正的partition table,GPT这就要求有额外的空间。
    2. 关于second stage boot loader的储藏地MBR是在所谓的MBR gap,但是GPT只能另外做一个分区了。
      On MBR disks, such boot loaders typically use the sectors immediately following the MBR for this storage; that space is usually known as the "MBR gap". No equivalent unused space exists on GPT disks, and the BIOS boot partition is a way to officially allocate such space for use by the boot loader.
      所以,教训就是我必须要创建一个传统的dos disklabel?不能使用gpt。
      
      Disk ubuntu-disk: 4.88 GiB, 5242880000 bytes, 10240000 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: dos
      Disk identifier: 0xed9152a5
      
      Device       Boot Start      End  Sectors  Size Id Type
      ubuntu-disk1 *     2048 10239999 10237952  4.9G 83 Linux
      
    3. 然后这个就是关键点,我看了很多文档就是没有理解这个偏移的问题。理论上我可以直接把这个文件mount,但是问题在于如果简单的mount它是从最开始起始的,那么随后创建文件系统等于是把分区表覆盖了,这里我们必须使用loop mount来控制偏移
      
      sudo losetup -Pf --show -o 1048576 ubuntu-disk
      
      这里的偏移1048576是怎么得来的呢?是在看到fdisk -l ubuntu-disk里的起始2048sector乘以sector size 512得来的。这个是我失败了好几次文件系统创建才明白的。这个是众多大神们反反复复提醒并且写脚本来防止这种错误的原因。但是我却无感,因为不理解。
    4. 然后我们再把这个loop mount,因为只有一个分区所以相对很简单了。接下去就是照旧使用debootstrap来创建文件系统,chroot安装内核,其实这个原本是 debootstrap的second stage部分,只不过我们手动去做了。然后就是安装grub因为没有efi所以也变得很简单了。然后启动看看成功与否?实际上这个暴露了我最基本的概念 的缺失,因为启动是一个针对实际物理环境的配置,你是无法预安装的,除非你预先知道运行的机器硬件信息,否则启动就是无意义的。而对于虚拟机本来就无所谓 硬件信息,只要虚拟机主机来支持根本就不用考虑配置的问题。所以,这个纯粹是无稽之谈,最多可以作为模拟实验来学习研究。
    5. 这里就是一个非常微妙的地方,因为我为了对齐文件系统loop mount有偏移了1M,但是当我要安装grub的时候,我却必须使用一个完全的loop device来些MBR,所以,我就另外的loop mount了一个。
      
      sudo losetup -Pf --show ubuntu-disk
      
      这条路又失败了,就是说之前只有GPT/BIOS boot是成功的。
    6. 重新来过!我发现我目前只能做成GPT/BIOS的启动,肯定是哪里有做错了?
  6. 遇到了loop mount不能被detach的问题,这里有很多的讨论。这里提到的mtab和mounts都是软链接
    
    nick@nick-sager:~/workspace/debootstrap$ ll /etc/mtab
    lrwxrwxrwx 1 root root 19 Mar 31  2023 /etc/mtab -> ../proc/self/mounts
    nick@nick-sager:~/workspace/debootstrap$ ll /proc/mounts
    lrwxrwxrwx 1 root root 11 Apr  3 12:19 /proc/mounts -> self/mounts
    
    而这位大侠使用strace发现这个flag:LO_FLAGS_AUTOCLEAR没有正确的设定?
    
    strace -e trace=ioctl,mount mount -o loop /tmp/block.img /mnt/
    
  7. 我怀疑无法detach loop mount是因为在sidebar上有这个volume的显示
    
    gsettings set org.gnome.shell.extensions.dash-to-dock show-mounts false
    
    找了一圈才发现是我自己糊涂居然有一个mount没有umount,这个怪不得loop mount,内核做的是对的!

四月十二日 等待变化等待机会

  1. 我现在开始有些明白了,不论是哪种模式GRUB的bootloader都比较大是需要额外的空间的,这里的解说非常的细致。

    On an MSDOS partitioned disk, this location is typically stuck between partitions, not even in a filesystem.

    On a GPT partitioned disk, there is no room to shoehorn in the rest of the bootloader between partitions, so an explicit place needs to be made for the code -- an unformatted small partition (1MB-2MB) with the BIOS-GRUB flag.
    所以,我怀疑这个所谓的bootloader的区域压根就是存放二进制码根本就不需要文件系统,那么也无需创建,反正dos模式下就是紧挨着0 sector后面,那么对于GPT要明确一个partition,那么也是不需要文件系统来支持的。 那么EFI的分区是怎么样的呢?

    To boot in UEFI mode (assuming that capability in the hardware exists), either partition type needs an EFI partition with 1)a FAT filesystem, 2)the boot flag and 3)the ESP (EFI System Partition) flag.

    看来我可能是没有设定boot flag! 而这个最最基本的东西我十年前就在看还是老是忘记名词:
    The two major partitioning types of PC disks, GPT and MSDOS may each be used in either of two modes, UEFI or BIOS/legacy. Ubuntu may be installed on either partitioning type in either mode, but Windows 8/10 in UEFI mode requires GPT partitioning, and in legacy mode MSDOS partitioning.
    两种分区方式GPT和DOS,启动模式也是两种UEFI还是BIOS/legacy,这样子共有四种组合,ubuntu都没有问题,但是win8/10却要求必须是(UEFI/GPT),(DOS/BIOS)的组合两种。
  2. 其实,我现在认为我的错误就在于没有给grub-bootloader生存的空间,也就是说在GPT模式下要拿出大约!M的BIOS boot partition来让grub存放。这个我觉得即便是DOS模式下也是需要的,但是DOS没有明确要求就直接使用了第一个sector后面的空间,这个 是投机取巧,但是为了让这个名正言顺起来,我们就人为的分配给它一个不用的分区让它自由自在的存。这个想法是错误的,我看看我自己的笔记本的分区是这样子的
    
    nick@nick-sager:~/workspace/debootstrap$ sudo fdisk -l /dev/nvme0n1
    Disk /dev/nvme0n1: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
    Disk model: WD Blue SN570 2TB                       
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: gpt
    Disk identifier: D107060B-0D72-4008-A966-22D0BE31C1FB
    
    Device           Start        End    Sectors  Size Type
    /dev/nvme0n1p1    2048    1050623    1048576  512M EFI System
    /dev/nvme0n1p2 1050624 3907028991 3905978368  1.8T Linux filesystem
    
    这里压根就不要有什么额外的boot bios分区,所以,要改!幸好fdisk有高级功能看能不能合并?
  3. 我发现fdisk有很多高级功能我以前不知道,比如可以使用fix partition order来解决合并删除分区后的空档。但这个都不是主要问题,最核心的问题是我创建了EFI分区是第一个分区,那么MBR要怎么办?我单纯的grub- install不会帮我处理MBR,我的理解是它还是要安装,但是问题是怎么做呢?如果我使用grub-pc的安装就会报错说是embedded不支持之 类的,我理解这个是危险的因为grub会占用额外的空间要越界所以不肯这么做,但是我的MBR要怎么办呢?这里有一些关于bootloader的表有一点我现在是清楚了,就是安装grub一定要在chroot里做,这个是因为首先安全一些,其次你如果把EFI分区mount到了/boot/efi下的话,这根本就省却了额外的参数。而且你设定的禁止os-probe本来也是在虚拟机环境上的。当然你还是要使用根部的设备,这个就是losetup里的参数-Pf里的P的妙用,因为它是能够识别分区的loop mount,这个是非常的强大的!
  4. 与其胡乱的搜索不如去读标准,这个是UEFI的标准!我顺手保存了一份。 我觉得我还是很机智的,因为我们知道MBR是一定要设定,所以,我就先使用目标i386-pc把MBR强制写下,这里要用--force告诉grub- install我知道在干什么。然后再运行一遍使用--target=x86_64-efi,当然前提是我已经把efi分区mount在了/boot/ efi目录下。那么我到底是怎么启动的呢?是MBR/DOS吗?应该不是,因为磁盘标志是GPT,那么它是怎么找到bootloader的呢?这个需要看 UEFI的文档才能回答。
  5. 我觉得我已经找对了方向,其他都是细节的学习,因为有了UEFI的规范几乎一切都可以迎刃而解,至少我是这么乐观的。本来只是想快速的做个实验结果连续好几天都在折腾,不过收获满满的。先歇一歇吧!

四月十三日 等待变化等待机会

  1. 这个是摘抄的UEFI关于MBR的规范
    LBA 0 (i.e., the first logical block) of the hard disk contains either
    • a legacy Master Boot Record (MBR)
      Mnemonic Byte Offset Byte Length Description
      BootCode 0 424 x86 code used on a non-UEFI system to select an MBR partition record and load the first logical block of that partition . This code shall not be executed on UEFI systems.
      UniqueMBRDiskSignature 440 4 Unique Disk Signature This may be used by the OS to identify the disk from other disks in the system. This value is always written by the OS and is never written by EFI firmware.
      Unknown 444 2 Unknown. This field shall not be used by UEFI firmware.
      PartitionRecord 446 16*4 Array of four legacy MBR partition records (
      Signature 510 2 Set to 0xAA55 (i.e., byte 510 contains 0x55 and byte 511 contains 0xAA).
      Reserved 512 Logical BlockSize - 512 The rest of the logical block, if any, is reserved.
      The MBR contains four partition records that each define the beginning and ending LBAs that a partition consumes on a disk.
      Mnemonic Byte Offset Byte Length Description
      BootIndicator 0 1 0x80 indicates that this is the bootable legacy partition. Other values indicate that this is not a bootable legacy partition. This field shall not be used by UEFI firmware.
      StartingCHS 1 3 Start of partition in CHS address format. This field shall not be used by UEFI firmware.
      OSType 4 1 Type of partition.
      • 0xEF (i.e., UEFI System Partition) defines a UEFI system partition.
      • 0xEE (i.e., GPT Protective) is used by a protective MBR to define a fake partition covering the entire disk.
      EndingCHS 5 3 End of partition in CHS address format. This field shall not be used by UEFI firmware.
      StartingLBA 8 4 Starting LBA of the partition on the disk. This field is used by UEFI firmware to determine the start of the partition.
      SizeInLBA 12 4 Size of the partition in LBA units of logical blocks. This field is used by UEFI firmware to determine the size of the partition.
    • or a protective MBR
      Mnemonic Byte Offset Byte Length Contents
      Boot Code 0 440 Unused by UEFI systems. 所以,这里可以放空,反正UEFI不看!
      Unique MBR Disk Signature 440 4 Unused. Set to zero
      Unknown 444 2 Unused. Set to zero.
      Partition Record 446 16*4 Array of four MBR partition records. Contains:
      Signature 510 2 Set to 0xAA55 (i.e., byte 510 contains 0x55 and byte 511 contains 0xAA).
      Reserved 512 Logical Block Size - 512 The rest of the logical block, if any, is reserved. Set to zero.
      这个是专门针对Protective MBR的partition record table的规定。
      Mnemonic Byte Offset Byte Length Description
      BootIndicator 0 1 Set to 0x00 to indicate a non-bootable partition. If set to any value other than 0x00 the behavior of this flag on non-UEFI systems is undefined. Must be ignored by UEFI implementations.
      StartingCHS 1 3 Set to 0x000200, corresponding to the Starting LBA field.
      OSType 4 1 Set to 0xEE (i.e., GPT Protective)
      EndingCHS 5 3 Set to the CHS address of the last logical block on the disk. Set to 0xFFFFFF if it is not possible to represent the value in this field.
      StartingLBA 8 4 Set to 0x00000001 (i.e., the LBA of the GPT Partition Header).
      SizeInLBA 12 4 Set to the size of the disk minus one. Set to 0xFFFFFFFF if the size of the disk is too large to be represented in this field.
  2. 现在使用mc来查看我的MBR是否符合规范,这里的OS-type是不对的,这个当然是因为我使用i386-pc先做了一次写的MBR,后面的grub-efi是不会写这个的。这个难道是错误的流程吗?EFI难道不需要写MBR吗?那么就使用xxd来看看第一个partition record的情况吧
    
    nick@nick-sager:~/workspace/debootstrap$ xxd -d -s 446 -l 16 ubuntu-efi-disk 
    00000446: 0000 0200 eeff ffff 0100 0000 ff3f 9c00  .............?..
    
  3. 文档里有大量的UEFI的文献。这一篇是特别关于Partition Types: OS Type values used in the MBR disk layout,而我关心的是这个OS-type的标志量的列表
    • ee Indication that this legacy MBR is followed by an EFI header
    • ef Partition that contains an EFI file system

      Bob Griswold (rogris@Exchange.Microsoft.com) writes: MS plans on using EE and EF in the future for support of non-legacy BIOS booting. Mark Doran (mark.doran@intel.com) adds: these types are used to support the Extensible Firmware Interface specification (EFI); go to developer.intel.com and search for EFI. (For the types ee and ef, see Tables 16-6 and 16-7 of the EFI specification, EFISpec_091.pdf.)

    所以,如果不读原始文献我差点上当了,因为这里的解释差点让我误以为应该是EF而不是EE,现在看来EE是更加的合适。
  4. 为什么需要partition table呢?在我看来最直接的就是打破DOS的2GB限制。什么是protective MBR呢?就是说GPT的规范压根儿就把第一个sector也就是MBR部分留空了,这个是fdisk的说明部分
    Note that the first sector is still reserved for a protective MBR in the GPT specification. It prevents MBR-only partitioning tools from mis-recognizing and overwriting GPT disks.
    所以,当我看到fdisk打出的DISK LABEL如果是GPT就意味着MBR不应当被检查。其实,任何人都应该毫无困难的熟悉MBR的结构,因为就是一句话的事情:
    the structure of the MBR (Master Boot Record, sector 0) is as follows: First 446 bytes boot loader code, then 64 bytes partition table (starting at offset 0x1be = 446), finally 2 bytes signature 0xaa55.
    这里有各种各样的关于磁盘尺寸的限制的问题,我实在是没有心思看了,因为这些都是BIOS的问题吧?ATA/IDE磁盘我早都扔光了吧?
  5. 这位荷兰的老教授的个人网页有很多的有趣的东西,我似乎从来没有浏览过维基解密
  6. 我所怀疑的是不是我设置了类似于CSM的机制所以才能够在一个有问题的系统来启动呢?
    CSM boot requires a hard disk with MBR partition type, while UEFI boot mode requires a disk with the GPT partition table.
    不过看起来这个似乎是不可能的,就是说如果我使用了GPT,那么CSM(Compatibility Support Module)应该是不可能吧?不过我也不是很确定,如果我在开机的时候强制使用系统的BIOS启动模式会怎么样呢?我之前没有考虑过实际系统在开机时候是可以设置使用什么模式启动的选择,这个是人为决定的,那么qemu是怎么选择的呢? 我觉得wiki还是一个好东西,吃完早饭来看wiki关于UEFI的权威
    1. 我并不知道UEFI还有所谓的runtime service
    2. 什么是EFI system partition呢?
      An EFI system partition, often abbreviated to ESP, is a data storage device partition that is used in computers adhering to the UEFI specification. Accessed by the UEFI firmware when a computer is powered up, it stores UEFI applications and the files these applications need to run, including operating system boot loaders. Supported partition table schemes include MBR and GPT, as well as El Torito volumes on optical discs. For use on ESPs, UEFI defines a specific version of the FAT file system, which is maintained as part of the UEFI specification and independently from the original FAT specification, encompassing the FAT32, FAT16 and FAT12 file systems. The ESP also provides space for a boot sector as part of the backward BIOS compatibility.
      谁说UEFI不支持MBR?当然这个具体内涵是什么现在还不清楚。
    3. 具体是怎么样的呢?第一句就明确了UEFI根本不理会MBR,这个够明确了吧!但是,且慢,后面的所谓的boot manager是根据存在NVRAM里的参数来决定如何boot的。
      Unlike the legacy PC BIOS, UEFI does not rely on boot sectors, defining instead a boot manager as part of the UEFI specification. When a computer is powered on, the boot manager checks the boot configuration and, based on its settings, then executes the specified OS boot loader or operating system kernel (usually boot loader). The boot configuration is defined by variables stored in NVRAM, including variables that indicate the file system paths to OS loaders or OS kernels.
      我觉得接下来是关键:这里要求OS boot loader必须明确的被定义,在x86-64 system is \efi\boot\bootx64.efi,就是说这个是写死的,或者是默认的。
      Booting UEFI systems from GPT-partitioned disks is commonly called UEFI-GPT booting. Despite the fact that the UEFI specification requires MBR partition tables to be fully supported, some UEFI firmware implementations immediately switch to the BIOS-based CSM booting depending on the type of boot disk's partition table, effectively preventing UEFI booting to be performed from EFI System Partition on MBR-partitioned disks. Such a boot scheme is commonly called UEFI-MBR.
      这个也许就是微软为什么强调windows只支持UEFI-GPT或者BIOS-MBR的原因,如果分区是MBR,而且你也设置了EFI system partition,可是因为CSM模块设计就是要支持BIOS-MBR的启动,结果自然是直接就呼叫了BIOS-MBR的启动了,根本不看是否存在 EFI system partition。到这里我算是初步明白为什么grub-efi对于MBR放空不写的原因了,就是不要给CSM机会,虽然这个可能性只是存在于BIOS-MBR,照例说UEFI-GPT应该是不可能调用CSM的。
    4. 这里非常的有必要把UEFI所心心念念的Protective MBRspec再看一下。所以,很清楚的就是我们只需要把MBR的第一个partition record和signature设定就可以了。这个需要做实验,于是我删除了假的MBR
      
      dd if=/dev/zero of=ubuntu-efi-disk bs=1 count=512
      
      我估计我这么干就把fdisk写上去的GPT信息也抹掉了吧?这么做太危险了!!! 然后chroot去安装grub-install遇到grub-install: error: cannot find a GRUB drive for /dev/loop5p1. Check your device.map.这里让我意识到我可以使用grub-mkdevicemap,但是查看之后
      
      root@nick-sager:/# cat /boot/grub/device.map 
      (hd0)	/dev/disk/by-id/nvme-WD_Blue_SN570_2TB_23032A802561
      (hd1)	/dev/disk/by-id/nvme-Fanxiang_S770M_4TB_FX232640342
      
      我决定使用自己手动创建的:
      
      root@nick-sager:/# cat /boot/grub/device.map 
      (hd0)	/dev/loop5
      
      当然这里的/dev/loop5是我当前loop mount的设备。决定再尝试安装grub
    5. 当我使用dd抹去MBR结果意外的也抹去了所有的GPT的信息,我只好重新进入fdisk创建一个空的GPT,这个是结果,就是说它会创建一个符合protective MBR的
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -d -s 446 -l 16 ubuntu-efi-disk 
      00000446: 0000 0200 eeff ffff 0100 0000 07f0 8e00  ................
      
      完蛋了!我对于大侠的抹去MBR的做法是认可的,可是这么样使用dd,导致文件就只有512byte了,这个只能是使用在设备上!比如我loop mount的设备!!!这个实在是太可怕了!磁盘丢失只能重新来过了!
  7. 从头来过!
    1. 什么是创建一个空的GPT表呢?这个默认就是创建了一个所谓的protective MBR
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -d -s 446 -l 16 ubuntu-efi-disk 
      00000446: 0000 0200 eeff ffff 0100 0000 ff3f 9c00  .............?..
      
      对比这个partition record表,就知道,标志是ee,同时最值得计算的是最后一个sizeInLBA是diskSize-1=0xff3f 9c00这个显然胡扯。
    2. 然后就是fdisk创建一个EFI system partion,大小512M,其余为一个Linux的分区。
    3. 使用losetup -Pf --show来mount这个磁盘文件,会根据分区自动产生连个子设备,然后分别制作文件系统mkfs.vfat -F 32,mkfs.ext4
    4. 然后使用debootstrap和arch-chroot来创建文件系统和内核以及安装工具包。
    5. 在安装完grub之后,我要手动创建所谓的device.map在/boot/grub/目录下。然后再去安装grub-install。
      
      root@nick-sager:/# cat /boot/grub/device.map
      (hd0)	/dev/loop5p1
      (hd1)	/dev/loop5p2
      
    不成功,我开始怀疑是不是qemu的参数有没有UEFI的选项?

四月十四日 等待变化等待机会

  1. 有必要学习GPT,因为我自认为对于MBR是很清楚了,虽然高级的extended部分还是不清楚,不过这个似乎是windows专属的吧我也不关心。
    Two GPT Header structures are stored on the device: the primary and the backup. The primary GPT Header must be located in LBA 1 (i.e., the second logical block), and the backup GPT Header must be located in the last LBA of the device.
    一头一尾两个就限制死了整个磁盘空间了。 关于GPT partition table其实也就是比较容易理解了,GPT表确实有点大,因为单单一个partition entry array就至少要16k,就是32个LBA(assume 512byte/LBA)
    The primary GPT Partition Entry Array must be located after the primary GPT Header and end before the First Usable LBA. The backup GPT Partition Entry Array must be located after the Last Usable LBA and end before the backup GPT Header.

    ...A minimum of 16,384 bytes of space must be reserved for the GPT Partition Entry Array.

    所以,这里的34就明白怎么来的吧?
    If the block size is 512, the First Usable LBA must be greater than or equal to 34 (allowing 1 block for the Protective MBR, 1 block for the Partition Table Header, and 32 blocks for the GPT Partition Entry Array); if the logical block size is 4096, the First Useable LBA must be greater than or equal to 6 (allowing 1 block for the Protective MBR, 1 block for the GPT Header, and 4 blocks for the GPT Partition Entry Array).
    这个让我想起来当年使用IPMI破解quanta的电源设备驱动dump出来的磁盘文件就是类似这个结构,就是说一个GPT的头部34k就是这么来的。 其实磁盘或者其他存储设备相当的复杂,因为物理的存储单元和逻辑的存储单元大小不是总是匹配的,因此分区要去对齐物理存储单元,这里还有所谓的SCSI设 备的传输最小单元(optimal transfer length granularity),这些对齐的细节我就懒得了解了。 这里是GPT头的结构,摘抄一下吧。
    Mnemonic Byte Offset Byte Length Description
    Signature 0 8 Identifies EFI-compatible partition table header. This value must contain the ASCII string “EFI PART”, encoded as the 64-bit constant 0x5452415020494645.
    Revision 8 4 The revision number for this header. This revision value is not related to the UEFI Specification version. This header is version 1.0, so the correct value is 0x00010000.
    HeaderSize 12 4 Size in bytes of the GPT Header. The HeaderSize must be greater than or equal to 92 and must be less than or equal to the logical block size.
    HeaderCRC32 16 4 CRC32 checksum for the GPT Header structure. This value is computed by setting this field to 0, and computing the 32-bit CRC for HeaderSize bytes.
    Reserved 20 4 Must be zero.
    MyLBA 24 8 The LBA that contains this data structure.
    AlternateLBA 32 8 LBA address of the alternate GPT Header.
    FirstUsableLBA 40 8 The first usable logical block that may be used by a partition described by a GUID Partition Entry.
    DiskGUID 56 16 GUID that can be used to uniquely identify the disk.
    PartitionEntryLBA 72 8 The starting LBA of the GUID Partition Entry array.
    NumberOfPartitionEntries 80 4 The number of Partition Entries in the GUID Partition Entry array.
    SizeOfPartitionEntry 84 4 The size, in bytes, of each the GUID Partition Entry structures in the GUID Partition Entry array. This field shall be set to a value of 128 x 2n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.). NOTE: Previous versions of this specification allowed any multiple of 8..
    PartitionEntryArrayCRC32 88 4 The CRC32 of the GUID Partition Entry array. Starts at PartitionEntryLBA and is computed over a byte length of NumberOfPartitionEntries * SizeOfPartitionEntry.
    Reserved 92 BlockSize – 92 The rest of the block is reserved by UEFI and must be zero.
    1. 我专门检验了一下这个GPT的签名,因为我对于endian始终很不舒服,od似乎没有这么个显示的功能,它显示的是byte order
      
      nick@nick-sager:~/workspace/debootstrap$ od -j 512 -Ad -t x4  -N8 ubuntu-efi-disk 
      0000512 20494645 54524150
      0000520
      
      转而发现xxd可以显示little endian的方式来展现
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -e -g8 -d -s 512 -l8 ubuntu-efi-disk 
      00000512: 5452415020494645                   EFI PART
      
    2. 我对于版本号也有些好奇:
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -e -g4 -d -s 520 -l4 ubuntu-efi-disk 
      00000520: 00010000                             ....
      
      当然byte order是
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -d -g1 -s 520 -l4 ubuntu-efi-disk 
      00000520: 00 00 01 00                                      ....
      
    3. GPT头的大小的确是92(0x5C)
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g4 -s 524 -l4 ubuntu-efi-disk 
      00000524: 0000005C                             \...
      
    4. 如果理解了就应该知道myLBA是1,因为对于GPT-header来说就是它自己的位置,那就是LBA-1在protective MBR后面。
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 536 -l8 ubuntu-efi-disk 
      00000536: 0000000000000001                   ........
      
      尽信书不如不读书,如果你相信FirstUsableLBA是老老实实的最优化的第34个LBA那就天真了。实际上是2048,当然这个很有可能是我使用fdisk随手这么取默认值造成的。
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 552 -l8 ubuntu-efi-disk 
      00000552: 0000000000000800                   ........
      
    5. 那么LastUsableLBA是多少呢?
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 560 -l8 ubuntu-efi-disk 
      00000560: 00000000009C3FDE                   .?......
      
      这个计算的逻辑是什么呢?我的磁盘文件大小是5242880000bytes换算成LBA的数量就是 5242880000/512=10240000=0x9C4000,那么最后的backup GPT header大小就是0x9C4000-0x9C3FDE=0x22=34,所以,这个34是一个最优解,而且是足够的,而我在使用fdisk时候和普通 人一样犯了懒惰的毛病使用默认值2048,这个也许对于最大可能性的GPT partition entry是必须的,但是对于普通的磁盘分区实在是太奢侈了!所以,以后我要养成习惯使用这个magic word 34不过好像fdisk压根儿就不让你这么做!2048是默认最小值!马上打脸,尴尬了。所以,应该使用gdisk!!!
    6. 显示DiskGUID有些问题,因为
      
      typedef struct _GUID {
        unsigned long  Data1;
        unsigned short Data2;
        unsigned short Data3;
        unsigned char  Data4[8];
      } GUID;
      
      从gdisk看到的GUID是
      Disk identifier (GUID): 5B513127-16FE-B34D-A5EF-B1996CE64DCD
      所以,这个显示的是byte order。
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u  -d -g1 -s 568 -l16 ubuntu-efi-disk 
      00000568: 27 31 51 5B FE 16 4D B3 A5 EF B1 99 6C E6 4D CD  '1Q[..M.....l.M.
      
    7. 分区表在第几个LBA呢?为什么是第2个,这个当然是很明显的,protective MBR后面是GPT header,然后就是PartitionEntryLBA,所以,当然是第2个(从0开始的)。
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 584 -l8 ubuntu-efi-disk 
      00000584: 0000000000000002                   ........
      
    8. NumberOfPartitionEntries分区表有多少个呢?我是没有想到是0x80=128个,也许是因为我还没有设定的默认值。
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g4 -s 592 -l4 ubuntu-efi-disk 
      00000592: 00000080        
      
    9. SizeOfPartitionEntry应该是8的128,256,512。。。指数倍数增长的。可以看到默认就是0x80=128
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g4 -s 596 -l4 ubuntu-efi-disk 
      00000596: 00000080    
      
    10. PartitionEntryArrayCRC32目前应该是空的吧?空的也不是0啊!
      
      nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g4 -s 600 -l4 ubuntu-efi-disk 
      00000600: A448F277                             w.H.
      
    你使用gdisk的一个好处是可以节省大约1M的磁盘空间,因为默认第一个可用的LBA在fdisk里是2048,这个有一点点浪费,因为实际上最少34 就可以,但是fdisk没有这个功能,gdisk在高级菜单(x)里有一个设置sector alignment(l),如果你设定为128(默认是2048)的话,再创建分区就允许你从默认128最少34开始了。这个是一个高级的功能。看看我修 改后的FirstUsableLBA
    
    nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 552 -l8 ubuntu-efi-disk 
    00000552: 0000000000000022                   ".......
    
    现在看看我修改后的FirstUsableLBA是0x22=34,非常的紧凑了。
  2. 这个是GPT Partition Entry的结构
    Mnemonic Byte Offset Byte Length Description
    PartitionTypeGUID 0 16 Unique ID that defines the purpose and type of this Partition. A value of zero defines that this partition entry is not being used. 这个不是分区自己的GUID而是类型的GUID,比如EFI system partition类型的GUID就是C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI system partition)。这个都是固定的。
    UniquePartitionGUID 16 16 GUID that is unique for every partition entry. Every partition ever created will have a unique GUID. This GUID must be assigned when the GPT Partition Entry is created. The GPT Partition Entry is created whenever the NumberOfPartitionEntrie s in the GPT Header is increased to include a larger range of addresses. 这个是真正的分区自己的GUID
    
    nick@nick-sager:~/workspace/debootstrap$ xxd -u  -d -g16 -s 1040 -l16 ubuntu-efi-disk 
    00001040: D3AC9215B87599478B122695DFCF56E2  .....u.G..&...V.
    
    验证使用gdisk/fdisk:
    
    Partition number (1-2): 1
    Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI system partition)
    Partition unique GUID: 1592ACD3-75B8-4799-8B12-2695DFCF56E2
    First sector: 128 (at 64.0 KiB)
    Last sector: 1050623 (at 513.0 MiB)
    Partition size: 1050496 sectors (512.9 MiB)
    Attribute flags: 0000000000000000
    Partition name: 'EFI system partition'
    
    StartingLBA 32 8 Starting LBA of the partition defined by this entry.
    
    nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 1056 -l8 ubuntu-efi-disk 
    00001056: 0000000000000080                   ........
    
    这个就是First sector: 128 (at 64.0 KiB),0x80=128
    EndingLBA 40 8 Ending LBA of the partition defined by this entry.
    
    nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 1064 -l8 ubuntu-efi-disk 
    00001064: 00000000001007FF                   ........
    
    这个就是Last sector: 1050623 (at 513.0 MiB),因为0x1007FF=1050623
    Attributes 48 8 Attribute bits, all bits reserved by UEFI 我重新安装grub之后,让我们看看EFI system partition的性质有没有变化
    
    nick@nick-sager:~/workspace/debootstrap$ xxd -u -e -d -g8 -s 1072 -l8 ubuntu-efi-disk 
    00001072: 0000000000000000                   ........
    
    为什么是空的呢?我查看我的笔记本的EFI表也是如此
    
    nick@nick-sager:~/workspace/debootstrap$ sudo xxd -u -e -d -g8 -s 1072 -l8 /dev/nvme0n1
    00001072: 0000000000000000                   ........
    
  3. 这个是GPT partition entry attribute table
    Bits Name Description
    Bit 0 Required Partition If this bit is set, the partition is required for the platform to function. The owner/creator of the partition indicates that deletion or modification of the contents can result in loss of platform features or failure for the platform to boot or operate. The system cannot function normally if this partition is removed, and it should be considered part of the hardware of the system. Actions such as running diagnostics, system recovery, or even OS install or boot could potentially stop working if this partition is removed. Unless OS software or firmware recognizes this partition, it should never be removed or modified as the UEFI firmware or platform hardware may become non-functional.
    Bit 1 No Block IO Protocol If this bit is set, then firmware must not produce an EFI_BLOCK_IO_PROTOCOL device for this partition. See Section 13.3.2 for more details. By not producing an EFI_BLOCK_IO_PROTOCOL partition, file system mappings will not be created for this partition in UEFI.
    Bit 2 Legacy BIOS Bootable This bit is set aside by this specification to let systems with traditional PC-AT BIOS firmware implementations inform certain limited, special-purpose software running on these systems that a GPT partition may be bootable. For systems with firmware implementations conforming to this specification, the UEFI boot manager (see chapter 3) must ignore this bit when selecting a UEFI-compliant application, e.g., an OS loader (see 2.1.3). Therefore there is no need for this specification to define the exact meaning of this bit.
    Bits 3-47 Undefined and must be zero. Reserved for expansion by future versions of the UEFI specification.
    Bits 48-63 Reserved for GUID specific use. The use of these bits will vary depending on the PartitionTypeGUID. Only the owner of the PartitionTypeGUID is allowed to modify these bits. They must be preserved if Bits 0–47 are modified.
  4. 我刚刚才发现我的旧笔记本之所以必须启动时候设定legacyboot(CSM)的原因是我当初安装的时候磁盘分区采用的是MBR/DOS而不是GPT,所以,压根就没有efi分区。
  5. 我发现了一些端倪,在/boot/efi/BOOT/grub.cfg里
    
    nick@nick-sager:~/workspace/debootstrap$ sudo cat /boot/efi/EFI/ubuntu/grub.cfg
    search.fs_uuid 92d635c6-8ab6-4bfb-8d12-2836774e77c5 root 
    set prefix=($root)'/boot/grub'
    configfile $prefix/grub.cfg
    
    这里的这个UUID是什么?我一开始以为是disk-uuid或者是partition-uuid,都不是,原来是filesystem-uuid。
    
    nick@nick-sager:~/workspace/debootstrap$ sudo blkid  /dev/nvme0n1p2
    /dev/nvme0n1p2: UUID="92d635c6-8ab6-4bfb-8d12-2836774e77c5" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="1cdd8b75-0e3f-4f9d-ad6d-785f38484626"
    
    但是这样子依然不成功。UEFI关于EFI system partition的说明:
    The first block (sector) of a partition contains a data structure called the BIOS Parameter Block (BPB) that defines the type and location of FAT file system on the drive. The BPB contains a data structure that defines the size of the media, the size of reserved space, the number of FAT tables, and the location and size of the root directory (not used in FAT32). The first block (sector) also contains code that will be executed as part of the boot process on a legacy system. This code in the first block (sector) usually contains code that can read a file from the root directory into memory and transfer control to it. Since EFI firmware contains a file system driver, EFI firmware can load any file from the file system with out needing to execute any code from the media.
    这里是BPB 这个表是我需要了解的。
    Sector offset BPB offset Field length Description
    0x00B 0x00 25 BYTEs DOS 3.31 BPB
    0x024 0x19 DWORD Logical sectors per FAT
    0x028 0x1D WORD Mirroring flags etc.
    0x02A 0x1F WORD Version
    0x02C 0x21 DWORD Root directory cluster
    0x030 0x25 WORD Location of FS Information Sector
    0x032 0x27 WORD Location of backup sector(s)
    0x034 0x29 12 BYTEs Reserved (Boot file name)
    0x040 0x35 BYTE Physical drive number
    0x041 0x36 BYTE Flags etc.
    0x042 0x37 BYTE Extended boot signature (0x29)
    0x043 0x38 DWORD Volume serial number
    0x047 0x3C 11 BYTEs Volume label
    0x052 0x47 8 BYTEs File-system type
    Format of standard DOS 2.0 BPB for FAT12 (13 bytes):
    Sector offset BPB offset Field length Description
    0x00B 0x00 WORD Bytes per logical sector
    0x00D 0x02 BYTE Logical sectors per cluster
    0x00E 0x03 WORD Reserved logical sectors
    0x010 0x05 BYTE Number of FATs
    0x011 0x06 WORD Root directory entries
    0x013 0x08 WORD Total logical sectors
    0x015 0x0A BYTE Media descriptor
    0x016 0x0B WORD Logical sectors per FAT
    Format of standard DOS 3.31 BPB for FAT12, FAT16 and FAT16B (25 bytes)。
    Sector offset BPB offset Field length Description
    0x00B 0x00 13 BYTEs DOS 2.0 BPB
    0x018 0x0D WORD Physical sectors per track (identical to DOS 3.0 BPB)
    0x01A 0x0F WORD Number of heads (identical to DOS 3.0 BPB)
    0x01C 0x11 DWORD Hidden sectors (incompatible with DOS 3.0 BPB)
    0x020 0x15 DWORD Large total logical sectors
    我发现了我第一个愚蠢的地方,我居然使用vfat来创建fat32文件!这个简直是白痴!!!但是改正这个错误并没有让我成功,于是我才意识到我可能需要qemu的支持:这里明确的说了我必须使用bios文件。这里是源头文章 我最大的一个错误认识就是以为UEFI本身有能力从媒介探索bootloader,错了,它必须依靠BIOS的功能,我之前和BIOS打了那么多交道居然没有意识到这个概念! 远比我想象的复杂,因为如何建立EFI-var这个就是一个大的问题。我当然不想搞乱我自己的系统吧!休息一下啊。

四月十六日 等待变化等待机会

  1. 每种partition type也是一个GUID,我保存一个拷贝
  2. 所以,按照这个我可以初步的用UEFI boot。具体是这样子的。
    1. 使用arch-chroot进入虚拟机在安装grub-install之前把UEFI system partition,比如(/dev/loopXp1,这里的X是具体loop mount的设备)mount到/boot/efi,这样子安装命令就是最简单的:grub-install /dev/loopX,因为默认的target就是x86_64-efi,而默认的efi-directory也是/boot/efi 我发现所谓的device.map是不需要的,因为你启动的时候已经把硬盘文件作为参数传递给qemu了,唯一不知道的是哪一个是内核存在的文件系统的uuid
    2. 安装的结果是什么呢?在efi目录下是针对ubuntu创建了一个所谓的grub.cfg。它设定了grub启动传递给内核的参数就是文件系统的uuid,注意这个不是UEFI而是你的linux系统所在的文件系统。
    3. 如何启动呢?首先安装ovmf,它里面有很多的bios文件,我根据指引使用最简单的这个OVMF_CODE_4M.fd,需要本地一个拷贝因为使用raw接口参数需要权限:
      
      qemu-system-x86_64 -drive if=pflash,format=raw,file=./OVMF_CODE_4M.fd -hda ubuntu-efi-disk -m 4G
      
      这样子就可以看到我们grub-install创建的grub启动菜单了。而且有一个额外的UEFI-var的编辑菜单,这个似乎是可以存储在UEFI system partition的这个似乎要配合使用另一个OVMF-Var的bios文件才可以吧?,只不过在进入内核启动的时候应该是驱动和console的参数设置的问题会花屏,不过登陆界面是正常的。
    总而言之,我终于可以qemu-UEFI-boot,并且全套制作磁盘文件的流程都走通了!!!
  3. 花屏似乎在我使用UEFI设置菜单里选择解析度为800x640以后就解决了。

四月十七日 等待变化等待机会

  1. 应该说模拟一个虚拟机是相当的复杂的,目前我初步解决了文件系统和启动部分,接下来一个很大的领域就是网络。这是一个巨大的课题,有很多要补课的。感觉这个arch-wiki不是太准确与系统,虽然是很好也许我没有完全理解,还是先从基本的qemu的文档看起来。这里是另一个大的文档。 我现在还是没有头绪,比如我在参数里创建了设备那么怎么设置应该是操作系统的事情和qemu没有关系了吧?
    
    qemu-system-x86_64 -drive if=pflash,format=raw,file=./OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF_VARS_4M.fd -hda ubuntu-efi-disk -m 4G -netdev user,id=mynet0,net=192.168.76.0/24,dhcpstart=192.168.76.9 -net nic,macaddr=52:54:00:12:34:56
    
    现在可以看到设备,但是dhcp并没有自己运行?
    
    root@nick-sager:/boot/grub# cat /etc/systemd/network/ethernet.network 
    [Match]
    Name=enp0s3
    
    [Network]
    DHCP=yes
    
    难道是要开机启动服务?

四月十九日 等待变化等待机会

  1. 昨天花了快一天时间保税,顺便把旧的笔记本电脑启动的问题解决一下,应该是多种原因,归根结底可能是网络和显示,前者是各种开机访问的网 络被防火墙屏蔽了,比如packagekit.service以及各种在线账户开机即链接等等。后者比较复杂可能是nvidia显卡驱动动态编译造成的? 以后再说吧?
  2. 现在需要从头学习网络的基本问题:
    1. There are two parts to networking within QEMU:

      • the virtual network device that is provided to the guest (e.g. a PCI network card).
      • the network backend that interacts with the emulated NIC (e.g. puts packets onto the host's network).

      There are a range of options for each part. By default QEMU will create a SLiRP user network backend and an appropriate virtual network device for the guest (eg an E1000 PCI card for most x86 PC guests), as if you had typed -net nic -net user on your command line.

      这个真的是提纲契领的精辟啊!两个问题一个是虚拟硬件,一个是怎么提供数据包!
    2. Note - if you specify any networking options on the command line (via -net or -netdev) then QEMU will require you to provide options sufficient to define and connect up both parts.
      这一点也是非常的关键,就是说两者缺一不可,而且一定要一一对应起来,如同两条腿走路一样必须都提供才行。
    3. Note - if you are using the (default) SLiRP user networking, then ping (ICMP) will not work, though TCP and UDP will. Don't try to use ping to test your QEMU network configuration!
      这一点应该是大多数初学者都会犯的错误,就是不能期待用ping来测试。
    4. 提供了两个更加详细的资料供详细阅读:
      • QEMU Networking on wikibooks.org, mainly dealing with Linux hosts
      • QEMU Networking on bsdwiki, showing used networking principles and dealing with BSD hosts
      我首先快速浏览发现这个图非常的说明问题,真是一图顶千言啊!
      1 User Mode Networking – In this mode, the QEMU virtual machine automatically starts up an internal DHCP server on an internal networkaddress -10.0.2.2/24. This is internal to the guest environment and is not visible from the host environment. If the guest OS is set up for DHCP, the guest will get an IP address from this internal DHCP server. The QEMU virtual machine will also gateway packets onto the host network through 127.0.0.1. In this way, QEMU can provide an automatic network environment for the QEMU user without any manual configuration.
      所以,一个最最简单的user模式包含了多少的细节啊,每一句每一个字都值得我反复理解!
    5. 现在回过头来看我的虚拟机究竟要怎么设置最最基本的呢?一个小问题是我的locale设置是这样子的
      
        C.UTF-8... done
        en_US.UTF-8... done
        zh_CN.UTF-8... done
      
      我如果在qemu-system-x86_64不加任何的network的参数的确是创建了最最基本的网络,但是我的ssh服务没有起来,我猜想是我在 debootstrap里安装了文件系统的包,但是没有激活服务吧?systemctl enable ssh。总之我无法从Host去ssh到虚拟机,我一开始验证我能否在虚拟机里使用ssh登陆,但是这个是特殊的root用户,ssh大概有默认的 PermitRootLogin设置为no吧?虽然尽管我在/etc/ssh/sshd_config里没有看到它是no,但是这个也许是pam之类的安 全问题,只能创建其他用户显示基本的openssh-server的功能是work的,那么从host不能login就纯粹还是网络的问题。
    6. 我是直接使用类似于原始的磁盘文件,而不是qemu或者kvm之类的其他虚拟机的特定格式,这个肯定是笨拙的,所以才会有这样子的警告
      
      WARNING: Image format was not specified for 'ubuntu-efi-disk' and probing guessed raw.
               Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
               Specify the 'raw' format explicitly to remove the restrictions.
      
      这个应该是我的文件系统为只读的原因吧?我对于怎样表达我的磁盘是raw有些疑惑,本来是很简单的问题,但是似乎是有冲突,直到看到这个帖子似乎他的问题和我的比较的相似,我决定采用这个更加简单的参数
      
      qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -drive format=raw,file=ubuntu-efi-disk -m 4G 
      
      不再有警告了,而且可以正常的发现UEFI的loader和我的ubuntu的菜单。与此相对应的是我之前采用的./OVMF_CODE_4M.fd被qemu说是qemu: could not load PC BIOS './OVMF_CODE_4M.fd'。这个是怎么回事? 尽管没有了警告但是我的文件系统依然是read-only!难道是我的grub菜单上写的吗?我猜想是不是因为我现在还是loopmount这个磁盘文件呢?这个猜测显然是无知的! 难道真的是我想的那样子是grub的设置吗?
      
              search --no-floppy --fs-uuid --set=root 98a1016c-7d13-4b2b-8db9-98e9ec541a69
              linux   /boot/vmlinuz-5.15.0-25-generic root=UUID=98a1016c-7d13-4b2b-8db9-98e9ec541a69 ro quiet splash $vt_handoff
              initrd  /boot/initrd.img-5.15.0-25-generic
      
      我自己都有些佩服自己了,正常的Linux启动对于内核所在的文件分区往往是要保护一下吧?所以就设定了ro,那么我现在这么做是否违反了这个原则呢?正 常的linux系统是在什么时间点设定文件系统是可写的呢?总之,我这么做的确解决了问题,login的速度明显加快了,原本应该就是read-only 的文件系统写操作失败耽误了返回。解决了磁盘只读的问题,先休息一下吧。其实我并没有解决,实际上我的虚拟机并没有直接写文件到磁盘文件,难道是cache,关机看看。
    7. 这里有很多的qemu的测试磁盘这个是debian的一些预编译的镜像吧?这个Debian Quick Image Baker是什么鬼?也许我之前做的一些实验就是qemu-debootstrap的一些工作吧?看到评论有提到mmdebstrap,不过这个应该还是debian的,并不是针对ubuntu的吧?更不要说有人提到debos,这些工具真的是五花八门。debos似乎更加的灵活?
    8. 看来我这么干是违背常识的文件镜像被损坏了!怎么办呢?我决定修复文件系统:
      
      sudo losetup --show -Pf ubuntu-efi-disk
      dev=$(losetup -l | grep ubuntu-efi-disk | cut -d' ' -f1)
      sudo mkfs.ext4 ${dev}p2
      
      果然看到我之前在两个方向同时写文件系统造成的问题,比如
    9. 但是/var依旧是一个镜像,因为文件并没有写到磁盘,这个说明了我对于boot过程还是不清楚,就比如一般的介绍都是浮于表面,语焉不详。吃饭以后再说吧。
    10. 其实是一个很简单的问题,我始终理解反了,我一直以为是我的host可以自由访问guest,实际上是相反的,因为默认的设置是这样子的:
      The guest OS will see an E1000 NIC with a virtual DHCP server on 10.0.2.2 and will be allocated an address starting from 10.0.2.15. A virtual DNS server will be accessible on 10.0.2.3, and a virtual SAMBA file server (if present) will be accessible on 10.0.2.4 allowing you to access files on the host via SAMBA file shares.
      我其实早就看到这个图,但是始终没有理解。你从host是不能简单的ping这些ip,但是在虚拟机上是可以自由访问所有的ip的,因为DNS在 10.0.2.3是做了解析的,这个怎么做到的,我不知道,肯定不是简单的在host操作系统层级,应该是qemu-system做了某种映射吧?总而言 之,从guest是可以访问外界的。比如你可以直接在虚拟机里ping www.baidu.com,这不就够了吗?
    11. 我遭遇了第一次的沉重打击,就是说我在所谓的虚拟机里的命令并不仅仅限于虚拟机,而是整个我的host machine!因为我在虚拟机里的关机居然直接把我的笔记本给shutdown了。这个怎么可能呢?一定是我不小心使用ssh登陆到了host才造成的!这个真的是非常的危险!
  3. 我原来不知道这个-boot的参数的意义:
    parameter description
    a,b stand for the floppy drives 1 and 2
    c stands for the first hard disk
    d stands for the first CD-ROM drive
    n stand for Ether-boot network adapters

    For example, qemu-system-ARCH [...] -boot order=ndc first tries to boot from network, then from the first CD-ROM drive, and finally from the first hard disk.

    所以,这个更像是boot-order参数。
  4. 关于设备的加载我必须非常的小心,比如说我的笔记本重启之后我注意到两块nvme的设备顺序就变了,我原来的操作系统是/dev/nvme0n1,现在就变成了/dev/nvme1n1,这个是非常的正常,很多时候硬盘也不一定每次都是hda,总之只有UUID是唯一的。
  5. 找了一上午可能需要就是这个host port forwarding
    
    qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -drive format=raw,file=ubuntu-efi-disk -m 4G -netdev user,id=network0,hostfwd=tcp::5555-:22 -device e1000,netdev=network0,mac=52:54:00:12:34:56
    
    因为这样子我们把guest的ssh默认port 22 forward到了host的5555,于是我们可以ssh localhost -p 5555来登陆guest!这个就是我需要的,可是我却一直没有摸到门路! 这个工作模式似乎我可以满足了!我真的需要创建TAP吗?也许是下一步有进一步的需求吧?因为这个学习曲线是很陡峭的。
  6. 重新阅读我惊奇的发现linux-efi-stub,这个是一个很神奇的feature:
    EFI stub feature in Linux means that the kernel image looks like an EFI application, so the kernel can be directly loaded without a separate bootloader - skipping GRUB (the most popular bootloader for Linux).
    这个我记得之前只能是在BIOS里的EFI菜单里才能做到,那么也就是说linux kernel遵循了EFI的spec实现了EFI的那些回调函数,然后存储了启动参数?但是这些本来是在ESP的efi的binary才。。。?我还是不 理解。我的理解是BIOS厂商遵循EFI规范允许你存储这些参数在NVRAM里,也就是意味着kernel要能够写这些参数,当然这个是在EFI的接口对 应下。。。
  7. 重点还是阅读我能够容易实验的部分吧:qemu和efi的部分。 我其实能够感受到作者的疑惑,因为之前我使用IPMI以及UEFI的一些经验告诉我bios里是有些值很让人摸不着头脑,大概就是这个boot option以及boot order能把人抓狂,因为配合一个所谓的next boot value有非常多的组合,尤其是在系统之前boot failure会自动更改,这一点类似于我在grub菜单里的所谓的recovery原理吧?总之,我怀疑是这些把作者搞糊涂了,尤其是怎么进入grub 菜单的,这个是很有趣很烧脑的机制。在EFI/BOOT里的efi文件是让你进到EFI/ubuntu下的efi,两者有区别吗?我做了一个小实验来验证 我的想法:
    1. 我首先要找到我的ESP,经过文件系统corruption的教训我不再敢于在host里直接mount我的磁盘镜像,读写都要在虚拟机里进行。
    2. 首先看看我的虚拟机的磁盘是怎么样的,我只能看到我只有一个/dev/sda设备,尽管我目前的分区设备是/dev/sda2
      
      $ sudo gdisk -l /dev/sda
      Partition table scan:
        MBR: protective
        BSD: not present
        APM: not present
        GPT: present
      
      Found valid GPT with protective MBR; using GPT.
      Disk /dev/sda: 10240000 sectors, 4.9 GiB
      Model: QEMU HARDDISK   
      Sector size (logical/physical): 512/512 bytes
      Disk identifier (GUID): 5B513127-16FE-B34D-A5EF-B1996CE64DCD
      Partition table holds up to 128 entries
      Main partition table begins at sector 2 and ends at sector 33
      First usable sector is 34, last usable sector is 10239966
      Partitions will be aligned on 128-sector boundaries
      Total free space is 94 sectors (47.0 KiB)
      
      Number  Start (sector)    End (sector)  Size       Code  Name
         1             128         1050623   512.9 MiB   EF00  EFI system partition
         2         1050624        10239966   4.4 GiB     8300  Linux
      
      所以,我要找的ESP在/dev/sda1上。
    3. 那么先把它mount吧:
      
      sudo mount /dev/sda1 /boot/efi
      
      这里为什么ESP没有正常的被mount在/boot/efi呢?我的笔记本就是这么做的,看来有些其他的配置需要让bootloader或者内核知道吧?
    4. 我究竟想看到什么呢?我在找/boot/efi/NvVars,因为我为了验证qemu的默认的EFI BIOS文件有能力配置EFI VAR的思路特意把ESP下的这个/NvVars删 除来确保我在grub启动菜单里的EFI配置是保留在这个文件里的,这个是可以理解的,因为qemu不大可能去直接和NVRAM交互改写系统的EFI var,这个简直就是灾难,qemu也没有这个权限与能力吧。所以,就在ESP里写下,这个到底是不是Linux EFI stub呢?还是说这个是grub制作的呢?因为它是在grub菜单最后加上的,我是没有在grub.cfg里看到这个,这个似乎是动态加入的?确认一下 吧!这个应该是grub自己加入的!看看这个 /etc/grub.d/30_uefi-firmware就知道了,这里定义的是什么呢?:
      
      EFI_VARS_DIR=/sys/firmware/efi/efivars
      EFI_GLOBAL_VARIABLE=8be4df61-93ca-11d2-aa0d-00e098032b8c
      OS_INDICATIONS="$EFI_VARS_DIR/OsIndicationsSupported-$EFI_GLOBAL_VARIABLE"
      
      if [ -e "$OS_INDICATIONS" ] && \
         [ "$(( $(printf 0x%x \'"$(cat $OS_INDICATIONS | cut -b5)"\') & 1 ))" = 1 ]; then
        LABEL="UEFI Firmware Settings"
      
      也就是说要看下面这个文件的第五个byte和1的与来决定是否动态添加这个菜单UEFI Firmware Settings。那么这个OsIndicationsSupported-xxx到底是谁写的呢?可能是安装包的时候设定的吧?。 这里/sys/firmware/efi/efivars/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c是什么呢?让我们看看二进制吧:
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c 
      00000000: 0600 0000 7f00 0000 0000 0000            ............
      
      这个我一时莫不清头脑,但是我坚信它是IPMI里的相关的启动参数吧?
    5. 其他的参数我倒是看明白了一点,比如我自己的笔记本里是这样的boot order:
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd -d -s64 -g1 Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c
      00000064: 02 02 04 04 34 00 5c 00 45 00 46 00 49 00 5c 00  ....4.\.E.F.I.\.
      00000080: 75 00 62 00 75 00 6e 00 74 00 75 00 5c 00 73 00  u.b.u.n.t.u.\.s.
      00000096: 68 00 69 00 6d 00 78 00 36 00 34 00 2e 00 65 00  h.i.m.x.6.4...e.
      00000112: 66 00 69 00 00 00 7f ff 04 00 52 43              f.i.......RC
      
      这个说明了boot order里第一个是/boot/efi/EFI/ubuntu/shimx64.efi,这个是什么呢?
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd -d -g1 Boot2001-8be4df61-93ca-11d2-aa0d-00e098032b8c
      00000000: 07 00 00 00 01 00 00 00 04 00 45 00 46 00 49 00  ..........E.F.I.
      00000016: 20 00 55 00 53 00 42 00 20 00 44 00 65 00 76 00   .U.S.B. .D.e.v.
      00000032: 69 00 63 00 65 00 00 00 7f ff 04 00 52 43        i.c.e.......RC
      
      很明显是USB
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd -d -g1 Boot2002-8be4df61-93ca-11d2-aa0d-00e098032b8c 
      00000000: 07 00 00 00 01 00 00 00 04 00 45 00 46 00 49 00  ..........E.F.I.
      00000016: 20 00 44 00 56 00 44 00 2f 00 43 00 44 00 52 00   .D.V.D./.C.D.R.
      00000032: 4f 00 4d 00 00 00 7f ff 04 00 52 43              O.M.......RC
      
      这个是DVD/CDRom
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd -d -g1 Boot2003-8be4df61-93ca-11d2-aa0d-00e098032b8c
      00000000: 07 00 00 00 01 00 00 00 04 00 45 00 46 00 49 00  ..........E.F.I.
      00000016: 20 00 4e 00 65 00 74 00 77 00 6f 00 72 00 6b 00   .N.e.t.w.o.r.k.
      00000032: 00 00 7f ff 04 00 52 43                          ......RC
      
      这个当然是network了
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd -d -g1 BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c 
      00000000: 06 00 00 00 00 00                                ......
      
      current是6?
      
      nick@nick-sager:/sys/firmware/efi/efivars$ xxd -d -g1 BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c 
      00000000: 07 00 00 00 00 00 01 20 02 20 03 20              ....... . . 
      
      这个肯定也是IPMI里相关的那些bits。
    6. 当然这里关于shimx64.efi远比我猜想的复杂:

      Typically, EFI/ubuntu/grubx64.efi on the EFI System Partition (ESP) is the GRUB binary, and EFI/ubuntu/shimx64.efi is the binary for shim. The latter is a relatively simple program that provides a way to boot on a computer with Secure Boot active. On such a computer, an unsigned version of GRUB won't launch, and signing GRUB with Microsoft's keys is impossible, so shim bridges the gap and adds its own security tools that parallel those of Secure Boot. In practice, shim registers itself with the firmware and then launches a program called grubx64.efi in the directory from which it was launched, so on a computer without Secure Boot (such as a Mac), launching shimx64.efi is just like launching grubx64.efi. On a computer with Secure Boot active, launching shimx64.efi should result in GRUB starting up, whereas launching grubx64.efi directly probably won't work.

      Note that there's some ambiguity possible. In particular, if you want to use a boot manager or boot loader other than GRUB in a Secure Boot environment with shim, you must call that program grubx64.efi, even though it's not GRUB. Thus, if you were to install rEFInd on a Secure Boot-enabled computer, grubx64.efi could be the rEFInd binary. This binary would probably not reside in EFI/ubuntu, though; both it and a shim binary would probably go in EFI/refind. Also, as you've got a Mac (which doesn't support Secure Boot), there's no need to install rEFInd in this way; it makes much more sense to install rEFInd as EFI/refind/refind_x64.efi (its default location and name).

      Note that the rEFInd documentation includes a whole page on Secure Boot. Chances are you won't benefit from reading it, user190735, since you're using a Mac. I mention it only in case some other reader comes along who's trying to use rEFInd in conjunction with Secure Boot.

      总而言之,是和secure boot有关,这个领域就太复杂了,我连碰都不敢碰! 这里给出了权威的解释,不用瞎猜了。
      
      	This directory exposes interfaces for interactive with
      	EFI variables.  For more information on EFI variables,
      	see 'Variable Services' in the UEFI specification
      	(section 7.2 in specification version 2.3 Errata D).
      
      	In summary, EFI variables are named, and are classified
      	into separate namespaces through the use of a vendor
      	GUID.  They also have an arbitrary binary value
      	associated with them.
      
      我下载的版本是2.8远远高于2.3,已经有了很多改变,不过模拟应该不受影响吧?这个实现也是交给各个BIOS提供商去自己在NVRAM里实现存储吧,只要你保证输出相应的实现接口函数,至于说具体存储就是无关的,所以,Linux也能够模拟这些吧?
  8. 总而言之,通过grub的custom菜单暴露EFI variable配置,这个是通过grub配置菜单得到的,但是这里牵扯到了巨大的内容,比如这个菜单它执行的是什么呢?fwsetup在哪里?它是grub内指的吗?为什么locate找不到?唯一相似的是
    
    nick@nick-sager:/etc/grub.d$ locate fwsetup
    /boot/grub/x86_64-efi/efifwsetup.mod
    /home/nick/Downloads/coreboot/payloads/external/GRUB2/grub2/grub-core/commands/efi/efifwsetup.c
    /usr/lib/grub/x86_64-efi/efifwsetup.mod
    
    我对于这个说法将信将疑:

    fwsetup (or firmware-setup) is an option to tell the computer to boot the manufacturer BIOS after the reboot (or to boot directly to manufacturer BIOS if this option is triggered by GRUB).

    On a computer using systemctl you can trigger this option with the command systemctl reboot --firmware-setup

    的确这个是grub的内部命令

    17.4.29 fwsetup

    Command: fwsetup [--is-supported]

    Reboot into the firmware setup menu. If --is-supported option is specified, instead check whether the firmware supports a setup menu and exit successfully if so.

  9. 今天太多的需要消化了,休息吧。
  10. 我的旧笔记本总是遇到启动死机的问题,应该是amd gpu相关的驱动问题吧,只能在grub里加上nomodeset来禁止一些显示的代码吧?

四月二十日 等待变化等待机会

  1. 昨天用眼过度,今天休息一天。

四月二十二日 等待变化等待机会

  1. 多休息了一天。
  2. Linux内核有一篇著名的文档详细的回答了关于TUN/TAP的很多基本概念:
    1. 什么:

      TUN/TAP provides packet reception and transmission for user space programs. It can be seen as a simple Point-to-Point or Ethernet device, which, instead of receiving packets from physical media, receives them from user space program and instead of sending packets via physical media writes them to the user space program.
      事实上我目前使用的openvpn本质上也是一个TUN设备。
    2. 怎么:

      In order to use the driver a program has to open /dev/net/tun and issue a corresponding ioctl() to register a network device with the kernel. A network device will appear as tunXX or tapXX, depending on the options chosen. When the program closes the file descriptor, the network device and all corresponding routes will disappear.
      到底是tun还是tap的选项是什么呢?下面有详细的解说。核心就是要先有/dev/net/tun这个设备文件,往往需要手动自己创建。
    3. tun or tap?

      Depending on the type of device chosen the userspace program has to read/write IP packets (with tun) or ethernet frames (with tap). Which one is being used depends on the flags given with the ioctl().
      tun是IP packet,而tap是ethernet frame。
    4. 文档在哪里:

      这里是tun/tap的大本营吧?我喜欢它的这个logo: 为了迅速理解,看看这个FAQ是值得的。不过很显然的这是来自同一个作者,大侠没有时间去写冗长的文档,基本上是同样的。因为大侠不会把时间浪费给专栏作家也能看懂的FAQ上,主要精力都在具体的例子里,只有真正的实践者才需要精准的指导,而对于浅尝辄止的随意浏览者是不值得浪费时间的。
    5. 例子:

      也许最好的例子是代表了千言万语的解说!这个设置例子说明了一切。为什么要学习呢?我的模糊认识是这个强大的工具是一个灵活的bridge它可以把不同桥段的主机自由的连接,而这一点在虚拟化里至关重要。所以,我需要掌握。但是很显然的,这个是一个非常古老的项目,也就是非常的成熟的,没有那么多的人在热衷于维护了。也许有其他的选择? 或者从高度抽象的角度来看,其实它是非常简单的想法,你创建一个设备,赋予它普通用户能够访问的权限,对它进行读写,而背后的内核代码可以根据设置压缩加密转发给另一个指定目的的设备,说白了就是一个程序,而在用户眼里似乎就是一个设备。确实也没有多少需要解释的。
    总而言之,还是回到qemu的文档上吧!
  3. 创建TAP/TUN_device设备一定是第一步!
    
       $ sudo mkdir /dev/net
       $ sudo mknod /dev/net/tun c 10 200
       $ sudo /sbin/modprobe tun
    
    tun是一个char设备,major 10,minor 200。它需要内核模块tun。
  4. 这个关键的脚本我还不敢尝试,因为还看不大懂,概念模糊。我需要一些更加系统的解说。不过我的总体感觉是TAP/TUN的途径是避免了routing table的复杂操作,而是通过一个转发程序来掩盖它吧?
  5. 看这个文档来应证一些共同的部分:
    if you want simplicity, use the “User Mode Networking” method and use DHCP in the guest OS. If you want strict control of your IP addressing and routing, use the “TUN/TAP Network Interface” method.
    我的感觉是兼听则明,一个复杂的议题最好是找两三家来从不同角度来互相应证,这个也是情报工作的基本方法。从他的例子的描述来看 using one virtual bridge on the host system. All the virtual machines will be attached to the virtual bridge, and in the same vlan (default vlan 0).,似乎需要把虚拟机和主机配置在一个vlan里然后虚拟机依靠virtual bridge把主机的物理设备转接到虚拟机的虚拟设备上?这里的虚拟设备显然是tun/tap,因为更加的灵活。否则要使用模拟物理设备的虚拟机的模拟网 卡有可能要给它配置全套的环境吧?我的理解就是在上面user mode下,实际上虚拟机的虚拟物理设备是在一个模拟的物理环境下工作,虚拟机好像真实的操作系统惯例开机使用dhcpclient自动搜索qemu-dhcp-server配置好的环境,这个过程是否可以称之为tunneling呢?我这方面的知识匮乏,感到力不从心。以前在工作中遇到网络问题都是绕着走,现在吃苦头了。

四月二十三日 等待变化等待机会

  1. 首先从宏观概念上明白这个是一体两面的一面,就是创建Virtual Network Device。它是所谓的backend,就是模拟的物理设备,它不同于tap/tun的思路,就是说这个是追求性能或者专门针对硬件的目的。它使用的是参数-device TYPE,netdev=NAME
  2. 这个所谓的nic option是一种捷径,就是它包含了一体两面:

    In case you don't care about configuring every detail of a NIC, you can also create a NIC together with a host backend by using the -nic parameter. For example, you can replace

    -netdev user,id=n1 -device virtio-net-pci,netdev=n1
    

    with:

    -nic user,model=virtio-net-pci
    

    Use -nic model=help to get a list of the supported NIC models.

    Supported NIC models:
    e1000
    e1000-82544gc
    e1000-82545em
    e1000e
    i82550
    i82551
    i82557a
    i82557b
    i82557c
    i82558a
    i82558b
    i82559a
    i82559b
    i82559c
    i82559er
    i82562
    i82801
    ne2k_pci
    pcnet
    pvrdma
    rtl8139
    tulip
    virtio-net-pci
    virtio-net-pci-non-transitional
    virtio-net-pci-transitional
    vmxnet3
    
  3. 偶然注意到boot log里有Unmounting /boot/efi...这里似乎解释的其他?
    
    systemctl daemon-reload
    
    并不能解决问题,因为我的/etc/fstab里是# UNCONFIGURED FSTAB FOR BASE SYSTEM这里的一些回答让我大概明白这个是要自己根据当前的磁盘设备写上去的。
  4. 这里的大侠的启动参数是一个经典的长,可以参考学习。
  5. 启动的时候花屏实际上是两个问题,第一,我可以强硬的在/etc/default/grub里设置显示模式为640x480来解决显卡设置不当的问题,这个基本上可以解决。不过因为默认的grub参数是把启动的文件系统设置为ro,这个grub-mkconfig -o /boot/grub/grub.cfg和直接update-grub似乎差别不是很多吧? 这里有很多的关于initramfs打log的方式,很长,似乎要重新编译? 这里编辑grub参数值得学习,主要是设置console参数

    setup serial/console access

    edit /etc/default/grub:

    1. Set GRUB_CMDLINE_LINUX="" to:

      GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8"
      
    2. Uncomment GRUB_TERMINAL=console

    3. Beneath, add the line:

      GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"
      

    Make the grub config - This MUST be done in a non-systemd-nspawn shell (that means chroot)

    grub-mkconfig -o /boot/grub/grub.cfg
    
  6. 我现在回过头来看我自己的笔记本的/etc/fstab很明显的这个是要自己制作的
    
    # / was on /dev/nvme0n1p2 during installation
    UUID=92d635c6-8ab6-4bfb-8d12-2836774e77c5 /               ext4    rw,errors=remount-ro 0       1
    # /boot/efi was on /dev/nvme0n1p1 during installation
    UUID=82C1-F9D0  /boot/efi       vfat    umask=0077      0       1
    
    那么我现在自己手动设置这个在虚拟机里吧?特别注意这里一定要设置为rw,errors=remount-ro,如果没有rw就不会自己设为文件系统可写。
  7. 总结一下,就是你创建好系统不等于文件系统自己就可以配置好,意思就是说linux是一个非常灵活的,你可以有多种不同的配置,必须要设置/ etc/fstab明确文件系统要怎么启动,而在grub菜单上kernel的分区设置为只读是正常的操作,想想看,这是启动的一个步骤而已,更何况还需 要initramfs要探索你的驱动硬件等等,这中间出错机会很多的,完全没有必要设定为可写,那样太危险了。所以,正确的做法是在/etc/fstab 里设定。而花屏的问题大可以通过禁止修改高解析度的做法,也许设定console也是一个好主意,因为这个几乎是无条件工作的。就是说显卡没有也是可以工作的。

四月二十四日 等待变化等待机会

  1. 一个古老的新问题是我想看看早期启动的log,这个很难吗?我找了一会儿发现Linux kernel的启动参数里有这个earlyprintk,我尝试使用vga/console,ttyS0等等,甚至于按照指示对于serial console使用它的地址/proc/tty/driver/serial里获得。但是都不成功。然后我看见很多大侠直 接命令行指定内核以及initrd来启动,这个是可以完全控制的。但是仅仅boot到initramfs是无聊的,我并不知道我要干什么,仅仅是好奇,那 么还是做一个正式的boot到ubuntu吧,于是我遇到了一个问题,如果是我自己使用内核的话,系统是不明白root文件系统在哪里的,因为省略了 bootloader发现的过程,于是我像当然的模仿grub给文件系统的uuid作为append的参数来指定文件系统,于是一个微妙的故事来了,文件 系统的uuid不行,需要partition的uuid。这个是多么的伟大啊,因为我是上个星期才开始有这个概念的,每个文件系统的uuid是藏在开头扇 区的某个结构中,可是linux要对付那么多种文件系统,它能够可能一个个的来识别获取吗?所以,它只是要写在GPT的partition table里的分区的uuid。所以,我最后的参数是
    
    qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -initrd tmp/initrd.img-5.15.0-25-generic  -m 2G -bios /usr/share/ovmf/OVMF.fd -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B"
    
    内核和initrd我只能拷贝一份改权限有读否则默认只能root来读太危险,而partition uuid可以使用这个脚本版的gdisk
    
    $ sudo sgdisk -i 2  /dev/loop3
    Partition GUID code: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem)
    Partition unique GUID: 8613E0D8-6556-7A47-922D-EDA26D53D20B
    First sector: 1050624 (at 513.0 MiB)
    Last sector: 10239966 (at 4.9 GiB)
    Partition size: 9189343 sectors (4.4 GiB)
    Attribute flags: 0000000000000000
    Partition name: 'Linux'
    
    生活瞬间变得很美好了。
  2. 有时候人就是贪心,我发现在qemu的窗口里看boot log非常的困难,这个是一个同理心,所以,这位大侠把stdio作为console的backend非常的棒,我是蒙了一下才清醒,原来这个设置console的backend是要在-append给linux kernel的
    
    qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -initrd tmp/initrd.img-5.15.0-25-generic -serial stdio -m 2G  -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyAMA0 console=ttyS0"
    
    很显然的,我现在根本不需要EFI来帮助启动了,这么着我看到的是早期阶段吗?屏幕上输出这样子
    
    [    0.000000] Command line: root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyAMA0 console=ttyS0
    
    这个是我传递给内核的参数,然后我发现console=ttyAMA0这个是不需要的,我根本没有这个设备,这里的解释很好!
    • ttyS0 is the device for the first UART serial port on x86 and x86_64 architectures. If you have a PC motherboard with serial ports you'd be using a ttySn to attach a modem or a serial console.
    • ttyUSB0 is the device for the first USB serial convertor. If you have an USB serial cable you'd be using a ttyUSBn to connect to the serial port of a router.
    • ttyAMA0 is the device for the first serial port on ARM architecture. If you have an ARM-based TV box with a serial console and running Android or OpenELEC, you'd be using a ttyAMAn to attach a console to it.
    这位大侠给出了图文并茂的解释,对于没 有多少嵌入式开发经验的人很有帮助。我虽然也用过一会儿ttyUSB0,但是对于真实的serial port却感觉意外。笔记本是没有这个port,那么操作系统还是创建了它的驱动,那么如果我指定stdio作为它的backend,它就输出到了我的命 令行console,是这个道理吗?当然qemu本身就是虚拟机,本身的标准PC就是包含这个硬件的。 其实我真的理解了吗?不理解,因为我尝试着把传递给内核的参数换回成-append root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=tty0"或者是console=stdio,结果输出又回到了qemu的窗口。这个说明了什么?我尝试着解释就是说必须要有额外的硬件比如usb设备并且为其专门创建了对应的驱动并且才能把console的输出redirect到这个设备,否则就只能在qemu的窗口里了? 当我再次精简命令干脆去掉initrd,我看到的输出有不同吗?我感觉没有。我原本的信念是在initrd作为早期临时的一个操作系统把硬件检测的工作做完,那么这个过程实在是太快了,我根本没有看到???
  3. 重新学习initrd的目的

    initrd provides the capability to load a RAM disk by the boot loader. This RAM disk can then be mounted as the root file system and programs can be run from it. Afterwards, a new root file system can be mounted from a different device. The previous root (from initrd) is then moved to a directory and can be subsequently unmounted.

    initrd is mainly designed to allow system startup to occur in two phases, where the kernel comes up with a minimum set of compiled-in drivers, and where additional modules are loaded from initrd.

    这是正常的boot流程,我即便读过很多次也并不代表真正理解:
    1. the boot loader loads the kernel and the initial RAM disk
    2. the kernel converts initrd into a “normal” RAM disk and frees the memory used by initrd
    3. if the root device is not /dev/ram0, the old (deprecated) change_root procedure is followed. see the “Obsolete root change mechanism” section below.
    4. root device is mounted. if it is /dev/ram0, the initrd image is then mounted as root
    5. /sbin/init is executed (this can be any valid executable, including shell scripts; it is run with uid 0 and can do basically everything init can do).
    6. init mounts the “real” root file system
    7. init places the root file system at the root directory using the pivot_root system call
    8. init execs the /sbin/init on the new root filesystem, performing the usual boot sequence
    9. the initrd file system is removed
    这里是大多数人引用的如何创建Initrd
    
    find . | cpio --quiet -H newc -o | gzip -9 -n > /boot/imagefile.img
    
  4. 我尝试把initrd解开来看看,结果我印象中很轻易的cpio的操作居然不行!因为它显示的是
    
    $ file initrd.img-5.15.0-25-generic 
    initrd.img-5.15.0-25-generic: ASCII cpio archive (SVR4 with no CRC)
    
    而我简单的使用cpio只有一部分就是一个文件!找了好久,因为我不相信是压缩的问题,才找到这个提示:使用其他工具比如lsinitramfs可以看到文件系统,可是直到我看了这个才意识到其中大有玄机,就是说你需要使用dd去skip才能看到所有的文件。这难道是一种安全机制吗?
    1. 
      $ dd if=../initrd.img-5.15.0-25-generic skip=0| file -
      /dev/stdin: ASCII cpio archive (SVR4 with no CRC)
      
    2. 
      $ dd if=../initrd.img-5.15.0-25-generic skip=0| cpio -it
      .
      kernel
      kernel/x86
      kernel/x86/microcode
      kernel/x86/microcode/AuthenticAMD.bin
      62 blocks
      
    3. 
      $ dd if=../initrd.img-5.15.0-25-generic skip=62| file -
      /dev/stdin: ASCII cpio archive (SVR4 with no CRC)
      
    4. 
      $ dd if=../initrd.img-5.15.0-25-generic skip=62| cpio -it
      kernel
      kernel/x86
      kernel/x86/microcode
      kernel/x86/microcode/.enuineIntel.align.0123456789abc
      kernel/x86/microcode/GenuineIntel.bin
      9004 blocks
      
    5. 
      $ dd if=../initrd.img-5.15.0-25-generic skip=9066|file -
      /dev/stdin: Zstandard compressed data (v0.8+), Dictionary ID: None
      
    6. 
      $ dd if=../initrd.img-5.15.0-25-generic skip=9066|unzstd | file -
      /dev/stdin: ASCII cpio archive (SVR4 with no CRC)
      
    7. 然后dd if=../initrd.img-5.15.0-25-generic skip=9066|unzstd | cpio -it这里的文件太多了
      
      $ dd if=../initrd.img-5.15.0-25-generic skip=9066|unzstd | cpio -it 
      .
      bin
      conf
      conf/arch.conf
      conf/conf.d
      conf/initramfs.conf
      etc
      etc/console-setup
      etc/console-setup/Uni2-Fixed16.psf.gz
      etc/console-setup/cached_UTF-8_del.kmap.gz
      etc/default
      etc/default/console-setup
      etc/default/keyboard
      etc/dhcp
      etc/dhcp/dhclient-enter-hooks.d
      etc/dhcp/dhclient-enter-hooks.d/config
      etc/dhcp/dhclient.conf
      etc/fstab
      etc/ld.so.cache
      etc/ld.so.conf
      etc/ld.so.conf.d
      etc/ld.so.conf.d/libc.conf
      etc/ld.so.conf.d/x86_64-linux-gnu.conf
      etc/modprobe.d
      etc/modprobe.d/amd64-microcode-blacklist.conf
      etc/modprobe.d/blacklist-ath_pci.conf
      etc/modprobe.d/blacklist-firewire.conf
      etc/modprobe.d/blacklist-framebuffer.conf
      etc/modprobe.d/blacklist-rare-network.conf
      etc/modprobe.d/blacklist.conf
      etc/modprobe.d/intel-microcode-blacklist.conf
      etc/modprobe.d/iwlwifi.conf
      etc/mtab
      etc/nsswitch.conf
      etc/udev
      etc/udev/udev.conf
      init
      lib
      lib32
      lib64
      libx32
      run
      sbin
      scripts
      scripts/functions
      scripts/init-bottom
      scripts/init-bottom/ORDER
      scripts/init-bottom/udev
      scripts/init-top
      scripts/init-top/ORDER
      scripts/init-top/all_generic_ide
      scripts/init-top/blacklist
      scripts/init-top/udev
      scripts/local
      scripts/local-premount
      scripts/local-premount/ORDER
      scripts/local-premount/fixrtc
      scripts/local-premount/resume
      scripts/nfs
      scripts/panic
      scripts/panic/ORDER
      scripts/panic/console_setup
      usr
      usr/bin
      usr/bin/cpio
      usr/bin/dd
      usr/bin/dmesg
      usr/bin/fstype
      usr/bin/halt
      usr/bin/ipconfig
      usr/bin/kbd_mode
      usr/bin/kmod
      usr/bin/loadkeys
      usr/bin/losetup
      usr/bin/minips
      usr/bin/nfsmount
      usr/bin/pivot_root
      usr/bin/poweroff
      usr/bin/resume
      usr/bin/run-parts
      usr/bin/setfont
      usr/bin/udevadm
      usr/bin/which
      usr/bin/wget
      usr/bin/wc
      usr/bin/uniq
      usr/bin/uname
      usr/bin/umount
      usr/bin/tty
      usr/bin/true
      usr/bin/tr
      usr/bin/touch
      usr/bin/test
      usr/bin/tee
      usr/bin/tail
      usr/bin/sync
      usr/bin/switch_root
      usr/bin/stty
      usr/bin/static-sh
      usr/bin/stat
      usr/bin/sort
      usr/bin/sleep
      usr/bin/sh
      usr/bin/setkeycodes
      usr/bin/seq
      usr/bin/sed
      usr/bin/run-init
      usr/bin/rmdir
      usr/bin/rm
      usr/bin/reset
      usr/bin/reboot
      usr/bin/readlink
      usr/bin/pwd
      usr/bin/ps
      usr/bin/printf
      usr/bin/pidof
      usr/bin/openvt
      usr/bin/nuke
      usr/bin/mv
      usr/bin/mount
      usr/bin/more
      usr/bin/modinfo
      usr/bin/mktemp
      usr/bin/mkswap
      usr/bin/mknod
      usr/bin/mkfifo
      usr/bin/mkdir
      usr/bin/lzop
      usr/bin/ls
      usr/bin/loadkmap
      usr/bin/loadfont
      usr/bin/ln
      usr/bin/kill
      usr/bin/ip
      usr/bin/ifconfig
      usr/bin/hwclock
      usr/bin/hostname
      usr/bin/gzip
      usr/bin/gunzip
      usr/bin/grep
      usr/bin/fstrim
      usr/bin/fold
      usr/bin/find
      usr/bin/fgrep
      usr/bin/fbset
      usr/bin/false
      usr/bin/expr
      usr/bin/env
      usr/bin/egrep
      usr/bin/echo
      usr/bin/dumpkmap
      usr/bin/du
      usr/bin/dirname
      usr/bin/df
      usr/bin/devmem
      usr/bin/deluser
      usr/bin/deallocvt
      usr/bin/date
      usr/bin/cut
      usr/bin/cp
      usr/bin/cmp
      usr/bin/clear
      usr/bin/chvt
      usr/bin/chroot
      usr/bin/chmod
      usr/bin/cat
      usr/bin/busybox
      usr/bin/blockdev
      usr/bin/basename
      usr/bin/awk
      usr/bin/ash
      usr/bin/arch
      usr/bin/acpid
      usr/bin/[[
      usr/bin/[
      usr/bin/yes
      usr/lib
      usr/lib/firmware
      usr/lib/firmware/3com
      usr/lib/firmware/3com/typhoon.bin
      usr/lib/firmware/acenic
      usr/lib/firmware/acenic/tg1.bin
      usr/lib/firmware/acenic/tg2.bin
      usr/lib/firmware/adaptec
      usr/lib/firmware/adaptec/starfire_rx.bin
      usr/lib/firmware/adaptec/starfire_tx.bin
      usr/lib/firmware/advansys
      usr/lib/firmware/advansys/3550.bin
      usr/lib/firmware/advansys/38C0800.bin
      usr/lib/firmware/advansys/38C1600.bin
      usr/lib/firmware/advansys/mcode.bin
      usr/lib/firmware/bnx2
      usr/lib/firmware/bnx2/bnx2-mips-06-6.2.3.fw
      usr/lib/firmware/bnx2/bnx2-mips-09-6.2.1b.fw
      usr/lib/firmware/bnx2/bnx2-rv2p-06-6.0.15.fw
      usr/lib/firmware/bnx2/bnx2-rv2p-09-6.0.17.fw
      usr/lib/firmware/bnx2/bnx2-rv2p-09ax-6.0.17.fw
      usr/lib/firmware/bnx2x
      usr/lib/firmware/bnx2x/bnx2x-e1-7.13.15.0.fw
      usr/lib/firmware/bnx2x/bnx2x-e1-7.13.21.0.fw
      usr/lib/firmware/bnx2x/bnx2x-e1h-7.13.15.0.fw
      usr/lib/firmware/bnx2x/bnx2x-e1h-7.13.21.0.fw
      usr/lib/firmware/bnx2x/bnx2x-e2-7.13.15.0.fw
      usr/lib/firmware/bnx2x/bnx2x-e2-7.13.21.0.fw
      usr/lib/firmware/cbfw-3.2.5.1.bin
      usr/lib/firmware/cis
      usr/lib/firmware/cis/DP83903.cis
      usr/lib/firmware/cis/LA-PCM.cis
      usr/lib/firmware/cis/NE2K.cis
      usr/lib/firmware/cis/PCMLM28.cis
      usr/lib/firmware/cis/PE-200.cis
      usr/lib/firmware/cis/PE520.cis
      usr/lib/firmware/cis/tamarack.cis
      usr/lib/firmware/ct2fw-3.2.5.1.bin
      usr/lib/firmware/ctfw-3.2.5.1.bin
      usr/lib/firmware/cxgb3
      usr/lib/firmware/cxgb3/ael2005_opt_edc.bin
      usr/lib/firmware/cxgb3/ael2005_twx_edc.bin
      usr/lib/firmware/cxgb3/ael2020_twx_edc.bin
      usr/lib/firmware/cxgb3/t3b_psram-1.1.0.bin
      usr/lib/firmware/cxgb3/t3c_psram-1.1.0.bin
      usr/lib/firmware/cxgb3/t3fw-7.12.0.bin
      usr/lib/firmware/cxgb4
      usr/lib/firmware/cxgb4/t4fw-1.26.6.0.bin
      usr/lib/firmware/cxgb4/t4fw.bin
      usr/lib/firmware/cxgb4/t5fw-1.26.6.0.bin
      usr/lib/firmware/cxgb4/t5fw.bin
      usr/lib/firmware/cxgb4/t6fw-1.26.6.0.bin
      usr/lib/firmware/cxgb4/t6fw.bin
      usr/lib/firmware/e100
      usr/lib/firmware/e100/d101m_ucode.bin
      usr/lib/firmware/e100/d101s_ucode.bin
      usr/lib/firmware/e100/d102e_ucode.bin
      usr/lib/firmware/ene-ub6250
      usr/lib/firmware/ene-ub6250/ms_init.bin
      usr/lib/firmware/ene-ub6250/ms_rdwr.bin
      usr/lib/firmware/ene-ub6250/msp_rdwr.bin
      usr/lib/firmware/ene-ub6250/sd_init1.bin
      usr/lib/firmware/ene-ub6250/sd_init2.bin
      usr/lib/firmware/ene-ub6250/sd_rdwr.bin
      usr/lib/firmware/intel
      usr/lib/firmware/intel/ice
      usr/lib/firmware/intel/ice/ddp
      usr/lib/firmware/intel/ice/ddp/ice-1.3.26.0.pkg
      usr/lib/firmware/intel/ice/ddp/ice.pkg
      usr/lib/firmware/isci
      usr/lib/firmware/isci/isci_firmware.bin
      usr/lib/firmware/kaweth
      usr/lib/firmware/kaweth/new_code.bin
      usr/lib/firmware/kaweth/new_code_fix.bin
      usr/lib/firmware/kaweth/trigger_code.bin
      usr/lib/firmware/kaweth/trigger_code_fix.bin
      usr/lib/firmware/liquidio
      usr/lib/firmware/liquidio/lio_210nv_nic.bin
      usr/lib/firmware/liquidio/lio_210sv_nic.bin
      usr/lib/firmware/liquidio/lio_23xx_nic.bin
      usr/lib/firmware/liquidio/lio_410nv_nic.bin
      usr/lib/firmware/mellanox
      usr/lib/firmware/mellanox/mlxsw_spectrum-13.2008.2406.mfa2
      usr/lib/firmware/mellanox/mlxsw_spectrum2-29.2008.2406.mfa2
      usr/lib/firmware/mellanox/mlxsw_spectrum3-30.2008.2406.mfa2
      usr/lib/firmware/myri10ge_eth_z8e.dat
      usr/lib/firmware/myri10ge_ethp_z8e.dat
      usr/lib/firmware/myri10ge_rss_eth_z8e.dat
      usr/lib/firmware/myri10ge_rss_ethp_z8e.dat
      usr/lib/firmware/netronome
      usr/lib/firmware/netronome/nic
      usr/lib/firmware/netronome/nic/nic_AMDA0058-0011_2x40.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0058-0012_2x40.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0081-0001_1x40.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0081-0001_4x10.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0096-0001_2x10.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0097-0001_2x40.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0097-0001_4x10_1x40.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0097-0001_8x10.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0099-0001_1x10_1x25.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0099-0001_2x10.nffw
      usr/lib/firmware/netronome/nic/nic_AMDA0099-0001_2x25.nffw
      usr/lib/firmware/netronome/nic_AMDA0058-0011_2x40.nffw
      usr/lib/firmware/netronome/nic_AMDA0058-0012_2x40.nffw
      usr/lib/firmware/netronome/nic_AMDA0081-0001_1x40.nffw
      usr/lib/firmware/netronome/nic_AMDA0081-0001_4x10.nffw
      usr/lib/firmware/netronome/nic_AMDA0096-0001_2x10.nffw
      usr/lib/firmware/netronome/nic_AMDA0097-0001_2x40.nffw
      usr/lib/firmware/netronome/nic_AMDA0097-0001_4x10_1x40.nffw
      usr/lib/firmware/netronome/nic_AMDA0097-0001_8x10.nffw
      usr/lib/firmware/netronome/nic_AMDA0099-0001_1x10_1x25.nffw
      usr/lib/firmware/netronome/nic_AMDA0099-0001_2x10.nffw
      usr/lib/firmware/netronome/nic_AMDA0099-0001_2x25.nffw
      usr/lib/firmware/ositech
      usr/lib/firmware/ositech/Xilinx7OD.bin
      usr/lib/firmware/phanfw.bin
      usr/lib/firmware/qed
      usr/lib/firmware/qed/qed_init_values_zipped-8.42.2.0.bin
      usr/lib/firmware/ql2100_fw.bin
      usr/lib/firmware/ql2200_fw.bin
      usr/lib/firmware/ql2300_fw.bin
      usr/lib/firmware/ql2322_fw.bin
      usr/lib/firmware/ql2400_fw.bin
      usr/lib/firmware/ql2500_fw.bin
      usr/lib/firmware/qlogic
      usr/lib/firmware/qlogic/1040.bin
      usr/lib/firmware/qlogic/12160.bin
      usr/lib/firmware/qlogic/1280.bin
      usr/lib/firmware/rtl_nic
      usr/lib/firmware/rtl_nic/rtl8105e-1.fw
      usr/lib/firmware/rtl_nic/rtl8106e-1.fw
      usr/lib/firmware/rtl_nic/rtl8106e-2.fw
      usr/lib/firmware/rtl_nic/rtl8107e-1.fw
      usr/lib/firmware/rtl_nic/rtl8107e-2.fw
      usr/lib/firmware/rtl_nic/rtl8125a-3.fw
      usr/lib/firmware/rtl_nic/rtl8125b-2.fw
      usr/lib/firmware/rtl_nic/rtl8153a-2.fw
      usr/lib/firmware/rtl_nic/rtl8153a-3.fw
      usr/lib/firmware/rtl_nic/rtl8153a-4.fw
      usr/lib/firmware/rtl_nic/rtl8153b-2.fw
      usr/lib/firmware/rtl_nic/rtl8153c-1.fw
      usr/lib/firmware/rtl_nic/rtl8156a-2.fw
      usr/lib/firmware/rtl_nic/rtl8156b-2.fw
      usr/lib/firmware/rtl_nic/rtl8168d-1.fw
      usr/lib/firmware/rtl_nic/rtl8168d-2.fw
      usr/lib/firmware/rtl_nic/rtl8168e-1.fw
      usr/lib/firmware/rtl_nic/rtl8168e-2.fw
      usr/lib/firmware/rtl_nic/rtl8168e-3.fw
      usr/lib/firmware/rtl_nic/rtl8168f-1.fw
      usr/lib/firmware/rtl_nic/rtl8168f-2.fw
      usr/lib/firmware/rtl_nic/rtl8168fp-3.fw
      usr/lib/firmware/rtl_nic/rtl8168g-2.fw
      usr/lib/firmware/rtl_nic/rtl8168g-3.fw
      usr/lib/firmware/rtl_nic/rtl8168h-1.fw
      usr/lib/firmware/rtl_nic/rtl8168h-2.fw
      usr/lib/firmware/rtl_nic/rtl8402-1.fw
      usr/lib/firmware/rtl_nic/rtl8411-1.fw
      usr/lib/firmware/rtl_nic/rtl8411-2.fw
      usr/lib/firmware/slicoss
      usr/lib/firmware/slicoss/gbdownload.sys
      usr/lib/firmware/slicoss/gbrcvucode.sys
      usr/lib/firmware/slicoss/oasisdownload.sys
      usr/lib/firmware/slicoss/oasisrcvucode.sys
      usr/lib/firmware/sun
      usr/lib/firmware/sun/cassini.bin
      usr/lib/firmware/tehuti
      usr/lib/firmware/tehuti/bdx.bin
      usr/lib/firmware/tigon
      usr/lib/firmware/tigon/tg3.bin
      usr/lib/firmware/tigon/tg3_tso.bin
      usr/lib/firmware/tigon/tg3_tso5.bin
      usr/lib/firmware/vxge
      usr/lib/firmware/vxge/X3fw-pxe.ncf
      usr/lib/firmware/vxge/X3fw.ncf
      usr/lib/initramfs-tools
      usr/lib/initramfs-tools/bin
      usr/lib/initramfs-tools/bin/gcc_s1-stub
      usr/lib/klibc-K8e6DOmVI9JpyGMLR7qNe5iZeBk.so
      usr/lib/modprobe.d
      usr/lib/modprobe.d/aliases.conf
      usr/lib/modprobe.d/blacklist_linux_5.15.0-25-generic.conf
      usr/lib/modprobe.d/fbdev-blacklist.conf
      usr/lib/modprobe.d/systemd.conf
      usr/lib/modules
      usr/lib/modules/5.15.0-25-generic
      usr/lib/modules/5.15.0-25-generic/kernel
      usr/lib/modules/5.15.0-25-generic/kernel/arch
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86/crypto
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86/crypto/blake2s-x86_64.ko
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86/crypto/chacha-x86_64.ko
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86/crypto/crc32-pclmul.ko
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86/crypto/curve25519-x86_64.ko
      usr/lib/modules/5.15.0-25-generic/kernel/arch/x86/crypto/poly1305-x86_64.ko
      usr/lib/modules/5.15.0-25-generic/kernel/crypto
      usr/lib/modules/5.15.0-25-generic/kernel/crypto/blake2b_generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/crypto/crc32_generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/crypto/xor.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/acpi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/acpi/platform_profile.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/acpi/video.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/acard-ahci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/ahci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/ahci_platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/libahci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/libahci_platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_acpi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_ali.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_amd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_artop.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_atiixp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_atp867x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_cmd640.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_cmd64x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_cypress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_efar.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_hpt366.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_hpt37x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_hpt3x2n.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_hpt3x3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_it8213.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_it821x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_jmicron.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_legacy.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_marvell.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_mpiix.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_netcell.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_ninja32.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_ns87410.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_ns87415.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_oldpiix.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_opti.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_optidma.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_pcmcia.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_pdc2027x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_pdc202xx_old.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_piccolo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_radisys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_rdc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_rz1000.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_sch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_serverworks.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_sil680.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_sl82c105.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_triflex.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pata_via.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/pdc_adma.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_dwc_460ex.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_inic162x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_mv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_nv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_promise.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_qstor.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_sil.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_sil24.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_sis.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_svw.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_sx4.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_uli.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_via.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ata/sata_vsc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/base
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/base/regmap
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/base/regmap/regmap-slimbus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/base/regmap/regmap-spi-avmm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/base/regmap/regmap-spmi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bcma
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bcma/bcma.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/aoe
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/aoe/aoe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/brd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/cryptoloop.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/drbd
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/drbd/drbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/floppy.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/mtip32xx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/mtip32xx/mtip32xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/nbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/null_blk
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/null_blk/null_blk.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/aten.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/bpck.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/comm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/dstr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/epat.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/epia.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/fit2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/fit3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/friq.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/frpw.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/kbic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/ktti.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/on20.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/on26.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/paride.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/pcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/pd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/pf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/pg.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/paride/pt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/pktcdvd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/rbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/rnbd
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/rnbd/rnbd-client.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/rnbd/rnbd-server.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/rsxx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/rsxx/rsxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/sx8.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/virtio_blk.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/xen-blkback
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/xen-blkback/xen-blkback.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/zram
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/block/zram/zram.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bus
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bus/mhi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bus/mhi/core
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bus/mhi/core/mhi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/bus/mhi/mhi_pci_generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-cdce706.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-cs2000-cp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-lmk04832.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-max9485.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-palmas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-pwm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-si5341.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-si5351.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-si544.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-twl6040.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/clk-wm831x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/xilinx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/clk/xilinx/xlnx_vcu.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dca
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dca/dca.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dma
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dma/dw
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dma/dw/dw_dmac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dma/dw/dw_dmac_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/dma/idma64.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/extcon
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/extcon/extcon-usb-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/extcon/extcon-usbc-cros-ec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/firewire
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/firewire/firewire-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/firewire/firewire-ohci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/firewire/firewire-sbp2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/fpga
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/fpga/dfl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/fpga/fpga-bridge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/fpga/fpga-mgr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/fpga/fpga-region.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-104-dio-48e.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-104-idi-48.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-104-idio-16.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-aaeon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-adp5520.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-adp5588.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-aggregator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-amd-fch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-amd8111.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-amdpt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-arizona.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-bd9571mwv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-da9052.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-da9055.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-dln2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-dwapb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-exar.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-f7188x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-gpio-mm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-ich.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-it87.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-janz-ttl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-kempld.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-lp3943.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-lp873x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-madera.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-max3191x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-max7300.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-max7301.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-max730x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-max732x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-mb86s7x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-mc33880.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-menz127.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-ml-ioh.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-pca953x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-pca9570.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-pcf857x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-pci-idio-16.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-pcie-idio-24.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-pisosr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-rdc321x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-sch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-sch311x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-siox.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-tpic2810.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-tps65086.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-tps65912.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-tqmx86.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-twl4030.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-twl6040.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-ucb1400.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-viperboard.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-virtio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-vx855.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-wcove.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-winbond.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-wm831x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-wm8350.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-wm8994.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-ws16c48.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/gpio/gpio-xra1403.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/amd-sfh-hid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/amd-sfh-hid/amd_sfh.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-accutouch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-alps.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-apple.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-appleir.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-asus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-aureal.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-belkin.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-cherry.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-chicony.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-cmedia.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-corsair.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-cougar.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-cp2112.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-creative-sb0540.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-elan.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-elo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-ezkey.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-ft260.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-gembird.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-gfrm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-glorious.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-google-hammer.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-gt683r.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-holtek-kbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-holtek-mouse.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-hyperv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-ite.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-jabra.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-keytouch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-led.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-lenovo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-lg-g15.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-logitech-dj.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-logitech-hidpp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-logitech.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-macally.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-maltron.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-mcp2221.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-mf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-microsoft.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-monterey.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-nti.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-ortek.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-penmount.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-plantronics.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-playstation.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-primax.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-prodikeys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-redragon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-retrode.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-rmi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat-arvo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat-common.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat-isku.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat-lua.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat-ryos.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat-savu.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-roccat.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-samsung.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-semitek.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-sensor-custom.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-sensor-hub.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-sjoy.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-steam.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-steelseries.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-sunplus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-thrustmaster.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-topseed.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-u2fzero.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-udraw-ps3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-viewsonic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-vivaldi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid-xinmo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/hid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/i2c-hid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/i2c-hid/i2c-hid-acpi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/i2c-hid/i2c-hid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/intel-ish-hid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/intel-ish-hid/intel-ish-ipc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/intel-ish-hid/intel-ishtp-hid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/intel-ish-hid/intel-ishtp-loader.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/intel-ish-hid/intel-ishtp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/surface-hid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/surface-hid/surface_hid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/surface-hid/surface_hid_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/surface-hid/surface_kbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/uhid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/usbhid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/usbhid/usbhid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/usbhid/usbkbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/usbhid/usbmouse.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hid/wacom.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hv
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hv/hv_utils.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/hv/hv_vmbus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/algos
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/algos/i2c-algo-bit.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/algos/i2c-algo-pca.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-ali1535.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-ali1563.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-ali15x3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-amd-mp2-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-amd-mp2-plat.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-amd756-s4882.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-amd756.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-amd8111.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-cbus-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-cht-wc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-cp2615.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-cros-ec-tunnel.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-designware-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-diolan-u2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-dln2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-i801.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-isch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-ismt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-kempld.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-mlxcpld.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-nforce2-s4985.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-nforce2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-nvidia-gpu.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-ocores.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-parport.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-pca-platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-piix4.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-robotfuzz-osif.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-scmi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-simtec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-sis5595.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-sis630.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-sis96x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-taos-evm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-tiny-usb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-via.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-viapro.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-viperboard.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-virtio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/busses/i2c-xiic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/i2c-mux.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/i2c-smbus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes/i2c-mux-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes/i2c-mux-ltc4306.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes/i2c-mux-mlxcpld.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes/i2c-mux-pca9541.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes/i2c-mux-pca954x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/i2c/muxes/i2c-mux-reg.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/iio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/iio/common
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/iio/common/hid-sensors
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/iio/common/hid-sensors/hid-sensor-iio-common.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/iio/industrialio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/core
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/core/ib_cm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/core/ib_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/core/ib_uverbs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/core/iw_cm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/core/rdma_cm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/hw
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/hw/mlx4
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/hw/mlx5
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/ulp
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/ulp/rtrs
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/ulp/rtrs/rtrs-client.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/ulp/rtrs/rtrs-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/infiniband/ulp/rtrs/rtrs-server.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/ff-memless.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/adc-keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/adp5520-keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/adp5588-keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/adp5589-keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/applespi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/cros_ec_keyb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/dlink-dir685-touchkeys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/gpio_keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/gpio_keys_polled.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/iqs62x-keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/lkkbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/lm8323.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/lm8333.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/matrix_keypad.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/max7359_keypad.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/mcs_touchkey.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/mpr121_touchkey.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/mtk-pmic-keys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/newtonkbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/opencores-kbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/qt1050.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/qt1070.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/qt2160.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/samsung-keypad.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/stowaway.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/sunkbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/tca6416-keypad.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/tca8418_keypad.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/tm2-touchkey.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/twl4030_keypad.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/keyboard/xtkbd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/matrix-keymap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/mouse
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/mouse/psmouse.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/rmi4
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/rmi4/rmi_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/serio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/serio/hyperv-keyboard.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/input/sparse-keymap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mcb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mcb/mcb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/common
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/common/videobuf2
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/common/videobuf2/videobuf2-common.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/common/videobuf2/videobuf2-memops.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/common/videobuf2/videobuf2-v4l2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/common/videobuf2/videobuf2-vmalloc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/mc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/mc/mc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/v4l2-core
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/media/v4l2-core/videodev.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message/fusion
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message/fusion/mptbase.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message/fusion/mptfc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message/fusion/mptsas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message/fusion/mptscsih.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/message/fusion/mptspi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/88pm800.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/88pm805.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/88pm80x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/arizona-i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/arizona-spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/arizona.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/atc260x-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/atc260x-i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/axp20x-i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/axp20x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/bcm590xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/bd9571mwv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/cros_ec_dev.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/da9062-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/da9150-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/dln2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/htc-pasic3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel-lpss-acpi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel-lpss-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel-lpss.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel-m10-bmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel_pmc_bxt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel_pmt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel_quark_i2c_gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel_soc_pmic_bxtwc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel_soc_pmic_chtdc_ti.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/intel_soc_pmic_mrfld.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/iqs62x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/janz-cmodio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/kempld-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/lm3533-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/lm3533-ctrlbank.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/lp3943.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/lp873x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/lpc_ich.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/lpc_sch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/madera-i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/madera-spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/madera.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/max8907.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mc13xxx-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mc13xxx-i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mc13xxx-spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/menf21bmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mfd-aaeon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mp2629.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mt6360-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/mt6397.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/pcf50633-adc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/pcf50633-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/pcf50633.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/rave-sp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/rdc321x-southbridge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/retu-mfd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/rt4831.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/rt5033.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/si476x-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/sky81452.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/sm501.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/ti-lmu.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/ti_am335x_tscadc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/tps6105x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/tps65010.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/tps6507x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/tps65086.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/tqmx86.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/ucb1400_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/viperboard.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/vx855.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/wcd934x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/wl1273-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mfd/wm8994.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/cardreader
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/cardreader/alcor_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/cardreader/rtsx_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/cardreader/rtsx_usb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/cb710
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/cb710/cb710.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/eeprom
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/eeprom/eeprom_93cx6.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/enclosure.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/misc/tifm_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/core
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/core/mmc_block.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/core/sdio_uart.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/alcor.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/cb710-mmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/cqhci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/mmc_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/mtk-sd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/of_mmc_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/rtsx_pci_sdmmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/rtsx_usb_sdmmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdhci-acpi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdhci-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdhci-pltfm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdhci-xenon-driver.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdhci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdhci_f_sdh30.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/sdricoh_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/tifm_sd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/toshsd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/usdhi6rol0.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/ushc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/via-sdmmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/vub300.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mmc/host/wbsd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mtd
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mtd/mtd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mux
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/mux/mux-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/bareudp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/caif
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/caif/caif_serial.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/caif/caif_virtio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53/b53_common.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53/b53_mdio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53/b53_mmap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53/b53_serdes.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53/b53_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/b53/b53_srab.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/bcm-sf2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/hirschmann
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/hirschmann/hellcreek_sw.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/lan9303-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/lan9303_i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/lan9303_mdio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/lantiq_gswip.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz8795.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz8795_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz8863_smi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz9477.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz9477_i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz9477_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/microchip/ksz_common.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/mt7530.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/mv88e6060.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/mv88e6xxx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/mv88e6xxx/mv88e6xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/ocelot
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/ocelot/mscc_seville.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/qca
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/qca/ar9331.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/qca8k.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/realtek-smi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/sja1105
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/sja1105/sja1105.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/vitesse-vsc73xx-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/vitesse-vsc73xx-platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/vitesse-vsc73xx-spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/xrs700x
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/xrs700x/xrs700x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/xrs700x/xrs700x_i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/dsa/xrs700x/xrs700x_mdio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/eql.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/3com
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/3com/3c509.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/3com/3c574_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/3com/3c589_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/3com/3c59x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/3com/typhoon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/8390
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/8390/8390.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/8390/axnet_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/8390/ne2k-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/8390/pcnet_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/adaptec
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/adaptec/starfire.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/agere
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/agere/et131x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/alacritech
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/alacritech/slicoss.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/alteon
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/alteon/acenic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/altera
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/altera/altera_tse.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amazon
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amazon/ena
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amazon/ena/ena.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amd
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amd/amd8111e.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amd/nmclan_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amd/pcnet32.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amd/xgbe
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/amd/xgbe/amd-xgbe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/aquantia
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/aquantia/atlantic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/aquantia/atlantic/atlantic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/alx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/alx/alx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atl1c
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atl1c/atl1c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atl1e
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atl1e/atl1e.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atlx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atlx/atl1.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/atheros/atlx/atl2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/b44.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/bcmsysport.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/bnx2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/bnx2x
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/bnxt
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/bnxt/bnxt_en.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/cnic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/genet
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/genet/genet.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/broadcom/tg3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/brocade
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/brocade/bna
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/brocade/bna/bna.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cadence
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cadence/macb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cadence/macb_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/common
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/common/cavium_ptp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/liquidio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/liquidio/liquidio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/liquidio/liquidio_vf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/thunder
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/thunder/nicpf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/thunder/nicvf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/thunder/thunder_bgx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cavium/thunder/thunder_xcv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb/cxgb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb3
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb3/cxgb3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb4
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb4/cxgb4.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb4vf
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/inline_crypto
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/ch_ipsec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/ch_ktls.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/libcxgb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/chelsio/libcxgb/libcxgb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cisco
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cisco/enic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/cisco/enic/enic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/de2104x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/de4x5.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/dmfe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/tulip.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/uli526x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/winbond-840.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dec/tulip/xircom_cb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dlink
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dlink/dl2k.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dlink/sundance.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/dnet.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/ec_bhf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/emulex
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/emulex/benet
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/emulex/benet/be2net.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/ethoc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/fealnx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/fujitsu
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/fujitsu/fmvj18x_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/google
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/google/gve
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/google/gve/gve.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/huawei
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/huawei/hinic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/huawei/hinic/hinic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/e100.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/e1000
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/e1000/e1000.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/e1000e
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/fm10k
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/fm10k/fm10k.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/i40e
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/i40e/i40e.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/iavf
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/iavf/iavf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ice
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ice/ice.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/igb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/igb/igb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/igbvf
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/igbvf/igbvf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/igc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/igc/igc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ixgb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ixgb/ixgb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ixgbe
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ixgbe/ixgbe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ixgbevf
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/jme.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell/mvmdio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell/prestera
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell/prestera/prestera.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell/prestera/prestera_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell/skge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/marvell/sky2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlx4
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlx5
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlx5/core
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxfw
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxfw/mlxfw.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxsw
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/micrel
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/micrel/ks8842.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/micrel/ks8851_common.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/micrel/ks8851_par.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/micrel/ks8851_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/micrel/ksz884x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microchip
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microchip/enc28j60.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microchip/encx24j600-regmap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microchip/encx24j600.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microchip/lan743x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microsoft
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microsoft/mana
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/microsoft/mana/mana.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mscc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/mscc/mscc_ocelot_switch_lib.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/myricom
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/myricom/myri10ge
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/myricom/myri10ge/myri10ge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/natsemi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/natsemi/natsemi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/natsemi/ns83820.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/neterion
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/neterion/s2io.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/neterion/vxge
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/neterion/vxge/vxge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/netronome
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/netronome/nfp
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/netronome/nfp/nfp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/ni
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/ni/nixge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/nvidia
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/nvidia/forcedeth.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/packetengines
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/packetengines/hamachi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/packetengines/yellowfin.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/pensando
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/pensando/ionic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/pensando/ionic/ionic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/netxen
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/netxen/netxen_nic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qed
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qed/qed.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qede
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qede/qede.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qla3xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qlcnic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qlogic/qlcnic/qlcnic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qualcomm
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qualcomm/emac
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qualcomm/emac/qcom-emac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qualcomm/rmnet
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/qualcomm/rmnet/rmnet.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/rdc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/rdc/r6040.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/realtek
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/realtek/8139cp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/realtek/8139too.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/realtek/atp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/realtek/r8169.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/rocker
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/rocker/rocker.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/samsung
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/samsung/sxgbe
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/samsung/sxgbe/samsung-sxgbe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sfc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sfc/falcon
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sfc/falcon/sfc-falcon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sfc/sfc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/silan
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/silan/sc92031.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sis
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sis/sis190.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sis/sis900.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/smsc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/smsc/epic100.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/smsc/smc91c92_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/smsc/smsc911x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/smsc/smsc9420.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sun
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sun/cassini.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sun/niu.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sun/sungem.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/sun/sunhme.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/synopsys
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/synopsys/dwc-xlgmac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/tehuti
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/tehuti/tehuti.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/ti
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/ti/tlan.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/via
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/via/via-rhine.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/via/via-velocity.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/wiznet
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/wiznet/w5100-spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/wiznet/w5100.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/wiznet/w5300.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/xilinx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/xilinx/ll_temac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/xilinx/xilinx_emac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/xilinx/xilinx_emaclite.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/xircom
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ethernet/xircom/xirc2ps_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/fddi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/fddi/defxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/fddi/skfp
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/fddi/skfp/skfp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/fjes
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/fjes/fjes.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/geneve.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/gtp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/hyperv
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/hyperv/hv_netvsc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/adf7242.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/at86rf230.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/atusb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/ca8210.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/cc2520.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/fakelb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/mac802154_hwsim.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/mcr20a.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ieee802154/mrf24j40.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ipvlan
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ipvlan/ipvlan.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ipvlan/ipvtap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/macsec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-bcm-unimac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-bitbang.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-cavium.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-mscc-miim.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-mvusb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mdio/mdio-thunder.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mhi_net.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/mii.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/net_failover.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/netconsole.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/netdevsim
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/netdevsim/netdevsim.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/nlmon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ntb_netdev.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/pcs
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/pcs/pcs-lynx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/pcs/pcs_xpcs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/adin.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/amd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/aquantia.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/at803x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/ax88796b.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/bcm-phy-lib.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/bcm54140.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/bcm7xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/bcm87xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/broadcom.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/cicada.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/cortina.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/davicom.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/dp83640.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/dp83822.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/dp83848.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/dp83867.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/dp83869.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/dp83tc811.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/et1011c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/icplus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/intel-xway.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/lxt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/marvell-88x2222.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/marvell.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/marvell10g.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/mediatek-ge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/micrel.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/microchip.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/microchip_t1.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/motorcomm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/mscc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/mscc/mscc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/mxl-gpy.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/national.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/nxp-c45-tja11xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/nxp-tja11xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/phylink.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/qsemi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/realtek.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/rockchip.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/sfp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/smsc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/spi_ks8995.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/ste10Xp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/teranetics.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/uPD60620.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/vitesse.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/phy/xilinx_gmii2rgmii.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/plip
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/plip/plip.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/bsd_comp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/ppp_async.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/ppp_deflate.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/ppp_mppe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/ppp_synctty.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/pppoe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/pppox.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/ppp/pptp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/rionet.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/slip
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/slip/slip.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/sungem_phy.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/tap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/thunderbolt-net.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/aqc111.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/asix.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/ax88179_178a.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/catc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/cdc_eem.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/cdc_ether.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/cdc_ncm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/ch9200.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/dm9601.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/int51x1.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/kaweth.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/lan78xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/mcs7830.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/pegasus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/r8152.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/r8153_ecm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/rndis_host.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/rtl8150.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/smsc75xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/smsc95xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/sr9700.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/sr9800.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/usb/usbnet.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/virtio_net.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/vmxnet3
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/vmxnet3/vmxnet3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/vrf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/vsockmon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/vxlan.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wireguard
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wireguard/wireguard.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan/iosm
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan/iosm/iosm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan/mhi_wwan_ctrl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan/mhi_wwan_mbim.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan/rpmsg_wwan_ctrl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/net/wwan/wwan_hwsim.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ntb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ntb/ntb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ntb/ntb_transport.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host/nvme-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host/nvme-fabrics.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host/nvme-fc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host/nvme-rdma.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host/nvme-tcp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/host/nvme.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/target
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/target/nvme-loop.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/target/nvmet-fc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/target/nvmet-rdma.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/target/nvmet-tcp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/nvme/target/nvmet.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/parport
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/parport/parport.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pci
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pci/controller
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pci/controller/pci-hyperv-intf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pci/controller/pci-hyperv.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pci/controller/vmd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pcmcia
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pcmcia/pcmcia.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pcmcia/pcmcia_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/broadcom
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/broadcom/phy-bcm-kona-usb2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/intel
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/intel/phy-intel-lgm-emmc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/marvell
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/marvell/phy-pxa-28nm-hsic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/marvell/phy-pxa-28nm-usb2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/motorola
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/motorola/phy-cpcap-usb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/phy-can-transceiver.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/phy-lgm-usb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/qualcomm
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/qualcomm/phy-qcom-usb-hs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/qualcomm/phy-qcom-usb-hsic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/samsung
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/samsung/phy-exynos-usb2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/ti
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/phy/ti/phy-tusb1210.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/cirrus
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/cirrus/pinctrl-madera.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-alderlake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-broxton.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-cannonlake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-cedarfork.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-denverton.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-elkhartlake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-emmitsburg.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-geminilake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-icelake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-jasperlake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-lakefield.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-lewisburg.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-lynxpoint.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-sunrisepoint.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/intel/pinctrl-tigerlake.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/pinctrl-da9062.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/pinctrl-mcp23s08.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/pinctrl-mcp23s08_i2c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pinctrl/pinctrl-mcp23s08_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/chrome
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/chrome/cros_ec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/chrome/cros_ec_lpcs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/chrome/cros_ec_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/chrome/wilco_ec
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/chrome/wilco_ec/wilco_ec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/surface
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/surface/aggregator
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/surface/aggregator/surface_aggregator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/x86
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/x86/asus-wmi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/platform/x86/wmi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/power
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/power/supply
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/power/supply/axp20x_usb_power.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pwm
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/pwm/pwm-cros-ec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/88pg86x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/88pm800-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/88pm8607.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/aat2870-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/act8865-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/ad5398.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/arizona-ldo1.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/arizona-micsupp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/as3711-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/atc260x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/axp20x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/bcm590xx-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/bd9571mwv-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/da903x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/da9052-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/da9055-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/da9062-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/da9210-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/da9211-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/fan53555.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/fixed.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/gpio-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/isl6271a-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/isl9305.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lm363x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lp3971.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lp3972.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lp872x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lp8755.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lp8788-buck.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/lp8788-ldo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/ltc3589.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/ltc3676.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max14577-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max1586.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max77693-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max77826-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8649.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8660.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8893.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8907-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8925-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8952.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8997-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/max8998.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mc13783-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mc13892-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mc13xxx-regulator-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mp8859.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6311-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6315-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6323-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6358-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6359-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6360-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/mt6397-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/palmas-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pca9450-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pcap-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pcf50633-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pv88060-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pv88080-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pv88090-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/pwm-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/qcom-labibb-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/qcom_spmi-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/qcom_usb_vbus-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rc5t583-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rpi-panel-attiny-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rt4801-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rt4831-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rt5033-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rt6160-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rt6245-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rtmv20-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rtq2134-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/rtq6752-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/sky81452-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/slg51000-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps51632-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps6105x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps62360-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps65023-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps6507x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps65086-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps65090-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps65132-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps6524x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps6586x-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps65910-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps65912-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/tps80031-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/twl-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/twl6030-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/userspace-consumer.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/virtual.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/wm831x-dcdc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/wm831x-isink.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/wm831x-ldo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/wm8350-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/wm8400-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/regulator/wm8994-regulator.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/reset
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/reset/reset-ti-syscon.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rpmsg
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rpmsg/rpmsg_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-88pm80x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-88pm860x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ab-b5ze-s3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ab-eoz9.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-abx80x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-bq32k.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-bq4802.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-cros-ec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-da9052.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-da9055.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-da9063.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1286.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1302.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1305.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1307.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1343.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1347.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1374.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1390.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1511.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1553.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1672.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1685.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds1742.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds2404.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ds3232.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-em3027.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-fm3130.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-ftrtc010.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-goldfish.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-hid-sensor-time.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-isl12022.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-isl1208.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-lp8788.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-m41t80.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-m41t93.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-m41t94.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-m48t35.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-m48t59.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-m48t86.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max6900.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max6902.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max6916.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max8907.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max8925.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max8997.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-max8998.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-mc13xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-mcp795.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-msm6242.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-mt6397.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-palmas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcap.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf2123.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf2127.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf50633.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf85063.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf8523.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf85363.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf8563.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-pcf8583.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-r9701.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rc5t583.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rp5c01.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rs5c348.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rs5c372.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rv3028.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rv3029c2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rv3032.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rv8803.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rx4581.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rx6110.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rx8010.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rx8025.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-rx8581.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-s35390a.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-sd3078.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-stk17ta8.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-tps6586x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-tps65910.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-tps80031.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-v3020.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-wilco-ec.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-wm831x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-wm8350.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/rtc/rtc-x1205.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/3w-9xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/3w-sas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/3w-xxxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/53c700.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/BusLogic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/a100u2w.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aacraid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aacraid/aacraid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/advansys.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aha1740.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aic7xxx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aic7xxx/aic79xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aic7xxx/aic7xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aic94xx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/aic94xx/aic94xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/am53c974.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/arcmsr
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/arcmsr/arcmsr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/atp870u.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/be2iscsi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/be2iscsi/be2iscsi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/bfa
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/bfa/bfa.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/bnx2fc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/bnx2fc/bnx2fc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/bnx2i
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/bnx2i/bnx2i.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/csiostor
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/csiostor/csiostor.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/cxgbi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/cxgbi/cxgb3i
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/cxgbi/cxgb4i
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/cxgbi/libcxgbi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/dc395x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/device_handler
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/device_handler/scsi_dh_alua.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/device_handler/scsi_dh_emc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/device_handler/scsi_dh_rdac.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/dmx3191d.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/dpt_i2o.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/elx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/elx/efct.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/esas2r
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/esas2r/esas2r.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/esp_scsi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fcoe
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fcoe/fcoe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fcoe/libfcoe.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fdomain.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fdomain_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fnic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/fnic/fnic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/hpsa.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/hptiop.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/hv_storvsc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/imm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/initio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ipr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ips.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/isci
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/isci/isci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/iscsi_boot_sysfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/iscsi_tcp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/libfc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/libfc/libfc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/libiscsi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/libiscsi_tcp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/libsas
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/libsas/libsas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/lpfc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/lpfc/lpfc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/megaraid
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/megaraid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/megaraid/megaraid_mbox.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/megaraid/megaraid_mm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/megaraid/megaraid_sas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mpi3mr
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mpi3mr/mpi3mr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mpt3sas
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mpt3sas/mpt3sas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mvsas
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mvsas/mvsas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/mvumi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/myrb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/myrs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pcmcia
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pcmcia/aha152x_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pcmcia/fdomain_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pcmcia/qlogic_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pcmcia/sym53c500_cs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pm8001
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pm8001/pm80xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/pmcraid.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ppa.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qedf
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qedf/qedf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qedi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qedi/qedi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qla1280.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qla2xxx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qla2xxx/qla2xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qla4xxx
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qla4xxx/qla4xxx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/qlogicfas408.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/raid_class.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/scsi_debug.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/scsi_transport_fc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/scsi_transport_iscsi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/scsi_transport_sas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/scsi_transport_spi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/scsi_transport_srp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ses.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/sim710.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/smartpqi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/smartpqi/smartpqi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/snic
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/snic/snic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/st.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/stex.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/sym53c8xx_2
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/sym53c8xx_2/sym53c8xx.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/cdns-pltfrm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/tc-dwc-g210-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/tc-dwc-g210-pltfrm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/tc-dwc-g210.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/ufshcd-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/ufshcd-dwc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/ufshcd-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/ufs/ufshcd-pltfrm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/virtio_scsi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/vmw_pvscsi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/wd719x.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/scsi/xen-scsifront.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/siox
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/siox/siox-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/slimbus
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/slimbus/slimbus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-altera-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-altera-dfl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-altera-platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-amd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-axi-spi-engine.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-bitbang.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-butterfly.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-cadence.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-dln2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-dw-mmio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-dw-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-dw.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-gpio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-lantiq-ssc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-lm70llp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-loopback-test.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-mux.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-mxic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-nxp-fspi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-oc-tiny.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-pxa2xx-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-pxa2xx-platform.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-sc18is602.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-sifive.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-slave-system-control.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-slave-time.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-tle62x0.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-xcomm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spi-zynqmp-gqspi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spi/spidev.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spmi
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/spmi/spmi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ssb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/ssb/ssb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/target
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/target/target_core_mod.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/thunderbolt
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/thunderbolt/thunderbolt.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/uio
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/uio/uio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/c67x00
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/c67x00/c67x00.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/chipidea
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/chipidea/ci_hdrc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/chipidea/ci_hdrc_msm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/chipidea/ci_hdrc_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/chipidea/ci_hdrc_usb2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/common
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/common/ulpi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/dwc2
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/dwc2/dwc2_pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/dwc3
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/dwc3/dwc3-haps.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/dwc3/dwc3-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/dwc3/dwc3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/gadget
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/gadget/udc
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/gadget/udc/udc-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/bcma-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/ehci-fsl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/fotg210-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/fsl-mph-dr-of.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/isp116x-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/max3421-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/oxu210hp-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/r8a66597-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/ssb-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/xhci-pci-renesas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/xhci-pci.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/host/xhci-plat-hcd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/isp1760
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/isp1760/isp1760.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/musb
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/musb/musb_hdrc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/phy
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/phy/phy-generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/phy/phy-gpio-vbus-usb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/phy/phy-isp1301.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/phy/phy-tahvo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/uas.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-alauda.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-cypress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-datafab.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-eneub6250.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-freecom.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-isd200.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-jumpshot.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-karma.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-onetouch.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-realtek.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-sddr09.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-sddr55.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/ums-usbat.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/usb/storage/usb-storage.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/vhost
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/vhost/vhost_iotlb.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/vhost/vringh.ko
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/video
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/video/backlight
      usr/lib/modules/5.15.0-25-generic/kernel/drivers/video/backlight/pwm_bl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/btrfs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/btrfs/btrfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/f2fs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/f2fs/f2fs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/fscache
      usr/lib/modules/5.15.0-25-generic/kernel/fs/fscache/fscache.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/isofs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/isofs/isofs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/jfs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/jfs/jfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/lockd
      usr/lib/modules/5.15.0-25-generic/kernel/fs/lockd/lockd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/netfs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/netfs/netfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs/nfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs/nfsv2.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs/nfsv3.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs/nfsv4.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs_common
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs_common/grace.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nfs_common/nfs_acl.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nls
      usr/lib/modules/5.15.0-25-generic/kernel/fs/nls/nls_iso8859-1.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/reiserfs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/reiserfs/reiserfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/udf
      usr/lib/modules/5.15.0-25-generic/kernel/fs/udf/udf.ko
      usr/lib/modules/5.15.0-25-generic/kernel/fs/xfs
      usr/lib/modules/5.15.0-25-generic/kernel/fs/xfs/xfs.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib
      usr/lib/modules/5.15.0-25-generic/kernel/lib/842
      usr/lib/modules/5.15.0-25-generic/kernel/lib/842/842_decompress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crc-itu-t.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crc7.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crc8.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto/libarc4.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto/libblake2s-generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto/libblake2s.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto/libchacha.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto/libchacha20poly1305.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/crypto/libcurve25519-generic.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/libcrc32c.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/lru_cache.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/lz4
      usr/lib/modules/5.15.0-25-generic/kernel/lib/lz4/lz4_compress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/lz4/lz4hc_compress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/objagg.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/parman.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/raid6
      usr/lib/modules/5.15.0-25-generic/kernel/lib/raid6/raid6_pq.ko
      usr/lib/modules/5.15.0-25-generic/kernel/lib/zstd
      usr/lib/modules/5.15.0-25-generic/kernel/lib/zstd/zstd_compress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net
      usr/lib/modules/5.15.0-25-generic/kernel/net/802
      usr/lib/modules/5.15.0-25-generic/kernel/net/802/garp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/802/mrp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/802/stp.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/8021q
      usr/lib/modules/5.15.0-25-generic/kernel/net/8021q/8021q.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/bridge
      usr/lib/modules/5.15.0-25-generic/kernel/net/bridge/bridge.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ceph
      usr/lib/modules/5.15.0-25-generic/kernel/net/ceph/libceph.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/core
      usr/lib/modules/5.15.0-25-generic/kernel/net/core/failover.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/dsa
      usr/lib/modules/5.15.0-25-generic/kernel/net/dsa/dsa_core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/hsr
      usr/lib/modules/5.15.0-25-generic/kernel/net/hsr/hsr.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ieee802154
      usr/lib/modules/5.15.0-25-generic/kernel/net/ieee802154/ieee802154.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv4
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv4/gre.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv4/udp_tunnel.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv6
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv6/ip6_tunnel.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv6/ip6_udp_tunnel.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/ipv6/tunnel6.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/llc
      usr/lib/modules/5.15.0-25-generic/kernel/net/llc/llc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/mac802154
      usr/lib/modules/5.15.0-25-generic/kernel/net/mac802154/mac802154.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/psample
      usr/lib/modules/5.15.0-25-generic/kernel/net/psample/psample.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/sched
      usr/lib/modules/5.15.0-25-generic/kernel/net/sched/sch_taprio.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/sunrpc
      usr/lib/modules/5.15.0-25-generic/kernel/net/sunrpc/sunrpc.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/tls
      usr/lib/modules/5.15.0-25-generic/kernel/net/tls/tls.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/vmw_vsock
      usr/lib/modules/5.15.0-25-generic/kernel/net/vmw_vsock/vsock.ko
      usr/lib/modules/5.15.0-25-generic/kernel/net/xfrm
      usr/lib/modules/5.15.0-25-generic/kernel/net/xfrm/xfrm_algo.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound
      usr/lib/modules/5.15.0-25-generic/kernel/sound/ac97_bus.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd-compress.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd-pcm-dmaengine.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd-pcm.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd-rawmidi.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd-seq-device.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd-timer.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/core/snd.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/soc
      usr/lib/modules/5.15.0-25-generic/kernel/sound/soc/snd-soc-core.ko
      usr/lib/modules/5.15.0-25-generic/kernel/sound/soundcore.ko
      usr/lib/modules/5.15.0-25-generic/modules.alias
      usr/lib/modules/5.15.0-25-generic/modules.alias.bin
      usr/lib/modules/5.15.0-25-generic/modules.builtin
      usr/lib/modules/5.15.0-25-generic/modules.builtin.alias.bin
      usr/lib/modules/5.15.0-25-generic/modules.builtin.bin
      usr/lib/modules/5.15.0-25-generic/modules.dep
      usr/lib/modules/5.15.0-25-generic/modules.dep.bin
      usr/lib/modules/5.15.0-25-generic/modules.devname
      usr/lib/modules/5.15.0-25-generic/modules.order
      usr/lib/modules/5.15.0-25-generic/modules.softdep
      usr/lib/modules/5.15.0-25-generic/modules.symbols
      usr/lib/modules/5.15.0-25-generic/modules.symbols.bin
      usr/lib/systemd
      usr/lib/systemd/network
      usr/lib/systemd/network/73-usb-net-by-mac.link
      usr/lib/systemd/network/99-default.link
      usr/lib/systemd/systemd-udevd
      usr/lib/udev
      usr/lib/udev/ata_id
      usr/lib/udev/rules.d
      usr/lib/udev/rules.d/50-firmware.rules
      usr/lib/udev/rules.d/50-udev-default.rules
      usr/lib/udev/rules.d/55-dm.rules
      usr/lib/udev/rules.d/60-block.rules
      usr/lib/udev/rules.d/60-persistent-storage-dm.rules
      usr/lib/udev/rules.d/60-persistent-storage.rules
      usr/lib/udev/rules.d/61-persistent-storage-android.rules
      usr/lib/udev/rules.d/71-seat.rules
      usr/lib/udev/rules.d/73-special-net-names.rules
      usr/lib/udev/rules.d/75-net-description.rules
      usr/lib/udev/rules.d/80-drivers.rules
      usr/lib/udev/rules.d/80-net-setup-link.rules
      usr/lib/udev/rules.d/95-dm-notify.rules
      usr/lib/udev/scsi_id
      usr/lib/x86_64-linux-gnu
      usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      usr/lib/x86_64-linux-gnu/libacl.so.1
      usr/lib/x86_64-linux-gnu/libacl.so.1.1.2301
      usr/lib/x86_64-linux-gnu/libblkid.so.1
      usr/lib/x86_64-linux-gnu/libblkid.so.1.1.0
      usr/lib/x86_64-linux-gnu/libc.so.6
      usr/lib/x86_64-linux-gnu/libcap.so.2
      usr/lib/x86_64-linux-gnu/libcap.so.2.44
      usr/lib/x86_64-linux-gnu/libcom_err.so.2
      usr/lib/x86_64-linux-gnu/libcom_err.so.2.1
      usr/lib/x86_64-linux-gnu/libcrypto.so.3
      usr/lib/x86_64-linux-gnu/libdevmapper.so.1.02.1
      usr/lib/x86_64-linux-gnu/libdns-export.so.1110
      usr/lib/x86_64-linux-gnu/libdns-export.so.1110.0.2
      usr/lib/x86_64-linux-gnu/libe2p.so.2
      usr/lib/x86_64-linux-gnu/libe2p.so.2.3
      usr/lib/x86_64-linux-gnu/libext2fs.so.2
      usr/lib/x86_64-linux-gnu/libext2fs.so.2.4
      usr/lib/x86_64-linux-gnu/libgcc_s.so.1
      usr/lib/x86_64-linux-gnu/libisc-export.so.1105
      usr/lib/x86_64-linux-gnu/libisc-export.so.1105.0.2
      usr/lib/x86_64-linux-gnu/libkmod.so.2
      usr/lib/x86_64-linux-gnu/libkmod.so.2.3.7
      usr/lib/x86_64-linux-gnu/liblzma.so.5
      usr/lib/x86_64-linux-gnu/liblzma.so.5.2.5
      usr/lib/x86_64-linux-gnu/libm.so.6
      usr/lib/x86_64-linux-gnu/libnss_dns.so.2
      usr/lib/x86_64-linux-gnu/libnss_files.so.2
      usr/lib/x86_64-linux-gnu/libpcre2-8.so.0
      usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4
      usr/lib/x86_64-linux-gnu/libpthread.so.0
      usr/lib/x86_64-linux-gnu/libresolv.so.2
      usr/lib/x86_64-linux-gnu/libselinux.so.1
      usr/lib/x86_64-linux-gnu/libudev.so.1
      usr/lib/x86_64-linux-gnu/libudev.so.1.7.2
      usr/lib/x86_64-linux-gnu/libzstd.so.1
      usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8
      usr/lib32
      usr/lib64
      usr/lib64/ld-linux-x86-64.so.2
      usr/libx32
      usr/sbin
      usr/sbin/blkid
      usr/sbin/dhclient
      usr/sbin/dhclient-script
      110945+1 records in
      110945+1 records out
      56804152 bytes (57 MB, 54 MiB) copied, 0.249625 s, 228 MB/s
      usr/sbin/dmsetup
      usr/sbin/dumpe2fs
      usr/sbin/modprobe
      usr/sbin/rmmod
      usr/sbin/wait-for-root
      var
      var/lib
      var/lib/dhcp
      371149 blocks
      
    这个有点太辛苦了,所以有大侠指出更容易感觉更复杂的做法是使用binwalk

四月二十五日 等待变化等待机会

  1. 先转回之前的网络设置。这里是使用NAT来配置虚拟网络,我比较看重它使用无线网络这一点,至少不至于把主机的主要网络都搞乱。第一步是安装dnsmasq,以前工作中接触到这款软件,总之我也很担心它和默认的systemd-resolv冲突
    
    $ journalctl -xeu dnsmasq.service
    Apr 25 06:38:25 nick-sager dnsmasq[80522]: dnsmasq: failed to create listening socket for 172.27.232.139: Address already in use
    Apr 25 06:38:25 nick-sager dnsmasq[80522]: failed to create listening socket for 172.27.232.139: Address already in use
    Apr 25 06:38:25 nick-sager systemd[1]: dnsmasq.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
    
    我看到这个错误一开始并不明白,以为是帖子里的和systemd-resolv关于127.0.0.1:53:53的冲突,后来enable了/etc/dnsmasq.conf里的bind-interfaces依然不解决问题才意识到可能是我已经使用openvpn的tun0设置的这个dns的关系吧?
    
    nick@nick-sager:~/workspace/debootstrap/tmp/initrd$ ifconfig tun0
    tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
            inet 172.27.232.139  netmask 255.255.248.0  destination 172.27.232.139
            unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)
            RX packets 29709  bytes 10335721 (10.3 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 28234  bytes 3116970 (3.1 MB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    也许我应该先配置我的NAT(Network Address Translation)之后再解决这个问题吧?我先下载这个脚本 可是且慢,我已经有一个/etc/qemu-ifup的脚本了,这个应该是qemu自带的,怎么办?
    1. 这个默认的脚本说
      
      # Script to bring a network (tap) device for qemu up.
      # The idea is to add the tap device to the same bridge
      # as we have default routing to.
      
    2. 首先是找出是否系统有ip或者brctl命令,如果有ipip link set "$1" up第一个参数应该是传入的设备名吧?
    3. 如果系统有brctl命令则把目标桥设备ip地址为0.0.0.0:
      
      ifconfig "$1" 0.0.0.0 up
      
    4. 接下来是找出默认的桥接的设备
      
      switch=$(ip route ls | \
          awk '/^default / {
                for(i=0;i<NF;i++) { if ($i == "dev") { print $(i+1); next; } }
               }'
              )
      $ echo $switch
      enp0s31f6
      
      理解这个命令需要先看看我的route table是如何的:
      
      $ ip route ls
      0.0.0.0/1 via 172.27.232.1 dev tun0 
      default via 192.168.1.1 dev enp0s31f6 proto dhcp metric 100 
      54.67.3.66 via 192.168.1.1 dev enp0s31f6 
      128.0.0.0/1 via 172.27.232.1 dev tun0 
      169.254.0.0/16 dev enp0s31f6 scope link metric 1000 
      172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
      172.27.232.0/21 dev tun0 proto kernel scope link src 172.27.232.139 
      192.168.1.0/24 dev enp0s31f6 proto kernel scope link src 192.168.1.11 metric 100
      
      而以上的命令是找出第一个dev的设备,这一点我估计就是dnsmasq的问题,它就是找route table第一个栏目的设备并不管它是不是桥接设备,所以,qemu-ifup的官方脚本找到的switch=enp0s31f6而不是默认的tun0
    5. 下面这一步的意图是这样子的
      
      # only add the interface to default-route bridge if we
      # have such interface (with default route) and if that
      # interface is actually a bridge.
      # It is possible to have several default routes too
      
      它的命令是什么呢?
      
      for br in $switch; do
          if [ -d /sys/class/net/$br/bridge/. ]; then
              if [ -n "$ip" ]; then
                ip link set "$1" master "$br"
              else
                brctl addif $br "$1"
              fi
              exit    # exit with status of the previous command
          fi
      done
      
      我把实际命令ip link set和brctl addif都改成echo来看看结果是什么,结果是空,因为很显然的/sys/class/net/enp0s31f6/bridge不存在,说明我们默认的route不是bridge,所以,不加它!
    总而言之,dnsmasq的启动错误不是和systemd-resolv的冲突,而是试图和我的openvpn创建的tun0设备抢夺?我之前的搜索找错了方向。
  2. 感觉我没有走在正确的道路上,dnsmasq仅仅是一个小的步骤,而且是否是必须的呢?需要绑定在这个service吗?重要的是tun/tap的配置上。所以,我先看看这个脚本说实在的,这个领域对于我这个网络新手是很让人抓狂的,因为看上去有很多的道路可走!
  3. 感觉非常多的线索,为了不漏掉先记录一下这个feature,它是可以自动设置tap和bridge的吗?还是我要手动配置?我的/etc/qemu/bridge.conf 配置是
    
    $ cat /etc/qemu/bridge.conf 
    allow virtbr0
    
    可是当我运行
    
    $ qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic  -serial stdio -m 2G  -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -netdev tap,id=hn0,br=virtbr0 -device virtio-net-pci,netdev=hn0,id=nic1
    qemu-system-x86_64: -netdev tap,id=hn0,br=virtbr0: could not configure /dev/net/tun: Operation not permitted
    
    所以,这个是权限问题!这里有一个思路 但是我很怀疑这个是否必要,因为我的/dev/net/tun是所有人都可以读写的,这个在内核文档似乎提到过控制权限不在这里
    
    $ ll /dev/net/tun 
    crw-rw-rw- 1 root root 10, 200 Apr 19 15:01 /dev/net/tun
    
    而这位大侠说的很对,具体看打开这个文件后来做了什么
    
    strace -o qemu.strace qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic  -serial stdio -m 2G  -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -netdev tap,id=hn0,br=virtbr0 -device virtio-net-pci,netdev=hn0,id=nic1
    
    结果输出在qemu.strace里,我们搜索打开文件的结果
    
    $ grep -A 3 /dev/net/tun qemu.strace 
    openat(AT_FDCWD, "/dev/net/tun", O_RDWR) = 14
    ioctl(14, TUNGETFEATURES, 0x7fffa04bc858) = 0
    ioctl(14, TUNSETVNETHDRSZ, 0x7fffa04bc85c) = -1 EBADFD (File descriptor in bad state)
    ioctl(14, TUNSETIFF, 0x7fffa04bc860)    = -1 EPERM (Operation not permitted)
    
    所以,很明显的,你就算让/dev/net/tun可以普通用户读写,你能把ioctl的权限加进去吗? 这里也许是部分解决了权限的问题:
    
    $ tunctl -t tap0 -g nick -u nick
    Set 'tap0' persistent and owned by uid 1000 gid 1000
    
    然后我启动的时候指定了我的tap设备
    
    qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic  -serial stdio -m 2G  -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -netdev tap,ifname=tap0,id=hn0,br=virtbr0,script=no,downscript=no -device virtio-net-pci,netdev=hn0,id=nic1
    
    权限解决了,但是我的虚拟机网络没有设置好,看来我还是需要手动配置桥啊?感觉太乱了,这个是预想得到的,因为这里有至少两个不同的途径:bridge,tap
    
    -netdev tap,id=str[,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile]
             [,br=bridge][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off]
             [,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off][,queues=n]
             [,poll-us=n]
                    configure a host TAP network backend with ID 'str'
                    connected to a bridge (default=br0)
                    use network scripts 'file' (default=/etc/qemu-ifup)
                    to configure it and 'dfile' (default=/etc/qemu-ifdown)
                    to deconfigure it
                    use '[down]script=no' to disable script execution
                    use network helper 'helper' (default=/usr/lib/qemu/qemu-bridge-helper) to
                    configure it
                    use 'fd=h' to connect to an already opened TAP interface
                    use 'fds=x:y:...:z' to connect to already opened multiqueue capable TAP interfaces
                    use 'sndbuf=nbytes' to limit the size of the send buffer (the
                    default is disabled 'sndbuf=0' to enable flow control set 'sndbuf=1048576')
                    use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag
                    use vnet_hdr=on to make the lack of IFF_VNET_HDR support an error condition
                    use vhost=on to enable experimental in kernel accelerator
                        (only has effect for virtio guests which use MSIX)
                    use vhostforce=on to force vhost on for non-MSIX virtio guests
                    use 'vhostfd=h' to connect to an already opened vhost net device
                    use 'vhostfds=x:y:...:z to connect to multiple already opened vhost net devices
                    use 'queues=n' to specify the number of queues to be created for multiqueue TAP
                    use 'poll-us=n' to specify the maximum number of microseconds that could be
                    spent on busy polling for vhost net
    
    这么多的选项是关于tap的!而关于bridge怎么这么简单?
    
    -netdev bridge,id=str[,br=bridge][,helper=helper]
                    configure a host TAP network backend with ID 'str' that is
                    connected to a bridge (default=br0)
                    using the program 'helper (default=/usr/lib/qemu/qemu-bridge-helper)
    
    问题是我的bridge设好了吗?
    
    $ brctl show
    bridge name	bridge id		STP enabled	interfaces
    docker0		8000.024265d25948	no
    
    所以,你真的是在瞎忙一气!再确认一下
    
    $ networkctl
    WARNING: systemd-networkd is not running, output will be incomplete.
    
    IDX LINK      TYPE     OPERATIONAL SETUP    
      1 lo        loopback n/a         unmanaged
      2 enp0s31f6 ether    n/a         unmanaged
      3 wlp109s0  wlan     n/a         unmanaged
      5 docker0   bridge   n/a         unmanaged
     12 tun0      none     n/a         unmanaged
     13 tap0      ether    n/a         unmanaged
    
    6 links listed.
    
  4. 看来我需要从头来,首先要真正理解我配置的是什么?等吃完早饭再说吧!再复习一下概念
    Though both are for tunneling purposes, TUN and TAP can't be used together because they transmit and receive packets at different layers of the network stack. TUN, namely network TUNnel, simulates a network layer device and operates in layer 3 carrying IP packets. TAP, namely network TAP, simulates a link layer device and operates in layer 2 carrying Ethernet frames. TUN is used with routing. TAP can be used to create a user space network bridge.
    两者相似但是功能是有些许差别吧?这个概念我刚刚学完就忘了因为根本就没有真正的领悟。因为它们具体是怎么实操的离开了实践空谈什么层级都是无用的。
  5. 关于一个小问题/dev/net的权限,默认就是可以的:
    
    $ ll -d /dev/net
    drwxr-xr-x 2 root root 60 Apr 19 15:01 /dev/net/
    
    也就是说默认sudo chmod 0755 /dev/net应该是足够的,因为连它下面的设备文件都是用户可读写的,因为本来内核就是设计这个tun设备是一个用户可以自由创建的设备。这里我有一个非常幼稚的问题,就是为什么目录需要的权利?我以前认为似乎执行的权利就够了,实在是老糊涂了。这个问题太初级了吧?可是我确实不知道!

    This is because the directory itself only contains filenames and inode numbers—that's all.

    Read access to the filenames is controlled by the read permission.

    Access to the inodes pointed to by the directory is controlled by the execute permission—not the read permission. The inodes contain all the actual details about the file, such as filesize, owner, permissions, time last modified, and the physical location (on your physical hard disk) of the binary data which comprises the file's contents.

    To view the names of the files in the directory—you need read permission on the directory. You don't need execute or write permissions for this.


四月二十七日 等待变化等待机会

  1. 有一个感人的视频,很多人全程泪目。
  2. 这个是以前找到的关于OVMF的官方指引。那么什么是EDK II呢?这个TianoCore,它和EDK II是紧密联系的。EDK II stands for Enhanced Build Environment。目前熟悉名词是我的最大的收获。
  3. 回过头来尽快解决TAP的问题,我决定深入研究这个指引
    1. 首先,TAP是什么?它是内核的一个feature能够创建虚拟网卡表现的好像真的网卡,但实际上包是送到一个用户级程序。我感觉我还是 在英译中的学习。但是这个确实是概念理解的第一步,每个字都是很重要的理解。这个和我在TAP/TUN的主页学习的概念是相一致的。当初内核驱动的设计思 想也是这样子的,/dev/net/tun是枢纽而它是用户可读写的,这个是从内核就开放的用户级别访问的。
      Tap devices are a Linux kernel feature that allows you to create virtual network interfaces that appear as real network interfaces. Packets sent to a tap interface are delivered to a userspace program, such as QEMU, that has bound itself to the interface.
    2. 这个是之前就明白的,TAP是以太网的级别的模拟,但是具体的机制和IP层级区别在哪里呢?我的概念依然模糊。
      QEMU can use tap networking for a virtual machine so that packets sent to the tap interface will be sent to the virtual machine and appear as coming from a network interface (usually an Ethernet interface) in the virtual machine. Conversely, everything that the virtual machine sends through its network interface will appear on the tap interface.
    3. Tap devices are supported by the Linux bridge drivers, so it is possible to bridge together tap devices with each other and possibly with other host interfaces such as eth0. This is desirable if you want your virtual machines to be able to talk to each other, or if you want other machines on your LAN to be able to talk to the virtual machines.
      TAP是被bridge支持的,并不是说它本身就是桥实现的,这个目的显然是方便在不同网段的主机,不管是虚拟机还是物理机互相通讯。这个我开始有些概念了。
    4. 这里提到另一个选项host only networking,我打算下一步再去研读。
    5. 但是我之前已经尝试过这个方式有什么前提没有设置吧?
      
      qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic  -serial stdio -m 2G  -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -netdev tap,ifname=tap0,id=network0,br=virtbr0,script=no,downscript=no -device virtio-net,netdev=network0
      
    6. 前进之前需要先后退半步,我打算再次理解user-mode的实质,这个也许能够比较而理解为什么我遗漏了什么?
      By default, without any -netdev arguments, QEMU will use user-mode networking with a built-in DHCP server. Your virtual machines will be assigned an IP address when they run their DHCP client, and they will be able to access the physical host's network through IP masquerading done by QEMU.
      这里的要点依然是两方面的,首先是qemu使用内部的DHCP server为你的虚拟机分配网址,但是另一方面是虚拟机需要自己主动使用DHCP client开机就寻找服务,这个听上去似乎是默认的,但是世界上没有配置是没有默认的。对于一个从头创建的操作系统任何配置都是需要自己去做的。所以, 这里才引出了开机服务的问题。这一点我以前是模糊的,因为到底是哪一个服务呢?ubuntu很久以前就专向了systemd,我似乎从那以后就不明白 Networking-Manager的角色到底是什么,至今我对于谁是主角依然是模糊的认识。
    7. 似乎是回应我的一些疑惑,这里提到的IPV6下的ping不工作似乎正是为什么虚拟机可以正常ping谷歌的原因,
      Note: ICMPv6 will not work, as support for it is not implemented: Slirp: external icmpv6 not supported yet. Pinging an IPv6 address will not work.
      因为我的观察是我的openvpn似乎对于配置IPv6有问题,反而我在主机需要ping -4 www.google.com才能工作,而在虚拟机则似乎天然不需要参数-4,也许是因为它天然就禁止了ipv6所以,默认就是ipv4。这个是一个额外的信息。
    8. user-mode的能力极限和特点是什么呢?
      QEMU's user-mode networking can offer more capabilities such as built-in TFTP or SMB servers, redirecting host ports to the guest (for example to allow SSH connections to the guest) or attaching guests to VLANs so that they can talk to each other. See the QEMU documentation on the -net user flag for more details.
      所以,这几样是我将来可以尝试的部分。
      
      -netdev user,id=str[,ipv4=on|off][,net=addr[/mask]][,host=addr]
               [,ipv6=on|off][,ipv6-net=addr[/int]][,ipv6-host=addr]
               [,restrict=on|off][,hostname=host][,dhcpstart=addr]
               [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]
               [,tftp=dir][,tftp-server-name=name][,bootfile=f][,hostfwd=rule][,guestfwd=rule][,smb=dir[,smbserver=addr]]
                      configure a user mode network backend with ID 'str',
                      its DHCP server and optional services
      
      这个是目前user-mode的所有选项吗?
    9. 所以,这里是我需要了解的user-mode的虚拟机方面的部分,就是如果使用systemd-networkd的基本配置和要求: 首先是如何排除其他的服务的干扰?这一点我其实很模糊,因为systemctl --type=service到底还有那些network相关的服务,我居然没有很清楚的概念。其次是通过查看networkd可以看到它启动的一些细节: --no-pager --full可以看到全部的log细节。
      
      ~# systemctl --no-pager --full status systemd-networkd
      ● systemd-networkd.service - Network Configuration
           Loaded: loaded (/lib/systemd/system/systemd-networkd.service; enabled; vendor preset: enabled)
           Active: active (running) since Sat 2024-04-27 08:45:10 CST; 9min ago
      TriggeredBy: ● systemd-networkd.socket
             Docs: man:systemd-networkd.service(8)
         Main PID: 170 (systemd-network)
           Status: "Processing requests..."
            Tasks: 1 (limit: 2363)
           Memory: 2.7M
              CPU: 439ms
           CGroup: /system.slice/systemd-networkd.service
                   └─170 /lib/systemd/systemd-networkd
      
      Apr 27 08:45:09 nick-qemu systemd[1]: Starting Network Configuration...
      Apr 27 08:45:10 nick-qemu systemd-networkd[170]: lo: Link UP
      Apr 27 08:45:10 nick-qemu systemd-networkd[170]: lo: Gained carrier
      Apr 27 08:45:10 nick-qemu systemd-networkd[170]: Enumeration completed
      Apr 27 08:45:10 nick-qemu systemd[1]: Started Network Configuration.
      Apr 27 08:45:19 nick-qemu systemd-networkd[170]: eth0: Interface name change detected, renamed to ens3.
      Apr 27 08:45:19 nick-qemu systemd-networkd[170]: ens3: Link UP
      Apr 27 08:45:19 nick-qemu systemd-networkd[170]: ens3: Gained carrier
      Apr 27 08:45:20 nick-qemu systemd-networkd[170]: ens3: DHCPv4 address 10.0.2.15/24 via 10.0.2.2
      Apr 27 08:45:21 nick-qemu systemd-networkd[170]: ens3: Gained IPv6LL
      
      总而言之,user-mode是通过qemu的DHCP server来获得一个外界不可见的内网ip来访问外网。那么所谓的外网不可见是真的吗?这个应该是qemu的控制吧?10.0.2.2是默认网关它不放行你是无法访问的吧?
    10. networkd和resolved两者是既独立又关联的关系。后者是DNS相关服务的。
      systemd-resolved is a systemd service that provides network name resolution to local applications via a D-Bus interface, the resolve NSS service (nss-resolve(8)), and a local DNS stub listener on 127.0.0.53.
      这短短一句话包含了多少的信息啊!我之前为了openvpn的DNS解析看了非常多相关的资料,但是至今也还是模模糊糊的。总之,它是域名解析,但是它的接口居然是有三个不同的来源,首先是D-bus,因为最早通过C-style的library是古老的,大概就是get-host-name之类的吧?我又忘了错了!这两个函数我总是搞混,不知道为什么!是glibc's getaddrinfo(3),而这个NSS我至今依然不明白是怎么回事,似乎都是历史遗留问题,至于127.0.0.53则是一个人人都可以访问的古老的来源,因为很多基于权限因素它们喜欢使用这个方式来侦听。这个领域看似简单但是错综复杂。
    11. 单单看看这个定义就知道这里面学问其实很大:
      systemd-resolved provides resolver services for Domain Name System (DNS) (including DNSSEC and DNS over TLS), Multicast DNS (mDNS) and Link-Local Multicast Name Resolution (LLMNR)
      对于不熟悉网络的我来说,这里每一个都是深渊,域名解析本身也是一个危险的领域,因为有安全问题,黑客绑架这个等于从源头垄断了你的访问,而很多政府防火墙的机制就是从这里来的。至于后者我都没有听说过。
    12. 对!有四种模式!天啊!
      To provide domain name resolution for software that reads /etc/resolv.conf directly, such as web browsers, Go and GnuPG, systemd-resolved has four different modes for handling the file—stub, static, uplink and foreign.
      这里引出了更多的疑问,为什么Go程序和什么GnuPG要单独拿出来说事呢? 浏览器是直接读这个/etc/resolv.conf的,这个我可以理解,但是另外两个是什么鬼?
      
      ~# cat /etc/resolv.conf 
      # This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8).
      # Do not edit.
      #
      # This file might be symlinked as /etc/resolv.conf. If you're looking at
      # /etc/resolv.conf and seeing this text, you have followed the symlink.
      #
      # This is a dynamic resolv.conf file for connecting local clients to the
      # internal DNS stub resolver of systemd-resolved. This file lists all
      # configured search domains.
      #
      # Run "resolvectl status" to see details about the uplink DNS servers
      # currently in use.
      #
      # Third party programs should typically not access this file directly, but only
      # through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
      # different way, replace this symlink by a static file or a different symlink.
      #
      # See man:systemd-resolved.service(8) for details about the supported modes of
      # operation for /etc/resolv.conf.
      
      nameserver 127.0.0.53
      options edns0 trust-ad
      search .
      
      首先,这里的确是一个软链接
      
      ~# ll /etc/resolv.conf 
      lrwxrwxrwx 1 root root 39 Apr 13 12:01 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
      
      其次,internal DNS stub resolver到底指的是什么?到底这里说的是127.0.0.53吗?
      
      ~# resolvectl status
      Global
             Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
      resolv.conf mode: stub
      
      Link 2 (ens3)
          Current Scopes: DNS
               Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
      Current DNS Server: 10.0.2.3
             DNS Servers: 10.0.2.3
      
      所以,支持这三种不同的协议-LLMNR -mDNS -DNSOverTLS,我的虚拟网卡额外的这个DefaultRoute是什么协议?我原来以为默认的DNS的ip是10.0.2.2,为什么这里是10.0.2.3又搞错了,10.0.2.2是DHCP server的ip,而DNS server的ip才是10.0.2.3
  4. 感觉好累啊!先吃早饭吧?
  5. 千呼万唤始出来,美国对于乌克兰开启了大规模军援模式,本来乌克兰是美国在围棋盘上一块打入的孤子没打算一定能够做活,可谓是一个轻投入试应手的 没有包袱的棋子,随时可以放弃,是很轻的资产。但是随着乌克兰透露出可以一战的能力以及前期的大投入,这个棋子越走越重,现在已经走成了快要既决胜负也决 生死的关键地区了。这个是美国始终都在避免而又不知不觉的滑入的深渊。面对着美国目前的广泛利用盟友的做法其实是有利有弊的,一战二战给德国的教训就是差 劲的盟友比没有盟友可能更糟糕!因为你在借重盟友的力量的同时也是被你的盟友借重的过程,最终的妥协与调和不知道会把既定方向导向何方。

四月二十八日 等待变化等待机会

  1. 我对于d-bus这个东西始终不得要领,目前看来这个是在安装桌面系统时候连带安装的,因为似乎dbus-x11这个包是启动服务的,而 这个包我猜想是桌面系统才需要的。总之,在启动时候如果没有这个d-bus就会出现无法检查文件系统之类的莫名其妙的错误。而且这个dbus似乎也是不受 systemctl控制的,因为我试图enable dubs报出的错是没有配置,也许是静态配置的。总之,我感觉这个服务是关键的服务也许就是不受systemd来控制的?
  2. 再次强调一下tap/tun的区别:

    Though both are for tunneling purposes, TUN and TAP can't be used together because they transmit and receive packets at different layers of the network stack. TUN, namely network TUNnel, simulates a network layer device and operates in layer 3 carrying IP packets. TAP, namely network TAP, simulates a link layer device and operates in layer 2 carrying Ethernet frames. TUN is used with routing. TAP can be used to create a user space network bridge.

    尽管强调了,但是始终没有建立起概念,因为tun/tap是水火不相容的,两者不可能同时起作用。它们都是所谓的tunneling的目的,但是层级不 同,而且操作的对象也不同,tun顾名思义是在routing table做文章,而tap更像是间谍窃取信息的彷佛是信息的短路。也许这个图才能加深印象:
  3. 但是我还是没有找到可靠的添加tun/tap的方法,但是很明显的修改routing table是很危险的,因为我的openvpn已经在那里做了很多文章了。

四月二十九日 等待变化等待机会

  1. 我依然在黑暗中,因为tap的基本概念还是不清楚,而且遇到我的无线网卡不接受作为bridge的slave,我猜想可能是因为我的无线 网卡本身是AP的关系吧?总之,两个无线网卡的共同模式增加了复杂度,这些是额外的干扰。然后毫无意外的是当我把我的网卡作为bridge的slave后 它就不工作了。这个几乎是完全可以预测的。我想重新启动恢复,因为另一个干扰因素是我使用的openvpn在我看来也是tap/tun,那么这些干扰因素 导致我越来越混乱。而且使用dhcp来分配ip很可能不是一个好主意。太多的因素了。我想能不能运用netplan这个工具先把我的网络配置保存以便恢复呢?否则这个重新修复的过程太痛苦了。
    On Ubuntu 20.04 Netplan replaces the traditional method of configuring network interfaces using the /etc/network/interfaces file; it aims to make things easier and more centralized (the old way of configuring interfaces can still be used: check our article about How to switch back networking to /etc/network/interfaces on Ubuntu 20.04 Focal Fossa Linux).
    就是说传统的/etc/network/interfaces这个配置文件被跳过了,这个也许是很多非ubuntu的指引不适合的原因吧?
  2. 这里我至少明白了一点就是为什么我的网卡名字变得那么难记住的什么ens,就是因为启动的时候被改名字了,这个我在log里看到当时不明所以然,现在看来是这里可以修改/etc/default/grub来改回去。 但是我感到困惑的是到底ubuntu是不是想让NetworkManager来管理网络而不是networkd?
    
    $ cat /etc/netplan/01-network-manager-all.yaml 
    # Let NetworkManager manage all devices on this system
    network:
      version: 2
      renderer: NetworkManager
    
  3. 我实在是不明白为什么能,也不明白为什么不能。这个是记录的网络状况:
    nick@nick-sager:~/workspace/debootstrap/tmp/initrd$ netplan --all status
    Unknown device type: none
    Unknown device type: none
    Unknown device type: none
         Online state: online
        DNS Addresses: 127.0.0.53 (stub)
           DNS Search: .
    
      1: lo ethernet UNKNOWN/UP (unmanaged)
          MAC Address: 00:00:00:00:00:00
            Addresses: 127.0.0.1/8
    
      2: enp0s31f6 ethernet UP (unmanaged)
          MAC Address: d4:93:90:21:08:3d (Intel Corporation)
            Addresses: 192.168.1.9/24
        DNS Addresses: 218.85.152.99
                       218.85.157.99
               Routes: 192.168.1.0/24 from 192.168.1.9 (link)
    
      3: wlp109s0 wifi/"1701_5G" UP (unmanaged)
          MAC Address: 0c:9a:3c:69:8f:7f (Intel Corporation)
            Addresses: 192.168.1.15/24
                       240e:37c:1a20:cd00:d2ce:2f6f:b1ec:9089/64
                       fe80::1ce8:8396:41f5:6725/64 (link)
               Routes: default via 192.168.1.1 metric 600 (dhcp)
                       54.67.3.66 via 192.168.1.1 (boot)
                       192.168.1.0/24 from 192.168.1.15 metric 600 (link)
                       240e:37c:1a20:cd00::/64 metric 600 (ra)
                       fe80::/64 metric 1024
                       default via fe80::1 metric 600 (ra)
    
      5: docker0 bridge DOWN/UP (unmanaged)
          MAC Address: 02:42:65:d2:59:48
            Addresses: 172.17.0.1/16
               Routes: 172.17.0.0/16 from 172.17.0.1 (link)
    
     13: tap0 ethernet UP (unmanaged)
          MAC Address: 46:2a:b1:11:3f:10
    
     20: virtbr0 bridge UP (unmanaged)
          MAC Address: 0a:36:50:42:8e:89
            Addresses: 172.20.0.1/16
                       192.168.1.11/32
                       192.168.1.14/24
        DNS Addresses: 218.85.152.99
                       218.85.157.99
               Routes: default via 192.168.1.1 (boot)
                       172.20.0.0/16 from 172.20.0.1 (link)
                       192.168.1.0/24 from 192.168.1.14 (link)
    
     26: tun0 other UNKNOWN/UP (unmanaged)
            Addresses: 172.27.232.148/21
        DNS Addresses: 8.8.8.8
           DNS Search: .
               Routes: 0.0.0.0/1 via 172.27.232.1 (boot)
                       128.0.0.0/1 via 172.27.232.1 (boot)
                       169.254.0.0/16 metric 1000 (boot, link)
                       172.27.232.0/21 from 172.27.232.148 (link)
    
    那么我究竟做了什么呢?这个是虚拟机的网络情况
    
    root@nick-qemu:~# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
        altname enp0s3
        inet 192.168.1.12/24 metric 1024 brd 192.168.1.255 scope global dynamic ens3
           valid_lft 85956sec preferred_lft 85956sec
        inet6 240e:37c:1a20:cd00:5054:ff:fe12:3456/64 scope global dynamic mngtmpaddr noprefixroute 
           valid_lft 245562sec preferred_lft 159162sec
        inet6 fe80::5054:ff:fe12:3456/64 scope link 
           valid_lft forever preferred_lft forever
    
    我启动的命令是
    
    $ qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -serial stdio -m 2G -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -net nic -net tap,ifname=tap0,br=virtbr0,script=no,downscript=no
    
    开始的时候虚拟机网卡并不能使用因为没有分配ip,随后我手动启动dhcp: dhclient -v ens3得到了ip。那么这个流程是正确的吗?我得到了我希望的结果吗?我的虚拟机是通过我的有线网卡得到了网络访问,可是我之前的设置是有线网卡通过 openvpn的tun0得到了额外的网络访问,而现在似乎没有了,虚拟机没有办法使用openvpn,我几乎都不知道应该问什么问题来解决这个问题。先 休息一下吧。
  4. 我准备冒险重启电脑看看会怎么样?

四月三十日 等待变化等待机会

  1. 我总算找到一个清晰简单的指导步骤, 先从创建tap和bridge一步步来,虽然这个是我目前比较有概念的,也就是bridge作为我的实际的网卡和tap的桥接,但是它本身也是需要分配地 址,这个是比较符合我的预期的,就是说我们需要dhcp来做这个,而很多指导跳过这个步骤,使用静态ip,这个是等效的,但是却让人感到困惑的是一开始怎 么知道什么网段的ip来分配,因为我原来认为我的tap设备是要分配在另一个网段里,所以,产生了困惑。
    1. 创建virtbr0,因为这个是
      
      $ cat /etc/qemu/bridge.conf 
      allow virtbr0
      
      里默认的设备名,我希望能够借重qemu的helper来使用普通用户启动
      
      sudo brctl addbr virtbr0
      
    2. 创建tap0
      
      $ sudo tunctl -t tap0 -u `whoami`
      Set 'tap0' persistent and owned by uid 1000
      
    3. 把这个tap0加入virtbr0
      
      $ sudo brctl addif virtbr0 tap0
      
    4. 这里本来应该对物理设备做类似的操作,但是在这个步骤之前我想先实验一个另外的做法,就是所谓的Host-only Network,也就是多个虚拟机都在一个桥下,但是桥并没有链接物理设备,这样子虚拟机并没有直接出海:
      If the bridge is given an IP address and traffic destined for it is allowed, but no real interface (e.g. eth0) is connected to the bridge, then the virtual machines will be able to talk to each other and the host system. However, they will not be able to talk to anything on the external network, provided that you do not set up IP masquerading on the physical host. This configuration is called host-only networking by other virtualization software such as VirtualBox.
      所以,如法炮制一遍以上过程:
      
      $ sudo tunctl -t tap1 -u `whoami`
      Set 'tap1' persistent and owned by uid 1000
      $ sudo brctl addif virtbr0 tap1
      
    5. 把这些设备都唤起来,并且分配地址给桥:
      
      $ sudo ifconfig tap0 up
      $ sudo ifconfig tap1 up
      $ sudo ifconfig virtbr0 up
      
      但是在分配ip的时候遇到一个小问题,就是这个命令一直没有结果
      
      $ sudo dhclient -v -s 192.168.1.1 virtbr0
      Internet Systems Consortium DHCP Client 4.4.1
      Copyright 2004-2018 Internet Systems Consortium.
      All rights reserved.
      For info, please visit https://www.isc.org/software/dhcp/
      
      Listening on LPF/virtbr0/0a:36:50:42:8e:89
      Sending on   LPF/virtbr0/0a:36:50:42:8e:89
      Sending on   Socket/fallback
      DHCPDISCOVER on virtbr0 to 192.168.1.1 port 67 interval 3 (xid=0xeb7a6a11)
      DHCPDISCOVER on virtbr0 to 192.168.1.1 port 67 interval 6 (xid=0xeb7a6a11)
      ...
      
      这是为什么呢?这里给出了一些信息,首先,可以查看
      
      $ cat /var/lib/dhcp/dhclient.leases 
      lease {
        interface "wlp109s0";
        fixed-address 192.168.1.9;
        option subnet-mask 255.255.255.0;
        option dhcp-lease-time 86400;
        option routers 192.168.1.1;
        option dhcp-message-type 5;
        option dhcp-server-identifier 192.168.1.1;
        option domain-name-servers 218.85.152.99,218.85.157.99;
        option vivso 0:0:0:0:14:2:6:48:47:57:2d:43:54:a:2:0:29:b:2:0:2b:d:2:0:2d;
        renew 5 2024/01/19 00:39:25;
        rebind 5 2024/01/19 11:01:00;
        expire 5 2024/01/19 14:01:00;
      }
      lease {
        interface "enp0s31f6";
        fixed-address 192.168.1.9;
        option subnet-mask 255.255.255.0;
        option dhcp-lease-time 86400;
        option routers 192.168.1.1;
        option dhcp-message-type 5;
        option dhcp-server-identifier 192.168.1.1;
        option domain-name-servers 218.85.152.99,218.85.157.99;
        option vivso 0:0:0:0:14:2:6:48:47:57:2d:43:54:a:2:0:29:b:2:0:2b:d:2:0:2d;
        renew 1 2024/04/29 10:35:53;
        rebind 1 2024/04/29 21:43:01;
        expire 2 2024/04/30 00:43:01;
      }
      lease {
        interface "virtbr0";
        fixed-address 192.168.1.14;
        option subnet-mask 255.255.255.0;
        option routers 192.168.1.1;
        option dhcp-lease-time 86400;
        option dhcp-message-type 5;
        option domain-name-servers 218.85.152.99,218.85.157.99;
        option dhcp-server-identifier 192.168.1.1;
        option vivso 0:0:0:0:14:2:6:48:47:57:2d:43:54:a:2:0:29:b:2:0:2b:d:2:0:2d;
        renew 1 2024/04/29 12:04:06;
        rebind 1 2024/04/29 21:52:15;
        expire 2 2024/04/30 00:52:15;
      }
      
      我发现virtbr0的lease还没有expire,所以,要么我强制更新,要么就硬性分配网址ip。
      
      $ sudo dhclient -v -r -s 192.168.1.1 virtbr0
      Internet Systems Consortium DHCP Client 4.4.1
      Copyright 2004-2018 Internet Systems Consortium.
      All rights reserved.
      For info, please visit https://www.isc.org/software/dhcp/
      
      Listening on LPF/virtbr0/0a:36:50:42:8e:89
      Sending on   LPF/virtbr0/0a:36:50:42:8e:89
      Sending on   Socket/fallback
      DHCPRELEASE of 192.168.1.14 on virtbr0 to 192.168.1.1 port 67 (xid=0x421343d8)
      
    6. 当我启动第二个虚拟机的时候,磁盘文件抱怨有write lock怀疑已经被第一个占用了,我只好拷贝了一份
      
      qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -serial stdio -m 2G -drive format=raw,file=ubuntu-efi-disk2 -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -net nic -net tap,ifname=tap1,br=virtbr0,script=no,downscript=no
      
      那么两个虚拟机如何通讯呢?
    7. 在做进一步实验前我发现我没有分配mac地址,这个在同一个网络里肯定是不行的,所以,必须要采用复杂一些的命令。 第一台虚拟机:
      
      qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -serial stdio -m 2G -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -device e1000,netdev=mynet0,mac=52:55:00:d1:55:00 -netdev tap,id=mynet0,ifname=tap0,br=virtbr0,script=no,downscript=no
      
      第二台虚拟机:
      
      qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -serial stdio -m 2G -drive format=raw,file=ubuntu-efi-disk2 -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -device e1000,netdev=mynet1,mac=52:55:00:d1:55:01 -netdev tap,id=mynet1,ifname=tap1,br=virtbr0,script=no,downscript=no
      
    8. 这样子我们可以实现所谓的host-only network的效果就是多台虚拟机在同一个bridge下可以通讯,这个需要我们在虚拟机配置基本的网络:
      
      root@nick-qemu:~# cat /etc/netplan/01-network-manager-all.yaml
      network:
        ethernets:
          enp0s3:
            addresses: [172.20.0.100/24]
            routes:
              - to: default
                via: 172.20.0.100
                on-link: True
            nameservers:
              addresses: [8.8.8.8, 1.1.1.1]
            dhcp4: false
            optional: true
        version: 2
      
      然后可以执行netplan generate生成配置文件在/run/systemd/network/下的运行文件,如果不想重启,可以执行netplan apply来运行systemd-networkd来运行配置。这样子,我们的两台虚拟机可以互相访问了。这个就是安全的host-only network。
    第一步总算是成功了,其实,很多努力归根结底是操作系统的具体的配置,就是说在没有安装桌面系统的情况下,很多网络配置是没有完成的,而这个对于 server是很正常的,所以,学习ubuntu-server的网络配置就可以理解这一切的网络的配置的由来。这个是今天的最大收获。休息一下。
  2. 这里我意识到我的虚拟机其实是不完备的,因为ubuntu22.04采用的是netplan机制,就是说/etc/network/ interfaces这个文件是依靠netplan来产生,而它的配置文件是在/etc/netplan/*下的yaml文件,所以,如果我没有正确的采 用这个机制生成必要的interfaces文件,操作系统是无从访问网络的。
  3. 这是qemu另一个小技巧,我想要在虚拟机里使用control+c,但是现在的行为是推出qemu。这里似乎有很多的方式。但是最简单的就是-serial mon:stdio
  4. 这里是非常全面的基本的ubuntu的网络配置,完全手动有各种各样的做法。但是现在的核心又回到了tap/bridge的一面,就是我的虚拟机 并不能使用分配给它的tap机制。所以,这个才是核心。折腾了一个上午才真正的触及问题的核心!这个还是有收获的。因为目前我明白了一个ubuntu的机 制,在启动过程中会去调用netplan读取配置类设置网络,这个是一个ubuntu独有的方式。默认可以通过查看默认的systemd- networkd服务看到
    
    root@nick-qemu:~# journalctl | grep -A 3 netplan
    Apr 30 12:05:29 nick-qemu systemd-networkd[171]: ens3: Re-configuring with /run/systemd/network/10-netplan-enp0s3.network
    Apr 30 12:05:29 nick-qemu systemd-networkd[171]: ens3: DHCPv6 lease lost
    
    大体的流程是这样子的,配置/etc/netplan/下面的yaml文件后,如果netplan generate,就会在/run/systemd/network/下产生对应的systemd-networkd的配置文件在yaml文件里的设备名会产生相应的文件名。,就是说systemd-networkd作为默认的网络服务启动的时候去执行它。不过这里我对于最终设备名是怎么获得的感到困惑,这名字我是可以指定,但是它是所谓的alt-name。
    
    2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
        link/ether 52:55:00:d1:55:00 brd ff:ff:ff:ff:ff:ff
        altname enp0s3
        inet 192.168.1.100/24 brd 192.168.1.255 scope global ens3
           valid_lft forever preferred_lft forever
        inet6 fe80::5055:ff:fed1:5500/64 scope link 
           valid_lft forever preferred_lft forever
    
    不过这些都是无关紧要的小问题。大问题是tap为什么不工作?
  5. 最后终于发现其实host-only network是可以工作的,而且根本就不需要bridge,至少我不需要给bridge分配ip,它的tap设备彼此是可以自己通讯的,这个也是host-only networkd的真谛所在。

五月一日 等待变化等待机会

  1. 关于host-only network的安全机制我做了一个进一步的实验。
    1. 首先,在qemu的启动参数里,你指定不指定bridge几乎是无关的。也就是说能不能通讯取决于所使用的tap设备所在的bridge里,而不是qemu能够左右的。
      
      qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -serial mon:stdio -m 2G -drive format=raw,file=ubuntu-efi-disk -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" -device e1000,netdev=mynet0,mac=52:55:00:d1:55:00 -netdev tap,id=mynet0,ifname=tap0,br=br0,script=no,downscript=no
      
      就是说我即便指定了br=br0,可是假如我的tap实际上是在另一个bridge里虚拟机照样通讯。但是这里并不是qemu对此放任不管!因为。。。
    2. 能不能通讯确实需要qemu的认可,就是说在/etc/qemu/bridge.conf被许可的桥才能够通讯!就是说以上的br0是不可以的,除非bridge.conf里有允许它才可以,反之,哪怕在以上命令里我要是实际把tap放在允许的virtbr0桥里而不是参数里指明的br0通讯照样可以!
      
      $ brctl show
      bridge name	bridge id		STP enabled	interfaces
      br0		8000.dedebd53b90b	no		
      docker0		8000.0242629f11ae	no		
      virtbr0		8000.0a3650428e89	no		tap0
      							tap1
      
    3. 桥本身需要分配ip地址吗?不需要因为host-only的目的就是它无法和其他外网联络,桥本身有没有地址根本就是无关的。
  2. 这里关于systemd-networkd的 信息非常的丰富,我才意识到它本身是支持ipv4-dhcp协议的,而我的抄来的netplan的配置似乎禁止了它。不过我的感受是dhcp其实还是相当 复杂的,简单的使用当然没有问题,但是以前对于dnsmasq的这部分也仅仅是依赖于同事的配置,根本没有理解任何的部分。当然题外话,就是我比较几天前 已经认识到netplan是一个ubuntu的很好用的工具,这个在其他平台不一定有,但是我开始欣赏它了。把繁复的网络配置集中在一个配置文件里来运行 一定是一个好主意,否则这里面包含了多少的内容啊?
  3. 我现在才知道为什么网卡的名字变得那么难记,原来是这个systemd的倡议:Predictable Network Interface Names。而具体的名字是这样子的:
    Table 1. Two character prefixes based on the type of interface

    Prefix Description
    en Ethernet
    ib InfiniBand
    sl Serial line IP (slip)
    wl Wireless local area network (WLAN)
    ww Wireless wide area network (WWAN)
    那么让我猜一猜网卡的那两个数字是什么?是设备的major/minor吗?当然不是!是pci的domain/slot[function]/port/device的序列码。
    Table 2. On-board naming schemes

    Format Description
    prefixonumber PCI on-board index
    prefixdnumber Devicetree alias index
    让我来验证一下吧?
    
    $ lspci | grep -i ether
    0000:00:1f.6 Ethernet controller: Intel Corporation Device 0dc8 (rev 11)
    
    但是为什么命名为enp0s32f6呢?我还是没有看懂。这里的例子到底验证了什么呢?
  4. 回到老问题,为什么我不能够使用默认的qemu-helper自动分配一个tap设备以便让我简单的使用-net bridge
    
    sudo qemu-system-x86_64 -kernel tmp/vmlinuz-5.15.0-25-generic -serial mon:stdio -m 2G -dr
    ive format=raw,file=ubuntu-efi-disk2 -append "root=PARTUUID=8613E0D8-6556-7A47-922D-EDA26D53D20B console=ttyS0 earlyprintk=ttyS0" 
    -device e1000,netdev=mynet1,mac=52:55:00:d1:55:01 -netdev bridge,id=mynet1,br=virtbr0
    
    因为必须要sudo,因为之前已经确定了创建tap设备需要调用ioctl的权限。所以,还是自己创建tap吧。
  5. 不知道怎么回事突然tap/bridge就可以访问internet了,我在虚拟机上看到了正确的routing table,但是是怎么创建的呢?
    
    # ip route
    default via 192.168.1.14 dev ens3 proto static onlink 
    default via 192.168.1.1 dev ens3 proto dhcp src 192.168.1.17 metric 100 
    192.168.1.0/24 dev ens3 proto kernel scope link src 192.168.1.17 metric 100 
    192.168.1.1 dev ens3 proto dhcp scope link src 192.168.1.17 metric 100 
    218.85.152.99 via 192.168.1.1 dev ens3 proto dhcp src 192.168.1.17 metric 100 
    218.85.157.99 via 192.168.1.1 dev ens3 proto dhcp src 192.168.1.17 metric 100 
    
  6. 这个帖子设置有些意思,我先记下来随后再看。
  7. 整个系统的网络出现了问题,这个是不言而喻的,因为把我的网卡作为桥的一部分,这个工作最好是双网卡才行。
  8. 我怀疑也许我的无线网卡也会干扰openvpn的选项?总之我关闭了才能重启openvpn,然后照例的需要resolvectl修改dns的搜 索顺序,至少这是我的理解。重新登陆google的第一个场景是看到google要让我们大家去纪念一个黑女人,我点进去发现是一个美国印度裔的所谓的诗 人,我对于这种依靠政治正确获得荣誉的人从来就是非常的讨厌,看了它一篇《New Yorker》 的所谓的作品,简直是臭不可闻,因为在我看来拼音文字几乎都不适合写诗,也许最多可以填词,因为只有中文方块字可以天然的有整齐化一的能力,而韵律在西方 诗词届几乎都不提了。就是说随便什么街谈巷议都可以称之为诗,门槛如此之低只能有一个目的,就是作为未来的评论仲裁者可以为自己的上位搭梯子。这是一个多 么堕落的世界。看看吧写的什么狗皮叫做诗,一个女人从医院出来看到另一个,说什么大腿疼,说什么跳到水里等男人治愈伤痛。。。现在看起来《围城》里的《拼 盘姘伴》都是高雅富有艺术的杰作了。到底是谷歌的堕落还是整个美国本来就是在粪坑里?

五月二日 等待变化等待机会

  1. 为了降低风险,我找了一个USB无线网卡来实验,但是好像和普通网卡的行为是不一样的?先补一下课熟悉一下新的命令

    Deprecated command

    Replacement command(s)

    arp ip n (ip neighbor)
    ifconfig ip a (ip addr), ip link, ip -s (ip -stats)
    iptunnel ip tunnel
    iwconfig iw
    nameif ip link, ifrename
    netstat ss, ip route (for netstat-r), ip -s link (for netstat -i), ip maddr (for netstat-g)
    route ip r (ip route)

五月五日 等待变化等待机会

  1. 从基本概念学习开始。首先遇到好几次这个promiscuous mode
    In computer networking, promiscuous mode is a mode for a wired network interface controller (NIC) or wireless network interface controller (WNIC) that causes the controller to pass all traffic it receives to the central processing unit (CPU) rather than passing only the frames that the controller is specifically programmed to receive. This mode is normally used for packet sniffing that takes place on a router or on a computer connected to a wired network or one being part of a wireless LAN. Interfaces are placed into promiscuous mode by software bridges often used with hardware virtualization.
    这里为什么说software bridge是在使用虚拟化技术才需要这个?其实很简单,因为原本所有的网卡都只会接收目的地是自己的包,这个是以太网的特征,每个人都在嘈杂的大厅里听 到别人在讲话,你不要听跟你无关的声音。但是虚拟化可能就不是这样子了,因为你要作为一个二传手来帮助虚拟机传递包。
  2. 但是我对于bridging和routing的区别依旧很模糊
    A network bridge is a computer networking device that creates a single, aggregate network from multiple communication networks or network segments. This function is called network bridging. Bridging is distinct from routing. Routing allows multiple networks to communicate independently and yet remain separate, whereas bridging connects two separate networks as if they were a single network. In the OSI model, bridging is performed in the data link layer (layer 2). If one or more segments of the bridged network are wireless, the device is known as a wireless bridge.
    所以,我感觉难道bridging就是switch的功能吗?来比较一下routing的概念:
    Routing is the process of selecting a path for traffic in a network or between or across multiple networks. Broadly, routing is performed in many types of networks, including circuit-switched networks, such as the public switched telephone network (PSTN), and computer networks, such as the Internet.
    所以,首先,bridging往往是一个设备的行为,而routing是一个过程。前者的结果是并网,后者并没有改变多个网段。,但是它们在实现上是不是很相似呢?
  3. 但是具体到openvpn的bridging和routing呢?

    TAP benefits:

    • behaves like a real network adapter (except it is a virtual network adapter)
    • can transport any network protocols (IPv4, IPv6, Netalk, IPX, etc, etc)
    • Works in layer 2, meaning Ethernet frames are passed over the VPN tunnel
    • Can be used in bridges

    TAP drawbacks

    • causes much more broadcast overhead on the VPN tunnel
    • adds the overhead of Ethernet headers on all packets transported over the VPN tunnel
    • scales poorly
    • can not be used with Android or iOS devices

    TUN benefits:

    • A lower traffic overhead, transports only traffic which is destined for the VPN client
    • Transports only layer 3 IP packets

    TUN drawbacks:

    • Broadcast traffic is not normally transported
    • Can only transport IPv4 (OpenVPN 2.3 adds IPv6)
    • Cannot be used in bridges

  4. 这是示意图,我还需要理解它的意义,因为提到加密的包的问题:
    
                             +--------------------------------+
                             |            FIREWALL            |
                  (public IP)|                                |192.168.0.1
     {INTERNET}=============={eth1                        eth0}=============<internal network / 192.168.0.0/24>
                             |   \                        /   |
                             |    +----------------------+    |
                             |    | iptables and         |    |
                             |    | routing engine       |    |
                             |    +--+----------------+--+    |
                             |       |*1              |*2     |
                             |     (openvpn)-------{tun0}     |
                             |                    10.8.0.1    |
                             +--------------------------------+
    
       *1 Only encrypted traffic will pass here, over UDP or TCP and only to the remote OpenVPN client
       *2 The unencrypted traffic will pass here.  This is the exit/entry point for the VPN tunnel.
    
  5. 感觉还是摸不着头脑,因为并不是因为有了openvpn我需要额外做什么那么简单,我还是先从最基本的,而这里我才明白无线网卡是和有线网卡不同的做法,它无法使用bridging!

五月七日 等待变化等待机会

  1. 首先还是概念:什么是Access Point?
    A software access point, also called virtual router or virtual Wi-Fi, enables a computer to turn its wireless interface into a Wi-Fi access point. It saves the trouble of getting a separate wireless router.
    它的用意是什么呢?
    Creating a software AP is independent from your own network connection (Ethernet, wireless, ...). Many wireless devices even support simultaneous operation both as AP and as wireless "client" at the same time. Using that capability you can create a software AP acting as a "wireless repeater" for an existing network, using a single wireless device.
    所以,就是一个虚拟的wireless repeater
  2. 我依然有很多的疑问,就是所谓的为什么要创建两个虚拟设备。这里是步骤的目的:

    Setting up an access point consists of two main parts:

    1. Setting up the Wi-Fi link layer, so that wireless clients can associate to your computer's software access point and exchange IP packets with it.
    2. Setting up the network configuration on your computer, so that it properly relays IP packets between its own internet connection and the wireless clients.
    就是说我的程序就是所谓的wireless client,而如何绑定它和无线设备是第一步,第二步是所谓的包的控制。不过第一步的前提是创建AP,这个我先创建一个再说:
    
    $ sudo iw dev wlp109s0 interface add wlan0_ap type managed addr 12:34:56:78:ab:ce
    
    顺便安装了macchanger可以随机mac地址。
    
    $ sudo macchanger -r wlan0_ap
    Current MAC:   12:34:56:78:ab:ce (unknown)
    Permanent MAC: 12:34:56:78:ab:ce (unknown)
    New MAC:       a2:bb:c0:a7:9d:3b (unknown)
    
  3. 要配置AP作者建议使用hostapd,但是安装完后它是默认masked,我感觉还是先实验一下再固化为服务。也就是说我暂时不把
    You have first to configure it by creating the file /etc/hostapd/hostapd.conf and reference to it in /etc/default/hostapd with option DAEMON_CONF="/etc/hostapd/hostapd.conf".
    而是命令行直接运行试试看:
    
    $ sudo hostapd /etc/hostapd/hostapd.conf 
    wlan0_ap: interface state UNINITIALIZED->COUNTRY_UPDATE
    wlan0_ap: interface state COUNTRY_UPDATE->ENABLED
    wlan0_ap: AP-ENABLED 
    
    然后我可以看到它的确自己把设备加到桥里了
    
    $ brctl show
    bridge name	bridge id		STP enabled	interfaces
    docker0		8000.0242f17331bf	no		
    virtbr0		8000.0a3650428e89	no		wlan0_ap
    

五月九日 等待变化等待机会

  1. 我感觉debian的wiki解释的更加平易近人,毕竟archlinux的用户的水平相对比较高端。这里是libvirt的解释看上去很有用。这个libvirt是什么角色我还不清楚。但是看了它的主页介绍才开始理解一点它是一个通用虚拟环境的库,而阅读它的脚本我感觉这些应证了作者的一些说法。这个的确是不错的学习方法。因为有两个信源的资料才是比较靠谱的。这里也是一个很好的步骤。而且它提到了无线网卡的做法只有routed network或者是NAT-based network的做法。
  2. 网上说道的这个天才真的是名不虚传!普通人仅仅做到他的百分之一也许就被称作天才了,而他居然依然在不断的让人惊异!!!
  3. 我大概的对于有线网卡的桥有了进一步的认识,尽管很肤浅,但是总是进步。就是说我们创建一个tap0设备不用对它分配地址,把它和真正的物理有线 网卡都放在桥下,然后我们只需要对于桥来使用dhcp来反旆地址。那么这个时候我们启动虚拟机会自动获得网络链接吗?不完全,因为我们仅仅是在qemu里 指定了tap0设备,但是我们虚拟机里的模拟设备依然要使用dhcp来分配地址。但是我发现使用netplan来做要好的多,因为逼我手动运行 dhclient要好。困死了。
  4. traceroute使用-I或者-T会收到奇效,前者是ICMP,后者是TCP。原本有很多星星,现在没有了。
  5. 我现在的总结结论似乎是这样子的,就是物理设备或者虚拟设备可以自由的放在桥里面,但是起到奇效的依然是我的虚拟机必须要使用它的模拟物理网卡通过dhcp获得一个正常的ip。我总觉得哪里有问题。然后我又重复了之前的实验,就是说使用-netdev tap,ifname=tap0,...-netdev bridge,br=virtbr0,...本质上一种做法,就是说前者是你自己手动创建tap设备并且放在bridge里,而后者是所谓的bridge helper帮 你,但是它是需要root权限的,因为ioctl的操作,但是不管如何都有两个前提,就是物理网卡必须先放在桥里,另一个则是作为虚拟机的网卡你必须给他 分配合适的网址ip。而这个ip还必须是在物理网卡的同一个网段,当然这个是使用dhcp的结果,问题是我原本的理解是可以通过桥分配在不同的网段。看来 这里面还是需要某种手动配置才能做到。总而言之tap通过有线网卡做到了模拟一个物理的有线网卡,虚拟机在外界看来是同一个网段里的普通主机。这个是巩固 了我之前的概念,在进到下一步无线网卡的AP之前有必要再复习巩固一下基础。
  6. 我的概念有断片,首先要创建AP: 这里我先把无线网卡的mac地址获得。
    
    $ ip a s wlp109s0 | awk '$1=="link/ether" {print $2}'
    0c:9a:3c:69:8f:7f
    
    添加虚拟设备
    
    $ sudo iw dev wlp109s0 interface add wlan0_ap type managed addr 0c:9a:3c:69:8f:7f
    
    但是这个混合的途径会造成错误:
    
    $ sudo ifconfig wlan0_ap up
    SIOCSIFFLAGS: Name not unique on network
    
    所以,还是不要使用同样的mac地址的做法:
    
    $ sudo macchanger -r wlan0_ap
    
    但是我尝试的方法是完全风马牛不相及的目的!我感觉脑子一片混乱!笔记记得太乱了,可能要从头整理一下!!!
  7. 补习arp,这个我以前很少用,因为遇到网络问题都是绕着走:

    Arp

    Deprecated arp commands

    Replacement

    arp -a [host] or --all [host]
    .
    Shows the entries of the specified host name or IP address. If the [host] parameter is not used, all entries will be displayed.
    ip n (or ip neighbor), or ip n show
    arp -d [ip_addr] or --delete [ip_addr]
    .
    Removes the ARP cache entry for the specified host.
    ip n del [ip_addr] (this “invalidates” neighbor entries)
    .
    ip n f [ip_addr]
    (or ip n flush [ip_addr])
    arp -D or --use-device
    .
    Uses the hardware address associated with the specified interface.
    Not apparent
    arp -e
    .
    Shows the entries in default (Linux) style.
    Not apparent
    arp -f [filename] or --file [filename]
    .
    Similar to the -s option, only this time the address info is taken from the file that [filename] set up. If no [filename] is specified, /etc/ethers is used as default.
    Not apparent
    arp -H or --hw-type [type] or -t [type]
    .
    When setting or reading the ARP cache, this optional parameter tells arp which class of entries it should check for. The default value of this parameter is ether (i.e. hardware code 0x01 for IEEE 802.3 10Mbps Ethernet).
    Not apparent
    arp -i [int] or --device [int]
    .
    Selects an interface. When dumping the ARP cache only entries matching the specified interface will be printed. For example, arp -i eth0 -s 10.21.31.41 A321.ABCF.321A creates a static ARP entry associating IP address 10.21.31.41 with MAC address A321.ABCF.321A on eth0.
    ip n [add | chg | del | repl] dev [name]
    arp -n or --numeric
    .
    Shows IP addresses instead of trying to determine domain names.
    Not apparent
    arp -s [ip_addr] [hw_addr] or --set [ip_addr]
    .
    Manually creates a static ARP address mapping entry for host [ip_addr] with the hardware address set to [hw_addr].
    ip n add [ip_addr] lladdr [mac_address] dev [device] nud [nud_state] (see example below)
    arp -v
    .
    Uses verbose mode to provide more details.
    ip -s n (or ip -stats n)
  8. 重新来过,看看这个指导!核心部分就是iptables的设定
  9. 春明共享的数学书:
    1. Key Advanced Engineering Mathematics Kreyszig Textbook 2011
    2. Key Higher Engineering Mathematics Bird Textbook 2010
    3. Key Engineering Mathematics Bird Textbook 2017
    4. Key Advanced Engineering Mathematics Kreyszig Textbook 2011
    5. Higher Engineering Mathematics Bird Textbook 2006

五月十二日 等待变化等待机会

  1. 我对于iptables很不熟悉,以至于一直怀疑根本没有起作用,因为我没有给合适的参数:sudo iptables -nvL
  2. 找到一篇看似很严肃的设置bridged openVPN的教程,可是我目前对于所有的这类东西不管有多少没有亲自实践成功的几乎都是对于我来说是一堆垃圾。因为网络实在是变化太多了,有几乎无穷的问题与方案。

五月十六日 等待变化等待机会

  1. 这个是一个惊人的网站,我是无意中听人说起,然后看了其介绍,需要尝试一下。不敢下结论。这里有一个运行中文程序的小技巧
    
    LANG=zh_CN.utf8 wine HD_Launcher.exe
    
    感觉小年轻还处于尝试阶段,地图似乎还不成熟,我因为是英文版无法读中文的地图名字就想改地图名,这个需要.h3m的文件格式,这个时候才闹了一个关于 midnight commander的老笑话,因为英三的地图文件格式实际上是gzip,那么我直接使用dd去修改它的内部文件title是有问题的,导致mc直接拒绝打 开,因为我始终没有意识到它是识别格式的,对于破坏了gzip格式的文件它这个号称二进制编辑器居然很贴心的不给你打开,因为F3/View不起作用让我 以为程序配置有问题只好重新安装,这个才是笑话!
    
    $ printf 'Atlantic Revolutions 2.5' | dd of=AtlanticRevolutions conv=notrunc bs=1 seek=14 count=24
    
    我当时是要把偏移14的长度24的部分替换掉,这个关于文件格式的信息还是来自于VCMI的代码部分。 这个小朋友收集了不少的工具,我先记录一下,但是都要我不熟悉的云盘的登陆很麻烦。最后一个值得记录的就是使用他的免安装包默认的launcher运行是所谓的openGL有报错,我折腾了winetricks之类安装windows各种库,后来发现牺牲一点性能直接使用GDI模式就好了,我的游戏性能要求不高!
  2. 老生常谈的事情还有一箩筐,如何显示国内小朋友使用GB2312/GB180303编辑的文档呢?这真是一个漫长的话题,我自认为已经花了无数的时间已经理解了其中的沟沟坎坎,实际上我还是近乎和普通人一样对于unicode的认识粗浅到令人可笑!
    1. 第一步我先要把locale-gen的封印打开,因为locale能够支持的是在/etc/locale.gen决定了你想有什么语言可以创建。
    2. 第二步才是使用locale-gen来生成相对应的locale文件。

五月二十二日 等待变化等待机会

  1. 偶然看到头条上的关于uefi/bios的科普文章还是值得看看他的总结的。
    这个是bios-boot的流程 这个是uefi-boot的流程 而这个图是我比较喜欢的uefi的具体流程
    UEFI 引导模式 Legacy引导模式
    UEFI 提供了更好的用户界面Legacy引导模式是传统的且非常基本的
    使用 GPT 分区方案(这个是微软的自我决定,UEFI有所谓的Compatibility Support Module, 但是微软觉得问题太多就决定新版windows不支持,以至于科普小白自以为uefi本身就不支持,这个是典型的在使用windows环境下被灌输的结 果。当然从现象上看,如果使用了UEFI结果又仅仅是MBR分区能不能启动?开了CSM是可以的,可是这个几乎就是BIOS/MBR,根本看不出来你使用 的是UEFI,这个往往是测试一个盲点,我记得大概以前工作中就遇到Quanta的固件里有一些小问题就是这个相关的。只是根本没有人关心这个问题,直到 我自己的旧笔记本是这么设置的,而长期不用掉电恢复为默认的UEFI禁止CSM的时候不能识别我的MBR/ubuntu系统我才意识到这个问题!) 使用 MBR 分区方案其实GPT本来就是兼容保护MBR的,也就是说GPT不用第0个分区,MBR和GPT是可以共存的,这个也是 微软放弃支持MBR/BIOS的一个原因吧,这里是可以混淆的,你到底要首先检查MBR后使用CSM来引导吗?你如果发现第1个分区里的GPT表怎么办? 或者还是一定就是使用GPT搜索方式先找一下再回过头来看MBR,总而言之很麻烦。
    UEFI 提供更快的启动时间关于启动速度我感觉评论区很多人说的对,因为现代的UEFI的复杂界面及其应用部分并不见得快,这个也不是它的核心优势了。相比UEFI,它的速度较慢(的 确它的古老的POST (开机自检)是默认一定要执行的,我以前工作中对于这个还是有些惊奇为什么现代的计算机总要检查内存,这个让我想起了二十几年前我的第一份工作的上司给我 组装电脑任务时候命令我一定要检查内存条的事情,那个时代内存的可靠性是很差的,就如同硬盘的可靠性很差是一个道理,几乎一定有若干区块已经坏了,这个也 就能够说明为什么服务器的内存一定要使用昂贵的自纠错内存条的原因了) 相比UEFI,它的速度较慢
    由于 UEFI 使用 GPT 分区方案,因此它可以支持多达 9 zB 的存储设备 Legacy使用的 MBR 分区方案仅支持最多 2 TB 存储设备之前我一直没有重视这个问题的危害性也是我自己的旧笔记本没有这么大的硬盘的原因吧,现在就刻不容缓了,新的电脑一定是要GPT,那么你不使用UEFI能行吗? 这里的问题问的很好也是我想问的:32bit的寻址最多就支持2T是没有错的((2^32)-1 x 512 byte = 2 TB.),但是MBR支持四个分区难道不能再乘以四吗?,这里杠精首先的问题是partition entry明明是16byte为什么非要说才支持32bit的寻址呢?其实这个又是老生常谈,这个应该是传统的CHS的体制造成的,和32bit寻址有些微妙的联系:The range for sector is 1 through 63; the range for cylinder is 0 through 1023; the range for head is 0 through 255 inclusive.,结果就是64(Sector)x1024(Cylinder)x256(Head)=16777216一个人不暴露思想永远不知道自己其实真的不懂,因为我居然连CHS和LBA两种寻址方式也不懂!尽管仅仅是计算方式的不同,可是它实际上决定了很多磁盘分区大小的上限。比如有些分区类型如 果支持的仅仅是CHS的寻址方式,那么它的上限就是16M,而LBA是扩展了CHS,据说是支持48bit,可是在MBR里它只用了4byte,那么一个 所谓的sector的size不论是物理的还是模拟的都是512半个k,所以,(4G-1)*0.5K=2T,如果分区类型是只支持CHS的那么就更小 了。
    UEFI 以 32 位和 64 位运行,支持鼠标和触摸板 Legacy在仅支持键盘,仅 16 位模式下运行这里的16位模式运行我猜想是dos16位吧?我对于这个概念是很模糊的,粗浅的理解就是内存上限只有16bit寻址吧?
    它允许安全启动,防止加载未经授权的应用程序它还可能阻碍双启动,因为它将操作系统(OS)视为应用程序凡是和安全相关的困难度都是几何级数增长的,它的复杂性我简直不敢想象,如果我需要学习的话这个领域绝对是耗费大量时间和精力的,而且还不一定能明白,因为大多数这领域的问题都是语焉不详,能者不言,言者不能 它不提供允许加载未经授权的应用程序的安全启动方法,未限制双启动
    它具有更简单的更新过程 与UEFI相比,它更复杂刷BIOS吗?不过对于主板上的固件大概都是一样的困难吧?这里的所谓的更新是指的存储在EFI分区的 EFI程序方面的更新,和BIOS完全只能更新主板固件比较当然是相对的容易,因为我的理解就是UEFI是一套标准,需要各个厂商去实现相对应的接口以及 调用,等于是主板厂商和操作系统启动厂商的协同规范,如果主板不更新的前提下启动程序其实是可以玩不少的花样的。
    这个MBR的分区表结构很值得收藏,我怀疑我以前就拷贝过又忘了:

    Layout of one 16-byte partition entry (all multi-byte fields are little-endian)

    Offset
    (bytes)
    Field
    length
    Description
    0x00 1 byte Status or physical drive (bit 7 set is for active or bootable, old MBRs only accept 0x800x00 means inactive, and 0x01–0x7F stand for invalid)
    0x01 3 bytes CHS address of first absolute sector in partition.The format is described by three bytes, see the next three rows.
    0x01 1 byte
    h head
    x x x x x x x x
    0x02 1 byte
    c9–8 s5–0 sector in bits 5–0; bits 7–6 are high bits of cylinder
    x x x x x x x x
    0x03 1 byte
    c7–0 bits 7–0 of cylinder
    x x x x x x x x
    0x04 1 byte Partition type
    0x05 3 bytes CHS address of last absolute sector in partition. The format is described by 3 bytes, see the next 3 rows.
    0x05 1 byte
    h7–0 head
    x x x x x x x x
    >0x06 1 byte
    c9–8 s5–0 sector in bits 5–0; bits 7–6 are high bits of cylinder
    x x x x x x x x
    0x07 1 byte
    c7–0 bits 7–0 of cylinder
    x x x x x x x x
    0x08 4 bytes LBA of first absolute sector in the partition
    0x0C 4 bytes Number of sectors in partition
  2. 感觉温故而知新是绝对正确的,学而时习之也是正确的,前阶段学习了启动部分可以说是比较全面,但是毕竟还是有遗漏和粗浅的,总结一下也是很好的学习。今天暂时到这里吧,目前对于网络配置的攻坚还是不顺利,看来要退一步从更加基础的部分做起,这个部分绝对是复杂的。
  3. 我自以为用了十几年的ubuntu也算是有点资历了,结果发现还是像是一个普通的windows用户对于windows这样的复杂的操作系统一样的无知。因为讨厌自带的邮箱所以想要把多余的evolution-data-server彻底删除,在purge的时候没有看居然把gdm也要卸载,结果就是整个的桌面系统的卸载,这还包含了gnome-session之类的,所以,我看不到launchpad,system-tray等等。那么重新安装就是一个盲目的过程了,总而言之,没有那么容易的卸载这个。容易的做法是把它disable,而不删除文件的mask的方式不是权限而是用/dev/null作为软链接的做法。这个服务是在/usr/share/dbus-1/services下。 我一直以来对于这两个设备不是很清楚,其实非常的简单的问题而不动脑筋的人就是一直搞不明白:/dev/null是黑洞是一个写入的设备,而相反的/dev/zero是一个虚无是一个读取的设备。这个是我自己凭空想象的,正确的说法是这样子的:

    Yes, both accept and discard all input, but their output is not the same:

    • /dev/null produces no output.
    • /dev/zero produces a continuous stream of NULL (zero value) bytes.

    You can see the difference by executing cat /dev/null and cat /dev/zero.

    • Try cat /dev/null > file and you will find an empty file.

    • Now try cat /dev/zero > file, while watching the size of the file (watch -n 1 du -h file) continuously increase. This is because reading from /dev/zero gives an endless stream of \0 (null) characters.

    Use dd to visualize the difference more appropriately:

    $ dd if=/dev/null of=file count=10
    0+0 records in
    0+0 records out
    0 bytes (0 B) copied, 0.000276193 s, 0.0 kB/s
    
    $ dd if=/dev/zero of=file count=10
    10+0 records in
    10+0 records out
    5120 bytes (5.1 kB) copied, 0.00090775 s, 5.6 MB/s
    

    /dev/zero is used to create dummy files or swap.

    两者的功能绝对不是可以轻易替代的,Linux不养闲人,不是可有可无的!!!
  4. 无意中注意到我的ubuntu的垃圾箱里保存了所有的删除的文件,我想我还是不要晴空的好,并不是为了能够恢复,主要是SSD的存储特性决定了块删除是一个很耗损的操作不仅仅是时间更主要是寿命。等到磁盘空间耗尽再说吧?
  5. 我试图把HP的打印机的服务禁止掉,但是有一个system-tray的hplip服务总是自己启动,我发现一个/etc/xdg/autostart下有一个hplip-systray.desktop文集,不知道删除它是否可以禁止服务启动?

五月二十三日 等待变化等待机会

  1. 对于时不时的出现
    
    Broadcast message from root@nick-sager (Thu 2024-05-23 08:03:56 +08):
    
    Password entry required for 'Enter Auth Password:' (PID 14230).
    Please enter password with the systemd-tty-ask-password-agent tool.
    
    让我非常烦,结果就是我去试图解决这个问题,首先要明白这个systemd-tty-ask-password-agent是个什么东西

    Password Agents

    systemd 12 and newer support lightweight password agents which can be used to query the user for system-level passwords or passphrases. These are passphrases that are not related to a specific user, but to some kind of hardware or service. Right now this is used exclusively for encrypted harddisk passphrases but later on this is likely to be used to query passphrases of SSL certificates at Apache startup time as well. The basic idea is that a system component requesting a password entry can simply drop a simple .ini-style file into /run/systemd/ask-password which multiple different agents may watch via inotify(), and query the user as necessary. The answer is then sent back to the querier via an AF_UNIX/SOCK_DGRAM socket. Multiple agents might be running at the same time in which case they all should query the user and the agent which answers first wins. Right now systemd ships with the following passphrase agents:

    • A Plymouth agent used for querying passwords during boot-up
    • A console agent used in similar situations if Plymouth is not available
    • A GNOME agent which can be run as part of the normal user session which pops up a notification message and icon which when clicked receives the passphrase from the user. This is useful and necessary in case an encrypted system harddisk is plugged in when the machine is already up.
    • A wall(1) agent which sends wall messages as soon as a password shall be entered.
    • A simple tty agent which is built into "systemctl start" (and similar commands) and asks passwords to the user during manual startup of a service
    • A simple tty agent which can be run manually to respond to all queued passwords
    这一段简直就是天书,我看了云里雾里。这个是在谈设想还是现实?我需要的是解决结论而不是远景愿景!对于我来说找不到wiki的官方解释,只能追本朔源来分析这个是谁在询问,很明显它是openvpn的结果因为是我运行了openvpn的命令行而不是服务或者daemon,那么为什么我在命令行已经输入了auth/passwd,它还是询问呢?应该是systemd-ask-password就 是systemd在询问。在manpage里的解释大体上就是上面这段天书说的。总而言之,这个是systemd在询问,也许是ssl-cert也许是加 密磁盘,后者跟我没关系,只有plymouth之类启动才能遇到,openvpn的确有用到ssl-certificate,有没有密码保护我不知道,也 许有但是openvpn也许可以选择不回答那么systemd就使用wall服务来通知, 那么后台运行的服务肯定是无法使用frontend来采集用户输入的,这个纯粹就是我的想象,这个只是大体的概念理解。我开始对于天书有些明白而已。尽管 man page说的很清楚,但是人的理解是一个很复杂的过程,正如chatGPT每个字都给你解释了,其实它根本什么都不懂,说的车古录话如同新闻发言人的套话 官话。可是我还是明白了应该怎么做!核心是怎么解决systemd-tty-ask-password-agent抛出的信息,那么照方抓药也从它入手,要明白一个queued概念,它很可能是把之前的事情又翻出来反复问,所以,我就先--list然后看到有进程在问,接下来就是回答
    
    nick@nick-sager:/etc/openvpn$ systemd-tty-ask-password-agent --list
    'Enter Auth Username:' (PID 14429)
    nick@nick-sager:/etc/openvpn$ systemd-tty-ask-password-agent --query
    Not querying 'Enter Auth Username:' (PID 14429), lacking privileges.
    nick@nick-sager:/etc/openvpn$ sudo systemd-tty-ask-password-agent --query
    🔐 Enter Auth Username: nick
    nick@nick-sager:/etc/openvpn$ sudo systemd-tty-ask-password-agent --query
    🔐 Enter Auth Password: ******                  
    nick@nick-sager:/etc/openvpn$ sudo systemd-tty-ask-password-agent --list
    'Enter Auth Username:' (PID 14449)
    nick@nick-sager:/etc/openvpn$ sudo systemd-tty-ask-password-agent --query
    🔐 Enter Auth Username: nick
    nick@nick-sager:/etc/openvpn$ sudo systemd-tty-ask-password-agent --query
    🔐 Enter Auth Password: ******                  
    
    也就是说经过两轮的问答,这个agent就归于沉寂了,不吭声了,它满足了。其实有些貌似搞笑的帖子其实也能说明一些问题,至少他的观察是透露了其中的奥秘。为什么要失败一次才能成功?这里面的问题才是不知道的关键。但是我已经感到有些疲惫了,至少这么大清早的一罐冰啤酒让人懒得再追究了。问题解决了。
  2. 这么多年来我的网络知识始终停步不前的原因就是没有从最基础的学起。我很多年前买了一个十块美金的switch看到它提到store and forward到今天也不明白是什么意思。今天才开始学习 forwarding information base (FIB) also known as a forwarding table or MAC table,原来它就是MAC table吗?
    It is a dynamic table that maps MAC addresses to ports. It is the essential mechanism that separates network switches from Ethernet hubs. Content-addressable memory (CAM) is typically used to efficiently implement the FIB, thus it is sometimes called a CAM table.
    字数少才事情大。它不是switch,因为我们map的是ports,switch和hub的区别在哪里呢?而实现FIB依赖的是这个CAM table。重要的是理解。
  3. 补课补课,到底layer2是什么概念呢?我OSI可以背出来就如同chatGPT一样一个字都不理解!
    The data link layer, or layer 2, is the second layer of the seven-layer OSI model of computer networking. This layer is the protocol layer that transfers data between nodes on a network segment across the physical layer. The data link layer provides the functional and procedural means to transfer data between network entities and may also provide the means to detect and possibly correct errors that can occur in the physical layer.
    就是说link这一层的目的是在具体的物理设备之间传递数据包,这个机制还没有任何的网络协议的概念,所以,IP是在上面。有必要复习一下OSI的七层架构
    OSI model
    Layer Protocol data unit (PDU) Function[27]
    Host
    layers
    7 Application Data High-level protocols such as for resource sharing or remote file access, e.g. HTTP.
    6 Presentation Translation of data between a networking service and an application; including character encoding, data compression and encryption/decryption
    5 Session Managing communication sessions, i.e., continuous exchange of information in the form of multiple back-and-forth transmissions between two nodes
    4 Transport Segment, Datagram Reliable transmission of data segments between points on a network, including segmentation, acknowledgement and multiplexing
    Media
    layers
    3 Network Packet Structuring and managing a multi-node network, including addressing, routing and traffic control
    2 Data link Frame Transmission of data frames between two nodes connected by a physical layer
    1 Physical Bit, Symbol Transmission and reception of raw bit streams over a physical medium
    对于link layer,它是 node-to-node data transfer,我不知道的是它还有两个子层
    IEEE 802 divides the data link layer into two sublayers:
    • Medium access control (MAC) layer – responsible for controlling how devices in a network gain access to a medium and permission to transmit data.
    • Logical link control (LLC) layer – responsible for identifying and encapsulating network layer protocols, and controls error checking and frame synchronization.
    古老的PPP居然就是这个层面的,阅读这些相关的名词我很多都反复听到过,简直就像是在考古了。那么这种老古董真的没有意义吗?看看我所想要了解的tunnel其实不就是一种点到点的通讯手段吗?
    As there are only two endpoints on a tunnel, the tunnel is a point-to-point connection and PPP is a natural choice as a data link layer protocol between the virtual network interfaces. PPP can assign IP addresses to these virtual interfaces, and these IP addresses can be used, for example, to route between the networks on both sides of the tunnel.
  4. 有时候一个最最基本的概念我 也是需要补习的,也许我学过可是不留痕迹的完全没有印象。而作为非通讯专业的接触到这些所谓耳熟却不能详的名词是有些小激动的。我想说的是wiki被台湾 人的繁体中文所绑架,甚至很多时候是孔夫子式的笔删春秋任意取舍,这个式当前简体中文世界的悲哀!因为wiki上一说多语言的中文竟然就默认成了繁体台湾 话!
  5. 也许在链路层里tunnel是把物理上的网段整合在一个网络里而与之相反的VLAN却是把同一个网段认为的分割为隔绝的网段,它们是不是相反的做法?
    In a network utilizing broadcasts for service discovery, address assignment and resolution and other services, as the number of peers on a network grows, the frequency of broadcasts also increases. VLANs can help manage broadcast traffic by forming multiple broadcast domains.
    我觉得似乎核心是广播,因为它的定义就是
    A virtual local area network (VLAN) is any broadcast domain that is partitioned and isolated in a computer network at the data link layer (OSI layer 2).
    字数越少越重要。很多时候过于技术的细节让我有些读了前面忘了后面,最后能够有印象的就是一些考古轶事。比如我一直认为以太网的碰撞是限制它规模的根本原因,这个难道是古老的故事了吗?
    Early network designers often segmented physical LANs with the aim of reducing the size of the Ethernet collision domain—thus improving performance. When Ethernet switches made this a non-issue (because each switch port is a collision domain), attention turned to reducing the size of the data link layer broadcast domain.
    我以前一直认为过多的switch会让包走更远的道路所以更慢,但是现在看起来它分割了网络让一些昂贵的广播变得更快反而是加快了传播?当然这个也许只有一定条件才成立吧?看来这个确实是学校里的古董概念了,难道不能靠硬件解决算法问题吗?
    For Gigabit Ethernet and faster, no hubs or repeaters exist and all devices require full-duplex links.
    就好像在一个拥挤的大厅里大家开会,难道还是用嘴和耳朵来说和听吗?不能使用耳机麦克风吗?无线的问题肯定是比有线更复杂,这个你们两个互相听不见对方但是却能够干扰碰撞对方的问题似乎在有线环境下很难吧?总之,我很庆幸我不是学网络通信的,没有太多的烦恼。
  6. 今天的网络基础教程告一段落吧?

五月二十四日 等待变化等待机会

  1. 暴露一些基础概念的缺失是一个好办法,对于最最基本的TCP/IP这套架构自以为很熟悉,其实还是只其然不知其所以然。应该是潜意识里我想的还是OSI的link layer2,但是实际上大家几乎都是在应用开发的角度在谈论TCP/IP架构下的Link layer。也就是说今天很多时候一言以蔽之:不经过router都算是Link layer。
    The protocols of the link layer operate within the scope of the local network connection to which a host is attached. This regime is called the link in TCP/IP parlance and is the lowest component layer of the suite. The link includes all hosts accessible without traversing a router.
    所以,我现在模糊概念的东西都在这个范畴:virtual private network (VPN) tunneling protocol
    A virtual private network (VPN) is a mechanism for creating a secure connection between a computing device and a computer network, or between two networks, using an insecure communication medium such as the public Internet.
    这里的概念是从它的目的和结果来入手的,对于寻求实现细节的人帮助不大。这不由得让我联想到所谓的Universal Property,你可以使用事物的普遍的特性来定义任何事物,而不是以描述事物的具体特征来定义。为什么每次我都想到图灵实验这个奇怪的定义人工智能的方式呢?有时候我真想嘲讽一下当今的时代潮人为能智工人,也就是所谓的人工智能的逆序。也许将来可以用在某部小说里来作为一个角色的名字吧?
    In computer networks, a tunneling protocol is a communication protocol which allows for the movement of data from one network to another. It can, for example, allow private network communications to be sent across a public network (such as the Internet), or for one network protocol to be carried over an incompatible network, through a process called encapsulation.
    如果纯粹从目的和结果来看VPN和Tunneling Protocol究竟有多大区别呢?也许字面上前者是注重了安全性,后者注重的是异质的网络环境的穿越。
  2. 因为VPN是所谓i的link layer的技术它实际上非常的复杂,原因是它实际上涵盖了OSI好几层的工作,看看这个复杂的地下先遣图就明白它有多么的多样性:VPN classification tree based on the topology first, then on the technology used 看这个图目前还超过了我的能力,提示是先看拓扑再看技术实现路径,目前我还不能完全体会。所以,从分类的角度看问题,如果是从应用场景分类基本普通人都能理解,就是个人到组织,组织内部到组织内部,组织到组织。估计这个都是无用的废话。这个分类似乎更有意义些:

    VPN systems also may be classified by:

    • the tunneling protocol used to tunnel the traffic
    • the tunnel's termination point location, e.g., on the customer edge or network-provider edge
    • the type of topology of connections, such as site-to-site or network-to-network
    • the levels of security provided
    • the OSI layer they present to the connecting network, such as Layer 2 circuits or Layer 3 network connectivity
    • the number of simultaneous connections
    从这个分类似乎也可以揭示出与其说VPN是一项技术还不如说是依靠tunneling实现的一种应用,真正应该关心的是它依赖的tunneling的技术实现。所以,VPN=Tunneling Protocol+Secure Encryption
  3. 我决定重点学习Port ForwardingNAT机制,虽然几乎一直在使用但是实际却不明白它的原理。这个目标争取在本周内完成。似乎看了一个早上有一点点的概念了,就是从大到小是VPN->Tunnel Protocol->NAT->Port Forwarding。大概是这么一个从高到低的概念。
  4. 定义永远是最重要的,没有正确的概念即便知其然也不知其所以然。
    In computer networking, port forwarding or port mapping is an application of network address translation (NAT) that redirects a communication request from one address and port number combination to another while the packets are traversing a network gateway, such as a router or firewall. This technique is most commonly used to make services on a host residing on a protected or masqueraded (internal) network available to hosts on the opposite side of the gateway (external network), by remapping the destination IP address and port number of the communication to an internal host.
  5. 本来就头疼再去阅读令人头疼的文章就更加的头疼了,我还是从具体的实例开始吧。等我从健身房回来再读吧,心跳的太快了。

五月二十五日 等待变化等待机会

  1. 我的理解就是Port Forwarding实际上是NAT的一个实现机制,所以,看NAT更加的容易理解。而理解一个东西有时候从它的问题入手更加的直观。因为阅读这些概略的介绍是很费劲的往往不知道为什么要有这么多的技术,可是看到可能的问题才意识到它的存在的理由。看上去这些可以使用电话总机分机或者快递转运分包来理解,但是实现的过程实际上是很多的细节。总而言之一句话:
    Hosts behind NAT-enabled routers do NOT have end-to-end connectivity and cannot participate in some internet protocols. Services that require the initiation of TCP connections from the outside network, or that use stateless protocols such as those using UDP, can be disrupted.
    第二个情况stateless protocols为什么不可以让我觉得难以理解???其它的一些明显的问题是可以理解的,就是有些协议比如ftp的命令层面是另一路而且直接把传输地址写在了数据包里,这就无解了。总而言之,没有采取问题导向的方式,空学一些原理原则基本是无意义的,一定要实践才有所增益。
  2. 也许使用ssh来做实验是最容易的。我发现还是ubuntu的帮助比较浅显易懂。但是且慢,我感觉使用这些工具我不太熟悉也不直观,所以,我决定使用sshd来做实验。
    1. 首先,sshd是可以在-d的debug模式下运行的,但是要全路径。
    2. 其次它需要它的host-key,我就顺手把root的/etc/ssh/ssh_host_rsa_key拷贝了来运行。当然权限owner要改成自己。
    3. sshd大概是设计成daemon运行状态吧,所以,连key也要绝对路径运行:
      
      /$ usr/sbin/sshd -d -p 8001 -h /home/nick/workspace/arena/ssh_host_rsa_key
      
      所以,现在我们在8001侦听ssh连接了。
    然后我按照这个指示做了这个实验,虽然我还不明白它的结果到底是怎么样子的。
    1. 我首先建立一个使用dynamic port forwarding的ssh连接到我的debug模式下的sshd
      
      $ ssh -C -D 1080 -p 8001 localhost
      
    2. 然后在firefox里根据指示设定proxy,设定SOCKS服务器地址是本机127.0.0.1端口为1080
    3. 然后我在firefox里的浏览就都要经过我现在的sshd了。
    然而这一切的意思是什么我脑子还是一团浆糊。这里又使用了SOCKS作为代理:
    SOCKS is an Internet protocol that exchanges network packets between a client and server through a proxy server. SOCKS5 optionally provides authentication so only authorized users may access a server. Practically, a SOCKS server proxies TCP connections to an arbitrary IP address, and provides a means for UDP packets to be forwarded. A SOCKS server accepts incoming client connection on TCP port 1080, as defined in RFC 1928.
    就是说这个协议把所有的1080的请求都发送到它该去的地址,而我的ssh客户端建立了一个dynamic port forwarding侦听1080端口让它们全部走压缩和加密的协议,为了能够看到这一切我使用了一个我自己debug运行的sshd进程侦听端口为 8001,这样子我就不必改动很多配置?其实这一点我没做好,本来应该指定一个自己运行的sshd的配置文件。结果没有所以它依然是使用全局的配置,害得我还是得把sshd_config配置里的AllowTcpForwarding改成yes,这个肯定是有安全隐患的,尤其是我胡里胡涂做实验完了又忘了改什么了。总而言之,我可以在debug模式的sshd看到所有的log输出看到一些流程请求。我觉得这个和openvpn有某些相似之处。
  3. wiki不能访问,那么能不能自己留一个拷贝呢?
  4. 脑子开始混乱了,今天只能到这里了。

五月二十六日 等待变化等待机会

  1. 如果要确认我的sshd没有残留下侦听这个端口:
    
    $ sudo lsof -nP -iTCP -sTCP:LISTEN | grep 8001
    
    对于lsof的参数我总是记不住。
  2. 问一个复杂的问题实际上是一个相当有意义的小项目的设想,这个也是我经常感兴趣想尝试的。这位大侠给出了非常系统的思路,对于高级的在DNS record的做法我还不太敢于探索,因为亚马逊上这个方面我吃了很多苦头一无所获,主要是基础知识储备不足完全摸不着头脑。第二种却是我所希望尝试的。但是我发现他只给出了方向而没有具体的指导性,难怪是0分,以后看到0分的帖子就不必看了。
  3. 只能自己做实验来理解local port forwarding:
    1. 依样画葫芦就是先运行我自己的sshd进程以便更容易的观察:
      
      $ /usr/sbin/sshd -d -p 8001 -h /home/nick/workspace/arena/ssh_host_rsa_key
      
    2. 然后发起一个ssh客户端使用-L来要求local port forwarding,目标是把特定服务的80端口的请求转变为指定的8080端口的请求。
      
      $ ssh -L 8080:www.ubuntuforums.org:80 -p 8001 localhost
      
      这里-p 8001是要求我连接是我刚刚运行的sshd,这里我原本是明白的,只是不明白要怎么验证它是成功的。
    3. 要验证这个有效必须明白我现在已经不再使用服务器地址www.ubuntuforums.org而是要用本地的localhost来访问,因为我的sshd正在侦听8080端口,它收到以后会转往我要求的www.ubuntuforums.org:80。这个验证在浏览器地址里输入http://localhost:8080/是会转向目标www.ubuntuforums.org:80,而我们可以观察到sshd
      
      debug1: server_input_channel_open: ctype direct-tcpip rchan 2 win 2097152 max 32768
      debug1: server_request_direct_tcpip: originator 127.0.0.1 port 43576, target www.ubuntuforums.org port 80
      debug1: connect_next: host www.ubuntuforums.org ([185.125.188.17]:80) in progress, fd=9
      debug1: channel 2: new [direct-tcpip]
      debug1: server_input_channel_open: confirm direct-tcpip
      debug1: channel 2: connected to www.ubuntuforums.org port 80
      debug1: channel 2: free: direct-tcpip, nchannels 3
      
      这个手段可以作为很多小公司限制上网的方式等于在防火墙上凿了一个开口让你访问特定的网页。它不仅变换了原本访问的端口也在原来的不加密协议上包装ssh的加密通讯协议。这个是非常好的,以前只是别人告诉你一个命令这么做,今天实验终于加深了理解。很好。
    4. 有一点小小的意外就是我无法使用w3m从命令行来浏览http://localhost:8080,似乎它拒绝访问,也许是w3m自己的问题吧?
  4. 在我打算继续测试的时候遇到一个老问题,就是使用ssh-key验证有些问题,为了能够彻底的测试我需要从头来做一个可用的使用:
    1. 首先,我先自己重新生成测试的pub key
      
      $ ssh-keygen -t rsa -f rsa_arena
      
      -f rsa_arena生成的文件在本地,否则又要去.ssh了。产生了一对文件:rsa_arenarsa_arena.pub 这个都是经常做的。
    2. 我决定不再使用系统的hostkey,那么最简单的实际上是使用config文件而不是每一次都在命令行上使用-h因为还有其他的需要指定。拷贝一份/etc/ssh/sshd_config到本地,修改默认的
      
      HostKey /home/nick/workspace/arena/rsa_arena
      AuthorizedKeysFile /home/nick/workspace/arena/rsa_arena.pub
      
      这个很重要,这两个构成了private/public key的指定。
    3. 运行我们的sshd
      
      $ /usr/sbin/sshd -d -p 8001  -f /home/nick/workspace/arena/sshd_config
      
    4. 然后我们可以连接它
      
      $ ssh -p 8001 -i rsa_arena localhost
      
    5. 这个实验说明了什么呢?我经常没有概念在目标机器上需要默认的.ssh/AuthorizedKeysFile或者.ssh/AuthorizedKeysFile2。这个是我们允许的public key,这个概念我始终有些模糊。这个和目标机器自己的public key没有一毛钱的关系,我不知道我为什么老是模糊这一点。

五月二十七日 等待变化等待机会

  1. 现在可以来实验Remote Port Forwarding了: 可是我还是要先解决sshd的一些问题。
  2. 我看到我的aws的ec2运行的sshd的命令行是没有存储hostkey,就是说ec2的安全性要求hostkey必须是动态获得的:
    
    $ systemctl status --full sshd | grep /usr/sbin/sshd
        Process: 512076 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
                 └─512098 "sshd: /usr/sbin/sshd -D -o AuthorizedKeysCommand /usr/share/ec2-instance-connect/eic_run_authorized_keys %u %f -o AuthorizedKeysCommandUser ec2-instance-connect [listener] 0 of 10-100 startups"
    
    就是说sshd虽然拒绝没有hostkey运行,但是你可以使用所谓的AuthorizedKeysCommand来动态运行一个脚本来获得hostkey,这里的%u %f是在man sshd_config | grep %u可以看到是所谓的username,%f是The fingerprint of the key or certificate。当然了尝试运行这个/usr/share/ec2-instance-connect/eic_run_authorized_keys脚本是徒劳的,aws不可能轻易让你随便就获得这里的机密信息,这个key-pair是你自己在创建ec2的时候得到的,在ec2上是不保存的。
  3. 然后我终于找到了我需要的使用场景,就是为什么要remote port forwarding的原因,我之前总是想这个是不可能的。现在明白大家都是这么想要实现的。
    1. 第一步先在我的aws上开一个2000端口让它映射到我的本地的ssh的22端口
      
      $ ssh -R 2000:localhost:22 openvpnas@54.67.3.66
      
    2. 然后我就可以回到我的本地来了
      
      $ ssh -p 2000 nick@localhost
      
    3. 为了能够更加方便的回来省去passwd,我再生成ssh-keygen -t rsa,然后把publickey:id_rsa.pub拷贝到本机的.ssh/authorized_keys2,这样子就实现了回到本机的目的。
    4. 这里还有一个小插曲,就是大多数人都会犯的,至少我是多次犯这个低级错误,就是scp使用的是大写的P作为参数,而使用这个形式不行
      
      openvpnas@ip-172-31-35-59:~$ scp nick@localhost:2000/tmp/id_rsa /tmp/
      nick@localhost: Permission denied (publickey).
      
      再次阅读scp的manpage,人家说的清清楚楚:
      
           The source and target may be specified as a local pathname, a remote host with optional path in the form
           [user@]host:[path], or a URI in the form scp://[user@]host[:port][/path].  Local file names can be made explicit us‐
           ing absolute or relative pathnames to avoid scp treating file names containing ‘:’ as host specifiers.
      
           When copying between two remote hosts, if the URI format is used, a port cannot be specified on the target if the -R
           option is used.
      
      所以,你只能这样子拷贝:
      
      openvpnas@ip-172-31-35-59:~$ scp -P 2000 nick@localhost:/tmp/id_rsa /tmp/
      id_rsa                                                                                      100% 2610    17.8KB/s   00:00 
      
    所以,今天实验了remote port forwarding,它的作用也是惊人的,就是说和local类似的地方是你一旦设定了一个地址,也就是hostname+port的组合,那么在 local或者remote就有一个tcp socket或者unix socket在监听,一旦你的应用使用到这个地址它都无条件的被ssh的客户端使用ssh的加密协议转发到目标,这里如果是remote port forwarding,目标是本地,如果是local port forwarding,体现出来的好像是绕开了原先的port,彷佛在本地把port转移了一下而已。当然remote是很复杂的。至于dynamic port forwaring虽然我已经实验了,但是还是不太真正的理解。今天暂告一段落吧?

五月二十八日 等待变化等待机会

  1. 回到最初的实验,实际上我最需要的是dynamic port forwarding,它几乎可以替代我的openvpn服务。
    1. 现在我使用我的aws的ec2来作为我的SOCKS服务
      
      $ ssh -C -D 1080 openvpnas@54.67.3.66
      bind [::1]:1080: Cannot assign requested address
      Welcome to OpenVPN Access Server Appliance 2.12.3
      ...
      
      出现这个错误意味着什么呢?这位大侠很厉害因为他已经自己系统排除了很多的错误可能。然而看起来我的错误源头和他的还不一样,因为我的vpn上的1080是有权限的吧?所以我看不到它被使用了。我压根没有看完回答,最后殊涂同归,问题是一样的是IPv6的问题
      
      openvpnas@ip-172-31-35-59:~$ sudo netstat -lnp | grep 1080
      openvpnas@ip-172-31-35-59:~$ 
      
      这个说明什么呢?但是大侠提醒了我要设置我的aws/ec2的sshd_config允许PermitTunnel yes
    2. 终于找到了部分的原因,正像这位大侠说的这个是ipv6的问题,而这位大侠几乎天天都在做这样子的操作!
      
      $ ssh -v -4 -C -D 1080 openvpnas@54.67.3.66
      ...
      Authenticated to 54.67.3.66 ([54.67.3.66]:22) using "publickey".
      debug1: Local connections to LOCALHOST:1080 forwarded to remote address socks:0
      debug1: Local forwarding listening on 127.0.0.1 port 1080.
      debug1: channel 0: new [port listener]
      debug1: channel 1: new [client-session]
      ...
      
      所以,至少错误是IPv6造成的。
    3. 而这里也暴露了我的理解的偏差,到底这个Listening Port是开在local还是remote,明明ssh的log已经说了 Local forwarding listening on 127.0.0.1 port 1080.,那么我为什么还是在remote的openvpn服务器上检查?这个纯粹是白痴行为:在本地的确是在工作的
      
      $ sudo netstat -lnp | grep 1080
      [sudo] password for nick: 
      tcp        0      0 127.0.0.1:1080          0.0.0.0:*               LISTEN      214210/ssh  
      
      那么结果是怎么样子的呢?我要怎么验证这个forwarding是工作的呢?
    4. 依样画葫芦就是在firefox里的proxy里设置manual并且把SOCKS的地址设为127.0.0.1,然后我把我的openvpn连接停掉以确保是我的port forwarding在起作用。
  2. 更进一步的是让firefox的DNS请求也是通过这个proxy 打开about:config查找network.proxy.socks_remote_dns设为true。这个要怎么检验呢?我无意中的错误似乎证明了它是工作的。我在地址栏里打错了wiki的网址,结果在/var/log/auth.log里看到sshd的抱怨:
    
    May 28 17:01:13 ip-172-31-35-59 sshd[523600]: error: connect_to www.wikpedia.com: unknown host (Temporary failure in name resolution)
    
    firefox本身这个就是一个复杂的机制。难道DNS over HTTPS就是简单的设置就可以了吗?其实我最基本的问题就是它这个server的地址谁来解析呢?难道是hardcoded-ip吗?very unlikely吧?这里是所谓的TRR的定义Mozilla’s Trusted Recursive Resolver (TRR) 它是怎么工作的呢?这个例子说的很好:
    
    $ curl -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=www.google.com&type=A' | jq .
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100   193  100   193    0     0    367      0 --:--:-- --:--:-- --:--:--   366
    {
      "Status": 0,
      "TC": false,
      "RD": true,
      "RA": true,
      "AD": false,
      "CD": false,
      "Question": [
        {
          "name": "www.google.com",
          "type": 1
        }
      ],
      "Answer": [
        {
          "name": "www.google.com",
          "type": 1,
          "TTL": 126,
          "data": "172.217.12.100"
        }
      ]
    }
    
    所以,这个是一种所谓的dns-json形式,当然这个是接受的形式,传参数还是所谓的name和type,至少目前我知道。
  3. 摘抄一下firefox目前支持的dns服务:
    CIRA Privacy policy https://private.canadianshield.cira.ca/dns-query
    Cloudflare Privacy policy https://mozilla.cloudflare-dns.com/dns-query
    Comcast Privacy policy https://doh.xfinity.com/dns-query
    NextDNS Privacy policy https://firefox.dns.nextdns.io
    Shaw Privacy policy https://dns.shaw.ca/dns-query
  4. 但是这里就触及了GFW的核心机制,为什么谷歌的DNS服务器你可以ping的到,但是DNS query却被阻断了呢?
    
    $ ping 8.8.8.8
    PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
    64 bytes from 8.8.8.8: icmp_seq=1 ttl=55 time=35.3 ms
    64 bytes from 8.8.8.8: icmp_seq=2 ttl=55 time=35.4 ms
    ...
    
    然后你使用kdig就不行了呢?不要说kdig,就是普通的dig也是不行的:普通的dig是可以的 比如你问谷歌自己的dns.google.com的地址是可以爽快的得到答案: 结果反而是使用tls不行了
    
    $ kdig -d @8.8.8.8 +tls-ca +tls-host=dns.google.com example.com
    ;; DEBUG: Querying for owner(example.com.), class(1), type(1), server(8.8.8.8), port(853), protocol(TCP)
    ;; DEBUG: TLS, imported 140 system certificates
    ;; WARNING: connection timeout for 8.8.8.8@853(TCP)
    ;; ERROR: failed to query server 8.8.8.8@853(TCP)
    
    我的感觉似乎是某些协议被有选择性的阻断了?
  5. 今天至少是试验可以使用SOCKS来配合我的aws/ec2作为一个上网工具,当然这个仅仅是在firefox设置代理的情况下的网页浏览,对于 命令行以及应用程序依然是需要openvpn的完全解决方案。顺便记录一下,我现在把openvpn的帐号密码存储在文件里,这样子就不用每次都输入密码 帐号那么麻烦了:
    
    $ sudo openvpn --config /etc/openvpn/nick.conf  --auth-user-pass /etc/openvpn/auth.txt
    
    我发现要么是我的英文理解力有问题,要么我真的是白读计算机文档了,这个manpage我居然理解错了
    
    --auth-user-pass
                  Authenticate with server using username/password.
    
                  Valid syntaxes:
    
                     auth-user-pass
                     auth-user-pass up
    
                  If  up  is  present,  it  must be a file containing username/password on 2 lines. If the password line is missing, OpenVPN will
                  prompt for one.
    
                  If up is omitted, username/password will be prompted from the console.
    
    总之,我是老糊涂了。 现在我们可以看到kdig是可以使用加密查询的 看起来这里的参数+tls-ca要求先提供对方的证书来验证网站的可靠性;; DEBUG: TLS, The certificate is trusted.,然后才是普通的dig的查询其实dig也是接受同样的参数做同样的证书验证,只不过kdig似乎输出更漂亮一些吧?
  6. 在休息间隙学习毛泽东选集的这篇伟大的文章中国革命战争的战略问题(一九三六年十二月)

五月三十日 等待变化等待机会

  1. 我想实践一下ssh JumpProxy,虽然这个看似很简单,但是我却没有合适的平台来实验。于是我只好自己运行一个sshd简单的检验一下,结果遇到一个小问题就是我本来 想运行一个特别端口的sshd,那么远程的就也成了使用特别端口的,看了manpage才意识到参数一般是使用在目标上的,对于ProxyJump一般大 家是使用ssh_config来描述的。所以,你只能使用-o,如果不想使用单独的配置文件-F的话。
    
    $ /usr/sbin/sshd -d -f /home/nick/workspace/arena/sshd_config -p 8001
    
    这个是最简洁的做法
    
    $ ssh -o ProxyJump=nick@127.0.0.1:8001 openvpnas@54.67.3.66
    
  2. 然后我尝试一直有问题的X11-forwarding,结果我完全理解错误,为什么要设置X11UseLocalhost no而不是yes呢?

五月三十一日 等待变化等待机会

  1. 有一个观念没有建立起来,就是说xclock可以检验X11Forwarding机制是否正常工作,换言之,当我发现firefox启动出现X11 connection rejected because of wrong authentication.错误时候而运行xclock正常就意味着这个很可能是firefox自身的问题,至少和X11 forwarding无关。这里虽然不解决问题但 是他给出的思路确实很好。首先有一些不了解的firefox的机制,比如他提到firefox试图检查本地的running instance,这些我还是听不太懂,而且也没有验证,但是基本上是可能的,因为其他的应用没有问题可以运行,说明了这个是纯粹的firefox的某些 问题,然后我发现了这个可以工作。就是说不知道什么原因环境变量$XAUTHORITY没有设置成默认的$HOME/.Xauthority,所以,我在我的$HOME/.bashrc里加上了export XAUTHORITY=$HOME/.Xauthority。问题是解决了。
  2. 有了一定的实践经验再来读一下这个解释
  3. 我制备DVD电影的流程是
    1. 首先备份DVD
      
      $ dvdbackup -M -p -o .
      
    2. 探测有多少title
      
      $ HandBrakeCLI -e x264 -E av_aac -i Dvd/VIDEO_TS/ -Z "Super HQ 720p30 Surround" -s 1 --subtitle-burned 1 --subtitle-default 1 -t 0
      
    3. 根据title多少循环
      
      $ for title in {1..6}; do HandBrakeCLI -e x264 -E av_aac -i Dvd/VIDEO_TS/ -Z "Super HQ 720p30 Surround" -s 1 --subtitle-burned 1 --subtitle-default 1 -t ${title} -o video_title_${title}.mp4; done
      

六月三日 等待变化等待机会

  1. 还是要问题导向解决实际问题入手。所以,我决定看看openvpn的文档,这里的前提的前提是明白我现在看到的是所谓的OpenVPN 3 Linux。它是几个openvpn下的项目。这里的一个核心前提是DBus系统,现在是补课的时候了。
  2. 什么是DBus呢?但是学习这个全景之前还要再补课。
  3. 所以,这个才是适合我的零基础的入门课
    D-Bus is first and foremost an Inter Process Communication (IPC) solution. But it does a bit more than just passing a blob of information from one process to another one. D-Bus is object oriented, where each object can have a set of Methods, Properties and Signals. All the data passed through D-Bus is also required to use the data types provided by D-Bus. This enforces a fairly strict and predictable type checking, which means the application implementing D-Bus will not need to care about parsing message protocols.
    这里说的很清楚了,就是一个IPC,只不过是更加的object-oriented。因为有了严格的类型因而成为一种类似于RPC的方式来进行通讯。
    The D-Bus design is built around a server/client approach, where the server side is most commonly referred to as a D-Bus service. And the client side uses, what is often called, a proxy to do operations on D-Bus objects provided by a specific D-Bus service. Operations can be calling methods or reading or writing object properties. D-Bus object are referenced through D-Bus paths. And a D-Bus service is accessed through a destination reference, which is a bus name and a bus type indicator.
    这里始终让我不理解的是所谓的path,到底这个用途是什么呢?是所谓的存储的路径吗?有点数据存储的概念吧?

六月七日 等待变化等待机会

  1. 我决定从我本机安装的openvpn的相关包来入手:
    
    $ aptitude search openvpn
    p   golang-github-apparentlymart-go-openvpn-mgmt-dev                - Go client library for OpenVPN's management protocol                       
    i A network-manager-openvpn                                         - network management framework (OpenVPN plugin core)                        
    i   network-manager-openvpn-gnome                                   - network management framework (OpenVPN plugin GNOME GUI)                   
    i   openvpn                                                         - virtual private network daemon                                            
    p   openvpn-auth-ldap                                               - OpenVPN LDAP authentication module                                        
    p   openvpn-auth-radius                                             - OpenVPN RADIUS authentication module                                      
    i   openvpn-systemd-resolved                                        - integrates OpenVPN with systemd-resolved
    
  2. 首先,openvpn-systemd-resolved究竟有什么玄机呢?令我意外的是这个基本就是空的,唯一的配置文件/etc/openvpn/update-systemd-resolved透露了一点安装的DBus的接口信息:
    
    DBUS_DEST="org.freedesktop.resolve1"
    DBUS_NODE="/org/freedesktop/resolve1"
    
  3. 透过这个线索我们可以探寻一下openvpn定义的接口
    
    $ gdbus introspect --system --dest org.freedesktop.resolve1 --object-path /org/freedesktop/resolve1
    
  4. 但是这里最大的收获是知晓了这个更加强大的工具busctl,看起来它比gdbus似乎更加的强大一些。阅读它的文档我们可以看到这个很有用的例子
    
    $ busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Environment
    as 2 "LANG=en_US.UTF-8" "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
    
    busctl list给我展示了很多没有接触过的服务。我不知道它们是不是服务,但是看起来似乎是吧?而运用status命 令可以查询到这些服务的命令行程序名,这个是一个非常有用的方法,我在猜想是不是systemctl使用的也是一个DBus的机制呢?因为在我看来似乎它 是一个更加基本的东西,不过也许我只是瞎猜,说不定是。。。反过来的?因为每一个DBus似乎后面对应着一个systemd的服务?
    
    ...
    fi.w1.wpa_supplicant1                 1295 wpa_supplicant  root             :1.5          wpa_supplicant.service        -       -          
    io.netplan.Netplan                       - -               -                (activatable) -                             -       -          
    net.hadess.PowerProfiles              1242 power-profiles- root             :1.13         power-profiles-daemon.service -       -          
    net.hadess.SwitcherooControl          1257 switcheroo-cont root             :1.14         switcheroo-control.service    -       - 
    ...
    
    就是说这些DBus的可执行程序很多都是在/usr/libexec下面的,似乎都是系统级的可执行程序不是给用户使用的。
  5. 突然发现我无法使用lld,然后才发现它指向了llvm的lld,这个是怎么回事呢?就是说lld就像它自己说的是一个所谓的generic driver,它取决于背后的实际运作者,这个是由编译器链接器直接掌控的。我真的是老糊涂了,我想的是ldd,为什么要去折腾lld,它实际上是真正的linker。 在DBus里一个叫做org.gnome.DisplayManager的,实际上却是一个服务:gdm.service,最后发现它实际上就是平时使用的截屏工具gdm-screenshot,但是它默认是只能系统级别的权限写在/run/gdm3/greeter/GDM-Screenshot.png,要如何绕过安全机制让普通用户使用呢?
  6. gdm的学习曲线也是陡峭的因为我对于桌面系统也是一窍不通。而且看到这种配置文件必须留空格的要求让我顿时无语了。我发现还是wiki比较懂的我是一窍不通。

    GNOME Display Manager (GDM) is a display manager (a graphical login manager) for the windowing systems X11 and Wayland.

    The X Window System by default uses the XDM display manager. However, resolving XDM configuration issues typically involves editing a configuration file. GDM allows users to customize or troubleshoot settings without having to resort to a command line. Users can pick their session type on a per-login basis.

  7. 另一个我长久以来就不理解的是PAM
    A pluggable authentication module (PAM) is a mechanism to integrate multiple low-level authentication schemes into a high-level application programming interface (API). PAM allows programs that rely on authentication to be written independently of the underlying authentication scheme.
    但是我的印象是这个并没有标准化,似乎也是一个比较混乱的状态,似乎各家都是有自己一套???我对于此很是疑惑,所有关于安全的都是一个大大的黑洞。
  8. 我连我自己使用的是KDE还是Gnome都分不清!一早上我是胡里胡涂因为很多从未接触的东西困扰着我。

六月八日 等待变化等待机会

  1. 首先安装必要的环境
    
    $ sudo apt install -y php php-intl php-mbstring php-xml php-apcu php-curl php-sqlite3 composer
    
    然后
  2. 然后克隆脚本 或者就是直接下载脚本
  3. 运行这个我不明白的脚本:composer update 再安装sqlcomposer mw-install:sqlite然后运行composer serve
  4. 这里说直接使用安装脚本:http://localhost:4000/mw-config
  5. 下载dumps我看来是搞错了,比如我们只需要这种镜像下的某个最新日期的enwiki-20240501-pages-articles.xml.bz2文件,当然其中的日期取决于你下载的目录日期。
  6. 然后把bz2文件解压缩:$ bzip2 -d enwiki-20240501-pages-articles.xml.bz2
  7. 现在就可以import了:php maintenance/run.php importDump.php enwiki-20240501-pages-articles.xml
  8. 遇到了这个错误,我一方面怀疑是文件句柄数限制,另一方面也可能是安装不完整。在进程监视器里我看到磁盘读就超过了100G而且还在增加。估计写磁盘也不会少吧?
  9. 我不知道为什么我会遇到ulimit的sudo执行报错说是不认识这个命令,最后我发现ulimit是一个用户级的命令,我为什么要sudo,可 能是以前工作中的redhat使用root习惯了?这里的soft/hard我也搞不清楚是否需要设置,总之,我根本不需要重启就可以设置soft,也许 是我把hard设的很高的缘故吧?这里有好几个文件需要设置,我已经烦死了。似乎不是文件的句柄数字的问题。我发现在LocalSettings.php里的$wgEnableUploads = false;这个是不是让人很尴尬?不过看起来的确是文件句柄的问题,我在运行之前先给这个shell设置了ulimit -n 4096,然后运行至少超过了之前的进度。

六月九日 等待变化等待机会

  1. 这里透露了一个事实就是说大概wiki有600万pages,这个对于我来说就是一个天文数字,我已经运行了一昼夜了还不到50万page,那么岂不是要运行十几天啊?这里有一个并行的方式,我准备实验一下。
    
    $ php maintenance/run.php importDump.php --skip-to 3000000 ../wikipedia/enwiki-20240501-pages-articles.xml
    
    我发现内存使用也许是一个速度的大敌,重新启动速度快了一百倍。
  2. 还有一个选项是这个kiwix,不仅仅是可以很容易的离线浏览,而且它分门别类的可以下载专项。这个功能是在open library里。或者这里也可以直接下载。
  3. 几乎是没有遇到过的问题,就是说ubuntu的官方的zim工具和库的版本和开发者版差别只有一点却造成了文件不能正确读的问题,我现在还是很佩服自己的,因为我感觉这个就是版本的问题。我下载的kiwix的zim文件是5.0版的,看起来有一些格式的调整,所以,我从源代码编译libzimzim-tools然后就可以使用zimdump来把kiwix的zim变成html文件了。
  4. 就是说我最后是这样子dump的
    
    $ zimdump dump ./Wikipedia_en_computer_maxi_2024-05.zim --dir=wiki
    
    然后上传到S3我也懒得折腾了:
    
    $ s3cmd put --recursive --acl-public --progress -v -M --default-mime-type="text/html" wiki s3://www.staroceans.org/
    
  5. 对于ACL我始终不是很清楚:
    AWS CLI S3cmd Description
    --public-read --acl-public Making an object publicly accessible
    --private --acl-private Making an object private
    --grant-full-control --acl-grant=full-control Granting full control over the bucket
    --grant-read --acl-grant=read Allowing the listing of objects in the bucket
    --grant-read-acp --acl-grant=read_acp Allowing the reading of ACLs
    --grant-write --acl-grant=write Allowing recording, overwriting, and deleting of objects
    但是我知道这个--acl-grant是旧的,因为新版的是
    
    --acl-grant=PERMISSION:EMAIL or USER_CANONICAL_ID
                  Grant  stated  permission  to  a  given amazon user.  Permission is one of: read, write, read_acp, write_acp,
                  full_control, all
    
    不过我实在是太累了,不想折腾这个了。
  6. 下载了一些拿破仑的电影,需要字幕,但是烧srt字幕的时候遇到编码的问题。那么中文GB2312编码要怎么写呢?这里的这个表是特别针对ffmpeg的编码名词。但是问题是这还不够因为ffmpeg的库必须要支持iconv!TNND还要编译源代码?我检查我的笔记才发现我的确以前是从源码编译的,我一点也想不起来了!
  7. 我现在一定要把每一个细节都记录下来,否则我的记忆力实在是太差了!
    
    $ git fetch --all --tags --prune
    $ git checkout tags/n7.0 -b nick-n7.0
    
    然后按照这里的步骤。我估计7.0的dependency不应该有新的,可是iconv的开发包应该是需要的根本没有开发包一说,使用的是就是运行库吧?只需要增加--enable-iconv
    
    $ PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure   --prefix="$HOME/ffmpeg_build"   --pkg-config-flags="--static"   --extra-cflags="-I$HOME/ffmpeg_build/include"   --extra-ldflags="-L$HOME/ffmpeg_build/lib"   --extra-libs="-lpthread -lm"   --ld="g++"   --bindir="$HOME/bin"   --enable-gpl   --enable-gnutls   --enable-libaom   --enable-libass   --enable-libfdk-aac   --enable-libfreetype   --enable-libmp3lame   --enable-libopus   --enable-libsvtav1   --enable-libdav1d   --enable-libvorbis   --enable-libvpx   --enable-libx264   --enable-libx265   --enable-nonfree --enable-iconv && PATH="$HOME/bin:$PATH" make
    
    但是我还是不能直接把字幕烧在视频上,第一步先嵌入吧:
    
    $ ffmpeg -i La.Révolution.française.1989.FR.DVDRIP.XVID.Part.1.avi -sub_charenc GB2312 -i La.Révolution.française.1989.FR.DVDRIP.XVID.Part.1.srt -map 0:v -map 0:a -c copy -c:s:0 mov_text -metadata:s:s:0 language=fre ~/DS918Video/Napoleon/La.Révolution.française.1989.FR.DVDRIP.XVID.Part.1.mp4
    
    似乎不行,试一试这个做法
    
    $ ffmpeg -i La.Révolution.française.1989.FR.DVDRIP.XVID.Part.1.avi -sub_charenc GB2312 -i La.Révolution.française.1989.FR.DVDRIP.XVID.Part.1.srt -c:s mov_text -metadata:s:s:0 language=fre ~/DS918Video/Napoleon/La.Révolution.française.1989.FR.DVDRIP.XVID.Part.1.mp4
    
  8. 我要把拿破仑的电影的字幕烧在屏幕上,结果犯了一个小错误,因为subtitle的index是从0开始的。
    
    $ ffmpeg -i '拿破仑1:战争的号角.mkv' -vf "subtitles='拿破仑1:战争的号角.mkv':si=0" '拿破仑1:战争的号角.mp4'
    
  9. 这个帖子解释了如何整理语音流的顺序以及设定默认语音。

    The ordering of the audio streams can be accomplished using FFmpeg's map and disposition options. Best syntax for this is arguably:

    ffmpeg -i input.mkv -map 0:v:0 \
           -map 0:a:2 -map 0:a:0 -map 0:a:1 -map 0:a:3 \
           -map 0:s -c copy \
           -disposition:a:0 default \
           reordered.mkv
    

    To unpack this a little:

    • -map 0:v:0: The first (and only) video stream is selected
    • -map 0:a:1 -map 0:a:0 -map 0:a:2 -map 0:a:3: The audio streams are individually placed. The final digit of each 'set' selects from the 4 audio streams with '0' being the first stream and '3' being the final audio stream. English is of course specified first and is stream 2.
    • -map 0:s: Select all of the subtitle files
    • -c copy: Copy the video, audio and subtitle streams with no re-encoding.
    • -disposition:a:0 default: This sets our required audio stream (English) as the default. Useful if this has been set on another, input audio stream.

    This worked well on a test file produced on my system and should work well on yours as well...

    Notes...

    这个帖子的另一个重要价值在于指出了一个非常棒的关于map的指引!当然这个官方的说明还 是权威的。我感觉如果我不摘抄原文的话我有可能反反复复的引用同一个文档。阅读这个说明本来应当是很快乐的事情,可是因为我似乎满足于最基本的需求就没有 动力了。不过我还是学到了一星半点,就是字幕是分所谓的text-based和image-based,前者是mkv的机制,后者的一个代表是dvdsub。而音频里的5.1channel确实是很多个channel,应该是5+1=6吧。这些都是基本的常识,不过很有用。

六月十四日 等待变化等待机会

  1. 需要把pdf转为word,这个方法似乎不错。期间我遇到libreoffice报出的警告:Warning: failed to launch javaldx - java may not function correctly,这位大侠指出了解决的办法。
    
    $ sudo apt-get install libreoffice-java-common default-jre
    
    看来是我的jre运行环境就没有装好。
  2. 我重新审视下载源发现这里的合集似乎涵盖了所有的计算机的文章。
  3. 这里是总结:
    1. Pleas note: These are two different big files even though only the starting character is different! I was fooled at first sight!
      
      $ wget https://download.kiwix.org/zim/wikipedia/wikipedia_en_computer_maxi_2024-05.zim
      $ wget https://download.kiwix.org/zim/wikipedia/Wikipedia_en_computer_maxi_2024-05.zim
      
      千万注意,这里两个文件名仅仅是开头字母不同,不要被骗了,我就是一晃眼以为是同样的下载而取消了!
    2. 下载libzim源代码
      
      $ git clone --recursive https://github.com/openzim/libzim.git
      
      编译安装
      
      meson . build
      ninja -C build
      ninja -C build install
      
    3. 下载zim-tools源代码
      
      $ git clone --recursive https://github.com/openzim/zim-tools
      
      编译安装
      
      meson . build
      ninja -C build
      ninja -C build install
      
    4. 现在我们要把两个zim文件合并到一个wiki文件夹下
      
      $ mkdir wiki
      $ zimdump dump --dir=wiki wikipedia_en_computer_maxi_2024-05.zim
      $ zimdump dump --dir=wiki Wikipedia_en_computer_maxi_2024-05.zim
      
      注意这里是两个文件
    5. 把它们都上传到网上,这里我分成两步是因为我想强迫给A目录下这些没有后缀名的文件设定为mime-type为"text/html"
      
      $ s3cmd sync --check-md5 --acl-public --progress -v --recursive --mime-type="text/html" wiki/A s3://www.staroceans.org/wiki/
      
      然后上传所有的图片这里需要猜测mime-type所以要使用-M
      
      $ s3cmd sync --check-md5 --acl-public --progress -v --recursive -M wiki/I s3://www.staroceans.org/wiki/
      
  4. 有时候你认为是理所当然的常识,却产生了怀疑,因为url-safe-charactor的确是只有这么几个
    ALPHA DIGIT "-" / "." / "_" / "~"
    可是我还是第一次听说domain name的encoding,因为中文域名并不是不存在啊!我实在是懒得看了。
  5. 终于把所有引用的wiki的链接换成了本地的,当然只是限于计算机的部分。

六月十五日 等待变化等待机会

  1. 这个才是困扰我一晚上的问题的核心:这两个链接是怎么做到并存的?
    
    C++语言是这个链接:https://en.wikipedia.org/wiki/C%2B%2B
    而C++/CLI是这个链接: c++语言是这个链接:https://en.wikipedia.org/wiki/C%2B%2B/CLI
    
    这个看起来没有什么可是在zimdump的目录里要怎么同时存在呢?C++即是一个html文件又要是一个文件夹? 提了bug才发现是老早就有的问题,很难解决。

六月十七日 等待变化等待机会

  1. 昨日行山遭雨泼,偶感风寒得小令一首:
    《喜迁莺--六月天》
    
    六月天,雨水多,
    行山遭雨泼。
    躲雨进寺不拜佛,
    只为观新荷。
    
    雨才霁,前院转,
    红墙黄瓦乍现。
    石板积水映蓝天,
    暗喜偷得闲。
    
  2. 感觉已经有思路解决问题了,随后再验证一下思路。但是一行代码看了我半天:
    
      for (auto& entry:m_archive.iterEfficient()) {
    
    我对于新版的c++的新语法就失去敏感性的关系,一直往循环的思路去想,其实这个就是对于语法不够明确的问题,这个不是传统意义上的for-loop,我记得当年我研究过这个实现模式,这个c++insights就给出了绝好的解释,它就是range的一个表达方式。也就是说作为一个循环变量entry它面对的是一个range,而注意所谓的range实际上一个concept,实际上就是说它要实现一个模板元计算的检验只要支持有实现begin/end两个方法就返回真而已。 那么这里的for是怎么实现的呢?是否一定要在编译器的最原始的部分实现呢?我记得这个似乎是在stdlib之类的层面实现的,也许吧?记忆就是这么无情的抛弃我了。
  3. 另一个让我感到非常震撼的是这个非常棒的函数功能,docopt。也就是说可以迅速的实现令人烦恼的函数参数功能。
  4. 另一个让我感到烦人的是s3cmd报出的一个警告信息:
    
    /usr/local/bin/s3cmd:4: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
      __import__('pkg_resources').run_script('s3cmd==2.4.0', 's3cmd')
    
    最容易的解决办法是锁定这个工具的版本:
    
    $ pip install setuptools==66.1.1
    

六月十八日 等待变化等待机会

  1. 做了一个patch来解决这个zimdump文件名和文件夹的冲突的问题。我觉得是比较完满的解决方案,虽然不是万能的,但是太长的文件名在哪里都是问题。
  2. 第一次创建PR。原本我一直不明白pull request是干什么的。这个也是一个复杂的过程。
    1. 首先你不能再使用密码提交。要所谓的personal access token
    2. 然后是在对方的repo基础上去fork一个,否则你没有写对方repo的权限怎么提交呢?
    3. 在你自己的fork开发提交完了之后你才能创建一个PR,实际上就是把自己的repo和对方的diff来作为一个review-approval的机制。这里在PullRequest里要选择compare across fork。
    4. 然后就是等待对方的讨论批准了。这个流程大体上都差不多。
  3. 老爸的词
    《喜迁莺--五月天》
    
    五月天,
    梅子甜。
    昼长夜渐短。
    乍晴乍雨连不完,
    湿热苦难堪。
    
    麦子黄,
    收割忙。
    机器不用人帮。
    往昔夏收齐下乡,
    人和天争粮。
    
    这是老爸的四句帖子
    深思高举洁白清忠,
    汨罗江上万古悲风。
    龙舟竞渡颂扬端午,
    纪念屈原爱国忠贞。
    

六月十九日 等待变化等待机会

  1. 行步道被蜂蜇得小令一首:
    《喜迁莺--走步道》
    
    走步道,不骑车。
    路上被蜂蛰。
    路过禅寺不拜佛。
    汗透青衫薄。
    
    触天怒,惹人怨。
    美帝嚣张好战。
    中华气定若等闲。
    盛世享天年。
    
  2. 又创建了一个PR,测试是否软链接之前已经创建过了。

六月二十日 等待变化等待机会

  1. 心血来潮的我突然对于AT&T的拆分感到好奇
    On January 1, 1984, these companies were NYNEX, Pacific Telesis, Ameritech, Bell Atlantic, Southwestern Bell Corporation, BellSouth, and US West.
    • NYNEX, merged with Bell Atlantic in 1996, now part of Verizon Communications
    • Pacific Telesis, acquired by SBC in 1997, now part of AT&T Inc.
    • Ameritech, acquired by SBC in 1999, now part of AT&T Inc.
    • Bell Atlantic, merged with GTE in 2000 to form Verizon Communications
    • Southwestern Bell Corporation, rebranded as SBC Communications in 1995, acquired AT&T Corporation in 2005
    • BellSouth, acquired by AT&T Inc. in 2006
    • US West, acquired by Qwest in 2000, which in turn was acquired by CenturyLink in 2011

六月二十一日 等待变化等待机会

  1. 非常混乱的一个晚上,首先是我的PR被打回来了,因为这个是早已尝试走 不通的路,很简单的道理我都没有想到。其次,我发现zimcheck可以帮助探究很多应该要提前解决的问题。更主要的是我折腾了一个晚上才明白 firefox的一些安全url的道理。就是说在地址栏里的文件名字的确是要安全的,而且刚刚才发现在某些目录下的文件是不被打开的,比如/tmp,我怀 疑这个是PAM之类的设定。太多线索,先记录下来一下。否则又忘了。
  2. 我折腾了一个晚上在url escape上。比如wiki/A/C++%2FCLI是一个html文件那么有一个它的html-redirect文件wiki/A/C++%2FCLR的内容是这样子的: 这个能不能行呢?比如这个命令可以吗?
    
    $ firefox wiki/A/C++2FCLR
    
    至少我目前的firefox拒绝打开,也许有某只设置阻止了这种不安全的打开方式吧?你需要安全的路径名
    
    $ firefox wiki/A/C%2B%2B%252FCLR
    
    这里的%2B%2B是安全字符++,而这个%25是针对原来的字符%
  3. 关于readLink结果的长度几乎是无限的。所以,最好的做法还是使用std::filesystem::read_link,它有一个无异常的版本更容易使用一些。这个PR是仅仅改进一下出错的情况而已。

六月二十三日 等待变化等待机会

  1. wiki的kiwix的dump无法完美的解决问题,理论上似乎是不可避免的要遗漏一些,这个是我的不完满的解决方案,告一段落吧:
    1. wikipedia关于计算机的文章它的入口在这里
    2. wikipedia关于计算机的文章它的入口在这里
    3. 这个是所有的wiki的zim文件,它的入口在这里
    4. 要让我爱上Ninja也 真的是难啊,我从来没有见过能够直接把一个terminal干消失的,你说你crash也行,但是不要消失啊,直接把firefox的所有的tab都 crash了,这个连带伤害真的是无敌了,我可以理解是因为我正在把wiki的sqldump导入占用了十几个G的内存,但是你也不能直接把别人都干趴下 啊!所以,最后我还是老老实实的关掉程序使用cmake来编译llvm,至于为什么要编译这个,我也不知道,也许是看见电脑闲着浪费吧?
      
      $ cmake -S ./llvm/ -B build -DLLVM_INSTALL_UTILS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/workspace/install -DLLVM_ENABLE_PROJECTS="clang;llvm" -DLLVM_PARALLEL_LINK_JOBS=4
      
    5. 关于enum class的古老的问题没有什么好办法吧?

六月二十五日 等待变化等待机会

  1. 这里是一个脚本来自动的编译clang,只不过需要加上一个安装gtest的选项。这个是从看cmake的代码明白错误的。
    
    #!/bin/sh
    
    build_llvm=`pwd`/build-llvm
    build_clang=`pwd`/build-clang
    installprefix=`pwd`/install
    llvm=`pwd`/../llvm-project
    mkdir -p $build_llvm
    mkdir -p $installprefix
    
    cmake -G Ninja -S $llvm/llvm -B $build_llvm \
          -DLLVM_INSTALL_UTILS=ON \
          -DCMAKE_INSTALL_PREFIX=$installprefix \
          -DCMAKE_BUILD_TYPE=Release \
          -DLLVM_INSTALL_GTEST=ON
    
    ninja -C $build_llvm install
    
    cmake -G Ninja -S $llvm/clang -B $build_clang \
          -DLLVM_EXTERNAL_LIT=$build_llvm/utils/lit \
          -DLLVM_ROOT=$installprefix \
          -DCMAKE_INSTALL_PREFIX=$installprefix
    
    ninja -C $build_clang install
    
    的确什么样的文档也不可能面面俱到,只有自己看代码。而我现在也开始理解为什么Ninja会如此的暴躁,因为编译的时候不由分说就开了十几个进程。比cmake快多了!!!
  2. 每一个复杂的项目的测试肯定也是非常的复杂的,这个和gcc的测试就有很大的不同,完全是另一个repo。
  3. 这里是一个细微的差别,据说不需要重新render。
    
    $ ffmpeg -i ChinaTalk_00000.mp4 -ss 00:00:00 -to 00:31:50 -c copy ChinaTalk.mp4
    
    这个简直是立刻就完成的!!!
  4. 在中美对抗走向白热化之际听这位战略大师的解读真的是令人醍醐灌顶啊!不愧为国策顾问的头衔,这个才是真正的战略分析家。相比之下如此多的戴白头巾的白袍客如此认真的聆听访谈让当事者的麻木不仁感到羞愧! 这里是使用Kazam录频的一个缺点,就是firefox居然不能播放
    
    $ ffmpeg -i ChinaTalk.mp4 -c:v libx264 -pix_fmt yuv420p ChinaTalk-new.mp4
    
    居然是pix-fmt这种导致firefox抱怨说没有合适的mime-type来播放。我决定拷贝一个音频版本:

六月二十六日 等待变化等待机会

  1. clang-check几乎就是一个万能的工具。而有一些很有意思的参数是clang可以使用的,比如-fsyntax-only,而另一个参数-ccc-print-phases打印出来步骤。
    
    $ ./clang -ccc-print-phases  /tmp/error.cpp 
                +- 0: input, "/tmp/error.cpp", c++
             +- 1: preprocessor, {0}, c++-cpp-output
          +- 2: compiler, {1}, ir
       +- 3: backend, {2}, assembler
    +- 4: assembler, {3}, object
    5: linker, {4}, image
    
    这个是所谓的内部文档
  2. 看这里似乎第一反应是应该使用std::declval来避免额外的constructor的调用? 但是看注解的意思它可能是要得到一个更加简单的问题的答案。我居然忘记了打印变量类型名字的方法是typeid(var).name()而更加搞笑的是我再一次编译clang的时候,那么默认设置就是要依赖系统的或者说gcc自带的标准库,这个常识的检验就在于一个helloworld的编译你都要加上这个链接库
    
    $ clang -std=c++17 -lstdc++ helloworld.cpp -o helloworld
    
    我查找笔记的时间也耗费了不少,要怎么容易搜寻这个参数呢?
  3. 上面这个问题其实对于我来说是一个高难度的工作。首先,它的问题是这样子的:给定一个shared_ptr,那么它的operator*返回类型是什么?很明显的是它包含的类型的引用这个很明显让我费了大半个早上 这个故事本来就算完了,可是我愣是没有想到decltype(*b)的类型是Bar的引用,结果花了好长时间去一步一步的推理它的类型是什么。那么*b是什么呢?很明显的它是shared_ptr::operator*,那么如果要得到一个函数的返回值类型我们是不是可以使用result_of,于是我就开始看看能不能通过它来获得类型: 这里是一个小的技巧,就是说如果你要调用一个类的成员函数,那么不创建实例是无法可想的,那么我们只是研究类型,于是可以使用类型的成员函数指针来做,因为成员函数说到底就是一个指针类型,所以,result_of是只能使用成员函数指针来做它的模板参数的。那么对于有函数名的是容易的,比如shared_ptr<Foo>::get,我们直接取它的地址&shared_ptr<Foo>::get,然后我们需要填入这个函数的参数,第一个就是shared_ptr自己,因为它是成员函数的第一个参数也就是this,其他没有参数了,所以,使用result_of就得到了它的返回类型,注意,get得到的是包含的类型的指针类型。相应的对于非名字的成员函数,比如operator*,其实也是一样的,看上去没有名字,实际上operator*就是它的名字,只不过我不知道重载的名字要怎么做,似乎是要把类型写全了才行吧?就是说要把参数都加上的完全类型。其实,这个也是本来就如此的。总而言之,它返回的就是所包含的类型的引用类型,我因为这么一个疏忽导致了一个早上的摸索。但是收获还是大大的。
  4. 说了半天我也没有真正的理解clang这段代码的用途,比如你有一个shared_ptr<Foo>类型,那么你当然知道它的operator*应该返回Foo&类型,对吗?看上去是不行的因为你在纯粹类型计算里能够有这个关系吗?因为根本就没有*shared_ptr<Foo>这个类型,你不能使用函数*来表达类型啊。那么怎么能够获得类型呢?通过这种伪函数似乎是最容易的,但是这个远远不如元计算大师们获得类型来的好,因为这个即便是inline也是有些问题的,我能够想到的就是constructor的问题,至少应该使用declval来做。我应该是没有理解这里的用途,它不是一个纯粹的类型计算而是对于实例的类型的结构吧,就是说我们已经有了一个对象实例想要得到它的真实类型。当然我不理解的是它和decltype的差别在哪里?我没有看完整的使用,它是有继承关系的,用途大概是计算一个类型从祖先类型一路的来的类型吧?总之不要妄加评论。记住要像九岁无知的小孩子一样的谦虚谨慎。

六月二十八日 等待变化等待机会

  1. 有时候一个小小的问题就难到了你一个小时。我试图编译clang下面的unittest,这个似乎没有什么文档,我只好手动编译,因为里面有单独测试的创建Parser/Lexer的代码,结果指定了-Iclang/include却依然有一个编译错误,我发现在vscode里include的代码来自于/usr/include/clang,这才意识到不知道什么时候我自己安装了clang的开发包,但是卸载各种开发包也不知道是哪一个安装的,最后只好硬性删除。以后遇到问题再说吧。
  2. 这也是一个常识吧?你要在llvm里build libc吗?那么你就会遇到严格的代码检查,比如printf/strncat之类的代码就是不合格的。没人要这么做。
    
    $ cmake -S ./llvm/ -B build -DLLVM_INSTALL_UTILS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/workspace/llvm-project/install -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;cross-project-tests" -DLLVM_ENABLE_RUNTIMES="all" -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INSTALL_UTILS=ON -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_STATIC_LINK_CXX_STDLIB=ON -DLLVM_TARGETS_TO_BUILD="X86" -DCLANG_INCLUDE_TESTS=ON -DLLVM_INCLUDE_TESTS=ON -DBUILD_SHARED_LIBS=OFF -DLLVM_BUILD_DOCS=ON -G "Ninja" -DLLVM_BUILD_EXAMPLES=ON -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_BUILD_TESTS=ON -DLLVM_BUILD_TOOLS=ON
    
    我比较期待的是-DLLVM_STATIC_LINK_CXX_STDLIB=ON这个据说-static-libstdc++,我期望我以后不要再使用clang的时候额外加链接。至于说很多的clang的选项我找不到,这个似乎不是编译相关的。
  3. 有时候一个最简单的问题也要困扰我,比如做测试的时候,报出错误openmp找不到,这个让我很困惑,因为gcc里openmp似乎是必备的一个,你从来不会没有,那么在clang里这个是可选的,也就是说-DLLVM_ENABLE_PROJECTS=里要包含openmp,那么compiler-rt呢?
  4. 我觉得真的是太傻了,这么简单的问题折腾了两个小时。gcc里也是类似的啊,你有很多的测试是一定要运行的,目标就是check-all,根本就不需要额外下载测试例。我折腾不出来额外的测试例是可以原谅的,但是为什么没有想到可以直接运行呢?比如使用ninja -C build check-all
  5. 关于这个unittest,就是clang/unittests/AST/CommentParser.cpp如何编译呢?我在check-allbuild.ninja里硬性的扣出了编译的命令,这个是使用刚刚编译的clang来编译的
    
    $ bin/clang++ -c /home/nick/workspace/llvm-project/clang/unittests/AST/CommentParser.cpp -DBUILD_EXAMPLES -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O3 -DNDEBUG  -Wno-variadic-macros -fno-exceptions -funwind-tables -fno-rtti -Wno-suggest-override -std=c++17 -I/home/nick/workspace/llvm-project/build/tools/clang/unittests/AST -I/home/nick/workspace/llvm-project/clang/unittests/AST -I/home/nick/workspace/llvm-project/clang/include -I/home/nick/workspace/llvm-project/build/tools/clang/include -I/home/nick/workspace/llvm-project/build/include -I/home/nick/workspace/llvm-project/llvm/include -I/home/nick/workspace/llvm-project/third-party/unittest/googletest/include -I/home/nick/workspace/llvm-project/third-party/unittest/googlemock/include -o /tmp/CommentParser.cpp.o
    
    $ bin/clang++ /tmp/CommentParser.cpp.o lib/libllvm_gtest_main.a lib/libllvm_gtest.a lib/libclangTesting.a lib/libLLVMTestingAnnotations.a lib/libLLVMTestingSupport.a lib/libclang-cpp.so.18.1 lib/libllvm_gtest.a lib/libLLVM.so.18.1 lib/libLLVM.so lib/libLLVMTestingAnnotations.a lib/libLLVMTestingSupport.a lib/libclang-cpp.so lib/libclangTesting.a lib/libllvm_gtest.a lib/libllvm_gtest_main.a lib/libclang-cpp.so lib/libLLVM.so -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O3 -DNDEBUG -static-libstdc++ -Wl,--gc-sections -Wl,-rpath,/home/nick/workspace/llvm-project/build/lib  lib/libllvm_gtest_main.a  lib/libllvm_gtest.a  lib/libclangTesting.a  lib/libLLVMTestingAnnotations.a  lib/libLLVMTestingSupport.a  lib/libclang-cpp.so.18.1  lib/libllvm_gtest.a  lib/libLLVM.so.18.1  -lpthread -o /tmp/CommentParser.exe
    
    编译obj文件可以分为三部分,就是一些定义的宏-DBUILD_EXAMPLES -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS,以及一些编译开关- fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O3 -DNDEBUG -Wno-variadic-macros -fno-exceptions -funwind-tables -fno-rtti -Wno-suggest-override -std=c++17,最后是一些include的搜索路径。 而链接部分也是类似的首先是大量的库文件,这里既有动态库也有静态库lib/ libllvm_gtest_main.a lib/libllvm_gtest.a lib/libclangTesting.a lib/libLLVMTestingAnnotations.a lib/libLLVMTestingSupport.a lib/libclang-cpp.so.18.1 lib/libllvm_gtest.a lib/libLLVM.so.18.1 lib/libLLVM.so lib/libLLVMTestingAnnotations.a lib/libLLVMTestingSupport.a lib/libclang-cpp.so lib/libclangTesting.a lib/libllvm_gtest.a lib/libllvm_gtest_main.a lib/libclang-cpp.so lib/libLLVM.so,以及一些编译开关,不过这里我不太理解的就是这几个库为何要使用rpath:- fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O3 -DNDEBUG -static-libstdc++ -Wl,--gc-sections -Wl,-rpath,/home/nick/workspace/llvm-project/build/lib lib/libllvm_gtest_main.a lib/libllvm_gtest.a lib/libclangTesting.a lib/libLLVMTestingAnnotations.a lib/libLLVMTestingSupport.a lib/libclang-cpp.so.18.1 lib/libllvm_gtest.a lib/libLLVM.so.18.1 -lpthread?当然我觉得大部分的开关也许对于这个简单的测试例是不需要的,但是对于所有的测试例应该是必须的。

六月二十九日 等待变化等待机会

  1. 关于这个错误error: unknown argument: '-fno-lifetime-dse'其 实也是非常的复杂的,大概就是使用gcc编译了clang,但是随后的工具使用clang结果就把之前gcc编译的开关带到了clang,它没有实现就报 错。这个问题本身就很复杂也没有完美的解决方案,因为clang还没有实现很多gcc的功能,也不可能做到完全一致,我想我自己就编译的时候使用 clang来编译clang吧:
    
    $ cmake -S ./llvm/ -B build -DLLVM_INSTALL_UTILS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/workspace/llvm-project/install-new -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;cross-project-tests;openmp;compiler-rt;lld" -DLLVM_ENABLE_RUNTIMES="all" -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INSTALL_UTILS=ON -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_STATIC_LINK_CXX_STDLIB=ON -DLLVM_TARGETS_TO_BUILD="X86" -DCLANG_INCLUDE_TESTS=ON -DLLVM_INCLUDE_TESTS=ON -DBUILD_SHARED_LIBS=OFF -DLLVM_BUILD_DOCS=ON -G "Ninja" -DLLVM_BUILD_EXAMPLES=ON -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_BUILD_TESTS=ON -DLLVM_BUILD_TOOLS=ON -DLLVM_INSTALL_GTEST=ON -DCMAKE_C_COMPILER=/home/nick/workspace/llvm-project/install/bin/clang-18 -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON
    
    但是这个又引出了另一个编译的问题
    Normally CMake uses the build tree for the RPATH when building executables etc on systems that use RPATH. When the software is installed the executables etc are relinked by CMake to have the install RPATH. If this variable is set to true then the software is always built with the install path for the RPATH and does not need to be relinked when installed.
    这一段话我读的不是很懂,为什么不是默认使用install path作为RPATH呢?
    In computing, rpath designates the run-time search path hard-coded in an executable file or library. Dynamic linking loaders use the rpath to find required libraries.
    概念就是可执行文件的搜索路径不再依赖系统的设置的,是否高于LD_LIBRARY_PATH呢?看起来不是一定的,相当的复杂,甚至于和是否是setuid/setgid有关,这里的深奥安全考量我也懒得去想了,总之,目前应该说大部分的可执行程序至少在ubuntu里是都有设置setuid的吧,否则sudo没法用怎么办呢?要su吗?
  2. 我发现我下载的kiwix.org的计算机部分不全,我打算下载一个总的zim然后手动去补缺补漏。这个实在是没有办法的办法。但是这个有个问题就是相关的图片不能自动下载,看来要自己完成这个功能了,这个也是一个小小的挑战了。等运动回来再说吧。
  3. 我发现了几个方向是我感兴趣的:clangQuery和CreateASTPrinter有一个搞笑的是我一直有一个错误的印象就是clang-cpp是c++编译器,这个错误在gcc就是如此,我始终也无法把cpp=C Preprocessor联系起来。为什么是C的preprocessor呢?应该是从语法实现来看c++是借用了相同的机制,使用的是相同的预处理器,当然额外语法部分怎么搞得我忘了,总之是几乎一样的。
  4. 经常遇到这个错误,我一开始以为是有特殊字符造成的,看了这里才知道是内核要保护自己的措施。
    
    $ getconf ARG_MAX
    2097152
    $ ulimit -s
    8192
    

    which tells you the maximum number of bytes a command line can have after being expanded by the shell.

    In Linux < 2.6.23, the limit is usually 128 KB.

    In Linux >= 2.6.25, the limit is either 128 KB, or 1/4 of your stack size (see ulimit -s), whichever is larger.

    这个问题连xargs也无法解决,因为问题出在ls吧? 终于找到了一个解决办法,尽管我并不是很理解,但是xargs是关键。 首先,我们要明白xargs能解决问题的源头
    
    $ xargs --show-limits
    Your environment variables take up 3488 bytes
    POSIX upper limit on argument length (this system): 6285920
    POSIX smallest allowable upper limit on argument length (all systems): 4096
    Maximum length of command we could actually use: 6282432
    Size of command buffer we are actually using: 131072
    Maximum parallelism (--max-procs must be no greater): 2147483647
    
    然后,我们可以先使用find再传递给xargs,所以,
    
    $ find wiki/I/ -name *.svg | xargs ls -l | wc -l
    351932
    
    这个做法远远优于在find里执行exec,因为我理解这个是每次执行一个任务,而xargs是把众多的参数传递给后面的命令一次性执行。
  5. 维基百科的文件名是五花八门的,那么单单罗列多少个文件都是一个问题,因为一千多万个文件可不是一个小问题:
    
    $ find . -type f -print0 | xargs -0 ls | wc -l
    11988634
    
    这里find使用-print0是使用null来分割文件名,那么xargs也是同样的-0也是使用null来分割。
  6. coredump是受apport控制的,它的log是/var/log/apport.log,所以,它在/var/lib/apport/coredump/
  7. 一开始我还以为是我编译的问题,等到我使用官方的clang-tool包运行clang-check才发现这个程序对于--syntax-tree-dump总是会crash,不管有多少代码。

七月一日 等待变化等待机会

  1. 发现这个crash不仅仅是--syntax-tree-dump才有,就是--tokens-dump也是如此,所以,有必要来debug编译一下,在build目录下已经有编译好的动态库,我决定图省事使用rpath来指定路径。这里解释的很清楚
    -rpath dir
    Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link; see the description of the -rpath-link option. If -rpath is not used when linking an ELF executable, the contents of the environment variable LD_RUN_PATH will be used if it is defined. The -rpath option may also be used on SunOS. By default, on SunOS, the linker will form a runtime search patch out of all the -L options it is given. If a -rpath option is used, the runtime search path will be formed exclusively using the -rpath options, ignoring the -L options. This can be useful when using gcc, which adds many -L options which may be on NFS mounted filesystems. For compatibility with other ELF linkers, if the -R option is followed by a directory name, rather than a file name, it is treated as the -rpath option.
    -rpath-link DIR
    When using ELF or SunOS, one shared library may require another. This happens when an ld -shared link includes a shared library as one of the input files. When the linker encounters such a dependency when doing a non-shared, non-relocateable link, it will automatically try to locate the required shared library and include it in the link, if it is not included explicitly. In such a case, the -rpath-link option specifies the first set of directories to search. The -rpath-link option may specify a sequence of directory names either by specifying a list of names separated by colons, or by appearing multiple times. The linker uses the following search paths to locate required shared libraries.
    1. Any directories specified by -rpath-link options.
    2. Any directories specified by -rpath options. The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time.
    3. On an ELF system, if the -rpath and rpath-link options were not used, search the contents of the environment variable LD_RUN_PATH.
    4. On SunOS, if the -rpath option was not used, search any directories specified using -L options.
    5. For a native linker, the contents of the environment variable LD_LIBRARY_PATH.
    6. The default directories, normally `/lib' and `/usr/lib'.
    If the required shared library is not found, the linker will issue a warning and continue with the link.
    
    $ g++ -ggdb -O0 /home/nick/workspace/llvm-project/clang/tools/clang-check/ClangCheck.cpp -I/home/nick/workspace/llvm-project/build/tools/clang/tools/clang-check -I/home/nick/workspace/llvm-project/clang/tools/clang-check -I/home/nick/workspace/llvm-project/clang/include -I/home/nick/workspace/llvm-project/build/tools/clang/include -I/home/nick/workspace/llvm-project/build/include -I/home/nick/workspace/llvm-project/llvm/include -DBUILD_EXAMPLES -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O2 -g -DNDEBUG  -fno-exceptions -funwind-tables -fno-rtti -std=c++17 -static-libstdc++  -Wl,-rpath,/home/nick/workspace/llvm-project/build/./lib  lib/libclang-cpp.so.18.1  -Wl,-rpath,/home/nick/workspace/llvm-project/build/./lib lib/libLLVM.so.18.1 -o ClangCheck.exe
    

七月二日 等待变化等待机会

  1. 对于wiki的首页有很多的分类页面,我要上传,需要一个简单的脚本,折腾了不少时间: 这里的一个技巧是xargs是不会给你做position args的,它就是简单的把参数传递,那么使用bash来传递的时候,bash的参数是从$0开始的,所以,这个地方折腾了我好久。
  2. 《游泳池归来偶得》
    
    青山本无心,
    绿水亦无情。
    待到云开日,
    无雨便是晴。
    

七月三日 等待变化等待机会

  1. 我现在终于知道之前删除的clang的binary是vscode安装的。这个可是比较麻烦了。
  2. 我终于发现了一些端倪,尽管是如此的细微与无用,但是总算是进展,就是说--tokens-dump是无辜的,罪魁祸首还是--syntax-tree-dump当代码是c++的标准库的头文件的时候就会出错。记录一下我的编译指令
    
    cmake -S ./llvm/ -B build -DLLVM_INSTALL_UTILS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=~/workspace/llvm-project/install -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;cross-project-tests;openmp;compiler-rt;lld" -DLLVM_ENABLE_RUNTIMES="all" -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INSTALL_UTILS=ON -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_STATIC_LINK_CXX_STDLIB=ON -DLLVM_TARGETS_TO_BUILD="X86" -DCLANG_INCLUDE_TESTS=ON -DLLVM_INCLUDE_TESTS=ON -DBUILD_SHARED_LIBS=OFF -DLLVM_BUILD_DOCS=ON -G "Ninja" -DLLVM_BUILD_EXAMPLES=ON -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_BUILD_TESTS=ON -DLLVM_BUILD_TOOLS=ON -DLLVM_INSTALL_GTEST=ON -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON
    
    g++ /home/nick/workspace/llvm-project/clang-tools-extra/pseudo/unittests/TokenTestCopy.cpp -DBUILD_EXAMPLES -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O0 -g -DNDEBUG  -Wno-variadic-macros -fno-exceptions -funwind-tables -fno-rtti -Wno-suggest-override -std=c++17 -I/home/nick/workspace/llvm-project/build/tools/clang/tools/extra/pseudo/unittests -I/home/nick/workspace/llvm-project/clang-tools-extra/pseudo/unittests -I/home/nick/workspace/llvm-project/clang/include -I/home/nick/workspace/llvm-project/build/tools/clang/include -I/home/nick/workspace/llvm-project/build/include -I/home/nick/workspace/llvm-project/llvm/include -I/home/nick/workspace/llvm-project/clang-tools-extra/pseudo/include -I/home/nick/workspace/llvm-project/build/tools/clang/tools/extra/pseudo/include -I/home/nick/workspace/llvm-project/third-party/unittest/googletest/include -I/home/nick/workspace/llvm-project/third-party/unittest/googlemock/include -I/home/nick/workspace/llvm-project/clang-tools-extra/pseudo/lib/../include -static-libstdc++   lib/libclang-cpp.so.18.1  lib/libclangPseudo.a  lib/libclangPseudoCXX.a  lib/libclangPseudoGrammar.a  lib/libLLVMTestingAnnotations.a  lib/libLLVMTestingSupport.a   lib/libclangLex.a  lib/libclangPseudoGrammar.a  lib/libclangBasic.a   lib/libLLVM.so.18.1  -lpthread -o TokenTestCopy.exe
    
    g++ -ggdb -O0 /home/nick/workspace/llvm-project/clang/tools/clang-check/ClangCheck.cpp -I/home/nick/workspace/llvm-project/build/tools/clang/tools/clang-check -I/home/nick/workspace/llvm-project/clang/tools/clang-check -I/home/nick/workspace/llvm-project/clang/include -I/home/nick/workspace/llvm-project/build/tools/clang/include -I/home/nick/workspace/llvm-project/build/include -I/home/nick/workspace/llvm-project/llvm/include -DBUILD_EXAMPLES -DGTEST_HAS_RTTI=0 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -fno-lifetime-dse -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O2 -g -DNDEBUG  -fno-exceptions -funwind-tables -fno-rtti -std=c++17 -static-libstdc++  -Wl,-rpath,/home/nick/workspace/llvm-project/build/./lib  lib/libclang-cpp.so.18.1  -Wl,-rpath,/home/nick/workspace/llvm-project/build/./lib lib/libLLVM.so.18.1 -o ClangCheck.exe
    
  3. 我大概有了一个方向,就是说ClangCheck实际上PP分辨不出C/C++的区别,结果就是#include是有问题的,搜索路径是不对的。
  4. 使用gdb要求编译debug版本,但是ninja是很霸道的,所以,这一点很重要:
    LLVM_PARALLEL_{COMPILE,LINK}_JOBS:STRING

    Building the llvm toolchain can use a lot of resources, particularly linking. These options, when you use the Ninja generator, allow you to restrict the parallelism. For example, to avoid OOMs or going into swap, permit only one link job per 15GB of RAM available on a 32GB machine, specify -G Ninja -DLLVM_PARALLEL_LINK_JOBS=2.

    所以,一定要加上这个开关-DLLVM_PARALLEL_LINK_JOBS=1,而鉴于我的cpu只有所谓的32,这个我是深表怀疑的,那么我使用-DLLVM_PARALLEL_COMPILE_JOBS=16吧?
    
    $ cmake -S ./llvm/ -B build -DLLVM_INSTALL_UTILS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=~/workspace/llvm-project/install -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;cross-project-tests;openmp;compiler-rt;lld" -DLLVM_ENABLE_RUNTIMES="all" -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INSTALL_UTILS=ON -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_STATIC_LINK_CXX_STDLIB=ON -DLLVM_TARGETS_TO_BUILD="X86" -DCLANG_INCLUDE_TESTS=ON -DLLVM_INCLUDE_TESTS=ON -DBUILD_SHARED_LIBS=OFF -DLLVM_BUILD_DOCS=ON -G "Ninja" -DLLVM_BUILD_EXAMPLES=ON -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_BUILD_TESTS=ON -DLLVM_BUILD_TOOLS=ON -DLLVM_INSTALL_GTEST=ON -DLLVM_PARALLEL_LINK_JOBS=1 -DLLVM_PARALLEL_COMPILE_JOBS=16 -DLLVM_USE_LINKER=lld
    
  5. 我越来越觉得这个功能是已经被放弃了的,应该是转而使用这个机制
  6. 我决定先提交bug,听听意见再说。

七月五日 等待变化等待机会

  1. 今天我本来以为悟出了SourceLocation是怎么回事了,但是后来发现完全不是那么回事。
    
    struct Foo {
    Foo() = default;
    };
    
    而以下就是Lexer得到的token及其location,
    
    $29 = (const clang::syntax::TokenBuffer &) @0x7fffffffcde0: {ExpandedTokens = std::vector of length 12, capacity 16 = {{Location = {ID = 2}, Length = 6, 
          Kind = clang::tok::kw_struct}, {Location = {ID = 9}, Length = 3, Kind = clang::tok::identifier}, {Location = {ID = 13}, Length = 1, 
          Kind = clang::tok::l_brace}, {Location = {ID = 15}, Length = 3, Kind = clang::tok::identifier}, {Location = {ID = 18}, Length = 1, 
          Kind = clang::tok::l_paren}, {Location = {ID = 19}, Length = 1, Kind = clang::tok::r_paren}, {Location = {ID = 21}, Length = 1, 
          Kind = clang::tok::equal}, {Location = {ID = 23}, Length = 7, Kind = clang::tok::kw_default}, {Location = {ID = 30}, Length = 1, 
          Kind = clang::tok::semi}, {Location = {ID = 32}, Length = 1, Kind = clang::tok::r_brace}, {Location = {ID = 33}, Length = 1, 
          Kind = clang::tok::semi}, {Location = {ID = 34}, Length = 0, Kind = clang::tok::eof}}, 
    
    这里似乎也看不出什么规律。

七月六日 等待变化等待机会

  1. 我找到了这个很好的一个入门的教学。它最大的好处是教会了你怎么把你的代码加入到clang的工具链里,因为繁琐的编译设置真的是很麻烦,我是从ninja.build里把编译开关一个个扣出来,这个是教会你直接制作CMakeLists.txt,另一个方面就是怎么使用libTooling这是一个强大的工具平台,应该是我认真学习的重点,因为它是很多人们心目中要实现的东西,你就不用走夜路摸黑探索了。
  2. 我尝试的修改了这个例子,这个是一点点的概念学习:
    
      auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory);
      if (!ExpectedParser) {
        // Fail gracefully for unsupported options.
        llvm::errs() << ExpectedParser.takeError();
        return 1;
      }
    
    这一小段代码有一个小问题,就是ExpectedParser.takeError(),当出现错误的时候它会导致crash,具体的原因我依然不是非常清楚,但是大体上这个takeError()的方法是把错误交出去,否则就是没有handled,就会引发某个assert。所以,我把它改为了toString的参数。这个至少没有才crash吧?
    
    llvm::errs() << toString(ExpectedParser.takeError());
    
  3. 另一个是我尝试直接使用现成的ASTDumpAction它是ASTFrontendAction继承而来的特性化的行动,但是它的参数传递我有些困难,因为命令行参数设置我还不会,只好自己继承一个硬性传递参数。也就是重载这个方法CreateASTConsumer
  4. 于是我的代码实现了基本的功能,就是clang -Xclang -ast-dump -c /tmp/test1.cpp 注意即便你没有任何的头文件,但是clang会有一些所谓的builtin的几个整数类型的定义,这个不知道用什么开关可以关闭。
  5. 头条上看到马凯朔的一个问答,讲的非常的棒!用手机录屏,但是要调整大小
    
    $ ffmpeg -i SVID_20240707_100442_1.mp4 -filter:v "crop=800:480:220:60" /tmp/cropped.mp4
    
  6. 我也顺便录了一个音频:
  7. 我要把一千多万个图片文件上传到aws,那么使用s3cmd sync命令是否还有优势呢?就是说白白占用好多个G的内存并且下载云端的文件列表的MD5和本地的文件MD5比较?这个实在是太浪费了,因为对于大文件来说是有必要的,可是对于小文件呢?所以,我想还是使用最古老的find/xargs吧?
    
    $ find I -type f -maxdepth 1 -print0 | xargs -0 -n 1 bash -c 's3cmd put $0 -M --progress -v --rr --acl-public --skip-existing s3://www.staroceans.org/wiki/$0'
    
    有大量的文件名是其他语言或者有特殊字符的或者空格等等,所以,-print0 | xargs -0。这么做的好处是消耗的内存资源很小,而且能够应付的了上千万文件名做参数的问题,以及特殊字符做文件名的问题。

七月八日 等待变化等待机会

  1. 对于新手先学习提交一些bug总是好的。至于说要学习怎么贡献,那就有点奢侈了,还是先学习怎么测试吧。这里倒是有无数多的任务就是is:open is:issue label:"good first issue" 的搜索词等着你去实践。
  2. 第一步是先fork github,那么我本地直接改为这个fork行不行呢?
    
    $ git remote set-url origin https://github.com/nickhuang99/llvm-project.git
    
    如果是测试本地的unittest,可以ninja check-llvm-unit。如果全部的测试都要运行check-all

七月九日 等待变化等待机会

  1. 《夜听老歌不能入眠恍惚间》
    
    遥想登山巅,
    只手擎苍天。
    梦醒揉双眼,
    惆怅更无边。
    
    光明 - 谭艳
    词:汪峰
    曲:汪峰
    当灰烬查封了凝霜的屋檐
    当车菊草化作深秋的露水
    我用固执的枯藤做成行囊
    走向了那布满荆棘的他乡
    当大地铺满了悲泣的落叶
    当杜鹃花化作远空的雾霭
    祝福我吧我最思念的亲人
    那就是我向你告别的身影
    也许迷途的惆怅
    会扯碎我的脚步
    可我相信未来会
    给我一双梦想的翅膀
    虽然失败的苦痛
    已让我遍体鳞伤
    可我坚信光明就在远方
    当灰烬查封了凝霜的屋檐
    当车菊草化作深秋的露水
    我用固执的枯藤做成行囊
    走向了那布满荆棘的他乡
    也许征程的迷惘
    会扯碎我的手臂
    可我相信未来会
    给我一双梦想的翅膀
    虽然挫折的创伤
    已让我寸步难行
    可我坚信光明就在远方
    我用翅膀掀起那天边的排浪
    我用身躯托起那血红的太阳
    就在这刺骨而凛冽的大风中
    你会听到我赞美未来的呼喊
    也许迷途的惆怅
    会扯碎我的脚步
    可我相信未来会
    给我一双梦想的翅膀
    虽然失败的苦痛
    已让我遍体鳞伤
    可我坚信光明就在远方
    也许征程的迷惘
    会扯碎我的手臂
    可我相信未来会
    给我一双梦想的翅膀
    虽然挫折的创伤
    已让我寸步难行
    可我坚信光明就在远方
    坚信光明 就在远方
    
  2. 我之前的一个错误观念在于ASTConsumer和ASTPrinter的,首先,创建ASTConsumer的CreateASTDumper内部使用的就是ASTPrinter,调用的就是ParseAST,所以,我犯了一个错误重复调用ParseAST所以就coredump了。另一个就是我想让Sema的ASTConsumer来做我的ASTPrinter的工作才发现它的Consumer是所谓的code_complete的consumer,而且所谓的ASTPrinter是一个内部的类,根本就不对外开放。
  3. 再提一个bug
  4. 统计一下llvm的代码行数,这个是粗略的估计
    
    $ find . -type f -name '*.cpp' -print0 | xargs -0 cat | grep -vcE '^[[:space:]]*$'
    
    结果是八百多万行!

七月十日 等待变化等待机会

  1. 这种隐藏技术是一种常见的做法。比如我们想要定义个万能的包含一切可能的错误类型Error,那么它背后的存储要怎么定义呢?肯定是指针,但是定义为void*吗?显然不行,因为我们要释放内存,那么就是std::unique_ptr,可是包含的类型呢?void*吗?这里就是使用一个有名无实的类型
    
    /**
     * Opaque reference to an error instance. Null serves as the 'success' value.
     */
    typedef struct LLVMOpaqueError *LLVMErrorRef;
    
    这里又是考验基本功的地方了,我始终对于typedefdefine感到脑子转不过弯来,说起来真的是弱智,这么多年的学习实践依然没有长进,这里定义的是LLVMErrorRef它就是一个不知名的结构LLVMOpaqueError的指针*类型,那么我们是否需要给编译器展示有名无实的结构LLVMOpaqueError呢?我们不能,因为要包罗万象,所以不可能,编译器也不需要。这种基本的技巧我不知道看过多少遍,好像自己也用过,可是时间长了就生疏了。尤其我们还是时不时的使用std::unique_ptr来包裹一下,就犯糊涂了。
  2. 我感觉似乎clang-check的syntax-tree-dump结果不太对,我对于这个简单的parser的正确性始终表示怀疑。
    
    TranslationUnit Detached
    `-SimpleDeclaration
      |-'typedef'
      |-'struct'
      |-'LLVMOpaqueError'
      |-DeclaratorList Declarators
      | `-SimpleDeclarator ListElement
      |   |-'*'
      |   `-'LLVMErrorRef'
      `-';'
    
    这里似乎把*指针符号归到了后面的声明里了。这个AST是正确的,因为它是clang的parser的回调函数,这个不可能错的。 注意这里的TypedefDecl是把整个struct LLVMOpaqueError *作为声明LLVMErrorRef的类型的,因为这里的关键是把PointerType指针类型struct LLVMOpaqueError *完全识别出来,这个不是那么容易的。这个C++宝典的阅读是非常的辛苦的。关键就是这个decl-specifier-seq的循环,
    1. 首先decl-specifier的循环找到typedef关键字。
    2. 然后,我们就找到defining-type-specifier,这里class-specifier的根基是struct
    3. 但是不论我怎么找都无法把*归结到decl-specifier里,直到最后我去反查指针类型定义才明白它是declarator的一部分!
    这个就是最最基本的T D;也就是Type Declarator;模式。
    
    simple-declaration:
    	decl-specifier-seq init-declarator-listopt ;
    
    上半部分是解决Type的问题:
    
    
    decl-specifier-seq:
    	decl-specifier attribute-specifier-seqopt
    	decl-specifier decl-specifier-seq 
    
    decl-specifier:
    	storage-class-specifier
    	defining-type-specifier
    	function-specifier
    	friend
    	typedef
    	constexpr
    	consteval
    	constinit
    	inline 
    
    class-specifier:
    	class-head { member-specificationopt } 
    
    class-head:
    	class-key attribute-specifier-seqopt class-head-name class-virt-specifieropt base-clauseopt
    	class-key attribute-specifier-seqopt base-clauseopt
    
    
    可是这里看到了我一直反反复复的纠结的错误观念,就是*属于类型还是属于声明变量?在我的深度的观念中是属于前者就是类型,这个似乎是可以从我们的AST得到支持,因为我们要得到一个所谓的指针类型PointerType 0x5f25aca33540 'struct LLVMOpaqueError *'。可是真的是吗?为什么程序员都习惯的把*紧紧挨着后面的声明类型名呢? *LLVMErrorRef;
    
    init-declarator-list:
    	init-declarator
    	init-declarator-list , init-declarator
    
    init-declarator:
    	declarator initializeropt
    	declarator requires-clause 
    
    declarator:
    	ptr-declarator
    	noptr-declarator parameters-and-qualifiers trailing-return-type 
    
    ptr-declarator:
    	noptr-declarator
    	ptr-operator ptr-declarator 
    
    看到这里我才意识到从语法的角度来看*指针符号是declarator的一部分!这个仅仅是BNF定义的结果吗?还是根深蒂固的观念呢?从这里看来syntax-tree-dump是正确的,是我自己的错误观念误导了我!
  3. 整个一个早晨我都在脑力耗费在这个晦涩艰深的语法问题上,但是收获还是不小的。
  4. 对于cpp-reference.com我很担心哪一天它也不让访问了,所以,我也留一个古老的拷贝吧?这里我有一点点的不太明白就是使用make source是更新到最新吗?似乎是的,因为它使用wget去除了一些无关的网页,
    
            rm -rf "reference"
            mkdir "reference"
    
            pushd "reference" > /dev/null; \
            regex=".*index\\.php.*|.*/Special:.*|.*/Talk:.*" \
            regex+="|.*/Help:.*|.*/File:.*|.*/Cppreference:.*" \
            regex+="|.*/WhatLinksHere:.*|.*/Template:.*|.*/Category:.*" \
            regex+="|.*action=.*|.*printable=.*|.*en.cppreference.com/book.*" ; \
            echo $$regex ; \
            wget --adjust-extension --page-requisites --convert-links \
              --force-directories --recursive --level=15 \
              --span-hosts --domains=en.cppreference.com,upload.cppreference.com \
              --reject-regex $$regex \
              --timeout=5 --tries=50 --no-verbose \
              --retry-connrefused --waitretry=10 --read-timeout=20 \
              http://en.cppreference.com/w/ ; \
            popd > /dev/null
    
            ./export.py --url=http://en.cppreference.com/mwiki reference/cppreference-export-ns0,4,8,10.xml 0 4 8 10
    
    这里的--reject-regex $$regex很强大! 为了得到最新的版本有必要执行一下make source再生成make doc_html

七月十一日 等待变化等待机会

  1. 为什么我越是学习越是感到自己的无知到了无以复加的地步!?什么是rvalue reference我都不明白了,难道我从来没有真正的理解过?还是忘记了?我不能总是用忘记来做借口。
    1. 首先,它是一个reference,而它是和lvalue reference一样延长了变量的生命周期
    2. 那么在编译器参数绑定阶段rvalue reference和lvalue reference是不同的,就是前者能够绑定后者的const型,而后者不能绑定前者。本来世界上只需要lvalue reference和const lvalue reference两个作为参数类型就能包打天下了,但是突然之间,依靠编译器绑定的能力增加我们有了rvalue reference的绑定,这个说到底是编译器语言的增加吧?(c++11)的新语法特征允许编译器根据参数类型寻找更加匹配的绑定,于是乎我们有了第三 种reference参数类型rvalue reference。至于说怎么利用它来优化参数传递那就是程序员必须去实现的。
    3. 可是说到绑定,我真的明白吗?这个是让我最为震惊的结果,如果你不使用std::move,你压根儿就无法使用rvalue reference作为参数来传递! 我从来没有想到过如果我的定义的类型和参数类型一致,而我却不能把它作为参数来传递,而这个才是引入std::move的最最现实的原因,因为没有它压根就不行!原因是什么呢?就是编译器在选择参数传递的时候是会偷懒的,也许还有兼容性的设计,它会做一个转换的。
    4. 号称学习过c++的程序员可能很多都分不清这些概念吧?
      • a glvalue (“generalized” lvalue) is an expression whose evaluation determines the identity of an object or function;
      • a prvalue (“pure” rvalue) is an expression whose evaluation
      • computes the value of an operand of a built-in operator (such prvalue has no result object), or
      • initializes an object (such prvalue is said to have a result object).
      The result object may be a variable, an object created by new-expression, a temporary created by temporary materialization, or a member thereof. Note that non-void discarded expressions have a result object (the materialized temporary). Also, every class and array prvalue has a result object except when it is the operand of decltype;
      • an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
      • an lvalue is a glvalue that is not an xvalue;
      这些实在是太抽象了!我发现还是代码容易理解:
  2. 这个学习是很痛苦的一个原因是我感到非常的沮丧,因为我学了二十几年居然还是这么的无知。
  3. 对于一个变量它是怎么匹配函数参数的呢?这个似乎是最最基本的问题,可是你真的有很清晰的概念吗?作者能问出这样的问题本身就是非常精深的专业玩家了。单单理解这位大侠的问题就是一个高难度的动作,我打算一点点的从不同侧面来接近。
    1. 如果给定这两样两个重载的函数,你有办法调用它们吗?比如一个是pass by value,一个是pass by rvalue reference。我一开始以为你没有办法让它们两个共存,后来才发现是可以的:
      
      void f( std::string&& x){
          std::cout << __PRETTY_FUNCTION__ << "rvalue reference overload f(" << x << ")\n";
      }
      void f(std::string x){
          std::cout << __PRETTY_FUNCTION__ << "no reference overload f(" << x << ")\n";
      }
      
      这个原本不是作者的问题,只不过我想先解决这个基本的使用问题,就是说这两个重载导致rvalue reference的根本不可能被使用。因为当它们同时存在的时候,即便是std::move传递的参数依旧被编译器认为模棱两可。
      Code Caller Argument Type Function Parameter Type Resulte
      
      std::string x("lvalue reference!");
      f(x);
      
      lvalue reference pass by value wins void f(std::string)no reference overload f(lvalue reference!)
      
      std::string x("lvalue reference!");
      std::string& y = x;
      f(y);
      
      explicitly using type lvalue reference y to call pass by value always wins void f(std::string)no reference overload f(lvalue reference!)
      
      std::string x("lvalue reference!");
      std::string& y = x;
      std::string&& z = "rvalue reference";
      f(z);
      
      explicitly using type rvalue reference z to call pass by value always wins void f(std::string)no reference overload f(rvalue reference)
      
      std::string x("lvalue reference!");
      std::string& y = x;
      std::string&& z = "rvalue reference"; 
      f(std::move("hello world temp")); // error     
      f("hello world");// error   
      f(std::move(x));// error   
      f(std::move(y));// error   
      f(std::move(z));// error   
      
      1. pass by literal
      2. pass by rvalue reference of literal
      3. pass by rvalue rerence of lvalue reference or rvalue reference etc.
      compiler considers they are ambiguous error: call to 'f' is ambiguous
      结论就是说:至少目前我想不出有什么办法能够让编译器选择rvalue reference的重载,就是说在它和pass by value并存的情况下。

七月十二日 等待变化等待机会

  1. 真的理解std::move吗?
    Names of rvalue reference variables are lvalues and have to be converted to xvalues to be bound to the function overloads that accept rvalue reference parameters, which is why move constructors and move assignment operators typically use std::move:
    这一套机制是所谓的overload resolution,这个似乎在语法里无法定义的,是实现层面的一个细节吧?
    1. Building the set of candidate functions.
    2. Trimming the set to only viable functions.
    3. Analyzing the set to determine the single best viable function (this may involve ranking of implicit conversion sequences).
    这是一个及其复杂的过程,可以说是c++重载机制的精华所在,更不要说模板实例化的种种关节技巧都来自于此。可以说一个普通人学习一辈子也未必真的精通。其中单单这个Template Argument Deduction几乎就可以耗费半生来学习所谓的元编程。这里的这个例子简直就是一个让人抓狂的,而使用c++ insights可以非常的完美的解释一切:
    
    int f1(int);
    int f2(float);
    struct A {
      using fp1 = int (*)(int);
      inline operator fp1 () {
        return f1;
      }  
      using fp2 = int (*)(float);
      inline operator fp2 () {
        return f2;
      }  
      // inline constexpr A() noexcept = default;
    };
    struct A a;
    int i = a.operator A::fp1()(1);
    
    最关键的就是最后一行int i = a.operator A::fp1()(1);,编译器要首先解决函数的调用问题,而这里没有函数名字,就只能寻找成员函数,而成员函数也不是有名字的而是所谓的conversion operator,这里依靠函数的返回值来确定函数的解决也是一个很颠覆人的例子。
  2. 更加高深的是这个ADL(Argument Dependent Lookup),我以前看过以为自己明白,可是看到下面的例子,就又感到不太理解了。 这里的.operator<<() 是一个成员函数,它需要一个参数std::endl;而与之相对应的是endl本身也可以是一个函数endl(std::cout); 这个是让人感到突兀的代码,因为endl是怎么样的一个函数呢? 那么当endl作为operator<<()的参数是怎么回事呢? 是的,参数是一个函数指针也就是endl的函数原型。
  3. lvalue也好,rvalue也好,它们都是reference,那么它们是在同一个水平上的,就是说它们正面刚才是有看头的。把函数稍稍改一下
    
    void f( std::string&& x){
        std::cout << __PRETTY_FUNCTION__ << "rvalue reference overload f(" << x << ")\n";
    }
    void f(std::string& x){
        std::cout << __PRETTY_FUNCTION__ << "lvalue reference overload f(" << x << ")\n";
    }
    std::string x("lvalue reference!");
    std::string& y = x;
    std::string&& z = "rvalue reference";
    
    Code Caller Argument Type Function Parameter Type Resulte
    
    f(x);
    f(y);
    f(z);
    
    x,y,z are passed as value, lvalue, rvalue reference compiler always picks up lvalue reference overload
    
    void f(std::string &)lvalue reference overload f(lvalue reference!)
    void f(std::string &)lvalue reference overload f(lvalue reference!)
    void f(std::string &)lvalue reference overload f(rvalue reference)
    
    
    f(std::move(x));
    f(std::move(y));
    f(std::move(z));
    
    all x,y,z are using std::move to pass as rvalue reference compiler will match the argument type to pick up rvalue reference overload
    
    void f(std::string &&)rvalue reference overload f(lvalue reference!)
    void f(std::string &&)rvalue reference overload f(lvalue reference!)
    void f(std::string &&)rvalue reference overload f(rvalue reference)
    
  4. 在我做了这么多的笔记之后我终于在这个帖子里找到了我这么辛苦的原因,因为这个是一个普遍的问题!因为几乎每个人都读过这段真理,可是真理真的那么容易掌握吗?函数参数类型是rvalue reference,可是参数名却是地地道道的lvalue,这个就像前两天我对于指针类型的疑惑,*究竟应该靠近类型一端还是应该靠近变量一端,也就是说它是declspecific还是declarator之类的想法。但是这里的真正的核心却不止这么多,就是所谓的绑定或者说兼容不是自动实现的,比如我尝试着来实现std::move就犯了一个错误,我以为返回值可以自动转换 这里的返回值t如果不是强制转换的话是会报错的,因为lvalue是不能轻易的转为rvalue的,除非你知道你在做什么,这里当然是安全的因为我们接受到的函数参数就是rvalue,所以,这个强制转换是合法的。可是这里的深意就是这个不是默认的。
  5. 这个实在是好辛苦啊,想想看,一个每天都不知道使用多少遍的函数调用与参数传递有这么多的复杂机制,可是我却随手一写。
  6. rvalue reference到底是一个什么玩意儿呢?它在多大程度上类比const reference呢?让我们对比一下 几乎是毫无悬念的,结果是?const reference获胜:
    
    void f(const std::string &)const lvalue reference overload f(lvalue reference!)
    void f(const std::string &)const lvalue reference overload f(lvalue reference!)
    void f(const std::string &)const lvalue reference overload f(rvalue reference!)
    
    这个说明了什么呢?就是一个朴素的道理是不管你如何定义一个变量,在它传递给函数做参数的时候,它就是一个有名的变量,那么它就是lvalue,无论如何不能当作rvalue来对待。那么我们如何才能得到rvalue的重载调用呢?使用std::move如何呢?
    
    f(std::move(x));
    f(std::move(y));
    f(std::move(z));
    
    结果呢?它们不论声明是什么类型的变量,在std::movestatic_cast之后就都是rvalue了:
    
    void f(std::string &&)rvalue reference overload f(lvalue reference!)
    void f(std::string &&)rvalue reference overload f(lvalue reference!)
    void f(std::string &&)rvalue reference overload f(rvalue reference!)
    
    有没有可能它是什么就是什么的调用呢?于是我仿造std::move做了三个小函数 它的目的是让我们能够来控制传递参数的类型:
    
    f(CopyValue(x));
    f(CopyReference(x));
    f(CopyRvalueReference(x));  
    
    结果就是reference归reference,value归value。因为本质上rvalue reference更加靠近value,当然它本质上是reference,可是它是比较保守的reference。
    
    void f(std::string &&)rvalue reference overload f(lvalue reference!)
    void f(const std::string &)const lvalue reference overload f(lvalue reference!)
    void f(std::string &&)rvalue reference overload f(lvalue reference!)
    
    同理可证,真正的lvalue reference y的表现和x应该是一样的:
    
    f(CopyValue(y));
    f(CopyReference(y));
    f(CopyRvalueReference(y));
    
    结果是和x一模一样的。让人比较期待的是真正的rvalue reference z的情况是怎么样的呢?
    
    f(CopyValue(z));
    f(CopyReference(z));
    f(CopyRvalueReference(z));
    
    结果就是看不出来区别
    
    void f(std::string &&)rvalue reference overload f(rvalue reference!)
    void f(const std::string &)const lvalue reference overload f(rvalue reference!)
    void f(std::string &&)rvalue reference overload f(rvalue reference!)
    
    总而言之,rvalue和value是最接近的吧?这里我才想起来我漏了一个const reference。那么这个结果会怎么样呢?
    
    const std::string& c = "const reference"; 
        f(CopyValue(c));
        f(CopyReference(c));
        f(CopyRvalueReference(c));
        f(c);
        f(std::move(c));
    
    它的结果呢?始终只能是调用和它最匹配的重载
    
    void f(const std::string &)const lvalue reference overload f(const reference)
    void f(const std::string &)const lvalue reference overload f(const reference)
    void f(const std::string &)const lvalue reference overload f(const reference)
    void f(const std::string &)const lvalue reference overload f(const reference)
    void f(const std::string &)const lvalue reference overload f(const reference)
    
    这才是最合理的结果,它不可能变为reference,因为const reference近似就是value,绝不可能是reference,哪怕是rvalue reference。整个测试有什么是比较令人意外的呢?
  7. 回到原来作者的问题, 我想回答另一个相关的问题,假如有人同时使用pass-by-value和pass-by-rvalue-reference的重载会出现什么问题?你有 可能调用的了后者吗?编译器似乎不可能做到,因为当两者都存在的时候,两个几乎无法分出差别,pass-by-rvalue-reference永远都不 可能被调用到。
    
    void f(std::string&& x){
        std::cout << __PRETTY_FUNCTION__ << "rvalue reference overload f(" << x << ")\n";
    }
    void f(std::string x){
        std::cout << __PRETTY_FUNCTION__ << "value overload f(" << x << ")\n";
    }
    
    这里是所有可能的调用,其他都会造成编译错误,因为编译器无法选择:
    
    std::string x("lvalue reference!");
    std::string& y = x;
    std::string&& z = "rvalue reference!";
    const std::string& c = "const reference"; 
    //f(std::move("hello world temp"));   
    //f("hello world");   
    f(x);
    f(y);
    f(z);  
    f(c); 
    //f(std::move(x));
    //f(std::move(y));
    //f(std::move(z));
    f(std::move(c));
    //f(CopyValue(x));
    //f(CopyReference(x));
    //f(CopyRvalueReference(x));    
    //f(CopyValue(y));
    f(CopyReference(y));
    //f(CopyRvalueReference(y));
    f(CopyValue(c));
    f(CopyReference(c));
    f(CopyRvalueReference(c));    
    
    所有的结果都是pass-by-value
    
    void f(std::string)value overload f(lvalue reference!)
    void f(std::string)value overload f(lvalue reference!)
    void f(std::string)value overload f(rvalue reference!)
    void f(std::string)value overload f(const reference)
    void f(std::string)value overload f(const reference)
    void f(std::string)value overload f(lvalue reference!)
    void f(std::string)value overload f(const reference)
    void f(std::string)value overload f(const reference)
    void f(std::string)value overload f(const reference)
    
  8. 那么现在来试着理解作者的问题。我试图使用自己听得懂的语言来描述他的问题,就是说pass-by-value和pass-by-rvalue-reference各有什么有缺点和限制。 如果我们只定义了pass-by-rvalue-reference
    
    void f(std::string&& x){
        std::cout << __PRETTY_FUNCTION__ << "rvalue reference overload f(" << x << ")\n";
    }
    
    那么有那些调用是违法的呢?
    
        std::string x("lvalue reference!");
        std::string& y = x;
        std::string&& z = "rvalue reference!";
        const std::string& c = "const reference"; 
        f(std::move("hello world temp"));   
        f("hello world");   
        //f(x);
        //f(y);
        //f(z);  
        //f(c); 
        f(std::move(x));
        f(std::move(y));
        f(std::move(z));
        //f(std::move(c));
        f(CopyValue(x));
        //f(CopyReference(x));
        f(CopyRvalueReference(x));    
        f(CopyValue(y));
        //f(CopyReference(y));
        f(CopyRvalueReference(y));
        //f(CopyValue(c));
        //f(CopyReference(c));
        //f(CopyRvalueReference(c));    
    
    1. 对于literal或者是temp之类的临时变量,我们可以自由的传递,如果使用std::move还可能提高效率。
    2. 我们不能直接调用任何的lvalue,或者说有名有姓的变量,我们必须使用std::move,这个就是作者所说的使用者必须被迫强制使用这个,我们就有可能在这个参数的拷贝里做优化实现。
    3. 假如我们强制进行类型的转换那么我们会有限制,比如普通的lvalue变量x只能被转化为value或者rvalue reference才行:CopyValue(x),CopyRvalueReference(x),这一点对于真正的lvalue reference y也是类似的,就是说我们不可能把一个所有权没有交出去的对象作为参数传递,这个其实很重要,就是本地的变量必须做拷贝才能被传递到函数里。这个应该是好事情,但是pass-by-value何尝不是呢?只不过我们有了优化的机会吧?
    4. 令人吃惊的是我们无法使用const reference的变量来传递了,这个怎么办?但是这个原因仅仅是因为编译器认为我们的函数有可能更改传入参数因而禁止我们直接传递,就是说必须强制做一个本地的拷贝才能传参数。
    这个是pass-by-rvalue-reference的所有可能性的测试代码
  9. 那么现在我们来对比一下如果是原始的pass-by-value的结果会如何
    
    void f(std::string x){
        std::cout << __PRETTY_FUNCTION__ << "value overload f(" << x << ")\n";
    }
    
    我应该能够想到这个最原始的是最最开放的,无任何限制!
    
        std::string x("lvalue reference!");
        std::string& y = x;
        std::string&& z = "rvalue reference!";
        const std::string& c = "const reference"; 
        f(std::move("hello world temp"));   
        f("hello world");   
        f(x);
        f(y);
        f(z);  
        f(c); 
        f(std::move(x));
        f(std::move(y));
        f(std::move(z));
        f(std::move(c));
        f(CopyValue(x));
        f(CopyReference(x));
        f(CopyRvalueReference(x));    
        f(CopyValue(y));
        f(CopyReference(y));
        f(CopyRvalueReference(y));
        f(CopyValue(c));
        f(CopyReference(c));
        f(CopyRvalueReference(c));    
    
    所有的可能性都允许。
  10. 基于以上两个比较,我自己来回答作者的问题事物是相比较而生存的,没有比较就没有伤害!我们只需要看pass-by-rvalue-reference带来了哪些限制,我们就能够回答它的好处,它最大的限制,就是禁止了随手带入任何的lvalue参数的随意调用,也就是任何的f(argument);成 为非法,除非是临时变量。好处就是保障了本地变量必须经过拷贝才能被传递。但是这个是和pass-by-value相比较而言的,pass-by- value也绝对是安全的传递拷贝,但是没有任何的效率。因为毕竟rvalue-reference也是reference,它是可以做优化的拷贝,而且 还限制了const reference的传递,这一点比pass-by-value更加的严格,因为照理说你传递一个拷贝不应该有人管你,可是编译器基于rvalue- reference的参数类型就会干涉你,这个可以纠正程序员的疏忽。
  11. 总结往往是最困难的,因为要提炼,回答的帖子能得到高分是因为它足够精炼,大多数读者都没有耐心,只希望一个简单的Yes或者No,就是所有的群 众只关心谁是好人,谁是坏人一样的只有三秒钟的注意力。这么一个复杂的问题,我怎么可能仅仅用三秒钟来回答?如果要的话,那么就是尽量使用rvalue- reference,而不是笼统的const reference,原因很简单,就是它和pass-by-value没有什么本质上的区别,因为它也是来者不拒,所有的调用都是可能的,只不过它比pass-by-value有某些效率的提升而已,随后的检查也是基于语法const在编译期发现。
  12. 当然pass-by-lvalue-reference是最严格的,但是和pass-by-rvalue-reference最大的不同就在于它接受任何的lvalue的直接调用,这个就是rvalue和lvalue的最大不同。其次,当然它必须是reference了。
  13. 我折腾了两天两夜也无法给出一个让我满意的答案,甚至于我都没有完全理解作者的问题,答案就更加的不能准确的把握了。有时候理解一个问题和解决一 个问题是同样的困难,这个也许就是NP-Complete问题的深奥所在。不是所有的治国理政的复杂问题都适合电视辩论里两分钟里回答的,如果是那样子两 分钟能够回答的问题,几乎就不是一个问题了。
  14. 是否可以告一段落呢?
  15. 这个是以前下载的里耶秦简。值得保存。这个太极图书馆有很多的文献资料。

七月十四日 等待变化等待机会

  1. 因为天气热客厅空调不够冷,我就躲在卧室的空调远程登陆客厅的笔记本电脑,那么如何使用qemu启动笔记本上插的usb呢?首先,对待任何设备都当作是文件是非常好的设计。
    
    $ sudo qemu-system-x86_64 -drive file=/dev/sdb,format=raw -m 4G
    
    然后我遇到了显示的问题X11 connection rejected because of wrong authentication,这个是X11的老问题了,
    1. 首先,是确定sshd的配置允许:

      setup the following values: /etc/ssh/sshd_config

      X11Forwarding yes
      X11DisplayOffset 10
      X11UseLocalhost yes
      
    2. 其次,是xauthority的问题:
      
      export XAUTHORITY=$HOME/.Xauthority
      
    然后我就犯傻了,我单单知道能够使用fdisk/gdisk去创建usb的partition,可是我要怎么mount它们呢?如果没有创建文件系统我是 无法mount的,我怎么这么愚蠢竟然忘记了创建文件系统是mount的前提,你只要设备号就行啊!真的是白白的浪费去使用gparted,这个号称可以 改变partition的size,但是我却无法操作?难道usb不可以吗?真的是笨啊。
  2. 我本来是信心满满的,可是看了这个关于--removable,但是最后我还是照样前进:
    
    $ sudo grub-install --target=x86_64-efi --efi-directory=EFI --no-floppy --efi-directory=EFI /dev/sdb
    
    这里的EFI是EFI partition的mount point,我觉得也许这个警告不是很严重吧?
    
    Installing for x86_64-efi platform.
    grub-install: warning: EFI variables cannot be set on this system.
    grub-install: warning: You will have to complete the GRUB setup manually.
    Installation finished. No error reported.
    
    那么检验一下吧,不成功,我决定使用这个--removable试一下
    
    ¥ $ sudo grub-install --target=x86_64-efi --efi-directory=EFI --no-floppy --efi-directory=EFI --removable --boot-directory=Linux/boot
    
    这个安装做了什么呢?主要是EFI的mount point它创建了什么?
    
    EFI
    └── BOOT
        ├── BOOTX64.CSV
        ├── BOOTX64.EFI
        ├── grub.cfg
        ├── grubx64.efi
        └── mmx64.efi
    
    而这里的grub.cfg是一个桥:
    
    nick@nickZen:~/Downloads/ISO/EFI$ cat EFI/BOOT/grub.cfg
    search.fs_uuid d5c1a5d5-fc17-4022-ad5a-4cbc7f73ef8c root hd1,gpt1 
    set prefix=($root)'/boot/grub'
    configfile $prefix/grub.cfg
    

七月十五日 等待变化等待机会

  1. 至少我成功了几乎一半,按照印度航天的说法你的成功是可以按照你工作的百分数来衡量,那么永远没有失败!,因为我 大体上明白了这么一个流程,的确在USB上制作一个启动盘是相当的麻烦的。首先,我们平常依赖的BIOS/MBR很可能不太适应,这个恰恰是反直觉的,原 本USB都很小大家能够很快乐的在4G文件范围内生活使用BIOS/MBR是很容易的,可是现在动辄几十个G让我们只能选择GPT/EFI模式,那么第一 步就是制作分区,这个使用fdisk/gdisk。原本也不是什么大不了的,但是安装grub使用grub-install的时候,似乎我们要遵循--removable的选项来分别把EFI和Linux的分区都事先mount,这样子使用--boot-directories--efi-directories, 而不是让grub自己寻找分区,因为这个总是有EFI参数没有设置的警告。那么作为检验使用qemu是节省大量测试时间的,我记得以前学习驱动硬件相关的 测试的时候,最头疼的就是真实的物理运行非常的花时间,而且总是打断开发环境,尤其实际的开机重启往往自己都忘了原来要测试什么了。
    
    $ sudo qemu-system-x86_64 -drive file=/dev/sdc,format=raw  -bios /usr/share/qemu/OVMF.fd -m 4G
    
    因为我们已经使用EFI,那么意味着你要设置-bios /usr/share/qemu/OVMF.fd来模拟EFI。这里我无意中发现了我以前制作的一个USB是这么一个结构,这个让我很吃惊,似乎这个是一个好的制作思路:
    
    $ sudo gdisk -l /dev/sdc
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              64         6427779   3.1 GiB     0700  ISO9660
       2         6427780         6437919   5.0 MiB     EF00  Appended2
       3         6437920         6438519   300.0 KiB   0700  Gap1
       4         6438912        60618751   25.8 GiB    8300  
    
    就是说我的USB第一个分区是我把lubuntu的ISO拷贝进去的,这个似乎是最简单的启动盘的制作方法,就是让BIOS先完成ISO的load过程这个是误解,它根本就是一个BDP分区,因为类型0700!,这个可以直接使用ISO的机制。当然了EFI分区是少不了的。看看
    
    $ tree EFI/
    EFI/
    ├── EFI
    │   └── boot
    │       ├── bootx64.efi
    │       ├── grubx64.efi
    │       └── mmx64.efi
    └── NvVars
    
    所以,我们就理解了这里我们还是要把grub的EFI安装好,只不过所有的ISO替我们做好了,看一下这个ISO的上两层目录就明白它的设计了
    
    $ tree -L 2 Linux/
    Linux/
    ├── boot
    │   ├── grub
    │   └── memtest86+x64.bin
    ├── boot.catalog
    ├── casper
    │   ├── filesystem.manifest
    │   ├── filesystem.manifest-remove
    │   ├── filesystem.size
    │   ├── filesystem.squashfs
    │   ├── filesystem.squashfs.gpg
    │   ├── initrd
    │   └── vmlinuz
    ├── dists
    │   ├── noble
    │   ├── stable -> noble
    │   └── unstable -> noble
    ├── EFI
    │   └── boot
    ├── install
    ├── md5sum.txt
    ├── pool
    │   ├── main
    │   └── universe
    ├── preseed
    │   └── lubuntu.seed
    └── ubuntu -> .
    
    因为Lubuntu是基于ubuntu的架构的,所以,几乎和ubuntu的ISO是一样的。
  2. 然后我做了一个简单的实验就理解了这个是怎么回事的了。我很早以前下载了某个版本的lubuntu然后dd到usb,结果它就呈现出了这个样子,那么它是怎么做到的呢?当然是ISO事先要做成这个样子,也就是说ISO里面包含了所有的分区:
    
    $ sudo gdisk -l lubuntu-22.04.4-desktop-amd64.iso
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: protective
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid GPT with protective MBR; using GPT.
    Disk lubuntu-22.04.4-desktop-amd64.iso: 5996404 sectors, 2.9 GiB
    Sector size (logical): 512 bytes
    Disk identifier (GUID): B898B0C3-ED82-4099-A75A-C2C1FA62C573
    Partition table holds up to 248 entries
    Main partition table begins at sector 2 and ends at sector 63
    First usable sector is 64, last usable sector is 5996340
    Partitions will be aligned on 4-sector boundaries
    Total free space is 1 sectors (512 bytes)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              64         5985671   2.9 GiB     0700  ISO9660
       2         5985672         5995739   4.9 MiB     EF00  Appended2
       3         5995740         5996339   300.0 KiB   0700  Gap1
    
    这个时候我才意识到我很愚昧的把label名字当作了partition type,这个0700 ISO9660实际上是0700 Microsoft basic data这个类型我还是很陌生。现在想起来很多笔记本是用它做隐藏分区,而且,它不一定会被windows分配盘符,它能够承载的文件系统则不受那么多的限制,很可能是NTFS。这个是一个对于windows用户友好的分区类型,看来这个是一个好办法。
  3. 我的一个初步的感觉就是国内的操作系统都是QT来做开发的。
  4. 麒麟的iso是不能被用作USB的Image来启动的,因为它是没有额外的增加EFI分区,或者说压根儿没有安装grub。
    1. 先创建原始文件,文件大小只需要比原来的iso大5M就可以了,因为EFI的分区大小才5M
      dd if=/dev/zero of=kylin.img bs=1M count 4200
    2. 然后使用gdisk去创建这个image文件上的两个分区,0700是BDP,ef00是EFI
    3. 随后这里的做法,就是使用
      
      $ sudo losetup -Pf --show kylin.img
      
      得到loop mount,而且是分区的,比如/dev/loop18p1是BDP, /dev/loopp2是EFI,那么分别创建文件系统,mkfs.ntfs /dev/loop18p1,以及mkfs.fat /dev/loop18p2
    4. 安装grub-install,有些tricky,我先把两个分区mount在Linux和EFI目录下,然后
      
      $ sudo grub-install --target=x86_64-efi --boot-directory=Linux/boot --efi-directory=EFI /dev/loop18
      
      其实,我不知道为什么我单独使用--removable不行?我理解是BIOS/MBR要在设备上安装一个grub的boot-loader,但是对于EFI应该是不用的。所以,我不明白为什么还要设备名/dev/loop18
    5. 然后这里就是关键的问题,我没有办法搞清楚了,我只能把Kylin的ISO里的内容拷贝到BDP分区,再把它包含的EFI部分再拷贝到我创建的 EFI,这里我总觉得有可能有问题。总而言之,也许这个也是导致失败的根本原因,因为它当初设计就没有包含USB启动,纯粹的CDROM的方式。
    6. 
      $ sudo qemu-system-x86_64 -boot d -drive file=kylin.img,format=raw --bios /usr/share/qemu/OVMF.fd  -m 6
      
      这里你解决了USB启动的问题,就是说正确的进入grub引导菜单,然后实验try without install部分也成功的调用casper,我为了debug,在grub菜单里把quiet splash删除换成了serialtty=tty,可以看到启动第二阶段的log,然后在init初始化后遇到了什么/init没有找到/dev/sr0的问题,大体上就是initramfs cannot find medium containing a live filesystem的错误,我的猜想是因为这个ISO是设计成CDROM安装的介质,可能就是hardcode使用CDROM设备来运行live filesystem,所以,我就卡在了这里,我试图解开casper的filesystem.squashfs
      
      unsquashfs filesystem.squashfs
      
      然后看到的initramfs的开发文件,包含的init是一个范例,具体是怎么创建initramfs那就不是一个小工程了。探索casper自带的 initrd.img,使用cpio -iv < initrd.img看到的是firmware的检查geniun intel cpu的东西吧?
    总而言之,我尝试制作USB启动的Kylin ISO是失败的,这个问题看似古老,其实还是相当复杂的,尤其是要做到使用USB启动live filesystem需要配合casper而且内核init部分也是要改写,相当的复杂,超过了我的能力。不过好消息是它的社区开源版本是可以使用USB启动的。
  5. 我一开始对于openKylin的开源代码感到困惑,不明白为什么不是一个repo,而是几百上千个,后来才有点理解就是github被禁止了,只好国内另起炉灶搞了一个所有项目的镜像一样的大一统的gitree.com

七月二十一日 等待变化等待机会

  1. 前两天明白了一个简单的道理,就是说至少在archlinux的安装程序是能够智能的判断当前的启动模式是EFI还是BIOS,也会根据 这个启动模式来给出相应的安装启动选择。我是无意中发现我改变了笔记本的BIOS设置里的启动模式然后看到archlinux的启动菜单发生相应变化才意 识到的。要想知道自己当前是否是EFI模式应该是这样子的:
    
    $ cat /sys/firmware/efi/fw_platform_size
    
    这个方法的确是很土,但是反过来想也没有什么好办法,因为这个启动发生在操作系统正常运行之前,只能是根据当初的蛛丝马迹来判断。
  2. 我们看一下archlinux的ISO就理解了:
    
    $ sudo gdisk -l archlinux-2024.07.01-x86_64.iso
    [sudo] password for nick: 
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid MBR and GPT. Which do you want to use?
     1 - MBR
     2 - GPT
     3 - Create blank GPT
    
    Your answer: 
    
    这里就是一个典型的模糊性,因为微软之所以不再支持就是为了防止这种模糊性。这个是两可的模式,假如我选择MBR的话:
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       2         1953792         2291711   165.0 MiB   EF00  EFI system partition
    
    这个MBR模式下我们只看到一个EFI分区,如果BIOS也支持EFI启动应该也可以启动吧?可是如果我们选择GPT模式:
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              64         1953791   954.0 MiB   0700  ISOHybrid
       2         1953792         2291711   165.0 MiB   0700  ISOHybrid1
    
    我们看到的是所谓的BDP分区(0700 Microsoft basic data),这个应该是为了能够兼容window/linux的设计,反正是数据分区。可是,这里就是玄机之处:在MBR表里这个分区是EFI(,可是在GPT表里它显示的是BDP(0700)!这个是怎么做到的呢?
  3. 说来话长,没有简单的答案,我需要先学习ISO9660的文件格式。不知道是不是使用UDF文件系统?其实,细分下去真的是非常的复杂,我不想看罗密欧,也不想看朱丽叶,因为我只对这个名字El Torito有印象。
    A 32-bit PC BIOS will search for boot code on an ISO 9660 CD-ROM. The standard allows for booting in two different modes. Either in hard disk emulation when the boot information can be accessed directly from the CD media, or in floppy emulation mode where the boot information is stored in an image file of a floppy disk, which is loaded from the CD and then behaves as a virtual floppy disk.
    那么GPT呢?只能看规范:原来我在mc里看到的就是这个top level。
    System area (32,768 B) Unused by ISO 9660
    Data area
    Volume descriptor set
    Path tables, directories and files
    也就是说这里32768B都是描述文字,可以直接无障碍读的。我需要关心的是这个
    Volume descriptor (2,048 bytes)
    Part Type Identifier Version Data
    Size 1 byte 5 bytes (always 'CD001') 1 byte (always 0x01) 2,041 bytes
    检验一下modetype
    
    $ xxd -d -s 32768 -l 1 archlinux-2024.07.01-x86_64.iso
    00032768: 01                                  
    
    这个type代表什么呢?
    Basic volume descriptor types
    Value Type
    0 Boot record volume descriptor
    1 Primary volume descriptor
    2 Supplementary volume descriptor, or enhanced volume descriptor
    3 Volume partition descriptor
    255 Volume descriptor set terminator
    所以,01代表了Primary volume descriptor。它的用途是什么呢?
    An ISO 9660 compliant disc must contain at least one primary volume descriptor describing the file system and a volume descriptor set terminator for indicating the end of the descriptor sequence. The volume descriptor set terminator is simply a particular type of volume descriptor with the purpose of marking the end of this set of structures. The primary volume descriptor provides information about the volume, characteristics and metadata, including a root directory record that indicates in which sector the root directory is located.
    primary volume descriptor的data是什么样子的呢?这个和我们在mc里如果使用formated得到的是一样的吧?
    
    $ xxd -d -s 32775 -l 2041 archlinux-2024.07.01-x86_64.iso
    00032775: 0020 2020 2020 2020 2020 2020 2020 2020  .               
    00032791: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00032807: 2041 5243 485f 3230 3234 3037 2020 2020   ARCH_202407    
    00032823: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00032839: 2000 0000 0000 0000 0010 be08 0000 08be   ...............
    00032855: 1000 0000 0000 0000 0000 0000 0000 0000  ................
    00032871: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00032887: 0001 0000 0101 0000 0100 0808 00ba 0000  ................
    00032903: 0000 0000 ba36 0000 0000 0000 0000 0000  .....6..........
    00032919: 3700 0000 0022 0023 0000 0000 0000 2300  7....".#......#.
    00032935: 0800 0000 0008 007c 0701 1209 3500 0200  .......|....5...
    00032951: 0001 0000 0101 0020 2020 2020 2020 2020  .......         
    00032967: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00032983: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00032999: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033015: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033031: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033047: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033063: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033079: 2020 2020 2020 2041 5243 4820 4c49 4e55         ARCH LINU
    00033095: 5820 3c48 5454 5053 3a2f 2f41 5243 484c  X <HTTPS://ARCHL
    00033111: 494e 5558 2e4f 5247 3e20 2020 2020 2020  INUX.ORG>       
    00033127: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033143: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033159: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033175: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033191: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033207: 2020 2020 2020 2050 5245 5041 5245 4420         PREPARED 
    00033223: 4259 204d 4b41 5243 4849 534f 2020 2020  BY MKARCHISO    
    00033239: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033255: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033271: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033287: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033303: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033319: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033335: 2020 2020 2020 2041 5243 4820 4c49 4e55         ARCH LINU
    00033351: 5820 4c49 5645 2f52 4553 4355 4520 4456  X LIVE/RESCUE DV
    00033367: 4420 2020 2020 2020 2020 2020 2020 2020  D               
    00033383: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033399: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033415: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033431: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033447: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033463: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033479: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033495: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033511: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033527: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033543: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033559: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033575: 2020 2020 2020 3230 3234 3037 3031 3138        2024070118
    00033591: 3039 3030 3030 0032 3032 3430 3730 3131  090000.202407011
    00033607: 3830 3930 3030 3000 3030 3030 3030 3030  8090000.00000000
    00033623: 3030 3030 3030 3030 0030 3030 3030 3030  00000000.0000000
    00033639: 3030 3030 3030 3030 3000 0100 2020 2020  000000000...    
    00033655: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033671: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033687: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033703: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033719: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033735: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033751: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033767: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033783: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033799: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033815: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033831: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033847: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033863: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033879: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033895: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033911: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033927: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033943: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033959: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033975: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00033991: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034007: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034023: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034039: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034055: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034071: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034087: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034103: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034119: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034135: 2020 2020 2020 2020 2020 2020 2020 2020                  
    00034151: 2020 2020 2020 2020 2020 2020 0000 0000              ....
    00034167: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034183: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034199: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034215: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034231: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034247: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034263: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034279: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034295: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034311: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034327: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034343: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034359: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034375: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034391: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034407: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034423: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034439: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034455: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034471: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034487: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034503: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034519: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034535: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034551: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034567: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034583: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034599: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034615: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034631: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034647: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034663: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034679: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034695: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034711: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034727: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034743: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034759: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034775: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034791: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034807: 0000 0000 0000 0000 00                   .........
    
    换言之,primary volume descriptor就是提供了一些说明性的东西,是无格式的数据,你想怎么写都无所谓,分隔符可能就是空格而已。 标识符
    
    $ xxd -d -s 32769 -l 5 archlinux-2024.07.01-x86_64.iso
    00032769: 4344 3030 31                             CD001
    
    我们真正需要关心的是下一个volume descriptor,让我们跳过2048看看: 检验一下:
    
    $ xxd -d -s 34816 -l 1 archlinux-2024.07.01-x86_64.iso
    00034816: 00                                       .
    $ xxd -d -s 34816 -l 6 archlinux-2024.07.01-x86_64.iso
    00034816: 0043 4430 3031                           .CD001
    
    这个说明它是可以启动的,可是当我满怀好奇的看它的数据的时候只看到一个开头的
    
    $ xxd -d -s 34823 -l 2041 archlinux-2024.07.01-x86_64.iso
    00034823: 454c 2054 4f52 4954 4f20 5350 4543 4946  EL TORITO SPECIF
    00034839: 4943 4154 494f 4e00 0000 0000 0000 0000  ICATION.........
    00034855: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034871: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034887: 7200 0000 0000 0000 0000 0000 0000 0000  r...............
    00034903: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034919: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034935: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00034951: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    ...
    
    也许这个就是规范吧?
  4. 每次遇到dmesg不能执行都要修改这个默认参数:
    
    $ sudo sysctl kernel.dmesg_restrict=0
    
  5. 经过艰苦的搜索,我总算找到了问题的线索。作者的问题和我的并不相同,但是他提到了一个我没有注意的事情,就是fdisk是根本无视MBR的设置,只认当前是GPT
    
    $ fdisk -l archlinux-2024.07.01-x86_64.iso
    Disk archlinux-2024.07.01-x86_64.iso: 1.09 GiB, 1173389312 bytes, 2291776 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0xc862cd74
    
    Device                           Boot   Start     End Sectors  Size Id Type
    archlinux-2024.07.01-x86_64.iso1 *         64 1953791 1953728  954M  0 Empty
    archlinux-2024.07.01-x86_64.iso2      1953792 2291711  337920  165M ef EFI (FAT-12/16/32)
    
    这个和gdisk在选择了GPT是样的:
    
    $ gdisk -l archlinux-2024.07.01-x86_64.iso
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid MBR and GPT. Which do you want to use?
     1 - MBR
     2 - GPT
     3 - Create blank GPT
    
    Your answer: 2
    Using GPT and creating fresh protective MBR.
    Disk archlinux-2024.07.01-x86_64.iso: 2291776 sectors, 1.1 GiB
    Sector size (logical): 512 bytes
    Disk identifier (GUID): 34323032-3730-4130-B130-303830393030
    Partition table holds up to 248 entries
    Main partition table begins at sector 2 and ends at sector 63
    First usable sector is 64, last usable sector is 2291712
    Partitions will be aligned on 64-sector boundaries
    Total free space is 1 sectors (512 bytes)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              64         1953791   954.0 MiB   0700  ISOHybrid
       2         1953792         2291711   165.0 MiB   0700  ISOHybrid1
    
    fdisk识别的第一个分区类型是0而且没有名字,第二个分区是EFI或者说是(FAT-12/16/32)? 那么很明显的这两个程序在读取ISO的时候有着巨大的不同!
  6. 我才意识到我的愚蠢,根据ISO9660的文件格式,它的开头部分32768是不用的,而这里恰恰是我们储存MBR的方式,因为对于一个fdisk来说它哪里知道这个是什么结构呢?所以,首先,gdisk/fdisk能够认定它是MBR only的原因在于它说它是bootable的flag
    
    $ xxd -d -s 510 -l 2 archlinux-2024.07.01-x86_64.iso 
    00000510: 55aa                                     U.
    
    既然如此看看四个primary partition的设置吧
    
    $ xxd -d -s 446 -l 64 archlinux-2024.07.01-x86_64.iso
    00000446: 8002 0100 003f e0b9 4000 0000 c0cf 1d00  .....?..@.......
    00000462: 0000 c1ba ef3f e0ff 00d0 1d00 0028 0500  .....?.......(..
    00000478: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000494: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    
    接下来我们可以看看它的Partition_table_entries,这里只有第一个entry是有效的80,它的地址
    The range for sector is 1 through 63; the range for cylinder is 0 through 1023; the range for head is 0 through 255 inclusive.
    这个CHS地址我始终不会计算!真的是太苯了。 head=2 (because 1-5LSB in range of 0-255); sector=1 (because LSB1-5 in range of 1-63), cylinder=0 这个似乎更加的清晰,原来这个换算还有大小的区别!真的是太复杂了。
    Disk sizeSectors/trackHeadsCylinders
    1 < X ≤ 504 MiB6316X ÷ (63 × 16 × 512)
    504 MiB < X ≤ 1008 MiB6332X ÷ (63 × 32 × 512)
    1008 MiB < X ≤ 2016 MiB6364X ÷ (63 × 64 × 512)
    2016 MiB < X ≤ 4032 MiB63128X ÷ (63 × 128 × 512)
    4032 MiB < X ≤ 8032.5 MiB63255X ÷ (63 × 255 × 512)
    不对不对,这个表是BIOS的什么INT-13中断翻译,这个完全无关啊! 这个可能才是正确的做法:
    LBA and CHS equivalence with 16 heads per cylinder
    LBA valueCHS tuple
    0 0, 0, 1
    1 0, 0, 2
    2 0, 0, 3
    62 0, 0, 63
    63 0, 1, 1
    945 0, 15, 1
    1007 0, 15, 63
    1008 1, 0, 1
    1070 1, 0, 63
    1071 1, 1, 1
    1133 1, 1, 63
    1134 1, 2, 1
    2015 1, 15, 63
    2016 2, 0, 1
    16,127 15, 15, 63
    16,128 16, 0, 1
    32,255 31, 15, 63
    32,256 32, 0, 1
    16,450,559 16319, 15, 63
    16,514,063 16382, 15, 63
    那么按照这个公式: LBA = (C × HPC + H) × SPT + (S − 1)
    CHS tuples can be mapped to LBA address with the following formula:
    LBA = (C × HPC + H) × SPT + (S − 1)

    where

    • C, H and S are the cylinder number, the head number, and the sector number
    • LBA is the logical block address
    • HPC is the maximum number of heads per cylinder (reported by disk drive, typically 16 for 28-bit LBA)
    • SPT is the maximum number of sectors per track (reported by disk drive, typically 63 for 28-bit LBA)
    CHS(0,2,1)的LBA=(0*16 + 2)*63 + (1-1) = 126???这个显然不对!!! 它的partition类型是0x00是无用的! 难道说它不是用CHS吗?我们直接来读这个LBA地址:
    
    $ xxd -d -s 454 -l 8 archlinux-2024.07.01-x86_64.iso
    00000454: 4000 0000 c0cf 1d00                      @.......
    
    这个也看上去不对啊!
  7. 我像无头苍蝇一样的碰壁,回过头来看:
    
    $ fdisk -l -x archlinux-2024.07.01-x86_64.iso
    Disk archlinux-2024.07.01-x86_64.iso: 1.09 GiB, 1173389312 bytes, 2291776 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0xc862cd74
    
    Device                           Boot   Start     End Sectors Id Type               Start-C/H/S  End-C/H/S Attrs
    archlinux-2024.07.01-x86_64.iso1 *         64 1953791 1953728  0 Empty                    0/2/1  953/63/32    80
    archlinux-2024.07.01-x86_64.iso2      1953792 2291711  337920 ef EFI (FAT-12/16/32)     954/0/1 1023/63/32 
    
    我使用-x发现我的数据CHS(0/2/1)是对的,但是怎么计算结果不是64?吃晚饭再说吧?

七月二十二日 等待变化等待机会

  1. 第一步先学习正确读取CHS的方法: 这个是开头的CHS的数据
    
    $ xxd -d -s 447 -l 3 archlinux-2024.07.01-x86_64.iso
    00000447: 0201 00                              ...
    
    这个是结束的CHS数据
    
    $ xxd -d -s 451 -l 3 archlinux-2024.07.01-x86_64.iso
    00000451: 3fe0 b9                                  ?..
    
    这个是读取的方法:
    h70 head
    x x x x x x x x
    c98 s50 sector in bits 50; bits 76 are high bits of cylinder
    x x x x x x x x
    c70 bits 70 of cylinder
    x x x x x x x x
    所以,已知Head=2, Sector=1, Cylinder=0,根据什么公式计算得到LBA=64 Sectors?我再次看这个表才明白这里有变化和不变化的地方。就是说对于Sectors/track总是63,对于H=2怎么也算不出64。

    如果看结束的地址:Head = 0x3F=63, Sector= 0xE0 & 0x3F = 0x20 = 32, Cylinder = (0xE0 & 0xC0) << 2 | 0xb9 = 0x3B9 = 953

    那么sector/Head = 63, head/cylinder=32:

    那么根据公式 LBA = (953*32+63)* 63 + (32-1) =

    就在我完全陷入混乱的时候,Linux的祖师爷的名言点醒了我:RTFC!遇事不明就去看源代码! 于是,我下载了fdisk的源代码,看到原来Start/End是读出来的,不是计算的:
    
    struct dos_partition {
    	unsigned char boot_ind;		/* 0x80 - active */
    	unsigned char bh, bs, bc;	/* begin CHS */
    	unsigned char sys_ind;
    	unsigned char eh, es, ec;	/* end CHS */
    	unsigned char start_sect[4];
    	unsigned char nr_sects[4];
    } __attribute__((packed));
    
    #define MBR_PT_OFFSET		0x1be
    #define MBR_PT_BOOTBITS_SIZE	440
    
    static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
    {
    	return (struct dos_partition *)
    		(mbr + MBR_PT_OFFSET + (i * sizeof(struct dos_partition)));
    }
    
    也就是说我们直接读MBR作为一个结构,它的start_sect是4个byte,主要看我们怎么翻译了,这里当然是little endian的!
    
    static inline uint32_t __dos_assemble_4le(const unsigned char *p)
    {
    	uint32_t last_byte = p[3];
    
    	return p[0] | (p[1] << 8) | (p[2] << 16) | (last_byte << 24);
    }
    
    static inline unsigned int dos_partition_get_start(struct dos_partition *p)
    {
    	return __dos_assemble_4le(&(p->start_sect[0]));
    }
    
    所以,这里对于MBR,我们根本不去使用它的CHS来计算,(我模糊记得除非start_sector有特殊标记我们才计算CHS吧?总之,先不管它了。)
    
    $ xxd -d -s 454 -l 8 archlinux-2024.07.01-x86_64.iso
    00000454: 4000 0000 c0cf 1d00                      @.......
    
    所以,这个起始地址是0x40000000,转为little endian就是0x40=64!我简直要吐了,我模糊记得我上一次就是在这里翻车,还模糊记得应该不要纠缠CHS,可是,这一次大概是一眼看过去脑子没 有设置little endian的翻译认为这个是无厘头,就无视了!害死人啊!我怀疑大概是写CHS数据的大侠把HS的顺序弄颠倒了才把我害死了!同理结束地址分区大小就是0x001dcfc0=1953728,这个就对了!我简直要气吐血了,一个早上就这么折腾没了!!!这里我根本不用看代码就知道这个size是end-start+1end=start+size-1=64+1953728-1=1953791计算出来的!
  2. 可是问题是为什么fdisk能找到第二个EFI分区?从partition entry来看第二个应该是不能使用的才对啊?难道是我英语读错了
    Status or physical drive (bit 7 set is for active or bootable, old MBRs only accept 0x80, 0x00 means inactive, and 0x01–0x7F stand for invalid)
    也就是说即便是inactive也是合法的,也要显示出来?那么这样就合理了:
    
    $ xxd -d -s 446 -l 64 archlinux-2024.07.01-x86_64.iso
    00000446: 8002 0100 003f e0b9 4000 0000 c0cf 1d00  .....?..@.......
    00000462: 0000 c1ba ef3f e0ff 00d0 1d00 0028 0500  .....?.......(..
    00000478: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000494: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    
    第二个分区的类型ef对应于代码
    
    MBR_EFI_SYSTEM_PARTITION	= 0xef, /* Intel EFI System Partition */
    

    起始地址是0x001dd000=1953792,大小是0x00052800=337920,结束地址是1953792+337920-1=2291711

    BINGO!
  3. 这个是针对天真无邪的fdisk,那么对于老谋深算的gdisk逻辑是什么呢?我估计这个就是windows放弃这个选项的原因,因为我们继续发掘是否是MBR-protective,这个不是MBR本身能够做到的,这个是我们发现了GPT后才能作出这个结论吧?GPT入口在哪里?
    
    $ xxd -d -s 512 -l 8 archlinux-2024.07.01-x86_64.iso
    00000512: 4546 4920 5041 5254                      EFI PART
    
    所以,的确这个是有GPT分区表的,这个就间接证明了之前的MBR是被protective的。它的分区表在哪里呢?每个有多大呢?
    
    $ xxd -d -s 584 -l 8 archlinux-2024.07.01-x86_64.iso
    00000584: 0200 0000 0000 0000                      ........
    
    一般都是2,所以,我们的偏移是512*2=1024
    
    $ xxd -d -s 596 -l 4 archlinux-2024.07.01-x86_64.iso
    00000596: 8000 0000 
    
    表的大小通常都是0x80=128

    那么看看第一个分区的类型吧,这个是一个GUID:

    
    $ xxd -d -s 1024 -l 16 archlinux-2024.07.01-x86_64.iso
    00001024: a2a0 d0eb e5b9 3344 87c0 68b6 b726 99c7  ......3D..h..&..
    
    这个要去查表,GUID很啰嗦不是直接这么little endian或者big endian,中间要转换,很麻烦。总而言之是BDP(Basic data partition EBD0A0A2-B9E5-4433-87C0-68B6B72699C7)
    Previously, Linux used the same GUID for the data partitions as Windows (Basic data partition: EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). Linux never had a separate unique partition type GUID defined for its data partitions. This created problems when dual-booting Linux and Windows in UEFI-GPT setup. The new GUID (Linux filesystem data: 0FC63DAF-8483-4772-8E79-3D69D8477DE4) was defined jointly by GPT fdisk and GNU Parted developers. It is identified as type code 0x8300 in GPT fdisk.
    原来这个是专门给windows的data partition,而Linux后来也有了自己的0x8300作为data partition!

    接下来看看这个分区的partition GUID和它的起始LBA

    ArchLinux的开发者偷懒的设置?
    
    $ xxd -d -s 1040 -l 16 archlinux-2024.07.01-x86_64.iso
    00001040: 3230 3234 3037 3041 b131 3038 3039 3030  2024070A.1080900
    
    起始地址和结束地址
    
    $ xxd -d -s 1056 -l 16 archlinux-2024.07.01-x86_64.iso
    00001056: 4000 0000 0000 0000 ffcf 1d00 0000 0000  @...............
    
    起始地址=0x40=64;结束地址=0x1dcfff=1953791;和之前的fdisk看到的一样。所以,这里的谜团都解开了。
  4. 我太累了懒得再看第二个分区了,估计就是这样子的。总而言之,你要是想要兼容MBR/BIOS和GPT/UEFI那么就是要在分区表上做文章,这个还都是小问题,关键是启动程序里要出现两种不同的菜单,相应不同的安装程序。这个才是工作量很大的地方。休息吧。
  5. 这个是受同学发来的某中文系大侠的佳作激发而作,附原诗在后
    夏月有感
    
    中秋未至月未圆,
    满腹闲愁不忍观。
    老来有酒邀双影,
    少时无邪呼玉盘。
    清辉只因心已冷,
    圆缺却为相思缠。
    敢问嫦娥何所思?
    应悔盗药月月惭1
    1: 厦门老刘建议改为“应悔盗药夜夜惭“?我给出的解释是:惭者,残也,指的是每个月都有残月圆月,应是人心辗转反侧左右难全所致,说的是嫦娥心绪。
    《邂逅重瓣晚樱》
    
    暮春重瓣晚樱稠,
    粉白嫣红韵自羞。
    杜牧春晚惜花落,
    陶潜地偏采菊悠。
    静极未惧时光老,
    寂照何忧岁月流。
    鞠躬尽瘁心自在,
    拈花一笑山更幽。
    

七月二十三日 等待变化等待机会

  1. archLinux的制作是很高明的,我现在还不明白是怎么回事,先记录下来:
    1. 首先,我们把ISO losetup mount一下出现了两个设备:
      
      $ sudo losetup -Pf --show archlinux-2024.07.01-x86_64.iso
      /dev/loop43
      $ ll /dev/loop43*
      brw-rw---- 1 root disk   7, 43 Jul 23 07:11 /dev/loop43
      brw-rw---- 1 root disk 259,  9 Jul 23 07:11 /dev/loop43p1
      brw-rw---- 1 root disk 259, 10 Jul 23 07:11 /dev/loop43p2
      
    2. 然后我们分别把这两个设备都mount一下:
      
      $ sudo mount /dev/loop43p1 Linux/
      $ sudo mount /dev/loop43p2 EFI
      
    3. 然后我开始比较这两个目录下的情况,让我大吃一惊,因为表面上看完全一样:
      
      $ ll Linux
      total 1957
      dr-xr-xr-x 1 root root    2048 Jul  2 02:09 ./
      drwxrwxr-x 9 nick nick    4096 Jul 21 08:17 ../
      dr-xr-xr-x 1 root root    2048 Jul  2 02:09 arch/
      dr-xr-xr-x 1 root root    2048 Jul  2 02:09 boot/
      dr-xr-xr-x 1 root root    2048 Jul  2 02:09 EFI/
      dr-xr-xr-x 1 root root    2048 Jul  2 02:09 loader/
      -r--r--r-- 1 root root  941056 Jul  2 02:09 shellia32.efi
      -r--r--r-- 1 root root 1048448 Jul  2 02:09 shellx64.efi
      $ ll EFI
      total 1950
      drwxr-xr-x 6 root root     512 Jan  1  1970 ./
      drwxrwxr-x 9 nick nick    4096 Jul 21 08:17 ../
      drwxr-xr-x 3 root root     512 Jul  2 04:09 arch/
      drwxr-xr-x 3 root root     512 Jul  2 04:09 boot/
      drwxr-xr-x 3 root root     512 Jul  2 04:09 EFI/
      drwxr-xr-x 3 root root     512 Jul  2 04:09 loader/
      -rwxr-xr-x 1 root root  941056 Jul  2 04:09 shellia32.efi*
      -rwxr-xr-x 1 root root 1048448 Jul  2 04:09 shellx64.efi*
      
    4. 我一开始还以为losetup把同样的分区mount了两遍?再深入比较一下:
      
      $ diff -qr Linux/ EFI
      Only in Linux/arch: grubenv
      Only in Linux/arch: pkglist.x86_64.txt
      Only in Linux/arch: version
      Only in Linux/arch: x86_64
      Only in Linux/boot: 2024-07-01-18-09-00-00.uuid
      Only in Linux/boot: grub
      Only in Linux/boot/memtest86+: memtest
      Only in Linux/boot: syslinux
      
      而且不要真的以为它们是同样的分区,不,它们是两个分区:
      
      $ df -h | grep loop43
      /dev/loop43p1                      954M  954M     0 100% /home/nick/workspace/Downloads/ISO/Linux
      /dev/loop43p2                      163M  157M  6.3M  97% /home/nick/workspace/Downloads/ISO/EFI
      
      所以,这个确实是在GPT分区表里的两个分区,这里当然是把MBR看作protective才获得的结果。
    5. 这个文件(Linux/boot/grub/loopback.cfg)相当的重要,它是archLinux能够产生不同启动菜单的一个核心吧?
      A loopback.cfg is basically just a grub.cfg that's designed to be used to boot a live distribution from an iso file on a filesystem rather than an actual physical CD.
      但是这个开宗明义的说明让我感到困惑,我开始怀疑这个不是必须的,也许是开发者电脑中的配置文件,在ISO内其实没有用?

      First some background:

      Most GNU/Linux distributions' LiveCDs support being booted with the iso file on a hard drive所以,这个根本不是在ISO里面的必须的,是在开发者电脑进行调试ISO的, without needing to actually burn the image to a CD. This works not by "chainloading" the iso (which is impossible) but by extracting the kernel from the iso, either before hand or at boot time with grub2's "loopback" command. The kernel is then passed a parameter that tells it that it needs to look for its root not in /dev/cdrom, but by loop mounting an iso file. This parameter also tells the kernel (more precisely the initrd scripts) what path the iso can be found at.

      The problem is that to actually boot such a distribution you need to know the path to the kernel / initrd within the iso但是这个功能是怎么获得的呢?看起来似乎是必须的?, the particular kernel parameter that distribution uses to specify the path to the iso, and any other kernel parameters relevant to the distribution. Even knowing that, when you boot the iso you don't get the menu and options you'd get when booting the CDROM. With a loopback.cfg you get the full menu of options at boot and don't have to worry about differences between different distributions.

      这个实在是太神奇了,怎么知道path to the kernel / initrd within the iso

七月二十四日 等待变化等待机会

  1. 我决定先从感性理解archLinux的这个installer的工作机制。如果我们使用BIOS/MBR方式启动就会看到
    
    $ qemu-system-x86_64 -boot d -cdrom archlinux-2024.07.01-x86_64.iso -m 4G
    
  2. 而相应的如果我们使用GPT/EFI模式启动看到的就是另一个界面,这个能力我本来以为是所有installer都能自动设置的,现在才明白这个是有高超的技巧才能做到:
    
    $ qemu-system-x86_64 -boot d -bios /usr/share/qemu/OVMF.fd -cdrom archlinux-2024.07.01-x86_64.iso -m 4G
    
  3. 那么在MBR/BIOS模式下它能够进入的partition显然的应该就是fdisk/gdisk看到的那个分区,现在来证实一下:启动后进入一个图形界面Hardware Information(HDT),原来这个是syslinux的命令行,这个是我今天才了解的。

    SYSLINUX is a boot loader for the Linux operating system which runs on an MS-DOS/Windows FAT filesystem. It is intended to simplify first-time installation of Linux, and for creation of rescue and other special purpose boot disks.

    When properly configured, SYSLINUX can be used to completely eliminate the need for distribution of raw boot floppy images. A SYSLINUX floppy can be manipulated using standard MS-DOS (or any OS capable of accessing an MS-DOS filesystem) tools once it has been created.

    就是说如果启动分区的文件系统是FAT我们才需要安装syslinux。实际上EFI分区就是本质上FAT文件系统,因为它要兼容所有的操作系统就选择windows才能支持的文件系统微软公司里兼容的概念就是你用我的方式。 这里的DISK概念很重要,就是说对于syslinux它必须是工作在一个文件系统,这个听上去是废话,我现在不理解的是它的boot loader在哪里?它说不改变MBR,那bootsector在哪里呢?这里从制作启动盘就能理解它就是建立在DOS-bootable的基础上,就是说专门为了MBR基础上的启动模式,而mkdiskimage之所以需要CHS参数就是假定我们不使用fdisk/gdisk之类的Linux工具吗?这个计算我真的是受够了,实在是太麻烦了,而且还要考虑磁盘大小来改变CHS的参数!
  4. 我像无头苍蝇一样的碰壁,我可以看出来MBR是使用了isolinux.bin,因为有这样的错误信息字句,那么它就是使用了syslinux来 做bootloader吗?这一点要怎么证明呢?原因是我在syslinux的安装包里没有找到这个文件?所以,这个纯粹是子虚乌有,apt-file告 诉我是有一个包叫做isolinux,我实在是孤陋寡闻啊!然后我在这个包里看到这个文档,就是说它是syslinux的一部分,并不是syslinux这个安装包的一部分。

    mbr.bin is a regular boot code for msdos-like partition tables.

    altmbr.bin is also for msdos-style partition tables, but it boots from a partition with a given fixed number, ignoring the "boot" flag -- see below.

    gptmbr.bin is a boot code that can be used on a drive with a GPT partition scheme.

    同时另一个文档我还是不太理解什么是isohybrid的意义?我看到这一段话才明白ISO能够在USB上实现启动也不是自然而然的,是有努力的。
    Although disk storage devices are usually booted via SYSLINUX, there is an isohybrid feature, which makes the ISO bootable from such devices (e.g. USB stick), in addition to DVD.
    回过头来看我才明白,所谓的isohybrid就是说它原本的确是一个符合iso9660的文件系统,但是我们钻了一个空子,因为它的开头部分是不用的, 我们把MBR/GPT分区都装在前面的两个sector里,结果就是hybrid了。这个是我的理解,应该是对的。我花了好几天才搞明白这么简单的道理。
  5. 有必要从头学习什么是isolinux
    ISOLINUX is a boot loader for Linux/i386 that operates off ISO 9660/El Torito CD-ROMs in "no emulation" mode. This avoids the need to create an "emulation disk image" with limited space (for "floppy emulation") or compatibility problems (for "hard disk emulation").
    这个言简意赅的定义信息量是很多的,首先,它就是一个bootloader,而且是仅仅支持Linux/i386的,这一点和grub2鲜明的对比,后者 支持众多的模式。其次它只能是很有限的CDROM格式启动,什么罗密欧和朱丽叶之类的都不支持了,我猜想吧,而且明确的不支持模拟软盘硬盘模式。这些都是 非常的硬核的玩意儿。
  6. 我看syslinux的东西越看越迷茫,主要是它的用途和我想象的完全不是一回事,我为什么要做这些呢?然后我重新搜索我想要的应该是这个archiso吧?这里是源代码,但是ubuntu没有类似的。我现在唯一的收获就是这个iso确实是用这个工具制作的这个就是废话!而它是一个高度特殊化的脚本集合,以及大量的配置文件,别的系统几乎无法使用。只能参考它的方法而已。其中的运行测试使用qemu我可以学习一下使用的参数,就是这个run_archiso.sh。还有一些细节,比如对于isolinux,如果我们只是安装它的mbr,那么就是使用dd把它的这个/usr/lib/syslinux/bios/mbr.bin写到设备里,这个的确是最简单的安装mbr的方法。因为我之前一直在想怎么把syslinux的bootloader安装到这个iso里形成所谓的hybrid。源代码有很详细的注释,可以学习很多东西。
  7. 出乎我的意料,syslinux的源代码是内核维护的!而且ubuntu官方的版本是6.04比kernel最新的6.03还要新!

七月二十五日 等待变化等待机会

  1. 在syslinux的文档里有这么一个非常重要的说明,是关于GPT boot protocol
    There are two ways to boot a GPT-formatted disk on a BIOS system. Hybrid booting, and the new GPT-only booting protocol originally proposed by the author, and later adopted by the T13 committee in slightly modified form.
    那么先看看hybrid booting是如何的,这里的hybrid和我理解是不同的,看来我是误解了。这一段文字极其难懂,也极其重要。
    Hybrid booting uses a standard MBR, and has bootable ("active") partitions present, as partitions, in the GPT PMBR sector. This means the PMBR, instead of containing only one "protective" partition (type EE), may contain up to three partitions: a protective partition (EE) *before* the active partition, the active partition, and a protective partition (EE) *after* the active partition. The active partition is limited to the first 2^32 sectors (2 TB) of the disk.
    我们需要回忆一下什么是protective partition(EE) 也就是EFI GPT protective MBR?而且注意到对于EE和它的兄弟EF(EFI system partition)都没有规定访问方法是否是基于CHS的。就是说它可以第一个是protective,然后是真正的Active,然后还可以有一个protective:
    
    $ xxd -s 446 -l 64 archlinux-2024.07.01-x86_64.iso 
    000001be: 8002 0100 003f e0b9 4000 0000 c0cf 1d00  .....?..@.......
    000001ce: 0000 c1ba ef3f e0ff 00d0 1d00 0028 0500  .....?.......(..
    000001de: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001ee: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    
    我怎么看都不符合这个hybrid,第一个分区是所谓的active(or bootable)80,但是它的类型是无效00。而第二个分区是inactive(00),而它的类型却是ef(EFI system partition),这个实在是非常的令人困惑。我认为这个不符合hybrid,那么看看new protocol怎么说吧。
  2. 实际上PMBR是一个从GPT的角度看问题的产物,所谓保护是在GPT下才有可能保护MBR,至少逻辑上是这样子的,所以,它的定义是在GPT里的:
    A single partition of type EEh, encompassing the entire GPT drive (where "entire" actually means as much of the drive as can be represented in an MBR), is indicated and identifies it as GPT. Operating systems and tools which cannot read GPT disks will generally recognize the disk as containing one partition of unknown type and no empty space, and will typically refuse to modify the disk unless the user explicitly requests and confirms the deletion of this partition. This minimizes accidental erasures. Furthermore, GPT-aware OSes may check the protective MBR and if the enclosed partition type is not of type EEh or if there are multiple partitions defined on the target device, the OS may refuse to manipulate the partition table.
    所以,保护是说我们设置的MBR有一个类型为EE的分区,它的大小要覆盖整个磁盘,至少是MBR能够表达的最大覆盖,

    What is a Protective MBR?

    The Protective MBR, beginning in sector 0, precedes the GPT partition table on the disk. The MBR contains one type 0xEE partition that spans the disk.

    这个是言简意赅的,只不过没有明确的说你要制作MBR的分区去覆盖整个磁盘,但是span the disk就是这个意思。另一个有意义的是UEFI的规定:

    What about mixing and matching GPT and MBR disks on the same system?

    GPT and MBR disks can be mixed on systems that support GPT, as described earlier. However, you must be aware of the following restrictions:

    • Systems that support UEFI require that boot partition must reside on a GPT disk. Other hard disks can be either MBR or GPT.
    • Both MBR and GPT disks can be present in a single dynamic disk group. Volume sets can span both MBR and GPT disks.
    这一点如果是UEFI的规定,那就是一个硬指标,你可以玩的花样是很有限的,MBR只能作为数据存储磁盘,除非它是protective。只不过我觉得archLinux没有严格的遵守PMBR的要求。 我想这里比较一下ubuntu的PMBR是如何的。
    
    $ xxd -s 446 -l 64 ubuntu-24.04-desktop-amd64.iso 
    000001be: 0000 0200 ee3f e0ff 0100 0000 1f3b b600  .....?.......;..
    000001ce: 8000 0100 0000 0100 0000 0000 0100 0000  ................
    000001de: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001ee: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    
    我不禁赞叹ubuntu是真正严格执行了协议的:
    1. 首先,为了安全起见,直接把MBR的这个唯一的分区设为00inactive,
    2. 然后它的CHS的起始地址是sector为2(我认为sector都是从1开始记数的,所以还是1),结束的CHS是0x3fe0ff ,先不去计算直接看后面给的绝对LBA地址: 起始是1,这个意味着所有的磁盘都包含?那么有多少个sector呢?是0x1f3b b600,转为little endian的十进制是11942687₁₀
    3. 让我们用磁盘工具看看整个磁盘有多大:
      
      $ gdisk -l ubuntu-24.04-desktop-amd64.iso 
      GPT fdisk (gdisk) version 1.0.8
      
      Partition table scan:
        MBR: protective
        BSD: not present
        APM: not present
        GPT: present
      
      Found valid GPT with protective MBR; using GPT.
      Disk ubuntu-24.04-desktop-amd64.iso: 11942688 sectors, 5.7 GiB
      Sector size (logical): 512 bytes
      Disk identifier (GUID): 1CF7D054-8DB5-4194-971E-3C8665684807
      Partition table holds up to 248 entries
      Main partition table begins at sector 2 and ends at sector 63
      First usable sector is 64, last usable sector is 11942624
      Partitions will be aligned on 4-sector boundaries
      Total free space is 1 sectors (512 bytes)
      
      Number  Start (sector)    End (sector)  Size       Code  Name
         1              64        11931883   5.7 GiB     0700  ISO9660
         2        11931884        11942023   5.0 MiB     EF00  Appended2
         3        11942024        11942623   300.0 KiB   0700  Gap1
      
      
      11942687-11942624=63,这个说明什么呢?就是说作为MBR里第一个分区不但标记为protective也就是EE,而且这个分区覆盖 了整个磁盘可用大小,因为总是要扣除分区表本身的63个sector吧?反正我是觉得不管起始扇区是1也好,2也好,总的大小让任何磁盘工具都感到恐怖, 因为整个磁盘都在这个分区里面,没有任何剩余空间,你想干什么都要删除这个分区才行吧?
  3. 从比较fdisk和gdisk的结果让我产生了查询gdisk文档的想法,这个还真的有很多有意义的资料
    Hybrid MBR 101

    As noted on other pages in this document, a conventional GPT disk contains a protective MBR with a single partition, of type 0xEE (EFI GPT), defined. This partition spans the entire size of the disk or 2 TiB, whichever is smaller. The intent is to keep GPT-unaware OSes and utilities from trying to modify the disk.

    A hybrid MBR is a variant on the normal protective MBR. A hybrid MBR contains a type-0xEE partition, but it also contains up to three additional primary partitions, which point to the same space that's marked out by up to three GPT partitions. For instance, suppose you've got a Macintosh that dual-boots macOS and an older version of Windows (Windows 7 or earlier). MacOS is happy on GPT, and so can use GPT partition definitions; but Windows versions prior to Windows 8 (on Macs) are less capable in this respect. Thus, you'll define your partitions first as GPT partitions (including your Windows partitions), and then you'll modify your protective MBR so that its 0xEE partition is smaller than normal and it contains one to three partition definitions that point to the same disk locations as corresponding GPT partitions. You can then install Windows on these hybridized partitions. In the past, Apple's Boot Camp helped automate this process, so you don't need GPT fdisk to set up a hybrid MBR on a Mac; however, GPT fdisk can be useful in maintaining your hybrid MBR after it's configured, and you might want to use it on BIOS-based computers for similar configurations involving other OSes. On any computer, the end result is that GPT-unaware OSes can use up to three primary partitions, while GPT-aware OSes can use all the partitions on the disk.

    什么叫做its 0xEE partition is smaller than normal and it contains one to three partition definitions that point to the same disk locations as corresponding GPT partitions?令人费解啊。 这里有大量的阅读要做,我先歇一些吧。这个是作者的关于grub-legacy和grub2的说法
    They appear to be very fussy about hybrid MBRs, though. In particular, if the EFI GPT (0xEE) partition resides anywhere but in the first position of the MBR partition table, these boot loaders treat the disk as if it were MBR-only. The likely result is that some or all of your OSes won't boot. For this reason, if you expect to use GRUB or GRUB 2 with a hybrid MBR, I suggest you place the EFI GPT partition first in the partition table.
    这个英语的anywhere but in the first position of the MBr partition table是说不放在第一位的话就是MBR-only。这个似乎不适用于archlinux因为它不是用grub*,而是syslinux。总而言之,我应该会大概率使用grub2,那么需要把EE这个protective MBR的partition放在第一位。
  4. new protocol非常的难以理解:
    The (P)MBR format is the normal PMBR specified in the UEFI documentation, with the first 440 bytes used for the boot code. The partition to be booted is marked by setting bit 2 in the GPT Partition Entry Attributes field (offset 48); this bit is reserved by the UEFI Forum for "Legacy BIOS Bootable".
    这个说的是这里的GPT partition entry attribute field。而所谓的bit 2是这个attribute。 但是这里我是完全摸不着头脑,因为如果是BIOS而且还要支持GPT才能有这个能力啊?也许作者瞄准的就是这个,可是针对不明白GPT的呢?那就是直接使 用MBR没有办法了,只能支持最大2T的磁盘,而据说大部分的问题是使用BIOS明白GPT但是不理解UEFI的boot。 这一段文字实在是太深奥了,我觉得这个是bootloader的工作,对于我们是否需要理解呢?我的理解就是archLinux也没有遵守这个新协议,你 需要设定为0x80bootable啊!那么让我再确认一下它是否在GPT partition entry里设定了hybrid的标志。
    
    $ xxd -s 1072 -l 8 archlinux-2024.07.01-x86_64.iso 
    00000430: 0100 0000 0000 0010                      ........
    
    这里的bit 2也没有set,所以,也不符合Legacy BIOS bootable
  5. 太多的知识点了,这个ESP我看到很多遍,现在才理解它的用途。

    What is the Extensible Firmware Interface System Partition (ESP)?

    The ESP contains the NTLDR, HAL, Boot.txt, and other files that are needed to boot the system, such as drivers. The Partition GUID defines the ESP:

    DEFINE_GUID (PARTITION_SYSTEM_GUID, 0xC12A7328L, 0xF81F, 0x11D2, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B)

    The ESP is approximately 100MBs.

    The ESP should be first on the disk. While there is no architectural requirement, there are numerous reasons why it is beneficial to place the ESP first. The primary reason for this is that it is impossible to span volumes when the ESP is logically between the two data partitions that you are attempting to span.

  6. 作者推荐了一个当时新的提案,我想就是之前提到的new protocol吧?我保存一份
  7. 我终于知道我关于hybrid的模糊概念是从哪里来的,在isolinux里它把能够从iso9660/usb/hd等不同媒体都能启动称之为hybrid,而在很多其他地方大家更加注重的是BIOS/UEFI的混合启动模式的hybrid。当然我个人认为这些都是非常好的东西,都不容易。

七月二十六日 等待变化等待机会

  1. 我要反思,要反思,因为感到迷茫,对于syslinux完全不熟悉导致迷茫。可是问题是我需要它吗?首先,我不需要在windows下安 装双重启动,其次,我对于lubuntu那种和windows共存的模式也不感兴趣,我最初的问题仅仅是openKylin不能USB安装启动而已,那么 聚焦grub和所谓的ISO9660/USB的混合模式(hybrid)就好了。这个是一个直接而显而易见的问题,入手也容易,验证也容易i,因为我现在 才意识到我对于ubuntu的安装ISO所知甚少,它在UEFI模式下的启动菜单我看到了,可是在BIOS下的启动菜单在哪里?这个才是问题的核心,它为 什么是混合的?我决定使用fdisk的混合模式来观察,在进入protective/hybrid MBR模式下看到了PMBR的分区表:
    
    Disk ubuntu-24.04-desktop-amd64.iso: 5.69 GiB, 6114656256 bytes, 11942688 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x00000000
    
    Device                          Boot Start      End  Sectors  Size Id Type
    ubuntu-24.04-desktop-amd64.iso1          1 11942687 11942687  5.7G ee GPT
    ubuntu-24.04-desktop-amd64.iso2 *        0        0        1  512B  0 Empty
    
    这里就是之前看到的PMBR的情况,第一个分区是全部的ISO文件,因为总共的大小是11942688 sectors,而它的起始结束分区就是1和11942687,这里我觉得可能我还是有些不太清楚,就是说sector是从1开始计数,那么总共的个数难道是不包含PMBR本身吗?这里又是一个英语理解的问题,作为非母语脑子总是转不过弯来,它谈的是在GPT下的识别,但是这里是BIOS如果只读到PMBR是什么反应?在DOS模式下应该拒绝启动吧?
  2. 在徒劳无功的搜索之后我才意识到只有一个grub菜单,那就是那个grub.cfg,因为它有条件变量,所以,在不同模式下有不同的菜单,所以,问题就是为什么ubuntu的iso能够在不同模式下启动的老问题了,我想这个问题我应该已经大概知道答案了。
    
    set timeout=30
    
    loadfont unicode
    
    set menu_color_normal=white/black
    set menu_color_highlight=black/light-gray
    
    menuentry "Try or Install Ubuntu" {
            set gfxpayload=keep
            linux   /casper/vmlinuz  --- quiet splash
            initrd  /casper/initrd
    }
    menuentry "Ubuntu (safe graphics)" {
            set gfxpayload=keep
            linux   /casper/vmlinuz nomodeset  --- quiet splash
            initrd  /casper/initrd
    }
    grub_platform
    if [ "$grub_platform" = "efi" ]; then
    menuentry 'Boot from next volume' {
            exit 1
    }
    menuentry 'UEFI Firmware Settings' {
            fwsetup
    }
    else
    menuentry 'Test memory' {
            linux16 /boot/memtest86+x64.bin
    }
    fi
    
    这个原本是一个简单的问题,我却想复杂了。
  3. 无意中找到关于创建winXP image的相关方法,以后再说吧。
  4. 这位大侠在debug qemu loading bios,这个以后有兴趣再看吧。
  5. 所以,又回到了原点,怎么样制作ISO的混合模式。不如我先从原先忽略的更加基本的如何保证ISO9660格式下的启动?我觉得我还是从基本做起,学习制作ubuntuLiveCD,ubuntu最大的好处就是资料全而且非常多的普通人都验证过了。

七月二十七日 等待变化等待机会

  1. 听闻台风已登陆,遂披雨衣走山海步道采风,浑身湿透得五言律诗一两首,甚慰。
    《雨中行步道》
    
    雨中漫步道,
    物比人更亲。
    山是我知音,
    海与我同心。
    雨来心正静,
    风吹体更轻。
    流连景不尽,
    踟蹰脚不停。
    
    不甚满意又得
    《步道行》
    
    雨中行步道,
    山远绿翠新。
    雨骤心却静,
    风狂体更轻。
    流连山亲近,
    踟蹰海知音。
    人言台风至,
    风雨我独行。
    
    又重读旧作
    《心》
    
    斜月三星伴,
    意马何物牵?
    喜怒忧思怨,
    灵台方寸间。
    
    最后这一首身为满意遂分享给同学品评
    《雨中行》
    
    远眺山知性,
    近观海同心。
    雨骤幽曲径,
    风狂独孤亭。
    山耸临风定,
    海阔迎波兴。
    小憩平心憶,
    昂首任我行11:我以为改作昂头我独行似乎更好,但是总有人不以为意。
    
    然则最后两句似嫌突兀,有人建议纯写景,遂又改
    《雨中行》
    
    远眺山知性,
    近观海同心。
    雨骤幽曲径,
    风狂独孤亭。
    草低知风劲,
    云乱见天惊。
    山耸临风定,
    海阔迎波兴。
    
    这一首随意吟之
    《雨中偶得》
    
    长风猎猎雨潇潇,
    独享曲径正逍遥。
    路转桥头低头看,
    雨打芭蕉更妖娆。
    
    早晨起来,决定再改一改:
    《雨中行》
    
    雨中步道行,
    路旧景却新。
    草低知风劲,
    云乱见天惊。
    雨骤幽曲径,
    风狂独孤亭。
    猎猎风啸啸,
    潇潇雨淋淋。
    雨急心却静,
    风紧体更轻。
    山耸临风定,
    海阔迎波兴。
    远眺山亲近,
    近听海知音。
    回首景不尽,
    仰天心意平。
    凭栏侧耳听,
    风雨催不停。
    
    渥太华的同学用chatGPT帮我修改了,也不错:
    《雨中行》
    
    雨中步道行,
    旧路焕新景。
    草低风力劲,
    云乱天威惊。
    雨骤曲径幽,
    风狂孤亭静。
    猎猎风声啸,
    潇潇雨滴淋。
    雨急心尤定,
    风紧体愈轻。
    山耸迎风立,
    海阔随波兴。
    远眺山常近,
    近听海有音。
    回首景无限,
    仰天心意平。
    凭栏侧耳听,
    风雨催不停。
    
  2. 这个multi-boot的介绍太简单了,我觉得我要先明白ISO9660或者说它的扩展El Torito是如何启动的。终于找到了这个看起来权威的ISO9660-bootable文档保存一份。不到20页(十进制:),但是这个阅读会是很辛苦的。
    1. 复习一下
      ISO-9660 defines that a “Primary Volume Descriptor” must reside at sector 10 (16 decimal), relative to the start of the session, followed by any number of other Volume Descriptors, followed by a “Volume Descriptor Set Terminator.”
      在偏移16*512=8192在碰壁以后我才想起来在ISO的世界里sector是2K,所以,偏移是16*2K=32K但是这个是在ISO9660开头32768不用的基础上的根据ISO9660它就是32768的偏移,所以,验证一下麒麟的iso的Primary Volume Descriptor
      
      $ xxd -d -s 32768 -l 7 Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso
      00032768: 0143 4430 3031 01                        .CD001.
      
      注意这里的01类型表明它是primary
    2. The El Torito Bootable CD Specification builds on this format by requiring a “Boot Record” Volume Descriptor as defined in section 8.2 of ISO-9660. See figure 7 for a description of the Boot Record. This “Boot Record” must reside at sector 11 (17 decimal) in the last session on the CD.
      那么看看偏移17*2K=34816
      
      $ xxd -d -s 34816 -l 7 Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso
      00034816: 0043 4430 3031 01                        .CD001.
      
      这里的00类型表明它是boot record volume descriptor
    3. he Boot Record contains an absolute pointer to the Boot Catalog.
      真的吗?首先还是要了解这个boot record volume descriptor结构 这个表一定是错的,偏移量的计算难道数数都不对吗?EL TORITO SPECIFICATION这几个字母加上空格怎么数都是23,而表里说是7-26的偏移,大概是在EL TORITO的小旅馆里喝多了吧?我怀疑是使用hex的结果,因为下面的boot catalogure的绝对地址就是十六进制!
      
      O$ xxd -d -s 34816 -l 30 Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso
      00034816: 0043 4430 3031 0145 4c20 544f 5249 544f  .CD001.EL TORITO
      00034832: 2053 5045 4349 4649 4341 5449 4f4e        SPECIFICATION
      
      瑕不掩瑜,boot catalogue绝对地址对了就行,这里的47应该是十六进制,所以偏移是71+34816=34887
      
      $ xxd -d -s 34887 -l 8 Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso
      00034887: c700 0000 0000 0000                      ........
      
      转换为little endian是在199*2K=407552
    4. The Boot Catalog is a collection of 20 byte entries (as described below), packed 40 entries to the sector. There is no limit to the number of sectors the Boot Catalog uses.

      There are 5 types of entries diagrammed in figures 2-6. These entries define a validation procedure for the bootable CD, an Initial/Default entry, a section header, a section entry, and a Section Entry Extension.

      看看是不是boot catalogue,首先是Initial/Default entry错!首先是validation entry
      This must be the first entry in the boot catalog. The Validation entry validates that a booting catalog is present on the disk and identifies the manufacturer of the CD. If this entry is valid, it is assumed that the rest of the entries are valid.
      
      $ xxd -d -s 407552 -l 32 Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso
      00407552: 0100 0000 0000 0000 0000 0000 0000 0000  ................
      00407568: 0000 0000 0000 0000 0000 0000 aa55 55aa  .............UU.
      
      它的定义是 我对这里的定义也表示怀疑,到底谁是对的?为什么偏移1C-1D 不是应该0吗?Checksum Word. This sum of all the words in this record should be 0. 为了验证是不是麒麟犯了错,我决定看看ubuntu或者archLinux的做法: 先找到ubuntu的boot catalogue的绝对地址:
      
      $ xxd -d -s 34887 -l 8 ubuntu-24.04-desktop-amd64.iso
      00034887: b403 0000 0000 0000                      ........
      
      换算成偏移是948*2K=1941504,然后看看它的validation entry是怎么样的:
      
      $ xxd -d -s 1941504 -l 32 ubuntu-24.04-desktop-amd64.iso
      01941504: 0100 0000 0000 0000 0000 0000 0000 0000  ................
      01941520: 0000 0000 0000 0000 0000 0000 aa55 55aa  .............UU.
      
      这里的确是 加总计算checksum没有任何花梢,可是为什么是aa55呢?查看了archLinux,大家都是一样,看来是我错了,我不会计算checksum, 我不会加法!原来是这样子的: 0x0001+0x0000+...+0x55aa+0xaa55=0x10000,但是0x10000溢出了,作为word它的确是0。这句英文我还是 理解错误,这个就是英语非母语吃亏的地方,它说的是如果把validation entry的所有的word加起来总和必须是0,并不是这个checksum的位置是0,这个是完全无意义的!这个都是基本的常识,我因为不熟悉大家的习 惯所以很费解。
    5. 接下来是所谓的Initial/Default Entry
      The initial entry must contain a boot image which consists of generic programs/drivers that use only the BIOS provided INT 13 interface. The BIOS INT 13 interface consists of functions 0-19 and may optionally include functions 40-48. This entry will always be used by a BIOS that does not use any of the provided section headers.
      
      $ xxd -d -s 407584 -l 32 Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso
      00407584: 8800 0000 0000 0400 7719 1f00 0000 0000  ........w.......
      00407600: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      
      所以,88代表bootable的,00代表No Emulation,所谓的sys type指的是MBR里的partition entry偏移5的类型,在这个表里它是0代表没有用? 我为了保险验证起见,又用midnight commander来读取了一下
    6. 一张图有千言万语!
    但是看这个我并没有任何的长进,我依然不知道怎么制作bootable。
  3. 这个是很多概念性的说明,具体的做法呢?

    The EL Torito Boot Catalog can offer in the same ISO filesystem alternative boot images for PC-BIOS and for EFI.

    But El Torito is interpreted by the firmware only if presented on an optical medium: CD, DVD, BD. For booting PC-BIOS and EFI from USB stick or other hard-disk-like devices, there is need for an MBR and, if desired, for a GPT.

    The machine code of the MBR of SYSLINUX can be adjusted to jump with its execution onto the BIOS El Torito boot image of ISOLINUX. This is known as "isohybrid". The MBR and BIOS El Torito boot image written by grub-mkrescue have the same capability. The MBR partition table does not matter for the first stage of BIOS booting.

    这里说了什么呢?就是说单纯的BIOS是只有在CD/DVD/BD媒介上才能起作用,这个当然是废话吧?这个文件格式本来就是瞄准这些媒介的吗?如果要使用USB,那么必须使用MBR,这个就是麒麟官方ISO不能使用USB安装的根本原因,因为没有MBR的帮助是不可能的

    UEFI regulates the relation of MBR partition table and GPT. So for booting via EFI firmware, the partition tables do matter. The MBR machine code is to be ignored by EFI, unless in Legacy BIOS mode.

    The most modern UEFI layout prescribes an MBR with a single partition which starts at Logical Block Address 1 and covers the whole image size.这就是PMBR的定义! It shall have MBR partition type 0xee. In this case there is a GUID Partition Table (GPT) which in one of its partitions marks the EFI System Partition with the FAT filesystem. Neither MBR partition nor GPT partitions can lead to an address where the ISO filesystem can be mounted, unless the ISO offers a second set of volume descriptors and file tree at a higher block address. Script grub-mkrescue produces this layout for booting via EFI.

    The UEFI legacy partition layout (not to be confused with Legacy BIOS mode) prescribes that the EFI System Partition shall be marked by an MBR partition of type 0xef. Other MBR partitions are permissible. E.g. one which starts at Logical Block Address 0 to make the ISO mountable. It is not allowed, nevertheless, to let the ISO partition enclose the EFI System Partition.

    这一段都是精华,可是我就看不大懂了,什么叫做UEFI legacy partition layout (not to be confused with Legacy BIOS mode)?UEFI有BCS兼容模式但是和这个也不一样吧?
    Several popular Linux distributions offer a layout that does not comply to either of the UEFI alternatives. The MBR marks the whole ISO by a partition of type 0x00. Another MBR partition of type 0xef marks a data file inside the ISO filesystem with the image of the EFI System Partition FAT filesystem. Nevertheless there is a GPT which also marks the EFI System Partition image file. This GPT is to be ignored by any UEFI compliant firmware. The nesting of the MBR partitions is made acceptable by giving the outer MBR partition the type 0x00, which UEFI specifies to be ignored.
    这一段我就直接看吐了,我学习了好几天,但是这些都是模模糊糊,没有系统的清晰的图画,现在我更加的糊涂了。

七月二十八日 等待变化等待机会

  1. 就是说我面临两个选择,一个是漫长的而且还没有很大希望的,一切从头来, 但是我已经看到最后作者实际上不知道可以制作hybrid启动模式,而且一切从源代码或者即便是debootstrap也是非常有难度的,因为最核心的问 题还是在最后打包生成ISO的过程,目前我已经知道麒麟是使用了isolinux,但是它没有用其他的比如syslinux之类做混合,所以另外的选项似 乎更容易,另一个似乎更加的切合实际,就是针对原来的ISO做customization,其中的关键就在于Hybrid Setup for BIOS and EFI from CD/DVD and USB stick的提示部分,使用
    
    xorriso -as mkisofs \
      ... \
      -c isolinux/boot.cat \
      -b isolinux/isolinux.bin \
      -no-emul-boot -boot-load-size 4 -boot-info-table \
      -isohybrid-mbr ./syslinux/usr/lib/syslinux/isohdpfx.bin \
      -eltorito-alt-boot \
      -e boot/grub/efi.img \
      -no-emul-boot -isohybrid-gpt-basdat \
      ... \
      ./boot1 \
      ...
    
    ,尤其是这一个选项-eltorito-alt-boot 是叫你给出另一个启动的所有的参数。但是这里的空白之处并非那么容易,所以,这个官方的说明方式更加的好,这里的这个反向得到麒麟的命令行可以试一下:
    
    $ sudo xorriso -indev Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso  -report_el_torito cmd
    [sudo] password for nick: 
    xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project.
    
    xorriso : NOTE : Loading ISO image tree from LBA 0
    xorriso : UPDATE :     548 nodes read in 1 seconds
    xorriso : NOTE : Detected El-Torito boot information which currently is set to be discarded
    Drive current: -indev 'Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso'
    Media current: stdio file, overwriteable
    Media status : is written , is appendable
    Boot record  : El Torito
    Media summary: 1 session, 2110114 data blocks, 4121m data, 1218g free
    Volume id    : 'Kylin-Desktop-V10-SP1'
    -volid 'Kylin-Desktop-V10-SP1'
    -volume_date uuid '2024040715475200'
    -boot_image any cat_path='/isolinux/boot.cat'
    -boot_image isolinux bin_path='/isolinux/isolinux.bin'
    -boot_image any platform_id=0x00
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=2048
    -boot_image any boot_info_table=on
    -boot_image any next
    -boot_image any efi_path='/boot/grub/efi.img'
    -boot_image any platform_id=0xef
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=29360128
    
    这里的Detected El-Torito boot information which currently is set to be discarded是怎么回事呢?比较一下ubuntu吧,看来这个消息是无害的。
    
    $ sudo xorriso -indev ubuntu-24.04-desktop-amd64.iso  -report_el_torito cmd
    xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project.
    
    xorriso : NOTE : Loading ISO image tree from LBA 0
    xorriso : UPDATE :    1210 nodes read in 1 seconds
    libisofs: NOTE : Found hidden El-Torito image for EFI.
    libisofs: NOTE : EFI image start and size: 2982971 * 2048 , 10140 * 512
    xorriso : NOTE : Detected El-Torito boot information which currently is set to be discarded
    Drive current: -indev 'ubuntu-24.04-desktop-amd64.iso'
    Media current: stdio file, overwriteable
    Media status : is written , is appendable
    Boot record  : El Torito , MBR protective-msdos-label grub2-mbr cyl-align-off GPT
    Media summary: 1 session, 2985672 data blocks, 5831m data, 1218g free
    Volume id    : 'Ubuntu 24.04 LTS amd64'
    -volid 'Ubuntu 24.04 LTS amd64'
    -volume_date uuid '2024042411290900'
    -boot_image grub grub2_mbr=--interval:imported_iso:0s-15s:zero_mbrpt,zero_gpt:'ubuntu-24.04-desktop-amd64.iso'
    -boot_image any partition_table=on
    -boot_image any partition_cyl_align=off
    -boot_image any partition_offset=16
    -boot_image any mbr_force_bootable=on
    -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b --interval:imported_iso:11931884d-11942023d::'ubuntu-24.04-desktop-amd64.iso'
    -boot_image any appended_part_as=gpt
    -boot_image any iso_mbr_part_type=a2a0d0ebe5b9334487c068b6b72699c7
    -boot_image any cat_path='/boot.catalog'
    -boot_image grub bin_path='/boot/grub/i386-pc/eltorito.img'
    -boot_image any platform_id=0x00
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=2048
    -boot_image any boot_info_table=on
    -boot_image grub grub2_boot_info=on
    -boot_image any next
    -boot_image any efi_path='--interval:appended_partition_2_start_2982971s_size_10140d:all::'
    -boot_image any platform_id=0xef
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=5191680
    
    而archLinux的做法似乎更加的复杂,因为我看到它有isohybrid的选项,这个做法和ubuntu不同,难道ubuntu是两个分别的iso合成的?
    
    $ sudo xorriso -indev archlinux-2024.07.01-x86_64.iso  -report_el_torito cmd
    xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project.
    
    xorriso : NOTE : ISO image bears MBR with  -boot_image any partition_offset=16
    xorriso : NOTE : Loading ISO image tree from LBA 0
    xorriso : UPDATE :     110 nodes read in 1 seconds
    libisofs: NOTE : Found hidden El-Torito image for EFI.
    libisofs: NOTE : EFI image start and size: 488448 * 2048 , 0 * 512
    xorriso : NOTE : Detected El-Torito boot information which currently is set to be discarded
    Drive current: -indev 'archlinux-2024.07.01-x86_64.iso'
    Media current: stdio file, overwriteable
    Media status : is written , is appendable
    Boot record  : El Torito , MBR isohybrid cyl-align-off GPT
    Media summary: 1 session, 572944 data blocks, 1119m data, 1218g free
    Volume id    : 'ARCH_202407'
    -volid 'ARCH_202407'
    -volume_date uuid '2024070118090000'
    -boot_image isolinux system_area=--interval:imported_iso:0s-15s:zero_mbrpt,zero_gpt:'archlinux-2024.07.01-x86_64.iso'
    -boot_image any partition_cyl_align=off
    -boot_image any partition_offset=16
    -boot_image any mbr_force_bootable=on
    -append_partition 2 0xef --interval:imported_iso:1953792d-2291711d::'archlinux-2024.07.01-x86_64.iso'
    -boot_image any part_like_isohybrid=on
    -boot_image any iso_mbr_part_type=0x00
    -boot_image any cat_path='/boot/syslinux/boot.cat'
    -boot_image isolinux bin_path='/boot/syslinux/isolinux.bin'
    -boot_image any platform_id=0x00
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=2048
    -boot_image any boot_info_table=on
    -boot_image any next
    -boot_image any efi_path='--interval:appended_partition_2_start_488448s_size_337920d:all::'
    -boot_image any platform_id=0xef
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=0
    -boot_image isolinux partition_entry=gpt_basdat
    
    如果是openKylin呢?我的猜测是openKylin和ubuntu的做法类似,而我的初步测试是它不论是CDROM还是USB都可以启动BIOS/UEFI,总之是全能。
    
    $ sudo xorriso -indev openKylin-1.0.2-x86_64.iso -report_el_torito cmd
    xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project.
    
    xorriso : NOTE : Loading ISO image tree from LBA 0
    xorriso : UPDATE :     376 nodes read in 1 seconds
    xorriso : NOTE : Detected El-Torito boot information which currently is set to be discarded
    Drive current: -indev 'openKylin-1.0.2-x86_64.iso'
    Media current: stdio file, overwriteable
    Media status : is written , is appendable
    Boot record  : El Torito , MBR isohybrid cyl-align-all GPT APM
    Media summary: 1 session, 1942080 data blocks, 3793m data, 1218g free
    Volume id    : 'openKylin-1.0'
    -volid 'openKylin-1.0'
    -volume_date uuid '2024031414052600'
    -boot_image isolinux system_area=--interval:imported_iso:0s-15s:zero_mbrpt,zero_gpt,zero_apm:'openKylin-1.0.2-x86_64.iso'
    -boot_image any partition_cyl_align=all
    -boot_image any partition_offset=0
    -boot_image any partition_hd_cyl=238
    -boot_image any partition_sec_hd=32
    -boot_image any mbr_force_bootable=on
    -append_partition 2 0xef --interval:imported_iso:7768320d-7783551d::'openKylin-1.0.2-x86_64.iso'
    -boot_image any apm_block_size=2048
    -boot_image any iso_mbr_part_type=0x00
    -boot_image any cat_path='/isolinux/boot.cat'
    -boot_image isolinux bin_path='/isolinux/isolinux.bin'
    -boot_image any platform_id=0x00
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=2048
    -boot_image any boot_info_table=on
    -boot_image any next
    -boot_image any efi_path='/boot/grub/efi.img'
    -boot_image any platform_id=0xef
    -boot_image any emul_type=no_emulation
    -boot_image any load_size=4096000
    -boot_image isolinux partition_entry=gpt_basdat
    -boot_image isolinux partition_entry=apm_hfsplus
    
  2. 所以,现在就比较明确了,我能不能使用类似的方法来重建一个ISO呢?这个说起来容易,纯粹就是给了一个命令:把大像装进冰箱!
  3. 第一步我想先从比较openKylin和ubuntu做起,也许它们两个是最接近的。
    
    $ fdisk -l openKylin-1.0.2-x86_64.iso 
    Disk openKylin-1.0.2-x86_64.iso: 3.71 GiB, 3985514496 bytes, 7784208 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x374ebf56
    
    Device                      Boot   Start     End Sectors  Size Id Type
    openKylin-1.0.2-x86_64.iso1 *          0 7768319 7768320  3.7G  0 Empty
    openKylin-1.0.2-x86_64.iso2      7768320 7783551   15232  7.4M ef EFI (FAT-12/16/32)
    
    虽然ubuntu有三个分区,我以为第三个是不重要的只是为了cylinder对齐的,我印象中在哪里看到过是一个选项。
    
    O$ fdisk -l ubuntu-24.04-desktop-amd64.iso 
    Disk ubuntu-24.04-desktop-amd64.iso: 5.69 GiB, 6114656256 bytes, 11942688 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: gpt
    Disk identifier: 1CF7D054-8DB5-4194-971E-3C8665684807
    
    Device                             Start      End  Sectors  Size Type
    ubuntu-24.04-desktop-amd64.iso1       64 11931883 11931820  5.7G Microsoft basic data
    ubuntu-24.04-desktop-amd64.iso2 11931884 11942023    10140    5M EFI System
    ubuntu-24.04-desktop-amd64.iso3 11942024 11942623      600  300K Microsoft basic data
    
    它们都是混合的MBR/GPT分区表,似乎EFI也是一样的吧,下面先比较两个内嵌的第一分区的ISO吧?因为EFI分区可以肯定是结构相同的,而且另一 个以前我没有想过的是为什么要在第一个ISO分区里同样有一个和EFI分区一样的EFI文件目录有着和EFI一样的EFI启动文件呢?这个我在之前的 grub安装的时候是使用--efi-directory的命令来指定路径,但是似乎是不用拷贝吧?因为linux启动的时候是把它mount在这里,就 是说分区是mount的而不需要拷贝:
    
    $ mount | grep  \/boot\/efi
    /dev/nvme0n1p1 on /boot/efi type vfat (rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
    
    而我们通常的文件系统是把EFI装在第一个分区
    
    $ sudo fdisk -l /dev/nvme0n1
    Disk /dev/nvme0n1: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
    Disk model: WD Blue SN570 2TB                       
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: gpt
    Disk identifier: D107060B-0D72-4008-A966-22D0BE31C1FB
    
    Device           Start        End    Sectors  Size Type
    /dev/nvme0n1p1    2048    1050623    1048576  512M EFI System
    /dev/nvme0n1p2 1050624 3907028991 3905978368  1.8T Linux filesystem
    
    启动的时候只是把它mount到/boot目录下的efi。似乎在回答我的一些疑惑,这里大概的意思我猜测是这样子的,在你安装grub的时候,这个grubx64.efi是需要你告诉EFI在哪里mount,如果不说默认就是在/boot/efi或者后来更流行的/efi,总之,这个是EFI启动的一个步骤,它作为bootloader的一部分首先是要把自己mount在一个相对于主文件系统,也就是内核所在的文件系统,从那个所谓的root才 能pivot交权给内核,所以,在grub-install需要顺便指定一下。这里是我的猜测,如果我们使用BIOS/MBR自然是不需要关心这个问题, 只有在UEFI模式下,在GPT表里指向了bootable的ESP,那么这个不可能是一个mount point,只能是分区,如果是分区照理说grubx64.efi不应该存在于/boot/efi目录下,我怀疑这个是多余的,也许是制作ISO无意识的 拷贝进来的?我不确定,这个是一个很重要的问题,只能在实践中验证了。
  4. 在这里我顺便验证我自己的本机的启动设置,它是支持UEFI的,原因是MBR是没有bootcode的,是一个纯粹的PMBR,也就是只设置了几个基本的点,一个是分区表,一个是MBR最后的55AA标志
    
    $ sudo xxd -d -s 446 -l 16 /dev/nvme1n1 
    00000446: 0000 0200 eeff ffff 0100 0000 ffff ffff  ................
    
    设定ee表示PMBR,并且分区大小覆盖整个磁盘防止别的工具无意识篡改。 而其余部分都是0。而这一段话的深刻含义怎么理解都不够:
    
    All modern personal computer operating systems support GPT. Some, including macOS and Microsoft Windows on the x86 architecture, support booting from GPT partitions only on systems with EFI firmware, but FreeBSD and most Linux distributions can boot from GPT partitions on systems with either the BIOS or the EFI firmware interface.
    
    字数越少越重要:
    1. 现代计算机都支持GPT。这个结论我以前还是模糊的,我以前一直认为存在一种古老的计算机,它只能使用BIOS模式启动 而且不支持GPT磁盘格式,只认MBR磁盘格式,这个应该只在很古老的一小段时间或者是我的头脑中存在。即便是早期的windows它也可以支持GPT的 磁盘作为数据盘,只是不能启动而已,能不能解读和能不能使用它启动是两个概念。
    2. 微软和苹果放弃了在EFI模式下支持MBR启动了,只能用GPT的分区表来寻找启动分区。这个也是一个我有些模糊认识的地方,EFI是有BCS的 照理说它可以使用MBR的分区表的,但是从现代存储发展的角度来看这个的确是麻烦,因为MBR支持的磁盘分区有限导致以后扩招存储受限,所以,作为成熟给 傻瓜使用的操作系统来说决定不支持是有商业考量的。而从纯技术角度其实模糊性并不是第一位的,相信微软和苹果的工程师不是不能解决一点点的小小的模糊,无 非是几行代码的试错而已,为什么Linux要支持?毕竟还有老系统,而且Linux本来就允许程序员自己拿枪打自己的脚,你这么做有你自己的道理,没人拦 着你。
    3. 但是就在我以为我已经完全明白这短短的几句话的时候,我又糊涂了。应该这么理解,就是要么是GPT/UEFI,要么就是MBR/BIOS,(我原来就是这么理解的,为什么糊涂了呢?)原因是BIOS/GPT没有那么简单。
    4. 这个帖子我给120分!这里 BIOS/GPT不是那么简单做到的,首先,BIOS理解GPT吗?我不确定,应该是不懂,因为之前所说的微软苹果的早期系统也支持GPT是在操作系统级 别的,那个是bootloader交给了OS之后的事情,在BIOS层面根本没有发生,所以,这个帖子说的很清楚,这个是bootloader比如 grub帮你做到的,首先,如果是BIOS,意味着你只能在MBR里的bootsector里写上grub的bootloader让它去帮你识别GPT, 如果有的话。

      The BIOS generally doesn't care anything about your hard drives1. It simply loads the MBR and transfer control to the boot loader in MBR. Therefore technically it'll be possible to boot a GPT drive in BIOS mode, because the GPT drive still has a protective MBR at the beginning. You just need a bootloader that supports GPT disks (such as Grub and many other Linux bootloaders)

      1There are some buggy BIOSes that eagerly unnecessarily do things they aren't supposed to do like checking the MBR signature or the active boot flag and refuse to boot on such drives

      顺便说一下MBR的signature和little/big endian应该是无关的,标准里没有说你要把它当作一个word,它是bytecode。当然写汇编的要使用word那就是它们的麻烦事情了。而至于说 boot flag。这个问题也一直困扰着我,因为从BIOS或者说MBR规范的角度看如果它不是0x80,而是PMBR的0x00的话,BIOS是应该拒绝启动 的,
      Some BIOSes test if the boot flag of at least one partition is set, otherwise they ignore the device in boot-order. Therefore, even if thebootloader does not need the flag, it has to be set to start the boot code from BIOS.
      就是说即便bootloader压根儿不考虑,因为它是知道这个是GPT/PMBR,但是启动它的BIOS不答应,那么bootloader也就没有机会 运行了。所以,这个是PMBR的困扰,我常常想应不应该用0x80替代0x00?当然这个会迷惑UEFI的BCS以为这个是真正的MBR/BIOS模式而 不去寻找GPT表?
    5. 但是真正困难的地方才出现,如果要支持BIOS/GPT模式,你的bootloader需要做的工作很多,决不是440个byte就能写完的,通 常在MBR模式下真正的分区通常至少有128或者2048等等之后,因为要和cylinder对齐,所以,你可以放心大胆的把bootloader写在 MBR的512之后,可是对于GPT表就在这里那怎么办?

      However, here a small problem arises. On MBR drives the boot loaders often cheat a bit by storing a part of them in the next sectors called "MBR gap", "boot track", or "embedding area" which are often left empty by disk partitioning tools. On a GPT disk the sectors right after the MBR are GPT data structures, hence can't be used for that purpose and you need to create a small BIOS Boot Partition for Grub to store its data

      所以,你不能幻想GPT的表有多长你可以安全的写其余部分,因此必须创建一个特别的分区,这个就是安装程序里额外要创建BIOS Boot Partition的原因,
      When used, the BIOS boot partition contains the second stage of the boot loader program, such as the GRUB 2; the first stage is the code that is contained within the Master Boot Record (MBR). Use of this partition is not the only way BIOS-based boot can be performed while using GPT-partitioned hard drives; however, complex boot loaders such as GRUB 2 cannot fit entirely within the confines of the MBR's 398 to 446 bytes of space, thus they need an ancillary storage space. On MBR disks, such boot loaders typically use the sectors immediately following the MBR for this storage; that space is usually known as the "MBR gap". No equivalent unused space exists on GPT disks, and the BIOS boot partition is a way to officially allocate such space for use by the boot loader.
      这里我才真正明白我在安装lubuntu的时候为什么遇到这个“grub-bios"的原因,因为我在笔记本的BIOS设置成了BIOS启动模式,因此安装菜单识别了我的机器如果想要用GPT磁盘格式就只能这样子来额外的要求存储空间来方grub的其余部分。
    6. 我发现archLinux的wiki写的非常的详尽,我只是扫了一眼,以后再看吧
    7. 当然对于UEF模式根本就不需要这个BISO Boot Partition,它需要的是一个bootable的EFI partition,这个需要在GPT表里标定。创建这个BBP有一个具体的指示:

      Create a megabyte partition (+1M with fdisk or gdisk) on the disk with no file system and with partition type GUID 21686148-6449-6E6F-744E-656564454649.

      • Select partition type BIOS boot for fdisk.
      • Select partition type code ef02 for gdisk.
      • For parted set/activate the flag bios_grub on the partition.

      GUID Partition Table (GPT) specific instructions

      非常的详尽,很好。
    8. 这个部分需要大量的阅读,我目前还没有精力:

      Grub also supports hard coding the sector that contains the next stage so it can boot without a post-MBR gap or BIOS boot partition, but that's fragile because you need to update Grub after every OS update. Therefore this isn't recommended

      For more information you can read

    9. 这个部分我就有点不太清楚了,难道MBR能够突破极限吗?

      Another way is to convert the GPT drive back to MBR if your HDD is not too big. In fact it's possible to have MBR disks above 2 TB, upto ~233 sectors (i.e. 4 TB and 16 TB for disks with 512-byte and 4096-byte sector respectively) with a big partition lasting from just before the half disk margin. There are multiple tools to do the conversion without loss of data like gdisk, MiniTool Partition Wizard, AOMEI Partition Assistant, EaseUS Partition Master... (I'm not affiliated with any of them).

      磁盘上限是由于MBR采用32位磁盘寻址造成的:
      The 2-TB barrier is the result of this 32-bit limitation. Because the maximum number that can be represented by using 32 bits is 4,294,967,295, it translates to 2.199 TB of capacity by using 512-byte sectors (approximately 2.2 TB). Therefore, a capacity beyond 2.2 TB isn't addressable by using the MBR partitioning scheme.
      这个在我看来是无解的,为什么能够突破呢?我暂时不想去深究了,今天实在是有点累了。
    今天早上似乎就解决了一个小问题。但是收获还是很多的,尤其是温故而知新。
  5. 也只有现在我才真正理解了bootloader的多个stage,第一个stage往往就是这个MBR的限制造成的,它在440左右的空间里的 code就能够把second stage装到内存里运行而已,那么第二阶段做的事情就是grub的很多工作最后还是要把linux内核本身装进内存运行,我猜想内核本身也是一个 bootloader,这些环环相扣紧密协作异常的复杂。
  6. 我发现在目前最好的学习方式是阅读xorrisofs的man page,这个是GNU的原生项目,似乎和grub一样都是样板项目吧?我也不知道从哪里得到的印象。原来Rock Ridge是这个功能,我一直对于罗密欧与朱丽叶之类的搞不清楚,只知道El Torito这个古怪名字是为了启动设计的。所以,Rock Ridge是为了Unix文件系统的额外功能设计的。
    Rock Ridge is the name of a set of additional information which enhance an ISO 9660 filesystem so that it can represent a POSIX compliant filesystem with ownership, access permissions, symbolic links, and other attributes. Rock Ridge allows filenames of up to 255 bytes and paths of up to 1024 bytes.
    看来朱丽叶仅仅是提供了文件树,这个还是一个比较简单的功能。
    Joliet is the name of an additional directory tree which provides filenames up to 64 characters encoded as UTF-16. A Joliet tree is mainly interesting for reading the ISO image by operating systems of Microsoft Corporation. Production of this directory tree may be enabled by option -J.
    至于HFS+这个是为了苹果准备的,我就没有多大兴趣了。至于说这里提到的工具mkisofs我感觉它是一个参考代码的角色,而且它的功能局限于文件系统层级和我关心的领域关系不大,还是一个底层机制。且慢,它的很多参数是非常关键的,xorrisofs understands options of program mkisofs from cdrtools by Joerg Schilling. Its implementation is part of program xorriso which shares no source code with cdrtools.,就是说其实我不一定要明白mkisofs的命令,因为xorrisofs重新实现了一遍所以它才能明白这些选项。不过两个manpage参照阅读也是好的。
  7. 看了这个才让我吃惊,原来还是这个东西
    xorrisofs is actually a command mode of program xorriso, which gets entered either by xorriso command "-as mkisofs" or by starting the program by one of the names "xorrisofs", "mkisofs", "genisoimage", or "genisofs".
    可是在我的系统里/usr/bin/mkisofs -> genisoimage,而/usr/bin/xorrisofs -> xorris,而这两个家伙/usr/bin/xorriso,/usr/bin/genisoimage是不同的程序啊!难道说它们之间达到了互操作级别的兼容?

七月二十九日 等待变化等待机会

  1. 遇到登陆我的旧笔记本电脑的问题,报出错误
    
    debug1: kex: host key algorithm: (no match)
    Unable to negotiate with 192.168.1.12 port 22: no matching host key type found. Their offer: ssh-rsa,spki-sign-rsa
    
    我知道要加上这些key,但是是什么参数man page里找不到,还是这里说的好:
    ssh -v -oHostKeyAlgorithms=+ssh-rsa username@ipaddress
    这些问答网站的内容很不错,但是国内访问可能有问题,我本来想使用kiwix的离线库来存储镜像,可是单单stackoverflow的zim文件就是80G,这个真的是太大了,而且没有查询功能的static html怎么使用也是一个问题。
  2. 我无意中发现这个帖子,值得研究一下:
    BIOS-mode GRUB on MBR -- This is the traditional PC configuration. In it, GRUB is split into multiple stages. The first stage resides in the first 440 bytes of the Master Boot Record (MBR). This first stage then loads and executes a second stage of GRUB, which typically resides in the sectors immediately following the MBR. This space is officially unallocated on most MBR disks, so putting GRUB code there is a bit risky; but it usually works OK. Additional code resides in files, typically in the /boot/grub directory of the OS used to install GRUB. There are numerous variants on this configuration possible. For instance, putting the GRUB first stage in a Partition Boot Record (PBR; the first sector of a partition) was once popular, but is pretty uncommon today, and in fact I'm not 100% sure that modern GRUB 2 still supports this option.
    这个基本上就是我昨天体会到的,虽然以前模模糊糊知道,但是没有这么清晰的理解。
    BIOS-mode GRUB on GPT -- In this variant, the first stage of GRUB still resides in the MBR (which for GPT is known as a protective MBR, which primarily exists to stop GPT-unaware tools from messing with the disk). The sectors following the MBR on a GPT disk, though, are GPT data structures and so cannot be used by GRUB. Instead, GRUB 2 relies on a partition known as the BIOS Boot Partition, which has a GPT type code of 21686148-6449-6E6F-744E-656564454649 ("bios_grub flag" set in libparted-based tools, or type EF02 in GPT fdisk). Additional files reside in the /boot/grub directory, as with BIOS/MBR GRUB installations.
    这个是我之前尝试安装lubuntu时候遇到的情况,当我无意中把笔记本的BIOS摄制成只能才有BIOS启动模式的时候,在安装程序里会建议打钩这个bios_grub 。但是我觉得安装程序可能有问题因为我在自定义安装而不是使用全部硬盘空间的情况下总是失败。
    EFI-mode GRUB on MBR -- In this configuration, the first GRUB code resides in a GRUB EFI binary stored on the EFI System Partition (ESP; type code 0xEF on an MBR disk). This file could be named anything, but it's typically called grubx64.efi in a subdirectory of EFI named after the distribution (such as EFI/ubuntu/grubx64.efi), or sometimes EFI/BOOT/bootx64.efi (the "fallback filename," most commonly used on bootable external media like USB drives). As with BIOS-mode GRUB, additional configuration and driver files reside elsewhere, typically in /boot/grub on the installation OS; however, some distributions put these file on the ESP alongside the main GRUB binary. Note that this is the least common of the four configurations, since few EFI-based computers boot from MBR disks.
    这个模式和目前最流行的下面的模式的唯一差别就在于分区是由MBR掌管还是GPT掌管,两者的区别当然是分区的类型代码的不同,此外,在EFI/GPT下还要标注这个ESP是bootable,这个也许是适应多个ESP的考虑吧?
    EFI-mode GRUB on GPT -- This configuration is just like EFI-mode on MBR, except that the ESP has a type code of C12A7328-F81F-11D2-BA4B-00A0C93EC93B ("boot flag" set on libparted-based tools, or type EF00 in GPT fdisk). This configuration is rapidly becoming the most common one, as the transition from BIOS to EFI is proceeding quickly.
    总之,这个帖子可以说非常全面的总结了grub在四种组合中的存在方式,非常的全面!值得收藏。
  3. 继续学习xorriso的manpage,我大体上比较了一下和mkisofs,但是后者偏重于苹果的HFS+的部分,而前者有大量的例子非常非常值得仔细研究,所以,先从最简单的说明学习。ISO真的是一个非常复杂的体系,也难怪当时是最主要的计算机媒体,像当年有多少CDROM/VCD/DVD/DVDR/DVDRW/BD等等数不胜数。 最直接的例子就是这个Create bootable images for PC-BIOS and EFI,因为阅读其他的帮助选项你会被转晕掉的。
    1. 如果我们仅仅要使用El Torito bootable的话,其实是很简单的。首先我们还是以麒麟作为原本,先把它的ISO mount在kylin
      
      $ loop=$(sudo losetup -Pf --show Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso)
      $ echo "loop device is $loop and prepare to mount to kylin"
      $ sudo mount $loop Kylin
      
      然后我们可以参照最简单的方式,注意这里的两个参数一直让我糊涂-b isolinux/isolinux.bin -c isolinux/boot.cat ,它们都是产生的ISO的文件位置,xorrisfs知道怎么调用isolinux的东西,根本不用你去指定哪个MBR来使用!大错特错!这里根本就不对!这里要参照isolinux的wiki来做 首先,我们还是要假装我们没有麒麟的启动菜单,所以,我先做一个麒麟的拷贝,比如
      
      $ mkdir CD_root
      $ sudo rsync -avr  ./Kylin CD_root
      
      然后我们看到麒麟已经做好了一个isolinux的目录,里面有相应的bios文件和菜单配置,这个是一个巨大的工程,至少对于我目前来说,因为有大量的 菜单制作需要配置,不是简单的拷贝一两个文件就可以了。所以,我只能假装我已经制作好了这个isolinux的部分,因此才有下一步在制作ISO的时候要 指定你的启动文件在哪里的命令:-b isolinux/isolinux.bin
      
      $ xorrisofs -o kylin_iso_boot.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table ./Kylin
      
      我们可以很容易的使用qemu来检验是否cdrom bootable
      
      $ qemu-system-x86_64 -cdrom kylin_iso_boot.iso -m 4G
      
      而使用USB不论是BIOS还是UEFI都会失败:
      
      $ qemu-system-x86_64 -drive file=kylin_iso_boot.iso,format=raw -m 4G
      $ qemu-system-x86_64 -bios /usr/share/qemu/OVMF.fd -drive file=kylin_iso_boot.iso,format=raw -m 4G
      
    2. 第二步,如果我们打算制作ISO/USB混合启动的话,就要多一个-isohybrid-mbr,这个和之前的isolinux.bin是不同的。

      -isohybrid-mbr disk_path

      Install disk_path as ISOLINUX isohybrid MBR which makes the boot image given by option -b bootable from USB sticks and hard disks via PC-BIOS. This preparation is normally done by ISOLINUX program isohybrid on the already produced ISO image.
      The disk path should lead to one of the Syslinux files isohdp[fp]x*.bin . The MBR gets patched according to isohybrid needs. The first partition describes the range of the ISO image. Its start is at block 0 by default, but may be set to 64 disk blocks by option -partition_offset 16.

      也就是说我需要这个isohdp[fp]x*.bin文件,locate isohdpfx.bin发现它的路径是/usr/lib/ISOLINUX/isohdpfx.bin,所以,产生ISO需要这么做:
      
      $ xorrisofs -o kylin_bios_boot.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -partition_offset 16 ./CD_root
      
      检验三种启动格式:ISO可以,USB/BIOS可以,USB/UEFI不行,这个是正确的:
      
      $ qemu-system-x86_64 -cdrom kylin_bios_boot.iso -m 4G
      $ qemu-system-x86_64 -drive file=kylin_bios_boot.iso,format=raw -m 4G
      $ qemu-system-x86_64 -bios /usr/share/qemu/ -drive file=kylin_bios_boot.iso,format=raw -m 4G
      
      最后验证一下它是否真的是BIOS/MBR only的:
      
      $ gdisk -l kylin_bios_boot.iso 
      GPT fdisk (gdisk) version 1.0.8
      
      Partition table scan:
        MBR: MBR only
        BSD: not present
        APM: not present
        GPT: not present
      
      
      ***************************************************************
      Found invalid GPT and valid MBR; converting MBR to GPT format
      in memory. 
      ***************************************************************
      
      
      Warning! Secondary partition table overlaps the last partition by
      33 blocks!
      You will need to delete this partition or resize it in another utility.
      Disk kylin_bios_boot.iso: 8440740 sectors, 4.0 GiB
      Sector size (logical): 512 bytes
      Disk identifier (GUID): BB6C4582-21B8-4CF6-92E0-86F05B2DA5AA
      Partition table holds up to 128 entries
      Main partition table begins at sector 2 and ends at sector 33
      First usable sector is 34, last usable sector is 8440706
      Partitions will be aligned on 64-sector boundaries
      Total free space is 30 sectors (15.0 KiB)
      
      Number  Start (sector)    End (sector)  Size       Code  Name
         1              64         8440739   4.0 GiB     0700  Microsoft basic data
      
      这里的这个警告Warning! Secondary partition table overlaps the last partition by 33 blocks!是有意思的,我找到它的源头才想起来,的确GPT是有一个备份表头放在磁盘的最后,而且和开头一样至少是33blocks,怎么办呢?纯属庸人自扰!杞人忧天!这个是MBR,是gdisk自作主张把MBR转为GPT给你看!Found invalid GPT and valid MBR; converting MBR to GPT format in memory. ,既然是Found invalid GPT and valid MBR;为什么不适用MBR的模式来显示,而是生生创造一个GPT,这个我觉得是gdisk的一个问题gdisk是有意这么做的,看后面的manpage引用部分。如果你使用fdisk就没有这个噪音了!所以,没有问题,不大可能这么简单的bug都没有发现。
      
      $ fdisk -l kylin_bios_boot.iso 
      Disk kylin_bios_boot.iso: 4.02 GiB, 4321658880 bytes, 8440740 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: dos
      Disk identifier: 0x4aa2f673
      
      Device               Boot Start     End Sectors Size Id Type
      kylin_bios_boot.iso1 *       64 8440739 8440676   4G 17 Hidden HPFS/NTFS
      
      阅读gdisk的manpage才理解它这么做是有意识的。
      The MBR partitioning system uses a combination of cylinder/head/sector (CHS) addressing and logical block addressing (LBA). The former is klunky and limiting. GPT drops CHS addressing and uses 64-bit LBA mode exclusively. Thus, GPT data structures, and therefore gdisk, do not need to deal with CHS geometries and all the problems they create. Users of fdisk will note that gdisk lacks the options and limitations associated with CHS geometries.
      因为gdisk决定不使用CHS,那么它怎么办呢?只能使用64位寻址方式,为此,它必须自己创建GPT,而由此引发的虚拟的存在于内存的GPT表肯定没有在MBR创建的分区的结尾部分有GPT的备用表,这个当然是无意义的警告了。
    3. 制作USB/UEFI就要麻烦的多了,吃完饭再说吧?
    4. 制作UEFI的确很复杂,尤其是要兼容MBR/BIOS的hybrid,更不要说这个还要能够ISO-bootable。首先,概念:
      The boot image file has to be the image of an EFI System Partition, i.e. a FAT filesystem with directory /EFI/BOOT and boot files with EFI prescribed names: BOOTIA32.EFI for 32 bit x86, BOOTx64.EFI for 64 bit AMD/x86 (in UEFI-2.4 there is indeed a lower case "x"), BOOTAA64.EFI for 64 bit ARM. The software in the FAT filesystem should be able to find and inspect the ISO filesystem for boot loader configuration and start of operating system. GRUB2 program grub-mkimage can produce such a FAT filesystem with suitable content, which then uses further GRUB2 software from the ISO filesystem.
      所以,第一步先看看openKylin是否符合,它的确是有这个boot/grub/efi.img
      
      $ file kLinux/boot/grub/efi.img 
      kLinux/boot/grub/efi.img: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", sectors/cluster 4, root entries 512, sectors 8000 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 6, sectors/track 32, heads 64, serial number 0x54c59c6c, unlabeled, FAT (12 bit)
      
      看看它的MBR是怎么样子的,我觉得这个是一个假的MBR:
      
      $ dd if=kLinux/boot/grub/efi.img bs=512 count=1
      �<�mkfs.fat@� @�)l��TNO NAME    FAT12   �[|�"�t
                                                     V���^��2�����This is not a bootable disk.  Please insert a bootable floppy and
      press any key to try again ... 
      U�1+0 records in
      1+0 records out
      512 bytes copied, 5.0976e-05 s, 10.0 MB/s
      
      如果确实要看其中的内容的话只能是使用loop mount:
      
      $  loop=$(sudo losetup -Pf --show kLinux/boot/grub/efi.img)
      $ echo "loop device is $loop and prepare to mount to ESP"
      $ sudo mount $loop ESP
      $ find ESP | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/|-\1/" 
      ESP
        |-efi
        |  |-boot
        |  |  |-bootx64.efi
        |  |  |-grubx64.efi
        |  |  |-mmx64.efi
        |-NvVars
      
      这里我不知道为什么tree针对loop mount不工作,我找到这个更加通用的方法,也许可以存成一个shell alias? 所以,我们可以使用grub-mkimage来制作我们自己的启动盘,但是究竟要选择哪一个形式呢?这些看起来都好像有点像:i386-multiboot, i386-pc, i386-pc-eltorito, i386-efi, i386-qemu, x86_64-efi。但是能不能取决于我系统里是否有预装这些库的支持,就是说/usr/lib/grub下有哪些平台。另一个很小的问题是所谓的prefix选项,就是-p,我感觉这个几乎大家都是选择/boot,或者大写因为ISO不分大小写?这个grub-mkimage远比我想像的复杂,或者说它太灵活了,我还没有明白我要选择什么module,而且要自己组合bootable的那些预制的image,比如在
      
      $ ll /usr/lib/grub/i386-pc/*.img
      -rw-r--r-- 1 root root   512 Dec 19  2022 /usr/lib/grub/i386-pc/boot_hybrid.img
      -rw-r--r-- 1 root root   512 Dec 19  2022 /usr/lib/grub/i386-pc/boot.img
      -rw-r--r-- 1 root root  2048 Dec 19  2022 /usr/lib/grub/i386-pc/cdboot.img
      -rw-r--r-- 1 root root   512 Dec 19  2022 /usr/lib/grub/i386-pc/diskboot.img
      -rw-r--r-- 1 root root   512 Dec 19  2022 /usr/lib/grub/i386-pc/g2hdr.img
      -rw-r--r-- 1 root root 10240 Dec 19  2022 /usr/lib/grub/i386-pc/g2ldr.img
      -rw-r--r-- 1 root root 10240 Dec 19  2022 /usr/lib/grub/i386-pc/grldr.img
      -rw-r--r-- 1 root root 29916 Dec 19  2022 /usr/lib/grub/i386-pc/kernel.img
      -rw-r--r-- 1 root root  1024 Dec 19  2022 /usr/lib/grub/i386-pc/lnxboot.img
      -rw-r--r-- 1 root root  2848 Dec 19  2022 /usr/lib/grub/i386-pc/lzma_decompress.img
      -rw-r--r-- 1 root root  1024 Dec 19  2022 /usr/lib/grub/i386-pc/pxeboot.img
      
      这里的例子让我才意识到grub-mkimage不会帮你选择这些image,你要自己去concat,真的是无语了。真的是好麻烦啊,即便选择grub-mkrescue也是很多啰嗦事情。 然后我才意识到我又是糊涂了,既然是一个ESP根本就没有什么MBR的事情,有那么复杂吗?根本不需要使用grub-mkimage,我手错一个文件系统不是很容易吗?
      
      $ dd of=esp.img if=/dev/zero bs=1M count=4
      $ mkfs.fat esp.img
      $ mkdir esp
      $ sudo mount esp.img esp
      sudo rsync -avr ESP/* esp
      
      这里我直接使用从openKylin上得来的EFI image文件的内容,其实这个也没有什么,因为都是标准的。然后我们就把这个创建的esp.img拷贝到我要创建的源目录的/boot/grub/ efi.img,这个也无所谓因为它是参数的一部分可以随便命名,其实仅仅是要能够让xorriosfs找到吧?
      
      $ xorrisofs -o kylin_efi_bios_boot.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot -append_partition 2 0xef ./CD_root/boot/grub/efi.img  ./CD_root
      
      这里的关键就是要让-eltorito-alt-boot -e知道我们的入口就是做好的ESP的镜像文件,而添加一个类型为0xef的partition也就是ESP,检验一下:
      
      $ qemu-system-x86_64 -bios /usr/share/qemu/OVMF.fd -drive file=kylin_efi_bios_boot.iso,format=raw -m 4G
      $ qemu-system-x86_64 -cdrom kylin_efi_bios_boot.iso -m 4G
      
      这个真的是好辛苦,如果要再兼容BIOS/MBR的混合模式只能明天再说了,非常的复杂。
    5. 这个就是混合所有的最终一个版本:
      
      $ xorrisofs -o kylin_bios_uefi_mbr_gpt_boot.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -partition_offset 16 -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot -append_partition 2 0xef ./CD_root/boot/grub/efi.img ./CD_root
      
      然后它有能力在ISO/USB,和BIOS/UEFI下启动,而且它的文件系统是MBR/GPT的混合这一点我不肯定,可能我的磁盘容量有问题,这个是需要研究的。
      
      $ gdisk -l kylin_bios_uefi_mbr_gpt_boot.iso
      GPT fdisk (gdisk) version 1.0.8
      
      Partition table scan:
        MBR: MBR only
        BSD: not present
        APM: not present
        GPT: not present
      
      
      ***************************************************************
      Found invalid GPT and valid MBR; converting MBR to GPT format
      in memory. 
      ***************************************************************
      
      Disk kylin_bios_uefi_mbr_gpt_boot.iso: 8399636 sectors, 4.0 GiB
      Sector size (logical): 512 bytes
      Disk identifier (GUID): A271E6AF-2F47-40FB-93A0-6713497B3CAB
      Partition table holds up to 128 entries
      Main partition table begins at sector 2 and ends at sector 33
      First usable sector is 34, last usable sector is 8399602
      Partitions will be aligned on 4-sector boundaries
      Total free space is 597 sectors (298.5 KiB)
      
      Number  Start (sector)    End (sector)  Size       Code  Name
         1              64         8390843   4.0 GiB     0700  Microsoft basic data
         2         8390844         8399035   4.0 MiB     EF00  EFI system partition
      
      如何创建GPT/PMBR需要想一下。

七月三十日 等待变化等待机会

  1. 所以,我的问题就是如何实现GPT/PMBR的多种启动模式,昨天的MBR多种启动的问题主要来自于磁盘容量的问题,因为MBR天生只支 持最多四个分区,而且最大限度受制于32位寻址,不能突破2T容量上限,所以,这个是一定要解决的问题。现在回头来看ubuntu它就不是使用MBR而是 GPT,虽然作为ISO不大可能有一个2T上限的问题,但是难保不会有四个分区的困扰,总之不是主流做法。
    
    $ fdisk -l openKylin-1.0.2-x86_64.iso | grep Disklabel\ type:
    Disklabel type: dos
    $ fdisk -l ubuntu-24.04-desktop-amd64.iso | grep Disklabel\ type:
    Disklabel type: gpt
    $ fdisk -l archlinux-2024.07.01-x86_64.iso | grep Disklabel\ type:
    Disklabel type: dos
    
    但是fdisk是比较蒙蔽人的,看gdisk才能明白它们的差别:
    
    $ gdisk -l lubuntu-22.04.4-desktop-amd64.iso | grep Partition\ table\ scan: -A 4
    Partition table scan:
      MBR: protective
      BSD: not present
      APM: not present
      GPT: present
    
    $ gdisk -l archlinux-2024.07.01-x86_64.iso | grep Partition\ table\ scan: -A 4
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    实际上,只有两种做法,就是伪装为MBR: MBR only的同时兼容MBR/GPT,或者是PMBR/GPT只兼容GPT的做法。而我的做法是不合格的因为没有GPT,这个是不可接受的!
    
    $ gdisk -l kylin_bios_uefi_mbr_gpt_boot.iso | grep Partition\ table\ scan: -A 4
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: not present
    
  2. 要使得ISO作为GPT出现只需要加一个选项-appended_part_as_gpt,所以,这个就是目前的一个做法:
    
    $ xorrisofs -o kylin_bios_uefi_mbr_gpt_boot_gpt.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -partition_offset 16 -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot -append_partition 2 0xef ./CD_root/boot/grub/efi.img -appended_part_as_gpt ./CD_root
    
    $ gdisk -l kylin_bios_uefi_mbr_gpt_boot_gpt.iso | grep Partition\ table\ scan: -A 4
    Partition table scan:
      MBR: protective
      BSD: not present
      APM: not present
      GPT: present
    
    照样检验是否ISO/USB/BIOS/UEFI启动都成功,除了USB/UEFI不成功。
    存储媒介启动模式测试命令启动结果
    CDROMBIOS
    
    $ qemu-system-x86_64 -cdrom kylin_bios_uefi_mbr_gpt_boot_gpt.iso -m 4G
    
    SUCCESS
    CDROMUEFI
    
    $ qemu-system-x86_64 -bios /usr/share/qemu/OVMF.fd -cdrom kylin_bios_uefi_mbr_gpt_boot_gpt.iso -m 4G
    
    SUCCESS
    USBBIOS
    
    $ qemu-system-x86_64 -drive file=kylin_bios_uefi_mbr_gpt_boot_gpt.iso,format=raw -m 4G
    
    SUCCESS
    USBUEFI
    
    $ qemu-system-x86_64 -bios /usr/share/qemu/OVMF.fd -drive file=kylin_bios_uefi_mbr_gpt_boot_gpt.iso,format=raw -m 4G
    
    FAILURE
    所以,这个是不合格的,为什么UEFI没有找到呢?是不是我没有加上bootable flag?我不理解isohybrid的原理,所以,现在很难debug根源!要么一步步回朔,要么。。。,这个很难!
  3. 行百里者半九十!这最后一步GPT/PMBR却让我找不到方向。一个思路是模仿ubuntu的命令解析,就是ubuntu重新制作的命令,但是这个似乎让我摸不着头脑如何给参数,另一个可能就是manpage里输出的是as_mkisofs的形式:
    
    $ xorriso -indev ubuntu-24.04-desktop-amd64.iso -report_el_torito as_mkisofs
    xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project.
    
    xorriso : NOTE : Loading ISO image tree from LBA 0
    xorriso : UPDATE :    1210 nodes read in 1 seconds
    libisofs: NOTE : Found hidden El-Torito image for EFI.
    libisofs: NOTE : EFI image start and size: 2982971 * 2048 , 10140 * 512
    xorriso : NOTE : Detected El-Torito boot information which currently is set to be discarded
    Drive current: -indev 'ubuntu-24.04-desktop-amd64.iso'
    Media current: stdio file, overwriteable
    Media status : is written , is appendable
    Boot record  : El Torito , MBR protective-msdos-label grub2-mbr cyl-align-off GPT
    Media summary: 1 session, 2985672 data blocks, 5831m data, 1186g free
    Volume id    : 'Ubuntu 24.04 LTS amd64'
    -V 'Ubuntu 24.04 LTS amd64'
    --modification-date='2024042411290900'
    --grub2-mbr --interval:local_fs:0s-15s:zero_mbrpt,zero_gpt:'ubuntu-24.04-desktop-amd64.iso'
    --protective-msdos-label
    -partition_cyl_align off
    -partition_offset 16
    --mbr-force-bootable
    -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b --interval:local_fs:11931884d-11942023d::'ubuntu-24.04-desktop-amd64.iso'
    -appended_part_as_gpt
    -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7
    -c '/boot.catalog'
    -b '/boot/grub/i386-pc/eltorito.img'
    -no-emul-boot
    -boot-load-size 4
    -boot-info-table
    --grub2-boot-info
    -eltorito-alt-boot
    -e '--interval:appended_partition_2_start_2982971s_size_10140d:all::'
    -no-emul-boot
    -boot-load-size 10140
    nick@nick-sager:~/workspace/Downloads/IS
    
    这两者结合起来比较看看怎么传参数,可是有些地址是计算出来的,很麻烦。还有一个思路就是xorrisofsxorriso的前端,而且似乎是为了兼容mkisofs的命令,传参数有很多的讲究,也许阅读xorrisomanpage能够有进一步的理解。这个是一个异常复杂的manpage,简直就是一篇使用手册的长度。
  4. 这个真的是很难,需要休息一下。
  5. 有道是:踏破铁鞋无觅处,得来全不费工夫。这里给出了关键的参数,不能使用-appended_part_as_gpt,而要使用-isohybrid-gpt-basdat
    -isohybrid-gpt-basdat

    Mark the current El Torito boot image (see options -b and -e) in an actually invalid GPT as partition of type Basic Data. This works only with -isohybrid-mbr and has the same impact on the system area as -efi-boot-part. It cannot be combined with -efi-boot-part or -hfsplus.
    The first three boot images which are marked by GPT will also show up as partition entries in MBR. The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick. The MBR partition for PC-BIOS gets type 0x00 rather than 0x17 in this case. Often the further MBR entries are the ones which actually get used by EFI.

    这段话非常的难懂,我在随后的f/gdisk的输出结果中才能慢慢体会到一点点。 所以,这个命令是最终正确的做法:
    
    O$ xorrisofs -o kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -partition_offset 16 -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot -append_partition 2 0xef ./CD_root/boot/grub/efi.img -isohybrid-gpt-basdat ./CD_root
    
    这个启动测试四种全部通过了。再看看它的分区表:
    
    $ gdisk -l kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso | grep Partition\ table\ scan:  -A 4
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    所以,它是类似于archLinux的做法,GPT/MBR共存,而不是GPT/PMBR的形式。在gdisk里从MBR的角度是全部都是一个分区,这个其实也是符合PMBR的理念就是唯一有用的EFI分区,这个从MBR分却表可以看到
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       2         8390844         8399035   4.0 MiB     EF00  EFI system partition
    
    可是换做是fdisk你看到两个分区:
    
    Device                                    Boot   Start     End Sectors Size Id Type
    kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso1 *         64 8390843 8390780   4G  0 Empty
    kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso2      8390844 8399035    8192   4M ef EFI (FAT-12/16/32)
    
    为什么会这样呢?
    
    $ xxd -s 446 -l 64 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
    000001be: 8001 0200 0083 fff0 4000 0000 7c08 8000  ........@...|...
    000001ce: 0000 c1f1 ef82 c2f1 bc08 8000 0020 0000  ............. ..
    000001de: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    000001ee: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    
    毫无疑义的,fdisk是纯粹从MBR的分区表来看问题的:It is what it is!,可是如果再多读一个block,看到GPT分区表就会得出gdisk的结论吧?但是gdisk在GPT模式下看到的是:
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              64         8390843   4.0 GiB     0700  ISOHybrid
       2             872            9063   4.0 MiB     0700  ISOHybrid1
       3         8390844         8399035   4.0 MiB     EF00  Appended2
    
  6. ubuntu的中文社区,我以后要常常去看看。

八月一日 等待变化等待机会

  1. 关于fdisk和gdisk对待MBR的不同之处在于这个分区的态度:
    
    $ xxd -s 446 -l 16 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
    000001be: 8001 0200 0083 fff0 4000 0000 7c08 8000  ........@...|...
    
    fdisk看来这个是一个标志为bootable(80)的很重要的分区,所以,要显示出来。可是在gdisk眼里,这个分区的类型是不可用或者说未定义00,所以,压根儿不显示。这个实在是见仁见智的抉择啊!
  2. 第二个分歧之处在于GPT的表上,这个是我们早已熟知的,fdisk一旦看到非PMBR那么它是不会再费心去看GPT表了,所以,对于MBR标志为非protective MBR都可以认为是gdisk所说的MBR only。那么既然是MBR only,那么根本就不屑于再去看看GPT存在与否了。这个也真的是无可厚非。
  3. 对于-isohybrid-gpt-basdat这个参数的意义,有四句话可谓是字字珠玑啊!
    1. The first three boot images which are marked by GPT will also show up as partition entries in MBR. 这句话是真的吗?我们先来看看GPT表:
      
      $ xxd -s 1024 -l 128 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
      00000400: a2a0 d0eb e5b9 3344 87c0 68b6 b726 99c7  ......3D..h..&..
      00000410: 1f34 d80a dcb0 1243 a64d 5889 7c3e b925  .4.....C.MX.|>.%
      00000420: 4000 0000 0000 0000 bb08 8000 0000 0000  @...............
      00000430: 0100 0000 0000 0010 4900 5300 4f00 4800  ........I.S.O.H.
      00000440: 7900 6200 7200 6900 6400 0000 0000 0000  y.b.r.i.d.......
      00000450: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00000460: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00000470: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      
      这个分区是一个微软的Basic Data Partition,它的type GUID是EBD0A0A2-B9E5-4433-87C0-68B6B72699C7,我现在还没有找到一个很容易转换的方式来翻译这个bytecode:a2a0 d0eb e5b9 3344 87c0 68b6 b726 99c7,只能看局部查找,因为GUID有些是endian相关的数字。而它的起始和结束的LBA就是64=0x40和 8390843=0x8008bb,至于说它的性质是一个64bits的字串0100 0000 0000 0010,根据规范第2个bit被set了就是EFI firmware should ignore the content of the partition and not try to read from it,而我们看最后第62个bit被set,它是专门针对Basic Data Partition的特性要求它是Hidden,换言之,我们就是要求GPT不要读这个宝贵的linux的真正的磁盘分区因为存放着大量的Linux的文件系统,是运行安装的部分:
      
      Number  Start (sector)    End (sector)  Size       Code  Name
         1              64         8390843   4.0 GiB     0700  ISOHybrid
      
    2. 第二个分区是这样子的:
      
      $ xxd -s 1152 -l 128 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
      00000480: a2a0 d0eb e5b9 3344 87c0 68b6 b726 99c7  ......3D..h..&..
      00000490: 1f34 d80a dcb0 1243 a64e 5889 7c3e b925  .4.....C.NX.|>.%
      000004a0: 6803 0000 0000 0000 6723 0000 0000 0000  h.......g#......
      000004b0: 0100 0000 0000 0010 4900 5300 4f00 4800  ........I.S.O.H.
      000004c0: 7900 6200 7200 6900 6400 3100 0000 0000  y.b.r.i.d.1.....
      000004d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      000004e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      000004f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      
      它也是一个Basic Data Partition,而且起始(0x0368=872)和结束(0x2367=9063)LBA是和之前的分区重叠的,这一点gdisk却不去抱怨!它的性质同样是不让EFI去读和隐藏的。这个用意是什么?难道是分区表搞错了?
    3. 第三个分区是这样子的:
      
      $ xxd -s 1280 -l 128 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
      00000500: 2873 2ac1 1ff8 d211 ba4b 00a0 c93e c93b  (s*......K...>.;
      00000510: 1f34 d80a dcb0 1243 a64f 5889 7c3e b925  .4.....C.OX.|>.%
      00000520: bc08 8000 0000 0000 bb28 8000 0000 0000  .........(......
      00000530: 0000 0000 0000 0000 4100 7000 7000 6500  ........A.p.p.e.
      00000540: 6e00 6400 6500 6400 3200 0000 0000 0000  n.d.e.d.2.......
      00000550: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00000560: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00000570: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      
      它的type GUID是2873 2ac1 1ff8 d211 ba4b 00a0 c93e c93b翻译过来就是C12A7328-F81F-11D2-BA4B-00A0C93EC93B,这个Fuchsia,我还是第一次听说,使用这个GUID真的是有些让人摸不着头脑啊。它没有任何的attribute,那么作为GPT来说它是bootable的分区,因为它是所谓的fuchsia-esp,我的理解是这个是分区本身定义了吧?我随后google才让我明白了wiki是有一点点的误导,这个首先是因为微软认定它是ESP,所以才随后被其他系统比如这个Fuchsia来使用作为启动分区的ESP。大家如果为了兼容微软最好就是遵循这个表来做:
      Value Meaning
      PARTITION_BASIC_DATA_GUID
      ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
      The data partition type that is created and recognized by Windows.

      Only partitions of this type can be assigned drive letters, receive volume GUID paths, host mounted folders (also called volume mount points), and be enumerated by calls to FindFirstVolume and FindNextVolume.

      This value can be set only for basic disks, with one exception. If both PARTITION_BASIC_DATA_GUID and GPT_ATTRIBUTE_PLATFORM_REQUIRED are set for a partition on a basic disk that is subsequently converted to a dynamic disk, the partition remains a basic partition, even though the rest of the disk is a dynamic disk. This is because the partition is considered to be an OEM partition on a GPT disk.

      PARTITION_ENTRY_UNUSED_GUID
      00000000-0000-0000-0000-000000000000
      There is no partition.

      This value can be set for basic and dynamic disks.

      PARTITION_SYSTEM_GUID
      c12a7328-f81f-11d2-ba4b-00a0c93ec93b
      The partition is an EFI system partition.

      This value can be set for basic and dynamic disks.

      PARTITION_MSFT_RESERVED_GUID
      e3c9e316-0b5c-4db8-817d-f92df00215ae
      The partition is a Microsoft reserved partition.

      This value can be set for basic and dynamic disks.

      PARTITION_LDM_METADATA_GUID
      5808c8aa-7e8f-42e0-85d2-e1e90434cfb3
      The partition is a Logical Disk Manager (LDM) metadata partition on a dynamic disk.

      This value can be set only for dynamic disks.

      PARTITION_LDM_DATA_GUID
      af9b60a0-1431-4f62-bc68-3311714a69ad
      The partition is an LDM data partition on a dynamic disk.

      This value can be set only for dynamic disks.

      PARTITION_MSFT_RECOVERY_GUID
      de94bba4-06d1-4d40-a16a-bfd50179d6ac
      The partition is a Microsoft recovery partition.

      This value can be set for basic and dynamic disks.

    4. 从GPT的角度来看,并不是明确的定义了bootable的启动分区,因为这个分区都是拥有GUID来识别,那么每个分区的意义是不言自明的,完 全取决于bootloader来决定,甚至于它们的起始结束是否冲突在GPT看来也是不管的,唯一的是属性的那64个bit,第2个bit定义了EFI不 要读取的保护性,第3个bit定义了兼容MBR的bootable的flag。照理说这个应该被set,但是xorrisofs没有这么做。总而言之,所谓ESP就是启动分区是对于EFI的bootloader毫无疑义的要读取的。要查UEFI规范来看启动逻辑。
    5. 第二句话 The MBR partition of type 0xEF is what actually is used by EFI firmware for booting from USB stick.
      
      $ xxd -s 446 -l 32 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
      000001be: 8001 0200 0083 fff0 4000 0000 7c08 8000  ........@...|...
      000001ce: 0000 c1f1 ef82 c2f1 bc08 8000 0020 0000  ............. ..
      
      这个的确如此,因为设定了分区类型为ef导致遵循BIOS启动规范的bootloader要把它认定为EFI分区。对比它的起始LBA是0x8008bc=8390844,它的确可以在GPT的分区表相对应。注意这里提到
      Partition Id OccurrenceAccess Bootable Type Origin Supported ByDescription
      EFhMBRService FS IntelEFIEFI system partition. Can be a FAT12, FAT16, FAT32 (or other) file system EEh
      这里明确提到了它是由EFI支持的分区类型,从这一点来说就是一个古老的系统它仅仅支持BIOS/MBR启动,那么它要如何做到支持UEFI/GPT启动呢?在MBR的启动分区里bootloader瞄准的这个虽然不是bootable,但是它的类型是0xEF,导致它被load了,而那个bootable的分区因为类型是不可用的0x00,反而被忽视了,它的作用仅仅是表示不要使用GPT,这个是MBR only。这个真的是很奇怪的组合啊!
    6. 第三句话The MBR partition for PC-BIOS gets type 0x00 rather than 0x17 in this case.是什么意思呢?它说的是这里的不可用的类型0x00吗?为什么要设定为0x17呢? 这个OS/2 boot manager是非常的重要!这里似乎有很多的知识点,我还没有时间来看。
    7. 第四句话Often the further MBR entries are the ones which actually get used by EFI.是和第三句话相联系的,就是说设定为bootable的不是要启动的,而真正要启动的是0xef分区,也就是下一个分区。这个说法是过于通俗化了。总而言之,这四句话大体上是理解了,但是还有一些疑问,再说吧。
  4. 实际上,xorriso和它的前端xorrisofs似乎还是有些不同的吧?等回过头来再看看。
  5. 实际上,从GPT的角度来看,这个属于所谓的Hybrid MBR

    Hybrid MBR (LBA 0 + GPT)

    In operating systems that support GPT-based boot through BIOS services rather than EFI, the first sector may also still be used to store the first stage of the bootloader code, but modified to recognize GPT partitions. The bootloader in the MBR must not assume a sector size of 512 bytes.

    就是说针对古老的系统只支持BIOS/MBR的而不能实现GPT/UEFI启动的话,做法是在bootloader这一层来实现对于GPT的支持,那么在这种方式下的MBR的boot code是bootloader的所谓的first stage,它要load后续的second stage bootloader,比如grub的另一部分,也许是grubx86.efi,这个对应于EFI模式,或者。。。等等再看。总之,是bootloader来实现GPT的机制,那么GPT存在于LBA1,那么只能使用一个单独的分区来存放这个second stage bootloader,这个在一些模式下叫做BIOS Boot Partition, which has a GPT type code of 21686148-6449-6E6F-744E-656564454649 ("bios_grub flag" set in libparted-based tools, or type EF02 in GPT fdisk)。
  6. 现在对于我还是一个疑问就是那个想幽灵一样的分区。要想理解先看看FAT的规范。我存了一个拷贝。 所以,怎么判断一个分区的文件系统是FAT呢?
    
    $ xxd -s 446464 -l 11 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso
    0006d000: eb3c 906d 6b66 732e 6661 74              .<.mkfs.fat
    
    这里的eb3c 90就是FAT的特征码,而后面的磁盘名字是随便写的,这里就是使用mkfs.fat写的时候的标记。那么这个奇怪的分区表要怎么解释呢? 我使用losetup -Pf是没有办法把这个奇怪的分区识别的,因为在fdisk或者losetup之类的角度来看就只有两个分区是在MBR里的,只有忽略MBR读GPT才能看到三个分区,那么就只能使用mount来强制的方式:
    
    $ sudo mount -o offset=446464 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso part1
    $ ll part1
    total 34
    drwxr-xr-x  3 root root 16384 Jan  1  1970 ./
    drwxrwxr-x 21 nick nick  4096 Aug  1 08:49 ../
    drwxr-xr-x  3 root root  2048 Jan 30  2021 efi/
    -rwxr-xr-x  1 root root 11741 Jul 30 09:17 NvVars*
    
    这里的偏移是根据分区表的起始LBA:872;偏移:872*512=446464得到的。这里遇到一个奇怪的现象,当我如法炮制另一个真正的EFI分区的时候(起始LBA=8390844;偏移:8390844*512=4296112128)mount说已经有了!
    
    $ sudo mount -o offset=4296112128 kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso part2
    [sudo] password for nick: 
    mount: part2: /home/nick/workspace/Downloads/ISO/kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso is already mounted.
    
    这个意思就是说这两个分区是同一个!至少在mount看来是一样的:
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              64         8390843   4.0 GiB     0700  ISOHybrid
       2             872            9063   4.0 MiB     0700  ISOHybrid1
       3         8390844         8399035   4.0 MiB     EF00  Appended2
    
    为什么?谷歌搜索是非常的强大,因为我自己都觉得我无法表达我遇到的问题是什么,要怎么搜索关键词,但是google第一时间就给出了相关的问题!很显然我不可能是小众的迷惑者。如果我限制mount的大小的话:
    
    $ sudo mount -v -o offset=$((872*512)),sizelimit=$(((9063-872)*512)) kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso part1
    mount: /dev/loop49 mounted on /home/nick/workspace/Downloads/ISO/part1.
    $ sudo mount -v -o offset=$((8390844*512)),sizelimit=$(((8399035-8390844)*512)) kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso part2
    mount: /dev/loop50 mounted on /home/nick/workspace/Downloads/ISO/part2.
    $ diff -rq part1 part2
    $ find part1 | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/|-\1/" 
    part1
      |-efi
      |  |-boot
      |  |  |-bootx64.efi
      |  |  |-grubx64.efi
      |  |  |-mmx64.efi
      |-NvVars
    
    这一切说明了什么呢?世界上不存在能够类似于alias的分区表,并不存在说第一个分区是隐含的指向第二个分区,在磁盘偏移不同的两个分区是彼此独立的,但是mount有一个优化:
    Since util-linux v2.29, mount re-uses the loop device rather than initializing a new device if the same backing file is already used for some loop device with the same offset and sizelimit. This is necessary to avoid a filesystem corruption.
    这里我还是持怀疑态度,两个不同偏移即便大小一样怎么就能当作同一个文件系统呢?难道这个是ISO的el-torito的机制在文件系统里我的确是把一个EFI的partition作为一个image文件传递给了xorrisofs,就是/boot/grub/efi.img,是不是在El Torito里它们是一个?
  7. 仔细使用midnight commander查看会看到一个错误是所谓的isoinfo报出的unable to find Joliet SVD,搞了半天我才明白这个mc是使用isoinfo来读取ISO的文件结构的,而我在制作ISO的时候忘记打开参数-J,就是同时再制作Joliet的文件系统表。所以,完整的命令是
    
    $ xorrisofs -J -o kylin_bios_uefi_mbr_gpt_boot_gpt_fix.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -partition_offset 16 -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot -append_partition 2 0xef ./CD_root/boot/grub/efi.img -isohybrid-gpt-basdat ./CD_root
    
    这个仅仅是表面上好看一些而已,根本的问题是这样子对于mount有改变吗?一开始我以为我发现了什么,后来清理环境重新制作发现还是原样,只是偏移量不同了,因为增加了Joliet的文件表吧?
  8. 我感觉这个问题越来越复杂,因为我已经有些茫然了。决定改变方向,因为最核心的问题不在于这里吧,我觉得我应该放在isolinux的机制上来看,到底这个isolinux的bootloader是怎么使用这个MBR的分区表的?也就是说我事先制作的EFI分区image文件是怎么制作成两个独立的分区的?
  9. 完全的失败,因为从一开始我的假设就是错的,虽然麒麟没有GPT/MBR,可是它照样支持UEFI/GPT/USB的启动啊!至于说MBR/ BIOS/USB不支持是情有可原的,原本微软和苹果也不支持啊!我怎么会一开始就得到了错误的印象呢?我对于isolinux的所知太少了,有重大的缺 失,这个真的是太丢人了。需要深刻反省。认真学习毛主席著作《目前形势和我们的任务》。其中最具实践指导意义的是著名的
    十大军事原则
    我们的军事原则是:(1)先打分散和孤立之敌,后打集中和强大之敌。(2)先取小城市、中等城市和广大乡村,后取大城市。(3)以歼灭敌人有生力量为主要 目标,不以保守或夺取城市和地方为主要目标。保守或夺取城市和地方,是歼灭敌人有生力量的结果,往往需要反复多次才能最后地保守或夺取之。(4)每战集中 绝对优势兵力(两倍、三倍、四倍、有时甚至是五倍或六倍于敌之兵力),四面包围敌人,力求全歼,不使漏网。在特殊情况下,则采用给敌以歼灭性打击的方法, 即集中全力打敌正面及其一翼或两翼,求达歼灭其一部、击溃其另一部的目的,以便我军能够迅速转移兵力歼击他部敌军。力求避免打那种得不偿失的、或得失相当 的消耗战。这样,在全体上,我们是劣势(就数量来说),但在每一个局部上,在每一个具体战役上,我们是绝对的优势,这就保证了战役的胜利。随着时间的推 移,我们就将在全体上转变为优势,直到歼灭一切敌人。(5)不打无准备之仗,不打无把握之仗,每战都应力求有准备,力求在敌我条件对比下有胜利的把握。 (6)发扬勇敢战斗、不怕牺牲、不怕疲劳和连续作战(即在短期内不休息地接连打几仗)的作风。(7)力求在运动中歼灭敌人。同时,注重阵地攻击战术,夺取 敌人的据点和城市。(8)在攻城问题上,一切敌人守备薄弱的据点和城市,坚决夺取之。一切敌人有中等程度的守备、而环境又许可加以夺取的据点和城市,相机 夺取之。一切敌人守备强固的据点和城市,则等候条件成熟时然后夺取之。(9)以俘获敌人的全部武器和大部人员,补充自己。我军人力物力的来源,主要在前 线。(10)善于利用两个战役之间的间隙,休息和整训部队。休整的时间,一般地不要过长,尽可能不使敌人获得喘息的时间。

八月二日 等待变化等待机会

  1. 下载了UEFI规范来结合isolinux代码来理解。保存一份UEFI规范。另外下载了ISO9660规范保存一份ISO9660规范
  2. 再次检视一下我的命令,这一次我参考的是openKylin的制作方法,毕竟这个是最接近的做法:
    
    $ xorriso -indev openKylin-1.0.2-x86_64.iso -report_el_torito as_mkisofs
    xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project.
    
    xorriso : NOTE : Loading ISO image tree from LBA 0
    xorriso : UPDATE :     376 nodes read in 1 seconds
    xorriso : NOTE : Detected El-Torito boot information which currently is set to be discarded
    Drive current: -indev 'openKylin-1.0.2-x86_64.iso'
    Media current: stdio file, overwriteable
    Media status : is written , is appendable
    Boot record  : El Torito , MBR isohybrid cyl-align-all GPT APM
    Media summary: 1 session, 1942080 data blocks, 3793m data, 1182g free
    Volume id    : 'openKylin-1.0'
    -V 'openKylin-1.0'
    --modification-date='2024031414052600'
    -isohybrid-mbr --interval:local_fs:0s-15s:zero_mbrpt,zero_gpt,zero_apm:'openKylin-1.0.2-x86_64.iso'
    -partition_cyl_align all
    -partition_offset 0
    -partition_hd_cyl 238
    -partition_sec_hd 32
    --mbr-force-bootable
    -append_partition 2 0xef --interval:local_fs:7768320d-7783551d::'openKylin-1.0.2-x86_64.iso'
    -apm-block-size 2048
    -iso_mbr_part_type 0x00
    -c '/isolinux/boot.cat'
    -b '/isolinux/isolinux.bin'
    -no-emul-boot
    -boot-load-size 4
    -boot-info-table
    -eltorito-alt-boot
    -e '/boot/grub/efi.img'
    -no-emul-boot
    -boot-load-size 8000
    -isohybrid-gpt-basdat
    -isohybrid-apm-hfsplus
    
    基于此,我重新组织了我的命令,顺序似乎是有关系的.
    
    $ xorrisofs -J -o kylin_new.iso -V 'kylin-new-iso' --modification-date='2024080214052600' -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -partition_cyl_align all -partition_offset 0 -partition_hd_cyl 238 -partition_sec_hd 32 --mbr-force-bootable -append_partition 2 0xef ./CD_root/boot/grub/efi.img -apm-block-size 2048 -iso_mbr_part_type 0x00 -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e 'boot/grub/efi.img' -no-emul-boot -boot-load-size 8000 -isohybrid-gpt-basdat -isohybrid-apm-hfsplus ./CD_root
    
    另外,这个选项-iso_mbr_part_type 0x00可以隐藏分区,就不再暴露原先nested partition的问题,至少是掩盖吧?
    
    $ gdisk -l kylin_new.iso
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid MBR and GPT. Which do you want to use?
     1 - MBR
     2 - GPT
     3 - Create blank GPT
    
    Your answer: 2
    Using GPT and creating fresh protective MBR.
    Warning! Main partition table overlaps the first partition by 64 blocks!
    You will need to delete this partition or resize it in another utility.
    Disk kylin_new.iso: 8399816 sectors, 4.0 GiB
    Sector size (logical): 512 bytes
    Disk identifier (GUID): ACD402AE-F8D9-4A80-8145-BF9477C809DC
    Partition table holds up to 208 entries
    Main partition table begins at sector 12 and ends at sector 63
    First usable sector is 64, last usable sector is 8399762
    Partitions will be aligned on 4-sector boundaries
    Total free space is 603 sectors (301.5 KiB)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       2             768            8959   4.0 MiB     0700  ISOHybrid1
       3         8390844         8399159   4.1 MiB     EF00  Appended2
    
    这里第1个分区看不到了,就不会感觉它被嵌入第2个分区的问题了。这个和openKylin的做法一致,当然archlinux怎么防止这个问题的我依然不知道。
  3. 我看到一个有希望的帖子,这个要好好研究一下,至少手动修改是一个可能性吧?
    
    Due to option -partition_offset 16, there is a mountable ISO 9660 superblock
    at the start of the first partition (64 * 512 = 32768):
    
      $ sudo mount -o offset=32768 /dvdbuffer/test.iso /mnt/iso2
      mount: /dev/loop1 is write-protected, mounting read-only
    
    这一段话很深奥,我还没有理解,这个partition_offset的含义我一直不懂,它和文件系统的superblock有什么关系?
  4. 现在的问题就是麒麟的ISO没有任何MBR/GPT的情况下能够在UEFI模式下模拟磁盘启动是怎么做到的?这个帖子是一些专家的讨论,非常的有指引性,尽管我是外行,他们讨论的是如何实现bootloader。很有可能这个关于Debian实现混合启动的文章是我需要的。 这个似乎的确是我需要的。 我的现在的命令是
    
    $ xorriso -as mkisofs -r -V "kylin-new.iso" -o kylin_new.iso -J -J -joliet-long \
    -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -b isolinux/isolinux.bin -c isolinux/boot.cat -boot-load-size 4 -boot-info-table -no-emul-boot \
    -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -isohybrid-apm-hfsplus \
    ./CD_root
    
    我不知道我之前为什么一定要添加这个多余的-append_partition 2 0xef ./CD_root/boot/grub/efi.img分区呢?现在的分区表就是很清楚了: MBR模式下
    
    $ gdisk -l kylin_new.iso 
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid MBR and GPT. Which do you want to use?
     1 - MBR
     2 - GPT
     3 - Create blank GPT
    
    Your answer: 1
    Disk kylin_new.iso: 8399160 sectors, 4.0 GiB
    Sector size (logical): 512 bytes
    Disk identifier (GUID): CE05F2F3-1BCC-4977-B53F-4D2AB4D5EB9B
    Partition table holds up to 128 entries
    Main partition table begins at sector 2 and ends at sector 33
    First usable sector is 34, last usable sector is 8399126
    Partitions will be aligned on 256-sector boundaries
    Total free space is 8390901 sectors (4.0 GiB)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       2             768            8959   4.0 MiB     EF00  EFI system partition
    
    在GPT模式下:
    
    $ gdisk -l kylin_new.iso 
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: MBR only
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid MBR and GPT. Which do you want to use?
     1 - MBR
     2 - GPT
     3 - Create blank GPT
    
    Your answer: 2
    Using GPT and creating fresh protective MBR.
    Warning! Main partition table overlaps the first partition by 64 blocks!
    You will need to delete this partition or resize it in another utility.
    Disk kylin_new.iso: 8399160 sectors, 4.0 GiB
    Sector size (logical): 512 bytes
    Disk identifier (GUID): 7F3E5F8A-71D9-47CF-A6D1-4A87DE97C955
    Partition table holds up to 208 entries
    Main partition table begins at sector 12 and ends at sector 63
    First usable sector is 64, last usable sector is 8399106
    Partitions will be aligned on 256-sector boundaries
    Total free space is 3 sectors (1.5 KiB)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       2             768            8959   4.0 MiB     0700  ISOHybrid1
    
    就是说在MBR/GPT两个分区表是一致的只显示一个EFI/ESP分区。而在fdisk下因为不明白还有GPT的存在显示了隐藏未定义的分区0x00,但是因为它是bootable的,所以,可以理解fdisk很纠结是否显示它。分区:
    
    $ fdisk -l kylin_new.iso 
    Disk kylin_new.iso: 4.01 GiB, 4300369920 bytes, 8399160 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x2ed51551
    
    Device         Boot Start     End Sectors Size Id Type
    kylin_new.iso1 *        0 8399159 8399160   4G  0 Empty
    kylin_new.iso2        768    8959    8192   4M ef EFI (FAT-12/16/32)
    
    不管怎么说这个都是完美的,其实很简单,但是我就是一直走不出来!大侠在这里解释这些参数的意义,我要加深印象一下:
    • -isohybrid-mbr .../isohdpfx.bin installs an MBR at the start of the ISO image所以,这里是否应该使用-partition-offset 16是一个考虑,我还是没有完全理解分区从0开始有什么问题。. This enables booting from USB stick via legacy BIOS. We need to extract the first 432 bytes of the ISO into a disk file and give that file as argument to our -isohybrid-mbr option. 如果我添加了-partition-offset 16就导致我会在gdisk里GPT模式下看到原先隐藏的主分区,这个似乎是个问题吧?
    • -b isolinux/isolinux.bin marks the file /isolinux/isolinux.bin in the ISO as EL Torito boot image for legacy BIOS这个解释的太好了!就是添加El Torito的boot record的动作吧?这个和下面的添加UEFI的启动分区影象类似的给了直接的boot loader的文件. This enables booting from CD, DVD or BD media. We keep it.
    • -c isolinux/boot.cat publishes the El Torito boot catalog as data file in the ISO. This is purely ornamental. 说的太好了,它没有什么大用,我看不到这个文件有什么用途。也许学习制作菜单才能明白吧?We keep it.
    • -boot-load-size 4 -boot-info-table -no-emul-boot are additional options for the BIOS boot image.的确他们的具体作用并不是都清楚 We keep them.
    • -eltorito-alt-boot ends这个说的太好了,我一直以为它是开始一个新的,原来是结束旧的 the definition of the BIOS boot image. We keep it.
    • -e boot/grub/efi.img marks the file /boot/grub/efi.img in the ISO as EL Torito boot image for EFI这个是非常关键的参数,是EFI启动的关键,但是分区表也是自动配置的吗?. This enables booting from CD, DVD or BD media. We keep it.
    • -no-emul-boot is a necessary addtitional option to -e. We keep it.
    • -isohybrid-gpt-basdat causes the creation of a partition of type 0xef in the MBR partition table这个说明太关键了,终于点出了它的重要性,我一直不理解它的作用,因为参数名称非常的令人费解,这里说是在MBR里创建,但是参数名字却说的是GPT所以,才迷惑人。. It also causes production of a GPT 所 以,它是两重的作用,相当于定调要使用GPT,因为添加了一个boot image,应该说hybrid就意味着MBR/GPT共存,所以一定会在两个分区表都添加这个分区,并且在MBR里设定它是EFI分区类型,至于说为什 么GPT变成invalid是因为没有设定成PMBR,可是如果是PMBR就纯粹是GPT,那也就不算是hybrid了。所以,hybrid是一个没有规 范的约定成俗,是纯粹为了兼而有之的做法,完全是一种漏洞。which stays invalid, nevertheless, because of the existence of the MBR partition. The MBR partition marks the -e boot image as EFI System Partition. This is needed for booting via EFI from USB stick. We keep it.
    • -isohybrid-apm-hfsplus causes production of an Apple Partition Map纯粹为了苹果 with a partition entry for the -e boot image. It is useless, though, because no HFS+ filesystem image is present which would be used by some Macs as boot entry point. We keep it just because we only make necessary changes here.
    它这个好的地方还包括了其他架构的CPU的解释,比如arm64根本就不支持传统的PC-BIOS,那么根本就不存在hybrid的选项,那么对于增加一个分区就必须使用-append_partition 2 0xef /srv/.../efi.img这种直截了当的命令参数,因为hybrid选项是帮助你创建分区了。这里分区序列号很关键,我之前就是在这里犯迷糊,如果已经有两个分区了,那当然就不是2了。
  5. 这份文档应该是libisofs的作者吧?可以说是一个综合的摘抄,都是精华,因为我之前读过el torito的规范感觉写的比较不好,所以看这个就比较舒服。这种文档一般都是真正实现者的精华笔记值得收藏反复阅读的,有些关键点比如el torito里Validation Entry里 的platform id,原始的只有三个平台,只有在UEFI规范里给它加了一个0xEF=EFI,这个是后来的规范是为了兼容它在原始的el torito规范是没有的,我为此还傻傻的做实验看是不是0xEF是HP笔记本不能启动的原因,这个纯粹是支持EFI的固件才能做到的吧?
  6. 我在两台笔记本上都实验了麒麟的光驱文件,感觉这个可能是qemu的自作多情在读取ISO的时候多余的识别了ISO9660格式,否则如果是纯粹的USB设备是不可能启动的,这里我在本机做了一个反例的验证,就是说不使用-drive iso-file-name而 是直接使用真实的USB设备,很明显的不可能启动,因为规范上说的只有在ISO格式存在于CDROM/DVD/BD媒介上才会应用el torito的boot image来启动。这个应该是qemu的一个bug。这里顺便说一下具体启动usb设备的做法:使用lsusb找到vendorid/ productid,然后使用参数-usb -device usb-host,vendorid=0xvendid,productid=0xprodid,同时这样子也可以省却读写具体的USB设备而给出-drive img-file-name
    The candidates for MBR booting will normally use El Torito rather than MBR if the ISO image is presented on CD, DVD, or BD media. The MBR comes into effect if the image is on a media that is interpreted by the BIOS as some kind of hard disk. Usually real hard disks, floppy disks, USB sticks, memory cards.
    这个就是大师的精句,我的观点就是从这里来的。我还是保存一份文本吧! 对于初学者最容易犯糊涂的一个就是ISO使用的block size是2K,所以,MBR_LBA = ISO_LBA * 4,在磁盘世界里大家用的都是LBA size=0.5K
  7. qemu有一个openBIOS的项目,将来也许可以参考学习一下。不过我发现主流的还是使用seabios,这个才是正解。也许这个能够揭示一些CSM的关键部分吧?
  8. 没有汇编的基础是麻烦的:
    • EAX is the full 32-bit value
    • AX is the lower 16-bits
    • AL is the lower 8 bits
    • AH is the bits 8 through 15 (zero-based), the top half of AX

    So AX is composed of AH:AL halves, and is itself the low half of EAX. (The upper half of EAX isn't directly accessible as a 16-bit register; you can shift or rotate EAX if you want to get at it.)

    x86-64 CPUs extend the integer registers to 64-bit:

    • RAX is the full 64-bit value, with EAX and its sub-components mapped to the lower 32 bits. The upper half of 64-bit registers is only accessible in 64-bit mode, unlike 32-bit registers which can be used in any mode on CPUs that support them.

    All of this also applies to EBX/RBX, ECX/RCX, and EDX/RDX. The other registers like EDI/RDI have a DI low 16-bit partial register, but no high-8 part, and the low-8 DIL is only accessible in 64-bit mode: Assembly registers in 64-bit architecture

    这个是完整的汇编的入门,可是我实在是没有兴趣也没有使用的场景,我仅仅需要理解大师文档里突然冒出来的汇编写法是设呢意思而已。其实我也不需要理解了,因为那个是计算CHS地址,真的需要吗?另一个事情是大师所说的MBR是一个非传统的结构而是现代的,就是说在分区表(偏移446)前6个byte是所谓的disk signature
  9. 这个标题Combinations of boot mechanisms下都是精华部分要仔细阅读!准备吃完早饭再读。 大部分都是我已经或多或少熟悉了的。这里是一个再记忆:
    As for booting Legacy BIOS from a GPT partitioned drive: UEFI specs define that a GPT begins by an MBR with a single partition in its table which must be of type 0xEE and begin at block 1. The code area of the MBR is to be ignored by UEFI and thus can be used for the first stage boot code which a Legacy BIOS will execute.
    这个是为什么PC-BIOS模式下能够启动的原因,因为在MBR的bootsector部分就是0-445的部分可以存放isolinux的first stage bootloader,而它的地址是el torito的boot catalogue(原理上吧?)

八月三日 等待变化等待机会

  1. 这里有很多的文件下载,我并不了解它的归属,不过有些挺有意思的。 这位大师有非常多的blog,而且看起来质量很高的样子。以后可以使用tag搜索看看。
  2. 这里是大师教你怎么手动创建hybrid MBR。有大量的阅读,我已经感到疲惫不堪,因为这个领域太复杂,很多OS的不同反应,而且似乎是和我关系不大的东西,我不需要关心。
  3. 感觉战线被拉长,补给困难,后勤不足,这时候应该集中兵力打歼灭战。什么是歼灭战呢?应不应把麒麟改造成USB-bootable的呢?这个才是 最最核心的战果,但是我现在的能力够吗?启动菜单和isolinux的安装似乎不需要我操心,只需要执行xorriso就可以了吧?
  4. 我现在觉得这个制作方法是有意为之的,就是说麒麟公司认为中国的普通用户都是使用windows的,那么对于这些用户最容易的安装U盘a反而是这样子的。的确,你把整个U盘做成vfat文件格式反而更容易,如果ISO有多个分区甚至隐藏分区用户反而无法拷贝。随便搜索一下发现压根儿在windows下没有很容易的dd类似物,这让人如何是好?看来我太浅薄了,怎么可能作为一个操作系统提供商连这个最起码都做不到呢?不可能的,是考虑中国的国情特意为之!我还妄自尊大写了一封邮件振振有辞的指出其缺陷。唉!
  5. 但是官方的麒麟iso确实是有缺陷的,因为它的EFI分区镜像文件都有问题吧?
    
    O$ ll Kylin/EFI/BOOT/
    total 1649
    dr-xr-xr-x 2 root root   2048 Apr  7 15:16 ./
    dr-xr-xr-x 3 root root   2048 Apr  7 15:16 ../
    -r-xr-xr-x 1 root root 561152 Apr  7 15:16 BOOT.EFI*
    -r-xr-xr-x 1 root root 561152 Apr  7 15:16 BOOTX64.EFI*
    -r-xr-xr-x 1 root root 561152 Apr  7 15:16 grubx64.efi*
    -r--r--r-- 1 root root    666 Apr  7 15:47 TRANS.TBL
    
    我不太相信这三个EFI文件都是相同的,这个不太可能吧?总之我不能UEFI模式下启动应该是这个原因,直到我把openKylin里的boot/grub/efi.img替换才可以成功启动:
    
    $ xorriso -as mkisofs -r -V "Kylin-Desktop-V10-x86_64" -o kylin_fixed.iso -J -joliet-long -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -b isolinux/isolinux.bin -c isolinux/boot.cat -boot-load-size 4 -boot-info-table -no-emul-boot -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat ./CD_data
    
  6. 我本来以为故事就结束了,没想到才刚刚开始,原来qemu的模拟是有限的,实际面对真正的笔记本的BIOS硬件居然不能启动,而且不同的电脑有不 同的反映,看来也许不是麒麟不想做,而是没有做成罢了。如何解决qemu模拟真实硬件也许是一个最有效的工作,否则不停的写USB测试硬件启动这个工作是 没法做了。看来工欲善其事,必先利其器。
  7. 这件事不是容易,而是非常的难!真正的挑战还没有开始!在legacy模式下是ldlinux.c32没有找到,在UEFI模式下是magjic number invalid, you need load kernel first之类的错误。总之,这个非常的复杂。

八月四日 等待变化等待机会

  1. 至少有一件小事情解决了,就是在qemu启动的时候,如果镜像文件你不想让它识别为ISO的而是希望使用USB之类的磁盘文件设备来读取的话,你可以强制加一个参数media=disk,原因是针对ISO9660的boot record之类如果媒介是DVD之类的话是需要做启动处理,因此,我现在可以准确的还原麒麟光碟不能在USB设备启动的过程:
    
    $ qemu-system-x86_64 --enable-kvm  -bios /usr/share/qemu/OVMF.fd -device qemu-xhci -drive file=Kylin-Desktop-V10-SP1-2403-HWE-Release-20240430-x86_64.iso,format=raw,media=disk -m 4G
    
    因为之前如果没有这个media=disk参数的话,qemu在读取道ISO9660/el-torito格式是会启动的,那 是因为在媒体是光驱下是允许的,如今我强调媒体是磁盘就不可以了,只能强制去读MBR/GPT分区表。这个是一个简单的小东西,我也化了不少时间才找到。 qemu的菜单异常的复杂,可想而知一个虚拟机模拟各种硬件设备需要多么复杂的实现啊!
  2. 也许dd的参数conv=sync能加快USB的写,因为我发现单独的执行sync每次都要等很久。但是难道操作系统不知道usb设备最理想的读写块大小吗?我在dd里设定的大小会影响操作系统吗?总之物理设备的实验非常的耗费时间,可是我又一次遇到kernel debug log不显示的问题,我模糊记得这个casperlive模式下是完全不同的机理,也许是内存式文件squashfs里 的内核命令参数才能起作用,而且很明显的这个是麒麟自己特殊化了,因为那个splashi是一只麒麟。这个是要大动干戈的。我目前遇到的是为什么qemu 可以在UEFI模式下启动,而实际的笔记本电脑不可以,报错说是magic number invalid之类的kernel first等等。这个是在哪一步都不知道。

    这个也许是这两天最大的笑话!如果我对于qemu有一点点的信心的话,我早该想到了!我之所以总是遇到启动失败的原因是我的USB坏了,因为我换了一个U盘一切就正常了!,很明显的我反复拷贝都出现同样的错误,应该是某些特定的存储区域坏了,很多时候可能都没有引起问题,但是某些内核敏感的代码检测某些magic number发现了拒绝启动。所以,在qemu上正常的情况应该是可靠的!

    当然还有很多问题,但是最起码第一步是可行的它可以让官方的麒麟ISO在UEFI模式下的USB启动安装。
  3. 关于MBR/BIOS模式下启动失败似乎也浮出水面了:我有两台笔记本电脑,一台的BIOS是可以设置只是legacy模式或者是 UEFI(CSM)模式,于是在前者是失败的;而另一台的BIOS设置是否允许支持legacy模式,而系统更加聪明在legcy模式下启动失败直接尝试 UEFI模式就成功了,于是就掩饰了BIOS/MBR失败的问题。
  4. qemu有很多神奇的功能我是闻所未闻啊!但是我发现我需要的是更简单的选项-nographic,之前之所以无法显示各种kernel的log就是因为console被图形模拟占据了,撇开这个是最简单的解决问题的方式,因为使用monitor是为了debug图形显示不得已再开一个,比如使用-monitor telnet::45454,server,nowait -serial mon:stdio,然后在另外的console里控制telnet localhost 45454,如果要退出就是quit命令。而在模拟图形窗口如果是使用了-curses需要ESC+2

    但是如果在-nographic模式下必须这样做:

    1. first press Ctrl + A (A is just key a, not the alt key),
    2. then release the keys,
    3. afterwards press X (lowercase x; no Shift, case matters).

  5. 我提前在光盘文件的启动菜单,就是isolinux/txt.cfg设定了kernel的debug
    
    default live
    label live
      menu label ^Try Kylin without installing
      kernel /casper/vmlinuz
      append  boot=casper initrd=/casper/initrd.lz fsck.mode=skip audit=0 security= console=ttyS0 debug break=top debug= live --
    
    这里的debug=是给casper的,而kernel看到的是另一个debug,而break=top导致initramfs暂停,我看了半天后不明所以,就按return让启动继续。总之,这个debug信息是非常的多,很方便,但是我依然没有看出问题的所在。
  6. 然后我就意识到,我看到内核信息就证明我已经启动成功了,我根本就不可能到这个地方,我需要的是early boot信息,这个qemu要怎么给我?即便是这位大侠展示的高超技巧我也用不上,我的问题是bootloader而不是kernel。
  7. 这是一个及其复杂的深水区,我根本就没有这个能力涉足。深水危险,止步!
  8. 收获很多,但是集中兵力打歼灭战的原则基础上更要讲究不打无准备之仗,不打无把握之仗。我现在唯一可能攻击的目标是isolinux,超出这个范围都是徒劳的。休息一下吧。
  9. 在三十几度的高温下流了几斤汗我想明白了这么几件事:
    1. 我应该先使用media=disk确认一下所有的ISO都能够在legacy模式下启动,结果确定大家都做到了。
    2. 我应该理解所谓的UEFI(CSM)模式是和纯粹的legacy模式有区别的,这个在我两台笔记本上就体现了,在前者实际上是能够识别UEFI/GPT格式,但是它能够在legacy模式下尝试。但是这里面的细节我实际上是不理解的,需要看规范才能明白。
    3. 实际上我有一个误解,就是iso/syslinux的启动和grub是不同的,它说到底是建立在BIOS理解ISO9660文件系统的基础上的, 所以,el torito的boot record可以指定一个启动文件,这里我又回忆到之前我抱怨在ISO文件里一个分区嵌入另一个分区,这个正是ISO9660文件系统的特点,它实际上是 可以这么做的,因为它的文件系统是一个描述的,并不是传统意义的block设备,所以,我认为它指定isolinux.bin作为启动文件是可以的,因为 制作iso文件当然知道每一个文件的其实地址了,应该就是bootrecord里的地址吧?重要的不是这里,重要的是我突然明白了几种启动模式指向的是同 一个分区,而这个都是isolinux.bin,是ISO9660-eltorito的启动文件。

八月五日 等待变化等待机会

  1. 还有一件事是我昨天想明白,或者说看懂了的,就是最好的学习就是实际做一个最简单的livecd,不是repacking现有的ISO,而是from scratch,但是是一个最简单的。因为这个太复杂耗费的精力太多模糊了焦点。我在头条上看到这个更容易一些,最重要的是它点出了那个精句,我使用公式来表达这个真理:LiveCD=kernel+fileysystem+bootloader。这里面虽然人人皆知,但是实现的细节有两个:filesystem=>busybox+initrd因为出乎很多人的意外的是三样东西最麻烦的不是看上去容易的另外两个,反而是最容易的文件系统,这个做起来千头万绪五花八门,因为操作系统的核心反而是它的内容也就是文件系统,所有的Linux的几乎内核都是可以替换的,唯有操作系统是不同的。这个才是实践的关键。
    1. 下载编译busybox,可以使用make menuconfig来像内核一样配置。最后使用make install会得到一个_install目录下安装好的成品:
      
      $ ll _install/
      total 20
      drwxrwxr-x  5 nick nick 4096 Aug  5 05:31 ./
      drwxrwxr-x 39 nick nick 4096 Aug  5 05:31 ../
      drwxrwxr-x  2 nick nick 4096 Aug  5 05:31 bin/
      lrwxrwxrwx  1 nick nick   11 Aug  5 05:31 linuxrc -> bin/busybox*
      drwxrwxr-x  2 nick nick 4096 Aug  5 05:31 sbin/
      drwxrwxr-x  4 nick nick 4096 Aug  5 05:31 usr/
      
      作者不要现成的linuxrc,因为这个是一个标准的busybox,作者做的更加像一个真正的操作系统
    2. 把以上的除linuxrc都拷贝过来,并且创建操作系统中最最基本的挂载点/dev,/proc,/sys。这个工作是init来做:
      
      $ cat init
      #!/bin/sh
      
      dmesg -n 1
      mount -t devtmpfs none /dev
      mount -t proc none /proc
      mount -t sysfs none /sys
      setsid nick /bin/sh
      
    3. 文件系统打包的一个基本技巧,我有盲点,这个选项-H newc是所谓的新兼容模式 newc The new (SVR4) portable format, which supports file systems having more than 65536 i-nodes. (4294967295 bytes)
      
      $ find . | cpio -R root:root -H newc -o | xz > ../rootfs.xz
      
      其中修改用户为root很重要。
    4. 接下来就是创建ISO了,创建busyboxISO目录,然后拷贝内核,文件系统和bootloader,这里的isolinux.bin和ldlinux.c32分别来自于isolinux和syslinux两个包
      
      $ cp /usr/lib/syslinux/modules/bios/ldlinux.c32 busyboxyISO/
      $ cp /usr/lib/ISOLINUX/isolinux.bin busyboxISO/
      $ sudo cp /boot/vmlinuz-6.5.0-44-generic busyboxISO/kernel
      $ cp rootfs.xz busyboxISO/
      
      然后需要创建一个最简单的bootloader的启动菜单
      
      $ cat isolinux.cfg
      default kernel initrd=rootfs.xz
      
    5. 然后是制作ISO:
      
      $ xorriso -as mkisofs -o ../busybox.iso -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table ./
      
    6. 使用qemu来检验,但是不幸的是我的内核启动crash了,不管怎么说启动是成功的。我甚至可以说测试bootloader压根儿不需要内核和文件系统,我只需要测试菜单就可以了!
      
      $ qemu-system-x86_64 -cdrom busybox.iso -m 4G
      
    所以说这个实践的最大收获就是我可以纯粹的依靠菜单来测试bootloader,完全不理会内核和文件系统。
  2. 在实践中发现EFI是非常的复杂的过程,这个archlinux的wiki似乎讲的很详细,值得仔细比对,我还没有找到syslinux自己的官方文档比它更详细的。
  3. 在跳入这个湍流的洪水前,我先搞清楚一个基本问题,就是isolinux/syslinux/extlinux/efilinux等等的关系,到底有几个包,都是干什么的:
    package namedescription
    isolinuxcollection of bootloaders (ISO 9660 bootloader)
    syslinuxcollection of bootloaders (DOS FAT and NTFS bootloader)
    syslinux-commoncollection of bootloaders (common)
    syslinux-eficollection of bootloaders (UEFI bootloader)
    syslinux-utilscollection of bootloaders (utilities)
    extlinuxcollection of bootloaders (Linux ext2/ext3/ext4, btrfs, and xfs bootloader)
    pxelinuxcollection of bootloaders (PXE network bootloader)
    这是一个相当庞大的家族啊!所以,有了这个清晰的图谱我才能明白为什么有这么多的工具,因为看似一个简单的混合启动实际上包含了多种工具的协调!
  4. Debian的讲解固然是专业的,但是归根结底还是手册式的讲解怎么使用,因为即便是syslinux的官网也是一个说明书
  5. 那么最最简单的BIOS启动是怎么一个过程?我始终看不懂el torito的规范的一个重要原因就在于BIOS基础的函数以及汇编的障碍,这里我只能明白个原理。就是说el torito的启动参数就是一个文件,难怪我们需要参数isolinux.bin就足够xorriso作为bootable的参数-b
  6. 再次总结一下,值得深入学习的是debian的wiki,和archlinux的wiki这两个都是宝贵的资料,可以说非常非常的有价值!

八月六日 等待变化等待机会

  1. 要怎么理解这个关于BIOS启动的解说呢?我是后来才明白这个仅仅是针对Linux的文件系统比如ext2/3/4的bootloader,它的名字和说明也是很明确这一点的,而syslinux让人烦恼的一点就是安装mbr的操作都要手动完成,这个总是一个安全的隐患,很担心误操作!
    1. 这个第一阶段基本上大家都清楚,它就是MBR的原始定义了,
      Stage 1 - Part 1 - Load MBR
      At boot, the BIOS loads the 440 byte MBR boot code at the start of the disk (/usr/lib/syslinux/bios/mbr.bin or /usr/lib/syslinux/bios/gptmbr.bin).
    2. 这里需要注意的是我们是BIOS模式,所以,一定要找的是bootable或者说active的分区。
      Stage 1 - Part 2 - Search active partition
      The Stage 1 MBR boot code looks for the partition that is marked as active (boot flag in MBR disks). Let us assume this is the /boot partition, for example.
    3. 这里才是重要的地方
      Stage 2 - Part 1 - Execute volume boot record
      The Stage 1 MBR boot code executes the Volume Boot Record (VBR) of the /boot partition. In the case of Syslinux, the VBR boot code is the starting sector of /boot/syslinux/ldlinux.sys which is created by the extlinux --install command. Note that ldlinux.sys is not the same as ldlinux.c32.
      这里要明白使用extlinux把它安装到ext2/3/4的分区里,而它仅仅是创建一个文件那么简单吗?对比一下Linux文件系统的第一个sector的变化吧?
      
      $ fdisk -l kylinUSB.img 
      Disk kylinUSB.img: 4.88 GiB, 5242880000 bytes, 10240000 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: dos
      Disk identifier: 0x7fc434ad
      
      Device        Boot Start      End  Sectors  Size Id Type
      kylinUSB.img1 *     2048 10239999 10237952  4.9G 83 Linux
      $ xxd -s $((2048*512)) -l 512 kylinUSB.img
      00100000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00100010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      ...
      
      在一个ext3的文件系统里,它的第一个sector是空白的,然后我们使用extlinux来安装一下。按照推荐创建子目录extlinux在这个分区比较好管理文件。
      
      $ sudo mkdir fat/extlinux
      $ sudo extlinux -i fat/extlinux
      fat/extlinux is device /dev/loop13p1
      Warning: unable to obtain device geometry (defaulting to 64 heads, 32 sectors)
               (on hard disks, this is usually harmless.)
      
      然后再来看看第一个分区
      
      $ xxd -s $((2048*512)) -l 512 kylinUSB.img
      00100000: eb58 9053 5953 4c49 4e55 5800 0200 0000  .X.SYSLINUX.....
      00100010: 0000 0000 0000 0000 2000 4000 0000 0000  ........ .@.....
      00100020: 0038 9c00 0000 0000 0000 0000 0000 0000  .8..............
      00100030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00100040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
      00100050: 0000 0000 0000 0000 0000 fafc 31c9 8ed1  ............1...
      00100060: bc76 7b52 0657 1e56 8ec1 b126 bf78 7bf3  .v{R.W.V...&.x{.
      00100070: a58e d9bb 7800 0fb4 370f a056 20d2 781b  ....x...7..V .x.
      00100080: 31c0 b106 893f 8947 02f3 64a5 8a0e 187c  1....?.G..d....|
      00100090: 884d f850 5050 50cd 13eb 628b 55aa 8b75  .M.PPPP...b.U..u
      001000a0: a8c1 ee04 01f2 83fa 4f76 3181 fab2 0773  ........Ov1....s
      001000b0: 2bf6 45b4 7f75 2538 4db8 7420 663d 2147  +.E..u%8M.t f=!G
      001000c0: 5054 7510 807d b8ed 750a 66ff 75ec 66ff  PTu..}..u.f.u.f.
      001000d0: 75e8 eb0f 5151 66ff 75bc eb07 5151 66ff  u...QQf.u...QQf.
      001000e0: 361c 7cb4 08e8 e900 7213 20e4 750f c1ea  6.|.....r. .u...
      001000f0: 0842 8916 1a7c 83e1 3f89 0e18 7cfb bbaa  .B...|..?...|...
      00100100: 55b4 41e8 cb00 7210 81fb 55aa 750a f6c1  U.A...r...U.u...
      00100110: 0174 05c6 0646 7d00 66b8 0010 2000 66ba  .t...F}.f... .f.
      00100120: 0000 0000 bb00 80e8 0e00 6681 3e1c 80dc  ..........f.>...
      00100130: 50a1 5f75 74e9 f802 6603 0660 7b66 1316  P._ut...f..`{f..
      00100140: 647b b910 00eb 2b66 5266 5006 536a 016a  d{....+fRfP.Sj.j
      00100150: 1089 e666 60b4 42e8 7700 6661 8d64 1072  ...f`.B.w.fa.d.r
      00100160: 01c3 6660 31c0 e868 0066 61e2 dac6 0646  ..f`1..h.fa....F
      00100170: 7d2b 6660 660f b736 187c 660f b73e 1a7c  }+f`f..6.|f..>.|
      00100180: 66f7 f631 c987 ca66 f7f7 663d ff03 0000  f..1...f..f=....
      00100190: 7717 c0e4 0641 08e1 88c5 88d6 b801 02e8  w....A..........
      001001a0: 2f00 6661 7201 c3e2 c931 f68e d6bc 687b  /.far....1....h{
      001001b0: 8ede 668f 0678 00be da7d ac20 c074 09b4  ..f..x...}. .t..
      001001c0: 0ebb 0700 cd10 ebf2 31c0 cd16 cd19 f4eb  ........1.......
      001001d0: fd8a 1674 7b06 cd13 07c3 426f 6f74 2065  ...t{.....Boot e
      001001e0: 7272 6f72 0d0a 0000 0000 0000 0000 0000  rror............
      001001f0: 0000 0000 0000 0000 fe02 b23e 1837 55aa  ...........>.7U.
      
      这个看起来像是一个MBR,但是我很怀疑,因为分区表部分都是extlinux写的信息,总之是它专有的吧?
      
      $ ll fat/extlinux/
      total 196
      drwxr-xr-x 2 root root   4096 Aug  6 06:33 ./
      drwxr-xr-x 4 root root   4096 Aug  6 06:33 ../
      -r--r--r-- 1 root root 119368 Aug  6 06:33 ldlinux.c32
      -r--r--r-- 1 root root  59904 Aug  6 06:33 ldlinux.sys
      
      而它安装的这两个文件中ldlinux.sys不能用通常的办法删除,因为它设定了immutable
      
      $ lsattr fat/extlinux/ldlinux.sys
      ----i----------------- fat/extlinux/ldlinux.sys
      
      若想删除必须先修改属性:chattr -i。接下来就是设立一个简单的菜单来检验。这个似乎是最最少的一个配置吧?
      
      $ ll
      -rw-r--r-- 1 root root    162 Aug  6 06:52 isolinux.cfg
      -r--r--r-- 1 root root 119368 Aug  6 06:33 ldlinux.c32
      -r--r--r-- 1 root root  59904 Aug  6 06:33 ldlinux.sys
      -r--r--r-- 1 root root  24708 Aug  6 06:56 libutil.c32
      -rw-r--r-- 1 root root  26148 Aug  6 06:55 menu.c32
      $ cat isolinux.cfg
      ui menu.c32
      
      menu title this is the title for menu
      
      label first
      	menu label this is actual menu text
      
      label second
      	menu label menu text for first
      
      我发现这个配置文件只能是isolinux.cfg,尝试这些名字都不行extlinux.conf, syslinux.cfg,extlinux.cfg
    4. Stage 2 - Part 2 - Execute /boot/syslinux/ldlinux.sys
      The VBR will load the rest of /boot/syslinux/ldlinux.sys. The sector location of /boot/syslinux/ldlinux.sys should not change, otherwise syslinux will not boot.
      我的猜测是之前看到的开始的sector里的代码和这个ldlinux.sys是紧密关联的吧?总之这里都是bootloader的核心部分。
    5. 这里说明了ldlinux.c32是这一阶段的第二步因为第一步的ldlinux.sys太小放不下。
      Stage 3 - Load /boot/syslinux/ldlinux.c32
      The /boot/syslinux/ldlinux.sys will load the /boot/syslinux/ldlinux.c32 (core module) that contains the rest of the core part of syslinux that could not be fit into ldlinux.sys (due to file-size constraints). The ldlinux.c32 file should be present in every Syslinux installation and should match the version of ldlinux.sys installed in the partition. Otherwise Syslinux will fail to boot.
    6. Stage 4 - Search and Load configuration file
      Once Syslinux is fully loaded, it looks for /boot/syslinux/syslinux.cfg (or /boot/syslinux/extlinux.conf in some cases) and loads it if it is found. If no configuration file is found, you will be dropped to a Syslinux boot: prompt. This step and the rest of non-core parts of Syslinux (/boot/syslinux/*.c32 modules, excluding lib*.c32 and ldlinux.c32) require /boot/syslinux/lib*.c32 (library) modules to be present (https://wiki.syslinux.org/wiki/index.php/Common_Problems#ELF). The lib*.c32 library modules and non-core *.c32 modules should match the version of ldlinux.sys installed in the partition.
      这一步和我的实验不符合,我的实验是它总是找isolinux.cfg。这个现象是比较subtle的,也许是因为我使 用ubuntu原装的extlinux,并且直接把麒麟的各种*.c32文件拷贝过来的的缘故吧?我重新编译extlinux并且拷贝所有我自己编译的结 果就是符合这个描述了。还有一个可能性是我测试的时候因为是loopmount没有sync文件的缘故。总而言之,这些个二进制的.c32文件很蹊跷必须使用相同版本的,否则问题会莫名其妙的。
    所以,这里就是总结了一个最最简单的使用extlinux/syslinux的实例,基本步骤就是
    1. 首先创建一个image文件,然后使用fdisk分区成bios的bootable的
    2. 其次使用syslinux的mbr.bin写镜像文件头440,因为extlinux的安装是安装在ext文件分区的
    3. 所以,也注定要先创建一个ext文件分区,然后使用extlinux来安装到这个分区,可以指定子目录。不要以为这个安装和拷贝两个一样,其实它是有写类似于MBR在分区的,只不过这个是所谓的1st stage bootloader,到底写的这个MBR应该和ldlinux.sys不是一个东西吧?这里面我看源代码都是汇编看不懂。总之最最开头的MBR跳转到这个启动分区依旧是在第0个sector执行,然后再调用ldlinux.sys和ldlinux.c32按顺序执行的。也就是ldlinux.sys,而它是要配合一起拷贝的文件ldlinux.c32作为2nd stage bootloader一起协同的。
    4. 为了检验这个流程,我只需要创建一个配置菜单文件就可以证明bootloader是否完成了工作,这里就是几个可能性,因为是extlinux,所以,首选是extlinux.conf,其次是syslinux.cfg
    5. 作为菜单我们必须增加menu.c32以及它依赖的libutil.c32,否则没有图形菜单出来很难确定是否启动成功。最后注意文件要sync到loopmount
    这个就是一个BIOS/EXT2/3/4文件系统的开发测试流程。这个是相对简单的。下一步的EFI是相当复杂的,而且牵扯到32/64位机器设置我之前没有想到有区别,就更加复杂了。
  2. 早上还是有收获的,至少这个BIOS模式的最简单的ext文件系统是走了一遍,再下去应该先把FAT/ISO9660文件系统先走一遍,因为EFI看起来相对的复杂?休息一下吧?

八月七日 等待变化等待机会

  1. 我为了编译安装本地的syslinux只能采取比较特殊的做法,因为它不给你配置其他的安装目录,查看Makefile只能设置自己的变量:
    
    $ INSTALLROOT=$PWD/install make bios install
    
    但是编译efi非常的乱,我决定暂时放弃自己实践,这个Makefile写的很不主流。
    1. 创建一个FAT的image
      
      $ dd if=/dev/zero of=kylinFAT.img bs=100M count=1
      
    2. 使用fdisk创建一个BIOS-bootable的
      
      $ fdisk -l kylinFAT.img 
      Disk kylinFAT.img: 100 MiB, 104857600 bytes, 204800 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: dos
      Disk identifier: 0x9f60c445
      
      Device        Boot Start    End Sectors Size Id Type
      kylinFAT.img1 *     2048 204799  202752  99M  b W95 FAT32
      
    3. 创建文件系统
      
      $ sudo losetup -Pf --show kylinFAT.img 
      /dev/loop18
      $ sudo mkfs.fat -F 32 /dev/loop18p1
      mkfs.fat 4.2 (2021-01-31)
      
    4. 安装syslinux,我想安装在一个子目录没想到syslinux自己不懂得创建直接coredump,只能自己先创建目录
      
      $ sudo mount /dev/loop18p1 fat
      $ sudo mkdir fat/syslinux
      $ sudo syslinux -i /dev/loop18p1 -d syslinux
      
    5. 手动安装mbr
      
      $ sudo dd if=/usr/lib/SYSLINUX/mbr.bin of=/dev/loop18 bs=440 count=1 conv=notrunc
      
    6. 手动拷贝菜单相关的文件
      
      $ sudo cp /usr/lib/syslinux/modules/bios/libutil.c32 fat/syslinux/
      $ sudo cp  /usr/lib/syslinux/modules/bios/menu.c32 fat/syslinux/
      $ sudo cp  /usr/lib/syslinux/modules/bios/ldlinux.c32 fat/syslinux/
      
    7. 创建一个启动菜单
      
      $ cat fat/syslinux/syslinux.cfg
      ui menu.c32
      
      menu title this is the title for menu
      
      label first
      	menu label this is actual menu text
      
      label second
      	menu label menu text for first
      
    8. sync磁盘,我发现有时候最好都umount设备。使用qemu检验启动菜单:
      
      $ qemu-system-x86_64 -drive file=kylinFAT.img,format=raw,media=disk -m 4G
      
    这就是一个BIOS的在FAT32文件系统的安装过程测试。
  2. 至于说制作一个bootable的ISO其实步骤是简单的多,尽管它的内部其实更加的复杂。你只需创建一个文件夹来存放数据
    
    $ mkdir kylinISO
    $ mkdir kylinISO/isolinux
    $ cp /usr/lib/ISOLINUX/isolinux.bin kylinISO/isolinux/
    $ cp /usr/lib/syslinux/modules/bios/libutil.c32 kylinISO/isolinux/
    $ cp /usr/lib/syslinux/modules/bios/menu.c32 kylinISO/isolinux/
    $ cp /usr/lib/syslinux/modules/bios/ldlinux.c32 kylinISO/isolinux/
    
    然后照样制作一个类似的启动菜单
    
    $ cat kylinISO/isolinux/isolinux.cfg 
    ui menu.c32
    
    menu title this is the title for menu
    
    label first
    	menu label this is actual menu text
    
    label second
    	menu label menu text for first
    
    制作ISO文件:
    
    $ xorriso -as mkisofs -o kylinISO.iso -J -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-tab ./kylinISO
    
    使用qemu检验启动菜单:
    
    $ qemu-system-x86_64 -cdrom kylinISO.iso -m 4G
    

八月八日 等待变化等待机会

  1. 很多时候我对于从源代码编译基本上没有感到特别的困难,那很可能是前人已经把明显的问题修复了,但是对于像syslinux这样子古老的 项目情况就不一样了。首先,最原始的代码的作者的最后一次更新已经是十几年前了,这就是为什么我看到的官方的代码居然是也被存在了kernel的库里。其 次,debian对于它有好几个patch,这个机制我平常很少留意。就是说ubuntu下载源码通常都有一个debian的目录下一大堆的patch要 去apply。而且编译规则也有变化。不管怎么说编译需要一个额外的gnu-efi,这个我之前一直没有注意到,甚至于在结合patch看编译 efi32/64错误的时候尝试搜索gnu-efi这个包的时候还将信将疑,因为普通apt配置下这个包是不存在的,因为它是一个devel包, 只有我打开了apt的sources.list里的源代码库开关才能看到,而且这个gnu-efi是分32位和64位的,我没有配置multi-arch 之类的兼容i386库,两个平台的东西可能是有冲突的,最后我也只能老老实实的编译我自己的平台efi64。这个领域的确是有些复杂。
  2. debian的开发者使用一个很nice的工具叫做quilt维护大量的patch,这个以前也是一个令人头疼的问题。读它的manpage,我怎么也理解不了push就是apply的意思,看来我真的看不懂manpage。而这里的小技巧是

    quilt uses a special directory for keeping patches. Unfortunatelly, that directory is ./patches/ by default. For Debian packages ./debian/patches/ is far more comfortable. To flawlessly fix this, add export QUILT_PATCHES=debian/patches to ~/.quiltrc (creating that file if needed).

    所以,一次性的创建这个~/.quiltrc吧。然后在ubuntu的代码包里就可以使用quilt push -a来加载所有的patches了,不过这个也是多余,因为正常的apt-get source就帮你做了,除非我自己手动一步步这么做,但是为什么会出现这个情况呢?几乎没有应用的场景啊?也是有的,比如我使用ubuntu提示的syslinux的git源代码 https://salsa.debian.org/images-team/syslinux.git下载代码后,你是不是需要这样子手动应用patches,当然也许一行脚本可以做到,可是quilt很多的功能告诉你哪些已经加载甚至于图形,当然这个dependency的图我看不懂,可是功能强大啊!一个push,一个pop瞬间控制各个patches,真的是太得心应手了。
  3. syslinux的编译不是那么轻而易举的,它的说明里的编译方法总是出错,即便你安装了必要的库gnu-efi也是如此,除非你加载了debian的那些patches,然后使用debian/rules这个脚本来编译。
  4. 编译syslinux的确是一个挑战,原因是我从未接触过如何使用debhelper这个强大的工具,这个有太多的需要学习的领域,我根本不知道如何入手,最直接的是使用debian/rules build需要root/sudo user,而这个太危险了,我只能使用arch-chroot进入我之前的ubuntu-image里做编译安装,事实证明这是一种非常灵活的虚拟机的做法,因为我仅仅是编译一些包而这个仅仅是文件系统的操作,我进入一个虚拟机实在是太过分了。而编译syslinux需要相当一些包,比如uuid-dev,gnu-efi,debhelper。不过现在看来我之前的担心是多余的,因为所谓的sudo debian/rules install install-arch install-indep并不会真的安装到系统目录,实际上是安装到了debian这个目录下,我如果要真的安装可以直接执行sudo make install,因为这个还是可以执行正确不需要额外的debian的patch来修理的。所以,这个编译过程还是相当的复杂,主要我以前没有接触过dh这样的有魔术的包。我现在确保编译成功的就是把dh的所有的sequence除了clean都走一遍$ sudo ./debian/rules binary binary-arch binary-indep build build-arch build-indep install install-arch install-indep

八月九日 等待变化等待机会

  1. 当我对一个领域了解的越多,我对于这个领域的敬畏反而越深。对于一个普通用户如果要最简单的达到使用一个LiveCD的ISO镜像文件拷 贝到USB直接能够启动当然是最理想和简单的,这么做的基本方式要怎么做到的?麒麟的做法我本来一直不是太理解,现在才有些感觉,其实也就是一个长久的问 题困惑我的根源,在ISO里的EFI目录是不小心残留下来的吗?在grub启动的时候我们指望它在一个单独的EFI分区里作为bootloader,可是 为什么要把这个本应放在另外一个分区的文件再拷贝一份呢?我原本一直以为是使用xorriso命令因为懒惰就把分区的文件拷贝一份省的麻烦,现在才意识到 是大多数使用USB的windows用户是直接使用FAT32文件系统它天然就是ESP的分区,所以,在GPT/EFI模式下天然就能启动,这个压根儿就 不需要创建额外的EFI分区,所以,在GPT/UEFI模式下拷贝文件就是一个很简单的创建bootable的方式。而我所心心念念的是一个几乎没有多少 意义的corner case,有多少人的电脑现在只支持BIOS/MBR模式?更何况我之前已经注意到了在windows平台上很难找到类似dd的低端读写工具,作为用户大多数只能做到文件拷贝,那么分区FAT32/NTFS是几乎最好的办法。当然了,如果用户把USB格式化为NTFS是不幸的,因为EFI只支持FAT为ESP分区,这个是无法启动的,我刚刚做了实验证实了这一点。很贴心的是mkfs.ntfs给了很多的提示:To boot from a device, Windows needs the 'partition start sector', the 'sectors per track' and the 'number of heads' to be set. Windows will not be able to boot from this device.
  2. 我花了好大功夫才找到这个liveCD的手册,看上去似乎是相当的重要的。因为我在开始EFI的实验前需要一些补充知识,总觉得有什么地方缺失了。温故而知新?

    What is a live system?

    • Linux kernel image, usually named vmlinuz*
    • Initial RAM disk image (initrd): a RAM disk set up for the Linux boot, containing modules possibly needed to mount the System image and some scripts to do it.
    • System image: The operating system's filesystem image. Usually, a SquashFS compressed filesystem is used to minimize the live system image size. Note that it is read-only. So, during boot the live system will use a RAM disk and 'union' mechanism to enable writing files within the running system. However, all modifications will be lost upon shutdown unless optional persistence is used (see Persistence).
    • Bootloader: A small piece of code crafted to boot from the chosen medium, possibly presenting a prompt or menu to allow selection of options/configuration. It loads the Linux kernel and its initrd to run with an associated system filesystem. Different solutions can be used, depending on the target medium and format of the filesystem containing the previously mentioned components: isolinux to boot from a CD or DVD in ISO9660 format, syslinux for HDD or USB drive booting from a VFAT partition, extlinux for ext2/3/4 and btrfs partitions, pxelinux for PXE netboot, GRUB for ext2/3/4 partitions, etc.
    debian的live-build思路和ubuntu是一致的,实际上后者是前者那里继承来的吧?总之,我稍微尝试了一下在debootstrap失败了,很明显的我的默认的是ubuntu的,不合适。
  3. 目前我需要制定一个思路,debian的liveCD是一个相当成熟的项目,而且我感觉它比ubuntu还要适合,因为后者也许就是学习它的,所 以,这个很可能是最好的路径。但是另一方面,我刚刚攻克了编译syslinux正是乘胜追击的好机会,因为syslinux这个bootloader相比 于grub有着无可替代的优势。而且它是最最贴近问题的前言,首先学习手动创建比使用成熟脚本工具来的更重要。
  4. 所以,总结一下如何从源代码编译syslinux,尤其是从最新的git repo:
    1. 首先下载代码
      
      $ giit clone --recursive https://salsa.debian.org/images-team/syslinux.git
      
    2. 手动应用所有的patch。这个要求安装quilt然后必须在syslinux的代码目录下:
      
      $ echo "export QUILT_PATCHES=debian/patches" > ~/.quiltrc
      $ quilt push -a
      
    3. 编译代码:
      
      $ sudo ./debian/rules binary binary-arch binary-indep build build-arch build-indep install install-arch install-indep
      
      这里不用担心所谓的install并不是真的安装到系统目录,而是在debian目录下编译。
    4. 真正的安装,我选择不安装到系统目录因为会覆盖官方版本,所以,这个是取巧的做法:
      
      $ make INSTALLROOT=~/syslinux-install install
      
      注意这里我没有sudo

八月十日 等待变化等待机会

  1. 关于syslinux需要从头学习,因为它是核心。
    1. 它的配置文件的搜索顺序及其复杂繁琐,对于不同版本相当的混乱。我想只记录最新版本的搜索策略吧?
      
      

      BIOS

      Since version 4.03, the resulting behavior is that the same "/[[boot/]syslinux/]syslinux.cfg" file can optionally be used for SYSLINUX / EXTLINUX / ISOLINUX, while specific isolinux.cfg and/or extlinux.conf files would take precedence if present.

      Since version 4.03, the resulting behavior is that any of the respective config files (or even all of them) — namely isolinux.cfg, and/or extlinux.conf, and/or syslinux.cfg — can optionally be located together in the same "/[[boot/]syslinux/]" directory.

      UEFI

      SYSLINUX defaults to searching for the configuration file in the installed directory, where syslinux.efi is located and containing also its corresponding ldlinux.* " file.

      [6.04+] In each searched-for directory, SYSLINUX searches first for either:

      • syslia32.cfg when booting in EFI_ia32 mode
      • syslx64.cfg when booting in EFI_x64 mode

      and finally SYSLINUX searches for syslinux.cfg before falling back to the next directory.

      The first configuration file that is found stops the search and the configuration file is parsed / used.

      Note that syslinux.efi could be optionally renamed.

      这一段描述也非常的不好记忆与理解不如这个简单粗暴:就是这个顺序
      
      /boot/syslinux/syslinux.cfg
      /syslinux/syslinux.cfg
      /syslinux.cfg
      
    2. 配置文件就是这个简单的形式
      
      DEFAULT linux
      LABEL linux
      	SAY Now booting the kernel from SYSLINUX...
      	KERNEL vmlinuz.img
      	APPEND ro root=/dev/sda1 initrd=initrd.img
      	SYSAPPEND bitmask
      
      还有一个高级的sysappend

八月十一日 等待变化等待机会

  1. 非常好的syslinux的高级概念幻灯片
  2. Debian的安装手册似乎非常的完备!我保存一个版本
  3. 一件事看起来很容易,其实魔鬼在细节,而且并非那么容易!UEFI是更加简化了启动过程吗?容易与困难看对于谁来说的。这个关于EFI启动的帖子值得认真研究。结合这个帖子才综合来做:
    1. 制作一个GPT/ESP
      
      $ dd if=/dev/zero of=kylinEFI.img bs=100M count=10
      $ gdisk kylinEFI.img
      
      最后结果就是
      
      $ gdisk -l kylinEFI.img 
      GPT fdisk (gdisk) version 1.0.8
      
      Partition table scan:
        MBR: protective
        BSD: not present
        APM: not present
        GPT: present
      
      Found valid GPT with protective MBR; using GPT.
      Disk kylinEFI.img: 2048000 sectors, 1000.0 MiB
      Sector size (logical): 512 bytes
      Disk identifier (GUID): 01878283-BFF4-48BE-AEB6-3534875CD5D5
      Partition table holds up to 128 entries
      Main partition table begins at sector 2 and ends at sector 33
      First usable sector is 34, last usable sector is 2047966
      Partitions will be aligned on 2048-sector boundaries
      Total free space is 2014 sectors (1007.0 KiB)
      
      Number  Start (sector)    End (sector)  Size       Code  Name
         1            2048         2047966   999.0 MiB   EF00  EFI system partition
      
    2. 创建FAT32文件系统
      
      $ sudo losetup -Pf --show kylinEFI.img 
      /dev/loop47
      $ sudo mkfs.fat -F 32 /dev/loop47p1
      
    3. 拷贝编译好的最基本的EFI文件:
      
      $ sudo mount /dev/loop47p1 fat
      $ sudo mkdir -p fat/EFI/BOOT
      $ sudo cp ~/syslinux-install/usr/share/syslinux/efi64/syslinux.efi fat/EFI/BOOT/bootx64.efi
      $ sudo cp ~/syslinux-install/usr/share/syslinux/efi64/ldlinux.e64 fat/EFI/BOOT/
      $ sudo cp ~/syslinux-install/usr/share/syslinux/efi64/libutil.c32 fat/EFI/BOOT/
      $ sudo cp ~/syslinux-install/usr/share/syslinux/efi64/menu.c32 fat/EFI/BOOT/
      
      注意这里的libutil.c32和menu.c32和之前的syslinux/isolinux的应该是不一样,尽管这两个是最基本的文件,可是不同平台媒介上是不同的binary。而且根据UEFI这个syslinux.efi必须改名字:

      According to the UEFI specs, the default location and naming conventions for storage media are:

      EFI_SYSTEM_PARTITION/EFI/BOOT/BOOTIA32.EFI
      for EFI IA32 firmware.
      EFI_SYSTEM_PARTITION/EFI/BOOT/BOOTX64.EFI
      for EFI X64 firmware.
      实际上,这个FAT文件系统里文件名大小写是不敏感的,所以,你要小心Linux下的不同文件互相覆盖。
    4. 配置启动菜单,这个和其他的类似:
      
      $ find fat
      fat
      fat/EFI
      fat/EFI/BOOT
      fat/EFI/BOOT/ldlinux.e64
      fat/EFI/BOOT/libutil.c32
      fat/EFI/BOOT/menu.c32
      fat/EFI/BOOT/bootx64.efi
      fat/EFI/BOOT/syslinux.cfg
      
      $ cat fat/EFI/BOOT/syslinux.cfg 
      ui menu.c32
      
      menu title this is the title for menu
      
      label first
      	menu label this is actual menu text
      
      label second
      	menu label menu text for first
      
    5. 用qemu来测试,这个是最最耗费我心血的地方,因为我隐隐约约觉得我应该注意32/64位的区别,结果还是在这里栽跟头,因为默认是32位,自然启动失败了!
      
      $ qemu-system-x86_64 -m 64 -bios /usr/share/qemu/OVMF.fd -drive file=./kylinEFI.img,format=raw,media=disk -m 4G
      
    这个就是最最简单的UEFI启动测试。

八月十二日 等待变化等待机会

  1. 这个教程是一个我最近实践的浓缩提高,我是否应该实践一下呢?
  2. 第一步我当然是不敢在我本机做实验,那么要在一个即有的ubuntu的image上用chroot来做,可是磁盘文件当初分配太小了,要扩容怎么办呢?这里其实可以放心,首先,是整个镜像文件拷贝是不影响任何设备驱动的问题的,其次,扩容先扩文件大小,使用$ dd if=/dev/zero of=ubuntu-efi-disk2 bs=512 count=8000000 seek=10240000之类的方法来pad文件。这里常犯的错误是bs是应用于count/seek的,计算要小心。然后就是明确一点在MBR/GPT里分区表的删除对于最后一个分区是无害的,所谓的扩容就是删除重建,保留文件系统签名就可以了。
  3. 我现在要实验的是在chroot环境下再次chroot是否会有问题?因为这个是在chroot下再次做的话?
    
    sudo mount --bind /dev $HOME/live-ubuntu-from-scratch/chroot/dev
    sudo mount --bind /run $HOME/live-ubuntu-from-scratch/chroot/run
    
    另一个我想问的问题就是如何表示我现在在chroot。但是答题者说到的副作用让我望而生畏,最后我看这个方法似乎更加的轻便一些:

    To get the status in the console directly, using ischroot:

    ischroot;echo $?

    Exit codes:

    0 if currently running in a chroot
    1 if currently not running in a chroot
    2 if the detection is not possible (On GNU/Linux this happens if the script is not run as root).
    
    这个方法好不好呢?
    
    $ arch-chroot /mnt bash -ic 'exec env PS1="(chroot) # " bash --norc'
    
  4. 真的是愚蠢,凡是不需要改变系统的都是在chroot外做的,只需要一次chroot就可以了。这个脚本其实也是不错的,只是各种情况太复杂,我今天才知道如果我使用debootstrap而我所在的文件夹处在一个分区没有使用exec之类mount是不行的,这个真的是细节,居然还有这个问题啊。如果遇到加密磁盘可能问题还更多。就是mount里要使用option -o remount,exec,dev mymountpoint
  5. 我现在觉得做这个实践是有一定意义的,因为这个很有可能也是麒麟安装盘制作的大致流程吧?感觉有些迷茫。

八月十三日 等待变化等待机会

  1. 昨天执行脚本出了大问题,具体原因不知道但是在chroot里把我母系统的/proc的项下各个进程的相关文件进行拷贝,这就是大问题,不知道哪里出了问题,但是这样子的脚本是非常难以debug的,尤其是在chroot下自动执行的脚本。今天我就一步一步的执行看看在focal下正常的在jammy下如何。然后这个细节就是一个大学问:我之前不知道麒麟的这个bootx64.efi是怎么制作的。这里有很长的解说,我要好好读一下。
  2. 吃完早饭我又看了一下,完全不明白重点!这个grafting是一种算法我还依稀记得名字。可是这篇博客讲的太不清楚了,我还是不懂。这个讲的似乎更清楚一点,可是我还是不太明白。
  3. 感觉我还是从syslinux回到更加实际的grub来吧?很多的概念还是模糊的,而grub作为linux原生的更加的容易理解。
  4. 学习grub先要学习历史,这里讲到了我一直听到的multiboot,它非常好的解释了动机。另一个重要的是multiboot specification,这个是grub遵循的,我还是第一次知道有这个规范。相当的复杂。我以前就对于操作系统是否能够使用所有的内存感到不是很确定,似乎linux kernel需要这个作为它的一个配置参数?这里说grub是可以使用BIOS的高级函数发现所有的内存。而有些操作系统不在乎这个。

八月十五日 等待变化等待机会

  1. 之前已经明白了这个BIOS Boot Partition的意义和作用,这里再次复习一下:
    Using GNU Parted, you can set this using a command such as the following:

    # parted /dev/disk set partition-number bios_grub on
    

    If you are using gdisk, set the partition type to ‘0xEF02’. With partitioning programs that require setting the GUID directly, it should be ‘21686148-6449-6e6f-744e656564454649’.

  2. 你知道grub有几种boot的方法吗? loading an operating system directly, using kexec from userspace, and chainloading another bootloader. 而什么叫做loading an operating system directly呢?世界上没有直接这个定义,它是multiboot specification定义的。要了解真正的核心读这个spec是必须的。只是目前先放一下,对照着来吧?
  3. 首先实践一下grub-install的最简单模式,假如我创建了BIOS boot partition那么grub-install要怎么利用它呢?它应该会自动寻找安装的。
    
    $ gdisk -l grubTest.img 
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: protective
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid GPT with protective MBR; using GPT.
    Disk grubTest.img: 102400 sectors, 50.0 MiB
    Sector size (logical): 512 bytes
    Disk identifier (GUID): 69F7A79A-BD23-4288-9AFD-62DA7BB968B5
    Partition table holds up to 128 entries
    Main partition table begins at sector 2 and ends at sector 33
    First usable sector is 34, last usable sector is 102366
    Partitions will be aligned on 2048-sector boundaries
    Total free space is 2014 sectors (1007.0 KiB)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1            2048            4095   1024.0 KiB  EF02  BIOS boot partition
       2            4096          102366   48.0 MiB    8300  Linux filesystem
    
    然后mount这个image并且安装grub的i386-pc看看如何?
    
    $ sudo losetup -Pf --show grubTest.img 
    /dev/loop13
    $ sudo grub-install --target=i386-pc /dev/loop13
    Installing for i386-pc platform.
    Installation finished. No error reported.
    
    那么到底安装了些什么呢?首先我们是MBR-protective的状态下安装BIOS启动,那么意味着MBR要能够启动:通过对比知道MBR的boot code有写入,因为正常的gdisk创建了GPT/PMBR后在MBR部分只有几个标志,末尾的0x55AA和一个覆盖全部的分区表类型为0x00。另一方面从BIOS boot partition,也就是偏移在目前设置的2048*512的部分也有写入。参考grub-install的参数大体就可以知道它做了哪些工作,(使用-v参数可以看的更加具体):
    1. 我其实是需要创建安装的文件系统的,grub并不能仅仅安装bootloader,因为它是多阶段的,就是说grub自己安装在文件系统里的地方要指出来,这个就是--boot-directory参数的用意
      
      $ sudo mkfs.ext4 /dev/loop13p2
      
    2. 安装locale文件,比如/usr/share/locale/zh_CN/LC_MESSAGES/grub.mo,默认是所有的,所以,我们最好来指定。令我惊讶的是居然默认的en_US的locale不存在?
      
      $ apt-file search grub.mo | grep en_US
      $ locate grub.mo | grep en_
      /usr/share/locale-langpack/en_AU/LC_MESSAGES/grub.mo
      /usr/share/locale-langpack/en_CA/LC_MESSAGES/grub.mo
      /usr/share/locale-langpack/en_GB/LC_MESSAGES/grub.mo
      
      这个让人真的是不明所以然。
    3. 安装mod,比如/usr/lib/grub/i386-pc/gfxmenu.mod,默认是所有的,所以,最好是指定。
    4. 扫描设备,这个是很聪明的,不知道怎么做到的,比如我只是给了--boot-directory,然后它就知道这个loop设备而且找到它的parent device 这里就是我以前没有明白的地方,这一次才理解了,你单单给一个母设备比如/dev/loop13它有多个分区的话,grub怎么知道你的操作系统在哪里?所以,你一定要指定分区,这里用的或者是devicemap,这个我还不明白怎么用,或者就是直接模仿removable的模式指定安装路径--boot-directory=
      
      $ sudo mount /dev/loop13p2 linux
      $ sudo grub-install -v --target=i386-pc --locales=zh_CN --locales=en_CA --boot-directory=linux/boot /dev/loop13
      
      这里如果不指定--boot-directory=linux/boot/grub,你根本就不知道工作压根儿没做因为没有报错。可是实际上你必须透露给grub你的目标分区是哪一个。注意这里只需要指出boot不要画蛇添足再加上grub子目录,因为默认grub要帮你创建的。而--locale=参数grub只接受最后一个,所以,只能传递一个locale。不传参数就默认安装所有的locale。这个确实是多此一举。
    5. 这个是grub-install最后自己生成的命令参数,也就是grub-mkimage的参数:
      
      $ sudo grub-install -v --target=i386-pc --boot-directory=linux/boot /dev/loop13 2>&1 | grep grub-mkimage
      grub-install: info: grub-mkimage --directory '/usr/lib/grub/i386-pc' --prefix '(,gpt2)/boot/grub' --output '/home/nick/workspace/Downloads/ISO/linux/boot/grub/i386-pc/core.img'  --dtb '' --sbat '' --format 'i386-pc' --compression 'auto'   'ext2' 'part_gpt' 'biosdisk' 
      
      注意,如果你不提供参数--boot-directory=linux/boot,那么虽然grub-mkimage参数是一样的,可是它实际上是不知道这个boot-device究竟是哪一个,而这里我们是事先把bootdevice就是/dev/loop13p2直接mount到了linux下,所以,这里才能真正的安装各种grub文件:
      
      O$ ll linux/boot/grub/
      total 32
      drwxr-xr-x 5 root root  4096 Aug 15 08:31 ./
      drwxr-xr-x 3 root root  4096 Aug 15 08:31 ../
      drwxr-xr-x 2 root root  4096 Aug 15 08:31 fonts/
      -rw-r--r-- 1 root root  1024 Aug 15 08:31 grubenv
      drwxr-xr-x 2 root root 12288 Aug 15 08:31 i386-pc/
      drwxr-xr-x 2 root root  4096 Aug 15 08:31 locale/
      
    6. 学习grub我认为是正确的选择因为我感觉syslinux能做的grub都能,而且应该做的更好,至少成熟度更高,所以,没有必要。只不过grub对于windows用户不太友好吧?我现在还没有探索过windows平台如何使用,这个是以后的问题。

八月十六日 等待变化等待机会

  1. 先记录一下,对于这个debian的live manual其实是非常值得学习的。避免忘记记录一下网址
  2. 什么是kexec呢?明白了syscall的exec机制就大概明白这个非常厉害的工具了,不敢尝试。而grub里的linux命 令正是这个作用,我想bootloader直接把kernel加载到指定内存然后运行,这个听上去似乎是loading directly,平常听到看到bootloader的作用大家也泛泛的这么说,可是细节就在这里,什么叫做直接运行和调用kexec,区别在哪里呢?
  3. 这篇linux-i386-boot文档的作者名字很熟,似乎是syslinux的作者?这个都是天书一样的东西,我只能看看历史。
  4. 再次读Multi-boot感 觉其实这个是很深奥的,不仅仅是一系列的规范,很多问题我从来没有想过,比如我只是听说过ubuntu可以和windows安装在一起,可是想想看这个是 不可思议的因为windows不支持运行在ext文件系统,而linux也不支持运行在NTFS,那么它们是怎么共存的呢?也许是很简单的LiveCD在 内存运行吧?另一个问题是所谓的boot manager,之前我一直以为这个是BIOS实现的一个机制,现在看起来似乎它就是一个特殊的bootloader?这个不仅仅是英文概念词汇的问题,而是OS2/boot manager似乎是传统的MBR的直接体现?
  5. 往往一篇好的文章是旁征博引,看它的索引可以看到很多的好的文章。比如我应该再熟悉一下启动的过程。而另一方面对于第一阶段的bootloader,理解这个coreboot/libreboot是很有帮助的。
  6. Linux Boot Process。我发现我下载的版本少了这一句非常重要的总结: Those are grouped into 4 steps: system startup, bootloader stage, kernel stage, and init process。是什么原因少了这一句呢?有什么问题吗?
  7. 这篇博客Legacy BIOS vs. UEFI似乎是有价值的:从这三个方面来看比较优势。
    1. Option ROM vs Drivers
    2. Use of Assembler vs C-Language
    3. Boot Speed
    这里的描述我还没有完全领悟,看来我的基础知识还不足:
    Legacy BIOS is run by option Read Only Memory (ROM’s), which collectively is limited to 64 KB of storage. The option ROMs that legacy systems run will only work if they are compatible with the hardware that is running with it. If you upgrade your hardware, you have to update the option ROMs to make sure every aspect of the booting process is compatible.
    就是说BIOS的驱动实际上是写死在这些ROM里的,而操作系统是依赖于BIOS提供的接口访问硬件?内核就算在运行起来可以加载更加先进的驱动,但在初始化阶段只能依赖于ROM里的驱动来访问?这里作者引用这篇文章,保存一份。就算是历史也很枯燥。似乎是UEFI的宣传广告。
  8. 我发现youtube-dl或者说yt-dlp不能正常工作,总是报错要求"Sign in to confirm you’re not a bot. This helps protect our community"。于是下载firefox的一个扩展export cookie到文件,再使用参数--cookies /my/cookie/file。这个招数必须小心,一定不要多开同时下载,需要在浏览器里真的看一下表现为一个正常的人类行为。

八月十七日 等待变化等待机会

  1. 源头来看LinuxBoot是一个很好的学习视角。

    ... there are several layers that run between the hardware and the OS...

    Even after the computer finishes loading the OS, there are multiple embedded systems also running on the system entirely separate from the OS. Most notably, the Intel Management Engine (ME) runs a complete Minix operating system, while System Management Mode (SMM) is used to run code for certain events (e.g. laptop lid gets closed) in a way that is completely invisible to the running OS.

    All of these add up to the LinuxBoot project's statement that there are "at least 2.5 kernels between the hardware and Linux,"; those kernels collectively make up the firmware.

    Linux尚且如此,Windows更加的糟糕,可以说WIntel联盟本身就是一个巨大的黑盒子,所有的用户都是被他们完全掌控的绵羊。
  2. 了解一下NERF, or the Non-Extensible Reduced Firmware历史:
    LinuxBoot is the firmware and kernel while the user-space initramfs image with Go tools for system booting is available as u-root. Due to this modularity, LinuxBoot can be used with a variety of initramfs images.
  3. 了解一下UEFI的启动过程:
    A UEFI computer boots in four main phases. The security phase (SEC) and the Pre-EFI Initialization Stage (PEI) are responsible for low-level operations to prepare the hardware and are usually specific to the hardware they are implemented for. After these two stages, the Driver Execution Environment (DXE) loads various drivers, and then the Boot Device Select (BDS) phase can begin.
    正如作者指出的实际上大多数人只有个模糊的概念:
    The BDS phase is where most of what we as users are aware of happens: the system is searched for storage devices, those devices are inspected for bootloaders, and then one of those bootloaders is started. This sounds simple but, due to the many different storage devices and boot schemes available, it can be quite complicated in practice. To manage this complexity, the previous DXE stage loads hundreds of drivers on most systems, implementing support for the majority of the devices the final operating system will be able to use. Further, hardware in the system can provide "option ROMs" that include additional drivers to be loaded and executed prior to BDS. All of these are potentially vulnerable, and it's easy to imagine incredibly damaging attacks based on the extensive capabilities of the largely hidden UEFI.
    这里的"option ROMs"我是最近才听说这个名字而已,具体是什么完全是一无所知,估计大多数人都是如此吧?
    This is where LinuxBoot comes in. It starts during the DXE stage, resulting in most of the drivers (and their associated attack surface) not being loaded. Instead, a Linux kernel is loaded as if it were a driver.
    就是说Linux内核本身就是一个超级驱动,与其让各个厂家自己提供的千奇百怪的驱动还不如使用Linux内核直接做驱动,这个思想我几年前就看到了,说不定那篇论文就是这个作者。
    This brings several immediate advantages. The first, and perhaps most exciting, is that the Linux kernel is an open-source system that has been subject to a great deal of security scrutiny and receives regular fixes. This is almost the exact opposite of the case with existing UEFI drivers. The second is a practical improvement: running Linux at this early stage provides a standardized and well-documented programming environment for developing further modules. This makes it far more practical to extend UEFI's capabilities.
    这个思路是很好的,问题是怎么做到呢?而且我们可以看到依然有一些是不可替代的部分:SECPEI似乎还是不可替代的,我最近得到的感觉是DRAM和它的控制器是最难的部分,内核启动的前提就是内存初始化部分,因为没有内存什么都是空,这个也是BIOS存在的最直接原因。
  4. 这句话到底是什么意思呢?
    A Linux kernel alone is not of much use to implement actual system features, a Linux user space is needed. That user space is provided in an initramfs filesystem, much like the standard Linux boot process.

八月十八日 等待变化等待机会

  1. 遇到一个奇怪的问题:manpage里printf的确是没有这个format b/B,gcc/clang编译器也发出了警告,但是运行期%08b确实起作用了,打印出了二进制。作者的猜测似乎是对的,就是我的编译环境有问题,因为作者是草莓派的交叉编译环境而我是默认的原生的ubuntu的编译环境,并且我在此环境的自己编译的clang也有类似的问题,看来这个是比较深入的原因。估计一时半会找不到。从goldbolt.org的结果来看似乎这个问题是和ubuntu有关系。我使用debian的livecd没有发现这个问题。另外发现麒麟的livecd更新apt总是报什么proxy的问题,我怀疑是不是麒麟还要注册激活才能使用?总之这个让人感受不好。
  2. 这一段话很深奥,很难体会:
    Much like the LinuxBoot initramfs depends on the LinuxBoot kernel to start, so too does the LinuxBoot kernel depend on a previous step in the boot process. This is where coreboot may be involved. LinuxBoot and coreboot are not competitors, rather they address different stages of booting. Remember that the UEFI boot process consists of four stages, of which the third (the driver execution environment or DXE) is the point at which LinuxBoot starts. Coreboot is an implementation of the first two stages, where it can replace the UEFI firmware provided by the motherboard vendor. Coreboot only supports a narrow range of hardware but, when it can be used in concert with LinuxBoot, it enables an almost completely open boot process.
    就是说coreboot=SEC+PEI,而LinuxBoot=DXE,那么如果它们两个结合起来就是一个完整的链条:coreboot+LinuxBoot=SEC+PEI+DXE,那么所谓的Boot Device Select(BDS)甚至可以理解为一个人为的输入动作,所以,仅仅因为现在coreboot支持的硬件还比较少所以还没有形成气候?

八月十九日 等待变化等待机会

  1. rootkit of your laptop还是有参考意义的。有些听说过比如和impi部分,也实践过一点点,但是大部分都仅仅知道个名字。类似的博客的东西差不多都是至少十年前了,也许这个领域成熟了就很难再有进展了,或者很少有业余兴趣的人了?这篇文章的好处在于大量的引用
  2. 如果U-Boot既可以做1st stage bootloader也可以做2nd stage bootloader,那么它为什么不能取代BIOS/UEFI直接写到主板的SPI之类的启动系统呢?我也上当了,这里的first/second stage不是真正意义上的应该是grub层面的。
  3. 这里BIOS Boot Specification规范。这里解释的一切我需要补课
  4. 我可能理解力或者记忆力有问题,因为转了一个圈发现似乎coreboot才能代替bios?而u-boot是它的payload?
  5. 实践coreboot:看样子这个是要先编译gcc了,因为我以前很熟悉这个流程。教程里说所有的x86都是用一个目标make crossgcc-i386 CPUS=$(nproc) # build i386 toolchain目标结束得到这个You can now run IASL ACPI compiler from /home/nick/workspace/Downloads/coreboot/util/crossgcc/xgcc,就是说我的toolchain编译好了。 模拟器还是安全的,最令人担心的就是这个部分

八月二十日 等待变化等待机会

  1. 频繁的被打断,但是也是有些失去明确短期目标的过程。这个概述图文并茂其实很不错的复习:
    • UEFI-GPT: The preferred & modern way
    • UEFI-MBR: The less preferred & “legacy” way
    • BIOS-GPT: The weird hybrid/combo way (via CSM)
    • BIOS-MBR: The good ol’ legacy BIOS way (via CSM)
    粗略一看似乎没有什么,可是细思极恐,UEFI是在模拟BIOS使用CSM但是没有发现MBR,然后试图寻找GPT的情况下启动的?这个是做了多大的跳跃呢?注意这里不是PMBR,应该是不存在MBR的情况下吧?也许更简单的就是使用PMBR?我的老笔记本似乎证实了这个行为,就是在纯粹的BIOS legacy下不能启动,只能在CSM下才行。而的确是PMBR/GPT的组合。 再强调一下UEFI的启动流程:

    When booting with UEFI-GPT, the firmware first checks for the existence of an ESP partition via its GUID of C12A7328-F81F-11D2-BA4B-00A0C93EC93B. After it’s found, a “very-specially-named” bootable program is searched under “another-very-specially-named” folder.

    The exact folder and file/program name for a bootable [U]EFI program has the following format <ESP>/EFI/BOOT/<.
  2. 关于U-Boot的介绍是很重要的,就是它为什么成为嵌入式系统最主要的bootloader,原因是什么?通过比较和x86架构的不同:

    In x86 platforms the BIOS and [U]EFI “automatically” initializes the memory controller. The difference is that they are physically “part” of the board and come pre-installed.

    The x86 CPUs are designed to work closely with the Chipset’s that reside physically on the motherboard.

    其实核心还是初始化内存控制器的问题,当然U-Boot有它的特点和它的任务:
    • be small enough to fit into CPU’s SRAM
    • initialize the DRAM memory controller (e.g. DDR3, DDR4, DDR5)
    • find the Second-Stage loader (using storage interface like SDHC)
    • load it into DRAM & pass the control
    因为它的主要职能在于初始化内存控制器它甚至于得到了Memory Loader (MLO)的名字:
    Fun fact: U-Boot SPL is also called U-Boot Memory Loader (MLO) because the main reason it exists is to initialize the DRAM memory controller! Hence the name: memory loader.
  3. U-Boot的README有多长?太复杂了。完全超过我的认知水平了。也许最核心的部分就是Boot ROM。一位世外高人

八月二十一日 等待变化等待机会

  1. 在雨中头脑比较冷静感觉要做战略的调整,还是回到现实中实践能够做的部分,结果安装debian12居然失败,怀着侥幸心理检查sha256sum发现下载了那么多的ISO唯独要安装的那个是坏的!运气就是这么好!写dd能够看到进度条而不是事后使用sync茫茫然不知所以然:
    
    $ sudo dd if=debian-live-12.6.0-amd64-standard.iso of=/dev/sdb oflag=direct bs=10M status=progress
    
  2. 具体安装遇到的经验教训是使用默认分区设置导致前一次的文件干扰后一次的因为没有格式化删除分区表重建分区表保留了原来的文件系统,我估计即便删除文件系统也有很大机会重建文件系统结果文件还是被发现了。而另一个问题是现在才意识到的,就是安装grub有一个所谓的removable media path的fallback选项:
    EFI\boot\bootx64.efi: Fallback bootloader path

    This is the only bootloader pathname that the UEFI firmware on 64-bit X86 systems will look for without any pre-existing NVRAM boot settings, so this is what you want to use on removable media.

    Windows will install a copy of its bootloader to this path automatically; when installing GRUB, the grub-install (or grub2-install depending on Linux distribution) command may also put a copy of the respective bootloader here if it does not already exist. If you want, you can use grub-install --removable to tell it to install to the fallback boot path, or grub-install --force-extra-removable to overwrite any existing bootloader in the fallback path and replace it with GRUB.

    这个位置我的理解是除非NVRAM我猜想是UEFI的环境变量吧?也就是这个存储文件NvVars有设置,否则一定要运行的,所以这里就是所谓的fallback path。我之前看manpage对于这个选项--force-extra-removable没有什么概念,现在才理解,我之前安装因为旧的安装有文件,所以,grub就不覆盖导致启动失败。因为和EFI/boot/肩并肩的有各个操作系统的目录存放各自的启动文件。
    If you want to create a Secure Boot-compatible USB stick for UEFI, you should place a copy of the shim as EFI\boot\bootx64.efi and a copy of GRUB as EFI\boot\grubx64.efi, as the shim bootloader will look for grubx64.efi in the same directory the shim bootloader is in.
    这句话我的理解就是UEFI的启动入口就是EFI\boot\bootx64.efi,而这个所谓的shim直接寻找grubx64.efi,因为这个是我们在grub-install里就这么设置编译的。
  3. 这个是非常重要的一点:

    Bootloader path for a permanently installed OS

    When an operating system is installed permanently to a UEFI system, there is one new step that absolutely did not exist on classic BIOS. When installing the bootloader, four things are written to the NVRAM memory that holds the firmware settings:

    • Bootloader pathname on the EFI System Partition (ESP) that holds the bootloader(s)
    • the GUID of the ESP partition
    • a descriptive (human-friendly) name for this particular bootloader instance
    • optionally, some data for the bootloader
    我之前一直不明白哪里来的boot manager,应该也是这么来的,就是说在NvVars里记录了一个shortcut,也就是安装程序建议的是否把debian操作系统启动直接写在UEFI设置里跳过其他的配置自动启动,类似于IPMI里遇到的哪些boot option里的默认当前启动之类吧?其实,原理是很容易理解的,在启动多个操作系统的时候,在EFI目录下多个目录是多个操作系统的启动文件,如果要跳过BOOT/bootx64.efi就是用这个方法在NvVars里写上
    For Windows, the standard UEFI pathname for the Windows boot process will be \EFI\Microsoft\Boot\bootmgfw.efi, and the descriptive name will be "Windows Boot Manager". The optional data seems to contain a GUID reference to something within the Windows bootloader's BCD configuration file.

    For Ubuntu, the pathname should be \EFI\Ubuntu\grubx64.efi if you don't need Secure Boot support, or \EFI\Ubuntu\shimx64.efi if the Secure Boot shim is used. The descriptive name is simply "ubuntu" and the optional data is not used.

    In Ubuntu, these UEFI NVRAM boot settings can be viewed using the sudo efibootmgr -v command; in Windows, you can start a Command Prompt as Administrator and then use the bcdedit /enum firmware command to view the settings.

    The UEFI specification has a standard convention that each vendor should place the bootloader for a permanently installed OS within the path \EFI\<vendor name> on the ESP, so having multiple UEFI bootloaders co-exist on the same ESP is actually supported and should make things easier than with classic BIOS that had a single Master Boot Record per disk.

    我查看了一下我的系统是这样子的:
    
    $ sudo efibootmgr -v
    BootCurrent: 0000
    Timeout: 2 seconds
    BootOrder: 0001,0002
    Boot0000* ubuntu	HD(1,GPT,78354593-973c-4716-bcc0-09f5975d09ee,0x800,0x100000)/File(\EFI\ubuntu\shimx64.efi)
    Boot0001* SYSLINUX	HD(1,GPT,67d65c32-557c-4adc-8e2e-4cfc252989f9,0x800,0x1f37df)/File(\EFI\BOOT\BOOTX64.EFI)
    Boot0002* SYSLINUX	HD(1,GPT,67d65c32-557c-4adc-8e2e-4cfc252989f9,0x800,0x1f37df)/File(\EFI\SYSLINUX\syslinux.efi)
    Boot2001* EFI USB Device	RC
    Boot2002* EFI DVD/CDROM	RC
    Boot2003* EFI Network	RC
    
    说明我默认是所谓的secure boot这个概念以后再看。
  4. 这里的

    /boot/grub/x86_64-efi/grub.efi: a temporary file for grub-install

    Note: On Debian/Ubuntu, the generated GRUB core image will include a baked-in UUID reference to whichever filesystem contains the /boot directory, so you won't be able to just make a copy of either /boot/grub/x86_64-efi/grub.efi or grubx64.efi from the ESP and transplant it to a removable media: it will just attempt to find the unique UUID of your /boot filesystem and will drop to rescue mode if it won't find it.
    这个是比较好理解的,就是你运行grub-install的时候它暂时存储编译好的grub.efi,但是为了安全起见它把目标文件系统的UUID也编译 进了,大概就是类似于内核启动寻找rootfs的uuid的机制一样,所以,你要安装在U盘之类最好就是我现在的做法全部在本机做镜像文件,最后在一次性 dd到U盘。
  5. 凡是和安全有关的东西都非常的难懂,我准备随后再去理解:
    Secure Boot: shimx64.efi and the reasons for it

    Secure Boot requires that a bootloader must be signed by a certificate that is included in the system's Secure Boot NVRAM variable db, or the bootloader's SHA256 hash must be whitelisted in the same NVRAM variable. A SHA256 hash will only match a specific version of a particular bootloader, so updates won't be possible unless the firmware variable is also updated. So the certificates are the way to go.

  6. 安装的时候没有做彻底的格式化就会出现ESP手下留情的问题,比如EFI/BOOT下还是上一个操作系统安装的bootx64.efi,我被迫使用grub-install来强制安装
    
    $ sudo grub-install --target=x86_64-efi --removable --force --efi-directory=/boot/efi/ -v
    grub-install: info: copying `/usr/lib/grub/x86_64-efi-signed/gcdx64.efi.signed' -> `/boot/efi/EFI/BOOT/grubx64.efi'.
    grub-install: info: copying `/usr/lib/shim/shimx64.efi.signed' -> `/boot/efi/EFI/BOOT/BOOTX64.EFI'.
    grub-install: info: copying `/usr/lib/shim/mmx64.efi' -> `/boot/efi/EFI/BOOT/mmx64.efi'.
    grub-install: info: copying `/usr/lib/shim/BOOTX64.CSV' -> `/boot/efi/EFI/BOOT/BOOTX64.CSV'.
    grub-install: info: copying `/boot/grub/x86_64-efi/load.cfg' -> `/boot/efi/EFI/BOOT/grub.cfg'.
    
    这里就是一个很好的示范EFI/BOOT/BOOTX64.EFI可能就是一个极其简单的bootloader,而真正起作用的是grubx64.efi。但是我遇到的问题是在BIOS启动菜单里的boot manager并没有改变,估计这个需要使用efibootmgr来修改。
  7. 所谓的NVRAM里存储的信息,或者使用工具efi-readvar,或者你直接看看/sys/firmware/efi/efivars/下的文件,我看到大量的微软的名字,我怀疑这些是我当初要安装windows的原装厂商预制的,否则怎么知道操作系统是原装的?这个是只有OEM厂商才能做到吧?

九月一日 等待变化等待机会

  1. 《客旅香江之一》
    
    客旅香江思从前,
    有酒无心愁万千。
    今月已解古人恨,
    古月难伴今人眠。
    
  2. 《客旅香江之二》
    
    客旅香江更无眠,
    辗转四更夜无边。
    儿时可期今夜月,
    今生再无儿时天。
    
  3. 《客旅香江之三》
    
    漂泊才感离愁多,
    霜鬓最恨铜镜磨。
    少小窃喜不更事,
    老来心绪向谁说?
    
  4. 《客旅香江之四》
    
    客旅香江叹蹉跎,
    漂泊四海不算多。
    摩诘也曾朝天阙,
    归隐山林只向佛。
    
  5. 汉语本来就是奇妙的语言,同样的话似乎可以翻来覆去的不同解读。小时候总是庆幸自己不知道自己还能干什么;老了却悲叹自己不知道自己还能干什么
  6. 《客旅香江之五》
    
    客旅香江夜最独,
    离愁失路也无殊。
    志短才疏学更浅,
    空搔白首悔当初。
    
  7. 《客旅香江之六》
    
    客舍难眠夜正稠,
    五更月黑近中秋。
    香江几曾缺过客,
    满街铜臭人欲流。
    

九月三日 等待变化等待机会

  1. 《香江地铁臆想》
    
    杏眼含春欲入魂,
    酥胸微动散体芬。
    谁家碧玉刚妆就,
    梨花带雨疑初婚。
    

九月九日 等待变化等待机会

  1. 学习就是一个反复酝酿与咀嚼的过程,再读一遍我的关于UEFI多重系统启动并存的摘抄才能理解,如果普通的安装新操作系统是不会覆盖原来的UEFI的ESP中的很多部分,相反的是保留原来的,而这个正是很多安装程序反而新系统安装不彻底的原因,比如我之前安装了一个操作系统启动有问题,我再次安装一个新的而没有使用全部的磁盘造成两个并存结果是两个可能都不能正确启动。
  2. 这里再次理解安装bootloader的四件事情
    
    $ sudo efibootmgr -v
    [sudo] password for nick: 
    BootCurrent: 0000
    Timeout: 2 seconds
    BootOrder: 0000,2001,2002,2003
    Boot0000* ubuntu	HD(1,GPT,78354593-973c-4716-bcc0-09f5975d09ee,0x800,0x100000)/File(\EFI\ubuntu\shimx64.efi)RC
    Boot2001* EFI USB Device	RC
    Boot2002* EFI DVD/CDROM	RC
    Boot2003* EFI Network	RC
    
    这里的GUID和人类方便阅读的名字以及路径都有了。注意这里的GUID是ESP作为文件系统的GUID不是分区的磁盘的GUID,这个在gdisk -l /dev/nvme0n1看不到的,因为它只是告诉你ESP的分区是第一个,比如
    
    $ sudo gdisk -l /dev/nvme0n1
    GPT fdisk (gdisk) version 1.0.8
    
    Partition table scan:
      MBR: protective
      BSD: not present
      APM: not present
      GPT: present
    
    Found valid GPT with protective MBR; using GPT.
    Disk /dev/nvme0n1: 3907029168 sectors, 1.8 TiB
    Model: WD Blue SN570 2TB                       
    Sector size (logical/physical): 512/512 bytes
    Disk identifier (GUID): D107060B-0D72-4008-A966-22D0BE31C1FB
    Partition table holds up to 128 entries
    Main partition table begins at sector 2 and ends at sector 33
    First usable sector is 34, last usable sector is 3907029134
    Partitions will be aligned on 2048-sector boundaries
    Total free space is 2157 sectors (1.1 MiB)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1            2048         1050623   512.0 MiB   EF00  EFI System Partition
       2         1050624      3907028991   1.8 TiB     8300  
    
    要查看ESP的文件系统的GUID需要在子菜单i下才有,
    
    $ sudo sgdisk -i 1  /dev/nvme0n1
    Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI system partition)
    Partition unique GUID: 78354593-973C-4716-BCC0-09F5975D09EE
    First sector: 2048 (at 1024.0 KiB)
    Last sector: 1050623 (at 513.0 MiB)
    Partition size: 1048576 sectors (512.0 MiB)
    Attribute flags: 0000000000000000
    Partition name: 'EFI System Partition'
    
  3. 被打断了一个多星期,脑子完全断篇了,完全想不起来之前在读什么东西,只好随便看看grub的手册

九月十日 等待变化等待机会

  1. 多数人是因为看到才相信,只有少数人是因为相信才看到。
  2. 有些人只要我们还记得他们就活着。
  3. 我又一次遭受没有备份的苦,文件链接都不对了,要一个个修改,只好写一个脚本来查找每一个链接可能的子目录是哪一个:
    
    dir=stable-diffusion
    for i in $(grep --only-matching -e "2024_files/[^\"]*" 2024.htm); do s=${i##2024_files/}; ls ${dir}/$s > /dev/null 2>&1 || ls ${dir}/$s; done
    
  4. 尝试编译这个pdfedit配置总是报错因为freetype2的版本号不够,只好编译freetype2,实际上后来才意识到pdfedit仅仅需要的是freetype-config,普通配置不会安装这个,必须加一个选项./configure --enable-freetype-config

九月十二日 等待变化等待机会

  1. 我以前要把html转为pdf文件就是直接使用它的代理命令convert,但是这个因为是万能命令并不是很能精细的控制,出错后才发现要直接控制使用背后的命令wkhtmltopdf,而如果是我本地的文件那么加上这个选项可以避免很多的网络链接错误:
    
    $ wkhtmltopdf --enable-local-file-access 2024.htm /tmp/2024.pdf
    
    这么做的一个原因是我刚刚发现人工智能是一个非常的高效的阅读理解助手,就是说所谓的co-pilot的模式。我先把要阅读的文档使用pdf格式上传给豆包,然后阅读的时候可以让它来解释,这个非常的强大。

九月十五日 等待变化等待机会

  1. 《天净沙--镇海路口警察》
    
    镇海路口警察,
    设卡抓车盘查,
    骑手认罚无话。
    红绿灯下,
    过路人你我他。
    

九月十七日 等待变化等待机会

  1. 花了差不多一早上才找到可能的原因,或者说只能讲究解决。就是使用libredraw打开pdf文件后除了第一张图片,其余所有的图片都是颜色inverted。也就是说这个是libredraw独有的问题,而且不是编辑显示的问题,因为导出成pdf文件依然如此,说明是libredraw打开pdf或者有可能是pdfimport的问题,我尝试使用ppa安装最新版的libreoffice依然如此。怎么解决呢?我使用libredraw编辑pdf文件已经是没有办法的办法了,市面上找不到一款免费好用的工具了。我只好选取每一幅图片在菜单format->image->filter->invert来机械的修改每一幅图片。准备给libreoffice提bug。
  2. libreoffice编译可没有那么简单,这个是可以预想的,我压根没有这么想过,我只是看看而已。比如设置gcc-12为默认的做法还是值得学习的:
    
    $ sudo apt install gcc-12 g++-12
    $ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 60 --slave /usr/bin/g++ g++ /usr/bin/g++-12
    $ git clone https://gerrit.libreoffice.org/core libreoffice
    

九月二十一日 等待变化等待机会

  1. 学习最最基本的CNC的常识

九月二十五日 等待变化等待机会

  1. 编译linuxcnc还是需要找对方向。我一开始看有一个meson就想着使 用meson来编译,但是依赖的检测似乎总是解决不了,然后发现还是使用官方的指引省事:首先就是要解决依赖,在debian系解决依赖问题
    
    $ cd linuxcnc-dev
    $ ./debian/configure
    $ sudo apt-get build-dep .
    
    难道这个是一个能够通用的做法?

九月二十八日 等待变化等待机会

  1. 这个vigotech的玩具确实有些门道。首先是要建立一个usb-serial的驱动,这个是因为控制板原生的控制是串口的,那么连笔 记本都是USB,所以,天然需要一个这样的驱动,那么使用windows版的安装驱动的做法是否可以呢?这个是在windows下的驱动模式,我使用wine来运行它应该也不过是把所谓的INF文件拷贝而已,并不会真的有什么驱动的安装。硬件的发现是ubuntu操作系统在作的,使用lsusb能够看到有一个所谓的设备CH340 serial converter,那么在linux下会识别设备自动加载驱动创建设备文件,可是相应的设备文件ttyUSB0没有发现,我查看内核日志看到它被一个brltty给关闭了,这不是我一个人的问题:我因为目前视力虽然不好还不至于需要这个专为盲人服务的服务,所以关闭它
    
    $ sudo systemctl stop brltty-udev.service
    $ sudo systemctl mask brltty-udev.service
    Created symlink /etc/systemd/system/brltty-udev.service → /dev/null.
    $ sudo systemctl stop brltty.service
    $ sudo systemctl disable brltty.service
    Unit /etc/systemd/system/brltty.service is masked, ignoring.
    
  2. 但是创建相应的驱动只是第一部,这个公司提供的candle的改版软件为什么不能连接,看来还是有机制,也许是为了保护知识产权 的防范措施,这个就难了。看看能不能通过Candle的log发现什么呢?顺便说一下,为了验证机器是工作的我使用配备的手动控制板可以操作,甚至直接支持wifi这个实在是方便。
  3. 到底GRBL是否还需要额外的驱动呢?它是怎么控制硬件的呢?这里说版本也许需要更老的?问题是老版本没有cmake,只有一个project文件,看来需 要自己用qmake编译,这里也许是有用的提示。

十月一日 等待变化等待机会

  1. 关于我的那个新玩具主要遇到了以下两个问题:
    1. 它的控制程序依赖于一个所谓的USB-serial的内核驱动,因为说到底GRBL是依赖于串口通讯的,然而这个期待中的设备/dev/ttyUSB0没有创建,为什么?查看内核log看到设备文件被创建然后又被关闭了,后来我google发现这个关闭的服务是一个ubuntu上专门为盲人服务的一个服务,这个真的是匪夷所思。但是事实就是这样子。
    2. 另一个问题其实很简单,但是折腾了更长的时间,就是运行Candle始终无法连接洗床,我一开始怀疑这个是厂家在GRBL基础上套壳做了一个只有他们厂家提供的版本才能通讯的机制,尝试了很久才想到有可能是权限问题,因为访问串口是需要root权限的。真的是好笑。
  2. 我对豆包说我这首诗写的不好缺乏主题和连续性,你能不能帮我改一下?
    仰面观沧海,
    俯首摸青山。
    形独幽小径,
    树绿蓝云天。
    鸟啼越蝉噪,
    钟鸣捧人暄。
    虑远忧常近,
    心高气难连。
    
    结果让我佩服的五体投地!豆包改的太好了!可以说化腐朽为神奇!
    《静思》
    仰面沧海阔,俯首青山绵。
    独步幽径处,绿树映蓝天。
    鸟啼破蝉噪,钟鸣息人喧。
    心忧常虑远,志高意自连。
    
    以下是豆包修改的思路:
    • 确定主题为 “静思”,围绕在自然环境中的思考与感悟展开。
    • 将诗句中的表述进行优化,使其更加通顺和富有诗意。如 “沧海阔”“青山绵” 等表述更加生动形象。
    • 调整诗句的顺序,使整首诗在逻辑上更加连贯。先描写自然环境,再由自然引出内心的思考。
    • 对一些字词进行调整,增强诗句的表现力。例如 “破” 字突出了鸟啼对蝉噪的打破,增加了画面感。
    相比之下,我只能写一些打油诗:
    车上偶得
    
    虽无子建七步诗,
    却有兄弟真义气。
    中秋刚过逢国庆,
    月缺人圆欢聚时。
    

十月三日 等待变化等待机会

  1. 寒山子诗云:“重岩中,足清风。扇不摇,凉气通。明月照,白云笼。独自坐,一老翁。”
    我问豆包能不能配一首诗?这是豆包的答案:
    《寒岩独思》
    
    重岩深处逸清风,
    明月白云映老翁。
    扇止凉生心自静,
    独思幽境意无穷。
    
    我觉得挺不错的,又想改一下:
    《寒岩独思》
    
    重岩深处沐清风,
    明月白云伴老翁。
    扇止凉生凝思刻,
    独坐观心意从容。
    
    结果针对我的修改,老爸不太满意而我的朋友却觉得比较好。

十月六日 等待变化等待机会

  1. 针对系统crash以及死机等问题我想从candle源代码编译开始。根据我的感觉似乎代码的更新偏重于在experimental branch,但是在tag v1.2b我遇到了qmake得到一个g++不认识的选项-Z7,查看candle.pro知道这个是给QT的,因为g++的选项是没有这个的。
    
    CONFIG(release, debug|release) {
        QMAKE_CXXFLAGS += -Z7 -Fdrelease\\candle.pdb
        QMAKE_CFLAGS += -Z7 -Fdrelease\\candle.pdb
        QMAKE_LFLAGS += /DEBUG /OPT:REF
    }
    
    就是说qmake直接把这个-Z7传递给了g++这个显然是不对的,因为这个是明显的MSVC++的debug文件的选项,尽管我已经有二十几没有用过vc++的debug功能了。看来只好把它屏蔽掉还是改成-g -ggdb吧。
  2. 编译v1.2b的时候遇到这个错误
    
    frmmain.h:73:17: error: looser exception specification on overriding virtual function ‘virtual const char* CancelException::what() const’
       73 |     const char* what() const override
          |                 ^~~~
    
    这个我还是感到陌生的,查看master也就是git diff master..HEAD src/frmmain.h可以看到在master里已经修正了这个问题,那么最接近客服提供的windows编译的版本似乎是v1.1因为我找不到所谓的1.17b版,也许是厂家自己编译的版本?我搜了一下,可能真的有,因为有大侠在hack它的firmware,因为这个才是核心技术的层面,我的搜寻是小儿科的小儿科,只是懒得动脑筋才干的事情。这个以后有机会再参考。这个是工厂的说明书,我怎么没有得到?这里有些讨论,不过我并不是很感兴趣,也许只有实践一下才有真的问题能问。

十月七日 等待变化等待机会

  1. 我安装的wps2019的一个服务wpscloudsvr总是开机以后crash然后要发出令人讨厌的系统报告,这里有一些线索如何禁止或者删除它。但是似乎都不是很可靠,这里是我的一个一般性的探索过程。
    1. 显然金山公司不会那么稚嫩的注册服务,因为太过分也会被分分钟禁止掉。systemctl是找不到线索的。
    2. 有人说是autologin,这个机制我还不熟悉,幸好我有下载的安装包可以探寻,.deb和.rpm原理差不多,都是安装文件加上安装脚本,那么在安装文件里似乎也找不到线索,因为适应不同的复杂问题,可能直接拷贝不能达到使得一个服务自动开机就运行的目的吧?
    3. 开机启动的脚本放在远古时代的/etc/init.d也是不合时宜的。
    4. 似乎以前我有一个盲区就是X11作为图形界面的一个必备,它在开机运行的自动脚本是一个途径,所以,可以重点看一下/etc/xdg/autostart,这里我还看到了不少我曾经想要禁止运行的服务。但是似乎金山也没有把自己拷贝在这里?
    5. 如何查看.deb里的安装脚本呢?最简单的就是直接解压缩ar -x,然后就看见一个control.tar.gz之类的压缩脚本,这里是金山wps安装的全部秘密所在?可是找到线索却并不容易,我能看到很多的安装方式的遗迹,比如X11的自动运行方式,但似乎都被摒弃了。
    6. 另一个简单粗暴的方式就是按照帖子的思路在金山安装的配置区${HOME}/.local/share/Kingsoft下想办法,比如有的人建议把那个daemon目录下的空文件删除掉,这个也许是有效的,可以检验一下。
    7. 另外,我先入为主的认为一定是postinst的工作是无道理的,现在看看preinstall脚本里作的是什么吧。
    8. 安装的可执行脚本里有一个是很特被的名字/urs/bing/et,我可以想象金山的程序员一定对于这个外星人寄予厚望,要看看。
    9. 事实上,在安装到/usr/bin/下的几个都是脚本,而且似乎大同小异,比如那个etwpp几乎是一样的,唯独这个/usr/bin/wps我看到了它运行wpscloudsvr的痕迹,但是它本身并不是一个开机运行的脚本,一定是在另一个服务或者脚本里运行它才对?
    10. 事实上,wpscloudsvr部分原因是每次启动wps的时候连带启动的,这个在wps脚本里可以看到,我把那部分注释掉解决了一部分。但是启动还是有另一个方式运行它,我的怀疑是那个外星人在哪里被注册为一个服务作为一个后门来运行其它的程序吧,这个是我的猜想。
    11. 此外连同一起crash的还有一个xfce4的wrapper-2.0,它运行了很多的所谓的panel,包括indicator之类的,这个也是crash的一个来源。这里我发现一个非常强大的工具apport-retrace,我还不知道它是做什么的,也许和coredump管理一样的强大的工具吧?
    12. 这些更加的复杂,等回来再说吧?

十月八日 等待变化等待机会

  1. 似乎都是一些无效的劳动?究竟做了什么呢?
  2. 首先,在G-code parser问题上,显然Candle是一个早已被荒废的项目,它的处理是没有那么完善的,但是似乎是足够用了。我看工厂提供的.nc,实际上如果单单搜索G0 Z5.000似乎是足够了,所以,我都懒得写这么一个parser了。
  3. 另一个进展是终于理解为什么大家使用inkscape来辅助CNC,我看了一个视频说明了如何在inkscape里把bmp格式文件通过trace bitmap这个功能改造为矢量图,然后再输出给CNC文件,具体细节我懒得看了,知道有这个路径就是了,同时我很惊讶为什么我以前默认安装的inkscape的模样很怪异,让我不知道怎么使用?
  4. 下载了一个QT5-OpenGL的helloworld演示程序,它基本上是使用一个QWindow然后同样load一个shader来作图,我是第一次接触这个QT的图形开发,在vscode里设置了QT5的include目录后可以非常轻松的浏览代码,非常的棒!同时我在看到很多的QT自己的宏才明白这个不是它扩展的c++语法只是给所谓的mock一类的来做注脚吧?纯粹是编译前给程序员提个醒而已,简言之都是空的。不过Candle的代码使用的是比较高级的QOpenGLFunctions,这个就比较高级了,没有那么容易来对比验证了,起码也得花上一些时间来熟悉验证,可是我已经感到累了。
  5. 我终于找到了所谓的工厂的版本1.1.7,这个没有什么特别的高级吧?这个就是一个问题,如果我直接使用wine来运行编译好的windows版本似乎显示是比较的正确的,不论是当前参数的显示还是那个可能是heightmap的绘画都是很清晰,这个看来还是linux的问题,也许是驱动吧?此外,我还是尝试使用root来运行wine的,结果要重新安装我就放弃了,然后我看到这个做法才意识到可以这样做:
    
    sudo chown -R root:root .wine
    sudo wine Candle.exe
    sudo chown -R $USER:$USER .wine
    
    这里有一点风险,但至少是可行的,不过似乎windows版本只是使用COM口,在Linux下的ttyUSB0这样的设备它手动输入也不认,我不敢再冒险,就放弃了。
  6. 所以,又回到了原点,还是要解决QT5-OpenGL的shader的问题,这里面也许还掩盖了很多其他的问题,也许顺便学习QT是一个副业吧?
  7. 另外,似乎侧面的应证了wps2019的wpscloudsvr的crash问题也许是和xfce4的桌面相关的问题,因为在gnome的传统的桌面似乎没有,因为xfce里的wrapper的crash也许就是一个信号?
  8. openvpn有一个非官方的下载链接
  9. 有一个很好的解释G-Code的网站。

十月十日 等待变化等待机会

  1. 跟着豆包学习编程那就是一个灾难,完全就是盲人骑瞎马,即便我自认为已经是身经百战也是被它折磨的完全失去信心了。这一点也可以说明编程,尤其是QT+OpenGL这样子的编程比所谓的一般的脚本语言复杂的多,就算是一个熟练的C++程序员应该也是被搞糊涂了,何况QT的版本变化相当的大,就算是豆包给的是正确的指引也架不住版本的差异,何况它的行为就是一个记忆的模式,而更加像是计算机专业老教授的记忆模式,细节的缺失与错乱导致一个正确的路径也走不下去。我最后只能放弃这种捷径转而走传统的开发者的学习道路,直接下载QT的源代码来学习它的标准的例子。这里使用vscode如果设置了QT的头文件安装路径那真的是太棒了:
    
    /usr/include/x86_64-linux-gnu/qt5;/usr/include/x86_64-linux-gnu/qt5/QtOpenGL;/usr/include/x86_64-linux-gnu/qt5/QtWidgets;/usr/include/x86_64-linux-gnu/qt5/QtGui;/usr/include/x86_64-linux-gnu/qt5/QtSerialPort;/usr/include/x86_64-linux-gnu/qt5/QtCore
    
    只不过这些例子是给更加专业的开发者,而最最简单的Hello World!的小儿科的东西不在其中,不过在官方网站上是有最基本的例子。
  2. 《自嘲》
    
    漂泊二十载,
    方知少真才。
    自顾无长技,
    乞骨故土埋。
    位高惧德坏,
    财巨忧子衰。
    卜完周天卦,
    纵歌述胸怀。
    
    这个是最初的版本
    《自嘲》
    
    漂泊二十载,
    方知无真才。
    自愧无长技,
    乞骨故乡埋。
    山耸迎风傲,
    海平破浪白。
    卜完周天卦,
    放歌述胸怀。
    
    请求豆包润色修改,结果它大改为:
    《自嘲》
    
    漂泊岁月二十载,
    方觉自身少真才。
    常愧无有专长技,
    唯愿归乡骨可埋。
    山立巍峨迎风傲,
    海呈壮阔破浪白。
    周天卦象卜完后,
    放歌一曲述襟怀。
    
    有时候,我对于豆包的修改并不是很满意。

十月十二日 等待变化等待机会

  1. 碰巧看到十几年前保存下来的内部文档才理解了一个简单的事实,就是所谓的启动的ROM因为区分于成本速度考量,一般分为所谓的NOR bootNAND boot,前者是有地址线的,可以理解就是平常说的BIOS之类的固件写在其中的代码可以不需要bootloader直接在启动时候运行,而后者不能直接启动运行,需要bootloader,这个就是困惑我好久的一个简单的细节。
  2. 尝试使用谷歌的notebookLM,非常的棒,我使用我以前涂鸦的小作文来实验,总结的比我自己都好多了。 我制作一个最最简单的视频
    
    $ pdftoppm -png LostInTranslation.pdf LostInTranslation
    
    这样子先把pdf转换为一系列的png图片,注意这里的参数LostInTranslation是所谓的prefix,你要是不给的话输出全部在console上了。,然后再把这些图片和音频合成视频:
    
    $ ffmpeg  -framerate 27/581 -i "LostInTranslation-%2d.png"  -i LostInTranslation.mp3 -c:a aac -c:v libx264 LostInTranslation.mp4
    
    注意这里我事先查看音频文件有大约581秒时长实际上有1200秒,因此我后来只好重新做了一遍。,而png图片有27张,于是-framerate 27/581取整以后调整为1/41。这是在油管上的结果 这里遇到一个小问题,就是我使用的ffmpeg不知道为什么默认创建了
    
    Stream #0:0[0x1](und): Video: h264 (High 4:4:4 Predictive) (avc1 / 0x31637661), yuv444p(progressive), 1500x844 [SAR 1:1 DAR 375:211], 58 kb/s, 0.05 fps, 0.05 tbr, 13824 tbn (default)
    
    然后在firefox里不能播放,我懒得去修正这个问题了,只好去修正这个pixfmt:
    
    $ ffmpeg -i LostInTranslation.mp4 -pix_fmt yuv420p  LostInTranslation-420.mp4
    

十月十四日 等待变化等待机会

  1. 停了好几天。今天看到一个帖子很有启发:
    The only reason (so far) I've been using QWindows explicitely is for a very specific use case: to draw pure OpenGL content. This is very easy to achieve (by setting an OpenGL surface type on the window), and avoids you to bring in additional dependencies (QtWidgets, QtOpenGL, etc., which have a cost in terms of library size); it allows to create a OpenGL drawing surface in like 10 lines of code which will work on Linux, Windows, Mac, QNX, "embedded Linux", and very likely Android and iOS too. From this point of view it acts as a perfect SDL replacement. :)

十月十七日 等待变化等待机会

  1. 编译vcmi的前提是:
    
    $ sudo apt-get build-dep vcmi
    $ sudo apt install qttools5-dev
    
  2. 据说是为了这个曲子China Gets Bomb剪辑的视频是为了摸黑中国的核武器,不过大家看了都觉得不错。

  3. 十月十八日 等待变化等待机会

    1. 提了一个bug,但是大神说有可能会影响其他的mods,比如Tides of War对于github的流程我还是不太明白,不过我意识到在我的PR(Pull Requestmention了相应的issue就会自动添加,这个是非常棒的做法。
    2. 其实这个问题在我下载的原版的HOMM3wiki里是很明确的,在新版的wiki里反而模糊了,大概是不值一提吧?
    3. MSVC提了一个bug,这个在GCC/Clang都是通过的。它的起源是我曾经关注的一个讨论的新的答案 其中的算法填充这个not_prime是有一些问题的,但是重点不在算法,而是MSVC对于这个lambda(primes_num_vector)的返回的constexpr的vector不承认,至少是不承认它的size()也是constexpr,于是当然认为作为模板参数是不合法的。
    4. QT里的这些所谓的MOC(Meta-Object Compiler)原来是这个意思以下是AI的总结,似乎更加的清晰明了
      Qt's Meta-Object Compiler (MOC) generates moc files, which are crucial for enabling Qt's unique features in C++. Here's their purpose:
      • Signals and Slots:
        MOC enables the signals and slots mechanism, which allows objects to communicate with each other in a flexible and decoupled way.
      • Runtime Type Information:
        MOC generates code that provides runtime type information for QObject-derived classes, allowing you to determine the type of an object at runtime.
      • Dynamic Properties:
        MOC provides support for dynamic properties, which can be added to objects at runtime.
      • QML Integration:
        MOC is also used to integrate QML with C++ code, allowing you to use QML to create user interfaces and C++ to handle the underlying logic.
      In essence, MOC files bridge the gap between Qt's extended features and standard C++.

    十月十九日 等待变化等待机会

    1. vcmilauncher启动的时候,总是看到错误:
      
      Loading translation 'english.qm'
      Failed to install translator
      
      我的一番调查发现这个问题相当的复杂。
      1. 首先,它是launcher的问题,这个是毫无疑问的。在它的launcher/mainwindow_moc.cpp里,这个是QT的一个和语言相关的问题吧?所谓的ENABLE_QT_TRANSLATIONS下,去load设置里的language相关的配置:
        
        	const std::string translationFile = settings["general"]["language"].String()+ ".qm";
        	QString translationFileResourcePath = QString{":/translation/%1"}.arg(translationFile.c_str());
        
      2. 但是这个过程是复杂的,首先我想知道vcmilauncher的log在什么地方,找代码太困难了,于是我使用strace。
        
        $ strace -e trace=file -e trace=openat -o /tmp/result.txt ./vcmilauncher
        
        注意,这里我找的是openat,因为出乎我的意料,居然统统不使用open,这两者的区别在哪里?以后再说吧?于是我看到了log写在这里: /home/nick/.cache/vcmi/VCMI_Launcher_log.txt 而不出意料的发现它失败了。
        
        [2024-Oct-19 08:22:15.344946] INFO global [70ea553be180] - Loading translation 'english.qm'
        [2024-Oct-19 08:22:15.345022] ERROR global [70ea553be180] - Failed to install translator
        
      3. 这里的原因是为什么?首先看看到底这个配置文件在哪里?然后我在这里/home/nick/.config/vcmi/settings.json看到它了
        
        $ grep general -A 10 settings.json 
        	"general" : {
        		"autosaveCountLimit" : 10,
        		"gameDataLanguage" : "english",
        		"lastDifficulty" : 0,
        		"lastMap" : "MAPS/MDEARTH",
        		"lastSave" : "SAVES/AUTOSAVE/_THE_EMPIRE_OF__20241017T215519/1",
        		"lastSettingsTab" : 1,
        		"longTouchTimeMilliseconds" : 750,
        		"music" : 37,
        		"sound" : 51
        	},
        
        注意看这里没有我们需要的language因为它变成了gameDataLanguage。这个难道就是原因吗?
      4. 这里的配置并不是代码里直接编译安装的,因为代码里的所谓的settings.json是有这个language的:
        
        $ grep language -A 10 ./config/schemas/settings.json
        				"language",
        				"gameDataLanguage",
        				"lastSave",
        				"lastSettingsTab",
        				"lastCampaign",
        				"lastDifficulty",
        				"saveFrequency",
        				"notifications",
        				"extraDump",
        				"userRelativePointer",
        				"relativePointerSpeedMultiplier",
        --
        				"language" : {
        					"type" : "string",
        					"enum" : [ "english", "czech", "chinese", "finnish", "french", "german", "hungarian", "italian", "korean", "polish", "portuguese", "russian", "spanish", "swedish", "turkish", "ukrainian", "vietnamese" ],
        					"default" : "english"
        				},
        				"gameDataLanguage" : {
        					"type" : "string",
        					"enum" : [ "auto", "english", "czech", "chinese", "finnish", "french", "german", "hungarian", "italian", "korean", "polish", "portuguese", "russian", "spanish", "swedish", "turkish", "ukrainian", "vietnamese" ],
        					"default" : "auto"
        				},
        				"lastSave" : {
        
        这里两个gameDataLanguage和language都有。
      5. 什么是gameDataLanguage呢?原来这个是和安装的具体版本相关的。在vcmilauncher里有一个getHeroesDataLanguage是它探索安装的游戏版本的语言,这个函数相当的复杂,很有学问,我不敢再多探究。总之大概是根据一些版本的特征值来推断出来的。那么我的配置为什么只有gameDataLanguage而没有language呢?
      6. 我搜索写配置["general"]["language"]发现是这个函数detectPreferredLanguage,那么它什么时候写呢?在mainwindow.cpp里
        
        	bool setupCompleted = settings["launcher"]["setupCompleted"].Bool();
        
        	if (!setupCompleted)
        		detectPreferredLanguage();
        
        不出意外的:
        
        $ grep launcher -A 2  ~/.config/vcmi/settings.json 
        	"launcher" : {
        		"setupCompleted" : true
        	},
        
      7. 至此,我已经找到了表面的根源。真正的根源难道是第一次运行的问题吗?在detectPreferredLanguage里,它是依靠QLocale::system().uiLanguages()来列举所有的语言的,这个返回值我感觉很多人有异议。我决定做一下实验。 qDebug() << QLocale::system().uiLanguages(); 它的结果其实和我的locale是一样的
        
        ("en-US", "en")
        $ locale
        LANG=en_US.UTF-8
        LANGUAGE=en_US:en
        
        这里似乎没有什么问题?我开始怀疑这个detectPreferredLanguage的实现的问题,我首先把settings.json里的launchersetupCompleted改为false以便强制执行这个函数,结局是language没有被加上,那么是这个所谓的SettingsStorage没有写成功吗?这个东西挺复杂的,我不明白它是怎么写的。因为声明了两个,难道一个是读,一个是写吗?
        
        SettingsStorage settings;
        SettingsStorage persistentStorage;
        
        做实验一下,我已经非常累了,再坚持一下。
      8. 总而言之,我大体上明白了问题的源头,第一次运行的时候,依靠QT的locale得到用户的操作系统的语言,这里可以优化一下,因为返回的是所谓的一个语言的两个部分("en-US", "en"),然后去和vcmi里内置的支持的语言比较他们的所谓的tagIETF其实就是这里的en,这里完全没有必要去循环比较两个部分。不过这个是小问题。最大的问题是这个配置是在内存里的,也就是说第一次运行的时候虽然设置了language而且QT的load_translation是成功的,但是这个配置不知道为什么没有写到文件配置里,所以,以后总是不能读到language因为文件里只有所谓的gameDataLanguage
      这个debug还是很辛苦的,虽然是小问题,也许无关紧要,但是我都块累吐血了。
    2. 晚上回来再次debug才意识到这个水是很深的,至少我是没有那么容易就找对了方向。
      1. 首先,表面上看这里的错误是因为要把所谓的QT的translation file加载,我先要明白这个是什么,它就是QT为你做的可以支持多语言的一个机制,linux原来也有很多的我都忘记它的名字了,以前用过。
      2. 其次,我现在才理解这个语言是launcher图形界面的语言,可以在设置里修改。(这个难道很难理解吗?)我一直以为是什么和游戏界面有关的语言,这个纯粹是风马牛不相干的,比如游戏是中文版,我就不知道你有什么办法支持英文版?当然英文版应该是可以支持多语言版,这个是资源文件的兼容性的问题吧?这个多语言翻译的模块我以前看过现在又忘了,过了好几年也许也有改进了吧。总之,这里纯粹就是launcher自己的语言配置。
      3. ~/.config/vcmi/settings.json下的配置为什么无法在general/languange添加呢?原因不是写的问题,而是在总的配置文件里config/schemas/settings.json定义了什么属性是required,而language不是必须的,所以,写不上去。这里的插曲是我曾经尝试使用persistentStorage这个全局变量,结果发现它是这个配置文件~/.config/vcmi/persistentStorage.json的写手。
      4. 那么我设置了
        
           "launcher" : {
                        "setupCompleted" : true
                },
        
        是不会再次检测用户偏好语言,那么是怎么得出语言是英语的呢?还是这个config/schemas/settings.json里定义了所有的属性以及他们的默认属性。所以,这个就是为什么能够得出英语的原因。
      似乎是瞎忙了一天,核心的问题似乎不是vcmi的配置,而是为什么QT的translation文件失效?又回到了原点,糟糕的是问题不知道为什么消失了,也许是我重新配置解除了问题?
    3. 这个消息 X11 forwarding request failed on channel 0 很讨厌,因为githubssh的X11 forwarding禁掉了。
      
      Host github.com
          ForwardX11 no
      
      Host *
          ForwardX11 yes
      
      顺序很重要!

    十月二十日 等待变化等待机会

    1. 之前的视频有一些问题,重新做一下。一个是调整播放PPT的速率,我真的是心算太差了,另一个是增加音量,在一个是因为firefox默认不支持yuv444格式。
      
      $ ffmpeg  -framerate 1/41 -i "LostInTranslation-%2d.png"  -i LostInTranslation.mp3 -c:a aac -af "volume=10dB" -c:v libx264 -pix_fmt yuv420p  LostInTranslation.mp4
      
      这里为什么是1/41,我是纯粹做实验得到的,因为似乎取整做的有问题还是怎么样子?

    十月二十一日 等待变化等待机会

    1. 这些似乎是一个常识:
      1. 使用cmake编译如果想要知道一些参数的话,可以直接自己查看,比如
        
        $ cmake .. --help-variable-list | grep -i install
        $ cmake .. --help-variable CMAKE_INSTALL_PREFIX
        
      2. 安装目录实际上是纯粹的几个可执行程序而已,因为所谓的代码里的config配置文件是编译的时候需要使用的,我不是很确定是不是编译期的作用,因为看起来是的很明显是运行期的,没有可能hardcode这些的配置。
      3. 而运行期的config在所谓的~/.config/vcmi下的
        
        $ ll ~/.config/vcmi
        total 32
        drwxrwxr-x  2 nick nick 4096 Oct 21 10:15 ./
        drwx------ 58 nick nick 4096 Oct 21 06:23 ../
        -rw-rw-r--  1 nick nick  399 Oct 21 10:15 launcher.conf
        -rw-rw-r--  1 nick nick   97 Oct 21 06:26 mapeditor.conf
        -rw-rw-r--  1 nick nick 5101 Oct 21 10:08 modSettings.json
        -rw-rw-r--  1 nick nick   45 Oct 20 17:19 persistentStorage.json
        -rw-rw-r--  1 nick nick  970 Oct 21 10:15 settings.json
        
        这个是全局都使用的运行期的配置,就是说不管是安装在哪里,怎么编译,从哪里下载,哪怕是ubuntu的官方版本都是找这个运行期的配置,大部分是所谓的用户偏好之类的。
      4. 在所谓的~/.local/share/vcmi/
        
        $ ll ~/.local/share/vcmi/
        total 1036
        drwxrwxr-x  9 nick nick   4096 Oct 21 10:08 ./
        drwx------ 47 nick nick   4096 Oct 21 06:39 ../
        drwxrwxr-x  2 nick nick   4096 Oct 17 21:38 Data/
        drwxrwxr-x  3 nick nick   4096 Oct 21 10:08 Generated/
        drwxrwxr-x  3 nick nick 516096 Oct 18 21:49 Maps/
        drwxrwxr-x  8 nick nick   4096 Oct 20 17:17 Mods/
        drwxrwxr-x  2 nick nick   4096 Oct 17 21:38 Mp3/
        drwxrwxr-x  3 nick nick   4096 Oct 17 21:57 Saves/
        
        是所谓的全局共享的游戏安装的目录,也就是所谓的game data吧?
      5. 而在~/.cache/vcmi
        
        $ ll ~/.cache/vcmi/
        total 13472
        drwxrwxr-x  3 nick nick     4096 Oct 21 06:22 ./
        drwx------ 79 nick nick     4096 Oct 17 21:32 ../
        drwxrwxr-x  2 nick nick     4096 Oct 21 10:08 downloads/
        -rw-rw-r--  1 nick nick 11928512 Oct 21 10:15 VCMI_Client_log.txt
        -rw-rw-r--  1 nick nick  1824507 Oct 21 06:26 VCMI_Editor_log.txt
        -rw-rw-r--  1 nick nick    19328 Oct 21 10:08 VCMI_Launcher_log.txt
        
        下则是vcmilaucher的下载安装目录,也是一个全局的,就是说本地编译的版本下载安装的mods也是在这里。
      6. 尽管你设定了安装目录而且安装后你也看到了它有相应的目录创建了,可是这些配置目录都是不起作用的。
      总而言之,配置文件的目录似乎是固定的,无法随着安装目录的改变而改变。而且,看起来我发现的异常大部分都是Mods造成的,相对于invite hero这个小的mod,其他是非常复杂的,因为前者看起来是一个小功能,看看代码好像就是从英雄池里挑选放入一个数据结构里下一次雇佣就能从这个结构里直接取,这里说白了就是一个作弊方法的实现而已。

    十月二十二日 等待变化等待机会

    1. 阅读这个文档可能是最最基本的,因为这两年这个领域又有了很大的进展。从哪里入手呢?battleAI很可能是更确定更简单实用的,因为相对于所谓的adventureAI前者就好像是战术,后者是战略,在战术级别很多是可以条例化,明确化甚至是通用的,可是战略是极其复杂的,而另一个名言是业余的才谈论战略,专业的谈论后勤,在一定层面上战略是用后勤来体现的。
    2. 还是从容易的BattleAI入手吧?
      • AttackPossibility

        one particular way to attack on the battlefield. Each AttackPossibility instance has multiple ...DamageReduce attributes. These represent how much damage an enemy will lose after our attack. Effects can reduce this damage. We add them up and this value is used as attack score. 很显然的,这个是最复杂的基础,而其他的都是在它的基础上的推理演绎,所以这一点看来它更像一种规则,因为它是某种可以预测的结果,是客观的,所以,是规则。
      • PotentialTargets

        a set of all AttackPossibility instances 是一种在目标层级的模拟,目的是计算所谓的DamageCache在给定的HypotheticBattle战场环境下。
      • BattleExchangeVariant

        it is an extension of AttackPossibility, a result of a set of units attacking each other for a fixed number of turns according to the turn order. It is kind of an oversimplified battle simulation. A set of units is restricted according to AttackPossibility which particular exchange extends. Exchanges can be waited (when stacks/units wait for a better time to attack) and non-waited (when stack acts right away). For non-waited exchanges the first attack score is taken from AttackPossibility (together with various effects like 2 hex breath, shooters blocking and so on). All the other attacks are simplified, only respect retaliations. At the end we have a final score. 代码里的注释有时候比文档写作者更贴切一些吧?不看代码从函数原型来猜测是针对某一个特定目标的模拟?但是这里就有最最基本的AI的博弈问题,你的策略的最大值就是对手策略的最小值,彼此一定要达到某种平衡才是可能的取值,否则就是一厢情愿。所以,不看代码我的妄想就是这里应该要有minmax算法的部分吧? 从形式上看它和PotentialTargets的函数原型实际上是一回事,因为两者如果运用博弈的观点是会互相影响的,到底你的伤害多少最终会影响到你选择目标,而目标的选择又会反过来影响伤害,而且加上对手的博弈考虑,你的策略是对手要极力避免的,导致你的选择无法达成最优解,只能是一种平衡,那么这两个函数是否应该合起来呢?当然这就太过复杂了,尽量的拆分,在更高一级取舍。大概这个是更高一级的BattleExchangeEvaluator的工作。
      • BattleExchangeEvaluator

        calculates all possible BattleExchangeVariants and selects the best
      • BattleEvaluator

        is a top level logic layer which also adds spellcasts and movement to unreachable targets
      这里我始终有一个愿望是把所谓的规则选择区分开来,前者在我看来是一种裁判性的中立的,后者则是AI的可选项目。但是这个说起来容易做起来很难啊,另一方面这个取决于是否真的对于将来有用都是问题。
    3. 看起来不读代码大概就只能到这个水平,到这里English Programmer的天花板就到了。我是否应该先阅读一下Bonus System呢?我注意到很多函数有两个形式:带bonus和不带bonus的。我感觉这个是另一个很大的题目,我现在脑袋瓜子嗡嗡的。休息吧。

    十月二十三日 等待变化等待机会

    1. 有一个很诱人的功能是save before battle,我猜想这个可以仿照performAutosave来实现。必须使用CGameHandlerstartBattle,这个是因为虽然BattleProcessorstartBattle方法是终极的workhorse,但是有一个所谓的restartBattle的情况,这个是自动战斗的结果在用户不满意的情况下可以选择的,而这个时候CGameHandler会直接调用BattleProcessorrestartBattle并且进一步调用它的startBattle,显然的这里是否需要存储是一个问题,我印象中是不需要的否则就重复了。不过似乎也差不多吧?
    2. 提了这个建议。然后在本地做了这个修改提交了PR。这里大致的流程是复习一下git操作,比如我在本地的branch是nick-save-before-battle,那么我在github上已经fork了一个vcmi的repo是git@github.com:nickhuang99/vcmi.git,那么这个时候我在本地需要push到相应的origin
      
      $ git remote set-url origin git@github.com:nickhuang99/vcmi.git
      $ git push --set-upstream origin nick-save-before-battle
      
    3. 我让谷歌的notebookLM来分析我的日记。这是分析的结果。
      yearnotebookLMtalk
      2023年日记notebookLM
      2022年日记notebookLM
      2021年日记notebookLM
      2020年日记notebookLM
      2019年日记notebookLM
      2018年日记notebookLM
      2017年日记notebookLM
      2016年日记notebookLM
      2015年日记notebookLM
      2014年日记notebookLM
      2013年日记notebookLM
      2012年日记notebookLM
      2011年日记notebookLM
      2010年日记notebookLM
      2009年日记notebookLM
      2008年日记notebookLM
      2007年日记notebookLM
      2006年日记notebookLM
      2005年日记notebookLM
      2004年日记notebookLM
      2003年日记notebookLM
      2002年日记notebookLM
    4. 这个是我的诗集的对话。
    5. 这个是关于c++标准谈话
    6. 这个是关于我克隆的cppreference谈话
    7. 这个是关于VCMI的VCMI的源代码谈话
    8. 这个是关于我的旧的很少的一部分纯英文日记的。那段时间我还曾有意识的强迫自己用英语思维只写英文日记,后来证明是徒劳的因为复杂的想法或者表达只能是在母语范围内才行。人工智能的谈话评论
    9. 这个应该是我出国前的一小段快乐时光的日记。那段时间我还沉浸在无忧无虑的无知的生活里。人工智能的谈话评论。结果AI对于我的中文提到的英语水平直接翻译为English water level然后它们迷惑不解有好几种奇怪的猜想,还以为是信息管制。这里人工智能根本不能理解中文至少我自己都不知道它们在谈论什么!
    10. 这个应该是我在蒙特利尔的杂记人工智能的谈话评论
    11. 这个应该是我出国前写的乱七八糟的杂记人工智能的谈话评论

    十月二十四日 等待变化等待机会

    1. 我一直以来有一个错误的观念,我以为vcmi是在原来HOMM3的游戏数据上的改造,因此我一直认为如果gameDataLanguage是决定了游戏的语言,实际上所谓的数据都是一些最基本的无地区差异的音乐与图像,它的用户界面是重新制作的,因此多语言不是问题啊!这个就是当我遇到我把launcher的语言调整为中文,游戏界面变成空白的时候我看代码才意识到的。这个是语言也就是我自己的font的配置问题,尽管vcmi有各种各样的bitmapfont的检查,那个实际上是适用于使用原版游戏字库的情况,大多数人应该使用truetype,因为这个才能真正支持自由缩放,实现高解析度的游戏画质才有扩展的可能,尽管这个高画质应该还没有实现,但是至少不应该在font上遇到问题吧?
    2. 实际上这个问题相当的复杂,旧版的某些实现也许的确是依赖于游戏自带字库,因为这个是兼容的需要。但是新版是很灵活了,虽然有很多的关于语言偏好以及encoding的识别,但是最终需要的是SDL2_TTFTTF_GlyphIsProvided32支持,这个是如何实现的对于我是一个黑盒子,也许并不需要关心,我需要的是为什么Noto Serif字库不支持中文,事实上的确是不支持,因为它在另一个包里。这个是谷歌原始的开源的字库,它是在一个很大的伞盖也就是Noto Serif的名头下,为什么是这个名字我懒得看了。但是这个是拉丁文,中日韩需要在所谓的CJK里才有,所以,在ubuntu事先包装好的是这个包fonts-noto-cjk
      
      $ apt-file search NotoSerifCJK-Bold.ttc
      fonts-noto-cjk: /usr/share/fonts/opentype/noto/NotoSerifCJK-Bold.ttc
      
      这个看起来不是所谓的truetype似乎是所谓的opentype。总之,我试图让vcmi使用这个opentype,但是遇到了coredump,不知道是不是动态库的问题?但是这里至少是发现资源文件是必须使用install来拷贝到data目录下的,而且,这个链接应该是使用了rpath吧?
      
      l$ ldd bin/vcmilauncher  | grep vcmi
      	libvcmiqt.so => /home/nick/workspace/vcmi-install/lib/vcmi/libvcmiqt.so (0x00007ae7f3a10000)
      	libvcmi.so => /home/nick/workspace/vcmi-install/lib/vcmi/libvcmi.so (0x00007ae7f2600000)
      
      问题是资源文件去哪里找好像是不一样的,我要看代码。看起来这个fonts.json的定义不是那么的灵活,我的猜测这个是最最基本的资源是被编译到了资源文件里吧?也就是说它也许不是在运行时拷贝到data目录下吧?这是我看到错误信息的打印的资源文件全部大写猜测的,要么是windows下目录,要么是所谓的zip文件结构大小写不敏感,那么归根结底还是要把它在编译期配置好?另外coredump的可能的原因也许是我又安装了发行版的binary的冲突造成的,这里不仅仅是动态库的问题,也许还有一些其他的资源文件配置文件共享的问题吧?总之环境是复杂的。这是一个如此简单的问题根本没有什么玄机。首先就是这个配置文件就是运行期需要读取的,这个从strace就可以证明,其次,log里的错误信息是针对多平台的文件系统显示的需要根本没有二进制资源文件一说,那种把资源文件打包在可执行程序里是不能跨平台的,没有人这么开发。那么根本的原因是.ttc.ttf是两种完全不兼容的格式,你不能指望SDL2_TTF能够正确加载前者,根本不可能,所以,唯一的办法是下载原生的谷歌字库
      
      $ file NotoSerifCJK-Medium.ttc 
      NotoSerifCJK-Medium.ttc: OpenType font collection data, 1.0, 5 fonts, at 0x20 OpenType Font data, 16 tables, 1st "BASE"
      $ file NotoSerif-Medium.ttf
      NotoSerif-Medium.ttf: TrueType Font data, 17 tables, 1st "GDEF", 20 names, Microsoft, language 0x409, Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)Noto 
      
      在下载的字库里有所谓的静态的.ttf文件:
      
      $ ls
      NotoSerifSC-Black.ttf  NotoSerifSC-ExtraBold.ttf   NotoSerifSC-Light.ttf   NotoSerifSC-Regular.ttf
      NotoSerifSC-Bold.ttf   NotoSerifSC-ExtraLight.ttf  NotoSerifSC-Medium.ttf  NotoSerifSC-SemiBold.ttf
      
      于是在fonts.json里配置就可以了。问题是我如何能够把它做成一个Mod呢?我相信一定有人这么做过了。
    3. 一个简单的问题是为什么vcmilauncher能够自由的支持多语言,而vcmiclient的多语言却不行呢?很简单,我们已经看到前者是一个QT程序,依靠的是QT-translation机制在代码里实际把各个widget的显示文字硬编码的翻译了,而后者只能依靠字库,这个是两种不同的机制。那么字库的支持是如何做到呢?我现在单单想做到中文那么就安装中文字库,很多拉丁语系是支持的,但是韩日不一定啊,这本来就是CJK的出发点,这个太复杂了,难道有一个CJK的ttf文件吗?感觉做不到吧?
    4. 这个是几年前的状况,现在不再支持那个bitmap font就是原版游戏自带的吧?

    十月二十五日 等待变化等待机会

    1. 考虑欠妥了,这里面水很深的,要考虑在移动之前的位置来存盘,这个可不容易啊!
    2. 中文的问题其实很复杂,我在解决这个问题的过程中遇到一个独特的serialization的做法,就是使用模板技术来实现。我目前只理解可以使用所谓的CSaveFile这个来做,其他的类有没有实现我还不知道。它需要一个boost的path,我只好硬编码的输入路径。反正是测试。以后可以试一下其他的类来输出CMapHeader
    3. 这里的翻译多语言依旧是依赖事先定义好的encoding,对于中文就是GBK,这个是当初中文版的encoding,那么地图照理说也是如此,这一点无可厚非,可是我后来的地图好像被我改过吗?至少是文件名是UTF-8,这个就是头疼了。这里应该要智能化一些吧?至少文件名是如此。
    4. 使用gdb跟踪老是跟丢了,因为根据经验肯定是优化了,那么怎么设定不优化呢?检查CMakeCache.txt才发现端倪:这个变量CMAKE_CXX_FLAGS_RELWITHDEBINFO是直接起作用的,因为默认的编译类型是CMAKE_BUILD_TYPE:STRING=RelWithDebInfo
      
      $ cmake .. -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O0 -g -DNDEBUG" -DCMAKE_INSTALL_PREFIX=/home/nick/workspace/vcmi-install
      

    十月二十六日 等待变化等待机会

    1. 其实,这些文档都有宝贵经验,我却没有认真看一遍就在黑暗中摸索。
      
      $ cmake .. -DCMAKE_INSTALL_PREFIX=/home/nick/workspace/vcmi-install -D CMAKE_BUILD_TYPE=Debug -D CMAKE_EXPORT_COMPILE_COMMANDS=O
      
      我不敢使用Ninja,因为以前发现它会无限制的占用内存导致自己崩溃。 这个战场标识图值得收藏。
    2. 可能这几年最大的新东西就是这个bonus system这个是非常的深奥的设计,是颠覆认知的想法,也是非常的聪明的解决办法,是非常有前途的体系,要在阅读代码中去慢慢体会学习。目前,我还不具备理解它的基础。
    3. 这些新的feature我都还没有全注意到,真是令人振奋!

    十月二十七日 等待变化等待机会

    1. debug了好久依然没有找到问题的源头,就是说文件后缀名在文件系统里处理的不对,也就是说所谓的filelistoriginalName是把文件名的twist.h3m.h3m的后缀名去掉了导致路径不对,但是这个是在哪一层出错我gdb了大半夜也没有搞明白。
    2. 先提这个小bug,程序员肯定是知道这个问题的。我之前的小改动已经merged

    十月二十八日 等待变化等待机会

    1. 又提了这个bug,我初步有了改bug的方案,但是需要很多测试,因为文件名和所谓的资源名的滥用不是一天两天了。提交了这个PR
    2. 又提了一个建议,改进中文显示是一回事,但是韩语和日语怎么办呢?

    十月三十日 等待变化等待机会

    1. 我下载的这个mod居然是我自己几年前做的,我一点印象都没有了。
    2. 关于语言的讨论太复杂了,还是再考虑成熟一些吧?
    3. git apply之后的警告说什么whitespace之类的可以用这个吧?但是好像依然有警告警告是必然有的,但是最后告诉你解决了。
      
      $ git apply --reject --whitespace=fix ~/Documents/vcmi-diff/resourcepath.diff
      /home/nick/Documents/vcmi-diff/resourcepath.diff:43: trailing whitespace.
      			std::time_t time = boost::filesystem::last_write_time(*CResourceHandler::get()->getResourceName(ResourcePath(p.resource.getOriginalName(), 
      Checking patch client/render/AssetGenerator.cpp...
      Checking patch client/windows/CMapOverview.cpp...
      Checking patch lib/filesystem/ResourcePath.cpp...
      Checking patch lib/modding/CModHandler.cpp...
      Checking patch test/map/CMapEditManagerTest.cpp...
      Applied patch client/render/AssetGenerator.cpp cleanly.
      Applied patch client/windows/CMapOverview.cpp cleanly.
      Applied patch lib/filesystem/ResourcePath.cpp cleanly.
      Applied patch lib/modding/CModHandler.cpp cleanly.
      Applied patch test/map/CMapEditManagerTest.cpp cleanly.
      warning: 1 line applied after fixing whitespace errors.
      
    4. 提交了第二版的修改。

    十一月七日 等待变化等待机会

    1. 我现在面临在树莓派上开发的可能性,发现这个在树莓派上的candle的变体,初步看起来改的还不少,这个让人感到担心。
    2. Candle实际上在我看来没有做多少工作,但是当我使用cutecom直接把G-code文件发送的时候老是出错,我开始怀疑是不是有什么额外的工作要做,或者是cutecom代码版本的问题?打算编译源代码。
    3. 这个是QT的readyRead一个基本常识,因为这个在代码里找不到是QT的一部分

      void QIODevice::readyRead() [signal]

      This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.

      readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).

      Note for developers implementing classes derived from QIODevice: you should always emit readyRead() when new data has arrived (do not emit it only because there's data still to be read in your buffers). Do not emit readyRead() in other conditions.

    4. 看来我尝试使用picocom/minicom来发送G-code给GRBL是有问题的。这里说的streaming的非常的重要,吃晚饭再说。

    十一月八日 等待变化等待机会

    1. 购买了一个Rasperry Pi Zero 2W做实验,下载它的操作系统。选择的是64位的应该是Debian的bookworm改造的吧。推荐是使用所谓rpi-imager,但是我估计我自己用dd也可以搞定。手动下载了镜像看看结构。
      
      $ xz -d ./2024-10-22-raspios-bookworm-arm64-full.img.xz
      $ fdisk -l ./2024-10-22-raspios-bookworm-arm64-full.img
      Disk ./2024-10-22-raspios-bookworm-arm64-full.img: 14.23 GiB, 15279849472 bytes, 29843456 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: dos
      Disk identifier: 0x60691ba7
      
      Device                                        Boot   Start      End  Sectors  Size Id Type
      ./2024-10-22-raspios-bookworm-arm64-full.img1         8192  1056767  1048576  512M  c W95 FAT32 (LBA)
      ./2024-10-22-raspios-bookworm-arm64-full.img2      1056768 29843455 28786688 13.7G 83 Linux
      
      然后使用loopmount来查看具体分区:
      
      $ sudo losetup -Pf --show ./2024-10-22-raspios-bookworm-arm64-full.img
      
      这里得到loop device,比如/dev/loop1 然后创建两个文件夹doslinux作为mountpoint
      
      $ sudo mount /dev/loop1p1 dos
      $ sudo mount /dev/loop1p2 linux
      
      然后我们可以看到所谓的替东分区
    2. 这个Rasperry Pi Zero 2W使用的是博通的BCM2710A1的芯片而不是通常的BCM2835,这款芯片对应的型号是ZeroZero W。它配的一个显示屏有些玄机。
    3. 编译Candle在树莓派有很多麻烦地方,首先就是很慢,我实在是想避免这个过程。

    十一月九日 等待变化等待机会

    1. 似乎是应证我的担心树莓派编译Candle似乎是有问题的,至少我看使用openGL之类的就是一个奢侈东西。感觉单纯使用普通的串口库也需要处理缓冲区溢出的问题,所以,GRBL的界面来熟悉一下是必要的。这个才是1.1版本的说明
    2. 昨天急着要吃晚饭没有很仔细的看这个代码,实际上这个就是文档的核心算法的样本。这里顺便说一下,我又忘记了regex里的这个'\s|\(.*?\)','',在这里的?所谓的Laziness (Curb Greediness for Repetition Operators): *?, +?, ??, {m,n}?, {m,}?,这个我以前用过但是现在都忘记了。实际上要去除G-code里的注释也是一个麻烦事。我猜想代码里注释了这一行代码就是它有问题吧?因为注释里面嵌套注释怎么办?或者说挂号里面套挂号怎么办?可能要参考一下Candle的代码。
    3. 修改原版的python脚本花了我不少的时间因为我根本不明白python的语法,而且这里牵扯到字符编码的问题。这里总算是把原始代码修改能够正常运行了,很主要的部分是print函数不再是随意的像bash那样子了。
    4. 我现在越来越懒了,如果脚本能做的事情我干嘛花力气去写一个c/c++程序呢?效率很可能不是考虑吧?
    5. 所以,为了一盘醋包一锅饺子的事情比比皆是。如果我仅仅需要使用python的脚本上传文件那么就不用再安装复杂的编译环境了,不过编译死机等等的问题也许是swap文件大小太小的问题,默认是200,我改成了2048也许能够解决这个问题吧?
    6. 显示屏不显示看来是配置的问题,使用厂家的测试程序至少显示是有了,按键不能用是另一回事,总之,我决定不再使用64位系统,使用古老的轻量级的版本再看看。
    7. 运行了一两个月的笔记本突然变得很慢,重启也是如此,至少找到了一个问题:
      1. 之前我禁止了snapd.service是灾难性的,因为浏览器等等很多都是ubuntu现在安装程序的来源,甚至都不能启动浏览器!
      2. 这个是一个steam的坏蛋在搜集你的资料看来我是错怪了这个程序。它是系统必须的至少nautilus是依赖它的。
        
        gsettings set org.freedesktop.Tracker.Miner.Files crawling-interval -2  
        gsettings set org.freedesktop.Tracker.Miner.Files enable-monitors false
        
        或者是使用图形工具
        The values could be changed using dconf-editor by navigating through org > freedesktop > Tracker > Miner > Files:
      3. 很多程序不再使用服务,而是在/etc/xdg/autostart里做坏事,这里删除未必是好办法,使用一个软链接让它以为自己已经安装了更稳妥。
        
        $ sudo ln -s /dev/null org.gnome.Evolution-alarm-notify.desktop
        $ sudo mv tracker-miner-fs-3.desktop tracker-miner-fs-3.desktop.backup
        $ sudo ln -s /dev/null tracker-miner-fs-3.desktop
        

    十一月十日 等待变化等待机会

    1. 如果要使用python的serial模块,很显然的要先安装python3-serial包。
    2. 我发现新版的HOMM3似乎消耗很多的电量,尝试改变这里的配置
    3. 新版的战略手册
    4. 发现我卸载了tracker-miner-fs居然连带的把nautilus也卸载了,这才明白这个应该不是木马之类的恶意软件。只是名字太让人富于联想了。
    5. 无意中发现我从视频制作的gif文件居然比原来的视频文件大小增加了好几倍!发现这个降低帧率的方式:
      
      $ ffmpeg -y -i video/mercy.m4v -filter_complex "fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=32[p];[s1][p]paletteuse=dither=bayer" video/mercy.gif
      
      顺便也把另一个gif也改造一下。

    十一月十三日 等待变化等待机会

    1. 昨天看一个视频马斯克说他面试通常只问几个小问题,比如你解决的最复杂的问题是什么之类的,然后我就蒙了,因为我想不起来我有解决什么复杂的问题,即便有我也忘记了。我模模糊糊的记得是什么问题就不错了,更不要说我是怎么解决的细节了。今天翻出来之前的记忆努力理解才明白这个问题是怎么回事?这个问题似乎也不算是多么复杂的问题,如果我使用svn/git来辅助也许更容易发现?不过也很难说,因为程序员一次添加大量代码你可能不会往哪些看似成熟的代码去想吧?
      1. 首先,线程死锁的问题很难debug,比如crash了你可以看到一些端倪,比如哪个指针哪端内存,当然随机野指针访问朔源是比较困难的。
      2. 在gdb里寻找deadlock仿佛是钓鱼或者设伏的计划扑空,因为你不知道什么时候发生的,而一旦发生了,你又不一定能够第一时间找到犯罪现场。这里能够定位到的mutex固然是犯罪者,但是如果不是知道通过mutex内部保留的threadid你还不知道是谁闯的祸。
      3. 知道了是主线程拿到了mutex控制权,可是为什么不释放从代码上分析是有困难的,因为主线程可以说是程序开始就运行的,它的一些东西是在main之前就初始化的,导致你使用gdb一般很难定位,因为大多数时候你认为main就是你能设定的第一个断点,你要怎么找更早的断点呢?
      4. 更加要命的是代码并不在主程序里写的而是通过动态加载dynamic loading的方式,首先这个断点就难找,我很怀疑这个能否设断点?在我看来动态库如果在运行之初的动态加载和静态加载的区别似乎有着微妙的区别,一个是系统自动根据操作系统设置,比如LD_LIBRARY_PATH,甚至于在配置文件夹/etc/ld.so.conf.d/下写死。而且这个又要求调用ldconfig把它写入真正使用的缓存里/etc/ld.so.cache,这个你可以通过ldconfig的manpage看到说明。
      5. 动态库加载的复杂当时也许遮盖了我搜索的方向,因为你总是在担心新的代码引入新的race condition,那么从代码分析能否轻易的找出端倪是观性思维。而大量使用宏是绝对的噩梦,因为很难一眼看出问题。尤其是宏里嵌套着其他的宏,这个就让人更加的头疼,有时候代码会变得似是而非,最可怕的不是错误,而是不算错误的错误,就是没有编译错误的宏。
      6. 宏是迷雾,尤其是嵌套之后更加难以看清逻辑。而宏的书写绝对是致命的问题,为了容易阅读而人为的添加换行符在有些编辑器里不明显,这就是另一个麻烦点。使用预处理g++ -E来把宏展开来阅读干净的代码是我最后解决问题的最重要的手段。
      现在看起来当初这个问题在当时看起来是一个复杂的问题。
    2. 我现在随便一翻旧的日记就发现很多概念知识我在很多年前就清楚的理解了,当时也写下了笔记,可是过后完全的忘记了,等于又从头学了一遍。比如这个关于MBR。不过话说回来,纸上得来终觉浅,当时明白了没有去实践过后忘记是正常的,直到最近我是亲手实践制作各种启动镜像需要深入理解启动分区这才真正的理解。我花了两年时间反复背诵《滕王阁序》就说明了我的记忆有多差。我曾经自己做的vcmi的中文语言包我是自己看到作者的名字是自己才意识到这个就是我自己的作品。
    3. 我现在越来越不想写代码,而且倾向于使用脚本,我终于开始体会到为什么程序员偏爱python之类的脚本的原因了。比如我要做一个文件选择的图形界面总不至于要求我去学习QT开发工具吧?这个也太夸张了。所以,使用这个easygui来做:
      
      import easygui
      path = easygui.fileopenbox()
      
      这个在ubuntu下是python3-easygui这个包。
    4. 如果我要做一个替代触摸屏的工控界面也许我会选择CGI,到时候看看这个perl脚本入门的一章吧?
    5. 昨晚快速看看有没有办法把便宜的平板改造为ubuntu看来没有那么容易,ubuntu-touch这个项目老早就被放弃了。很难。

    十一月二十日 等待变化等待机会

    1. 购买了一个工业控制的触摸屏的电脑安装的是ubuntu 18.04,看来又不少的问题,很核心的似乎都是X11显示以及和登陆相关的问题。这里提示一种debug X11的问题的途径。核心的办法是单独运行服务器端的shd -ddd,但是前提是关闭sshd daemon的运行,这个对于正在链接的没有影响我感到很安心。但是发现一个错误
      Missing privilege separation directory: /run/sshd
      我读了这个解释也是不得要领。总之它要创建那个目录让旧体制的systemD的方式运行吗?然后我发现这个目录即便我创建了运行之后程序结束它又被删除了,就是说/run下面的目录是自动被删除的,那么它的创建也是要在运行之初自行创建的。其实,我的核心问题之一是要解决pkexec的DISPLAY问题,我随后发现xclock等等通过ssh -X是没有问题的,问题出在我自己胡乱在/etc/profile里hardcoded了DISPLAY,这个在使用sshd -ddd之后在客户端打印就可以看到 DISPLAY=localhost:12.0注意这个是我本机按照默认DISPLAY跳10个自动分配的。所以,只要你服务端是正确的,客户端也是自动生成的。所以,又回到原点,这个纯粹是pkexec出于安全性考量专门制造出来的问题,和ssh无关。
    2. 我运行pkexec得到如下的错误
      polkit-agent-helper-1: error response to PolicyKit daemon: GDBus.Error:org.freedesktop.PolicyKit1.Error.Failed: No session for cookie
    3. 另一个相关的问题是关于.desktop文件的问题,看来最省事的是使用这个工具来检验desktop-file-edit

    十一月二十一日 等待变化等待机会

    1. 为什么ubuntu把桌面的快捷方式去掉了?
    2. 改了第一个比较好看的传输GRBL的python脚本。主要解决了一些错误处理,以及传输过程的输出,以及给了一个图形界面来选择文件以及输出结果。运行方式因为需要root权限,于是就直接使用pkexec来执行
      
      $ pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY python3 /home/nick/workspace/pythonTest/sendFile.py
      
      这里有几个需要注意的,就是pkexec相当于在root用户执行,那么它之前是没有创建DISPLAY以及权限之类的,所以,应该要执行xhost把root用户加入,这个是很出乎我意外的,我以前一直不太明白这一系列的原因,就是X11本身是有权限管理的,而且挺复杂的,结果导致据说很多潜在的问题,所以,很多其他机制又不愿意使用这个机制。总之,很混乱。也就是说我的root用户必须在这个列表里才行:
      
      $ xhost +SI:localuser:root
      $ xhost
      access control enabled, only authorized clients can connect
      SI:localuser:root
      LOCAL:
      SI:localuser:nick
      
      另一个小问题就是关于脚本我必须保证文件完全传输完毕才能安全关闭程序,原来的脚本没有做这个处理,也就是说有一些命令已经在GRBL的buffer里了,那么我在文件所有有效行都传输结束后还是要等待足够多的时间,那么就是很简单的计数收到多少执行ok或者error的行数,所以,我基本上都是在改这部分代码,还有就是要把传输和收到的打印结果要及时更新,这个就是python里的所谓的
      
          text_box.insert(tk.END, str + "\n")
          text_box.see(tk.END)
          root.update()    
      
      在命令行里传输环境变量是一个稳妥的解决方法env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY这个当然是因为转换成root用户的问题。 但是这个把它制作成所谓的desktop文件就有问题了,我花了好久时间在琢磨这个问题,它的复杂程度超出我的想像,只能将就着用。
      1. 首先,在任意位置创建你的desktop文件,不一定要在~/Desktop文件夹,而且即使在哪里也不会显示出来。
      2. Exec部分要改成使用shell命令,这个是最关键的部分:
        
        Exec=sh -c "pkexec env DISPLAY=\\$DISPLAY XAUTHORITY=\\$XAUTHORITY python3 /home/nick/workspace/pythonTest/sendFile.py"
        
        注意这里的$之前一定要加两个\\,这个是硬性规定,你可以使用desktop-file-validate来检验
        
        $ desktop-file-validate sendFile.desktop
        
      3. 检验成功就可以安装
        
        $ sudo desktop-file-install sendFile.desktop
        
        这里具体的做法应该是把它拷贝到了/usr/share/applications/sendFile.desktop,但是似乎不需要改变可执行,看来这个做法和以前的双击desktop的icon的做法已经不一样了,而且还有所谓的allow launch之类的复杂的做法,我已经不想再研究了,太复杂了。凡是涉及安全性的东西都是无比复杂。
      4. 那么无法把它放在桌面怎么让用户方便执行呢?我只好在app launcher里搜索然后点击右键把它添加到favorite里,这个看来是唯一简单的解决方法。
      整个系列涉及了好几个方面的问题,首先是python的GUI的问题,还有是pkexec的DISPLAY的问题,我估计就算是引入旧版的gksudo可能也是有root访问X11的问题,还是需要使用xhost来添加,最后是最反直觉的就是desktop的Exec必须使用shell命令来包装pkexec的问题,我至今也不知道是什么原因,模模糊糊stackoverflow上有大侠解释了这个是很tricky的问题,但是我当时也没有看懂。也没有心思去看。因为一个简单的问题没想到耗费了两天才解决。
    3. 饿死了!

    十一月二十四日 等待变化等待机会

    1. 我的vpn服务器似乎有些问题,至少客户端看到crash了,这个还是一年以来的第一次,我始终不是很清楚服务器端是怎么运行的。就看看它的服务的启动命令:
      
      $ sudo systemctl status openvpnas.service
      ● openvpnas.service - OpenVPN Access Server
           Loaded: loaded (/lib/systemd/system/openvpnas.service; enabled; vendor preset: enabled)
           Active: active (running) since Sun 2024-11-24 01:26:25 PST; 2min 1s ago
         Main PID: 224978 (python3)
            Tasks: 10 (limit: 1130)
           Memory: 249.8M
              CPU: 3.639s
           CGroup: /system.slice/openvpnas.service
                   ├─224978 python3 -c "from pyovpn.sagent.sagent_entry import openvpnas ; openvpnas()" --nodaemon --logfile=/var/log/openvpnas.log --pidfile=
                   ├─225004 /usr/bin/python3 -c "from pyovpn.cserv.wserv_entry import start ; start()" -no -u openvpn_as -g openvpn_as --pidfile /usr/local/openvpn_as/etc/tmp/wserv.pid
                   ├─225005 /usr/bin/python3 -c "from pyovpn.log.logworker import start ; start()"
                   ├─225032 /usr/bin/python3 -c "from pyovpn.sagent.iptworker import start6 ; start6()"
                   ├─225033 /usr/bin/python3 -c "from pyovpn.sagent.iptworker import start ; start()"
                   ├─225041 openvpn-openssl --errors-to-stderr --config stdin
                   ├─225043 openvpn-openssl --errors-to-stderr --config stdin
                   └─225049 openvpn-openssl --errors-to-stderr --config stdin
      
      这个需要下载python的这个开发包才能运行吧?我对于python的知识极其有限。但是aws我的服务器端似乎安装包有问题,本地是可以的吧? 但是本地的pip也需要先升级:
      
      $ python3 -m pip install --upgrade pip
      $ pip install pyovpn-as
      
      我直到看到这个项目的说明才有点明白这个大概是什么,否则一头雾水!就是说openVPN的所谓的AS代表的是Access Server的意思,这个是可以通过一个命令行工具sacli来配置的,它有着极其复杂的配置选项,慢慢探索吧?首先,openvpnas.service是怎么启动的呢?我对于systemd的服务启动总是不理解,这里看到服务文件/usr/lib/systemd/system/openvpnas.service
      
      # Note: setting PYTHONUNBUFFERED is necessary to see the output of this service in the journal
      # See https://docs.python.org/3/using/cmdline.html#envvar-PYTHONUNBUFFERED
      Environment=PYTHONUNBUFFERED=true
      ExecStart=/usr/local/openvpn_as/scripts/openvpnas  --nodaemon --logfile=/var/log/openvpnas.log --pidfile=
      ExecStop=/bin/bash /usr/local/openvpn_as/scripts/openvpn_service_cleanup
      
      这个就是运行中的服务的启动命令。那么这个脚本/usr/local/openvpn_as/scripts/openvpnas是如何的呢?
      
      #!/usr/bin/env bash
      if [ -z "$OPENVPN_AS_NOEXPORT" ]; then
      export AS_VERSION="2.12.3"
      export AS_BUILD="76774795"
      export OPENVPN_AS_BASE="/usr/local/openvpn_as"
      export OPENVPN_AS_CONFIG="/usr/local/openvpn_as/etc/as.conf"
      export PATH="/usr/local/openvpn_as/scripts:/usr/local/openvpn_as/bin:/usr/local/openvpn_as/sbin:$PATH"
      export LD_LIBRARY_PATH="/usr/local/openvpn_as/lib"
      export PYTHONPATH="/usr/local/openvpn_as/lib/python:/usr/local/openvpn_as/lib/python"
      fi
      export PYOVPN_CMDNAME="openvpnas"
      exec python3 -c "from pyovpn.sagent.sagent_entry import openvpnas ; openvpnas()" "$@"
      
      终于又回到了起点,这个python的模块是怎么工作的呢?完全没有头绪。
    2. 系统突然变得很卡,我关闭了大部分程序依然没有找到原因,这里说有可能是xdg-desktop-portal-gnome
      
      $ systemctl restart --user xdg-desktop-portal-gnome
      
      这里为什么可以不用sudo?难道这个服务是我的用户运行的吗?

    十一月二十五日 等待变化等待机会

    1. 我甚至于对于怎么使用openvpn都是一知半解,这里下载的源代码是它基本的库吧?而这里的android的apk其实也不是真正意义上的客户端,因为我看到一个市面上的所谓的openvpn connect android app似乎是能够配置android系统的dns解析。

      Q: Why doesn't the app support tap-style tunnels?

      A: The Android VPN API supports only tun-style tunnels at the moment. This is a limitation of the Android platform. If you try to connect a profile that uses a tap-based tunnel, you will get an error that only layer 3 tunnels are currently supported.

      If you really want to see tap-style tunnels supported in OpenVPN Connect, we would encourage you to contact the Google Android team and ask that the VpnService API be extended to allow this. Without such changes to the VpnService API, it is not possible for non-root apps such as OpenVPN Connect to support tap-style tunnels.

      我至今也没有搞明白TAP和TUN是怎么回事!这里是论坛也许能找到一些帮助!
    2. 我问了豆包很多关于openvpn的问题,但是因为众所周知的原因,我不能在问题里出现这个敏感词。我的理解似乎多了一些。

    十一月二十六日 等待变化等待机会

    1. 我想再强化一下我之前的认识:当你看到浏览器里说
      
       

      Did Not Connect: Potential Security Issue

      Firefox detected a potential security threat and did not continue to www.google.com because this website requires a secure connection.

      What can you do about it?

      www.google.com has a security policy called HTTP Strict Transport Security (HSTS), which means that Firefox can only connect to it securely. You can’t add an exception to visit this site.

      The issue is most likely with the website, and there is nothing you can do to resolve it. You can notify the website’s administrator about the problem.

      说明什么?首先看看nslookup的结果
      
      $ nslookup www.google.com
      Server:		127.0.0.53
      Address:	127.0.0.53#53
      
      Non-authoritative answer:
      Name:	www.google.com
      Address: 31.13.94.10
      Name:	www.google.com
      Address: 2001::1
      
      类似的使用dig也是差不多 于是我只能使用resolvectl来重置dns解析的设备,现在我们明确一下为什么时不时的会出现这个问题呢?因为昨天豆包给我解释的很清楚了,在系统中我们的dns服务器配置不再是硬编码在某个配置文件里的时候:
      
      $ resolvectl status
      Global
             Protocols: -LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
      resolv.conf mode: stub
      
      Link 2 (enp0s31f6)
          Current Scopes: DNS
               Protocols: +DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
      Current DNS Server: 218.85.152.99
             DNS Servers: 218.85.152.99 218.85.157.99 fe80::1%24077
      
      
      Link 3 (wlp109s0)
      Current Scopes: none
           Protocols: -DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
      
      Link 31 (tun0)
          Current Scopes: DNS
               Protocols: +DefaultRoute +LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
      Current DNS Server: 8.8.8.8
             DNS Servers: 8.8.8.8
              DNS Domain: ~.
      
      
      系统里有多个设备而它们各自有各自的配置的DNS服务,这个时候我们怎么知道要用哪一个呢?可能windows下也是有先后,或者说在android里有类似的cache记录之前成功的案例并且缓存,问题就是多长时间这个缓存被更新一次,或者什么情况下更新呢?其实,我如果仔细看nslookup和dig帮助就明白它们只不过是遵循/etc/resolv.conf里的配置来查询,而这个文件现在的ubuntu版本是自动生成的,第一行就说是/run/systemd/resolve/stub-resolv.conf,我的理解是/run这个目录下面都是服务在跑的临时目录,所以,这里是一个软链接,因为服务是/run/systemd/resolve产生的,可以看看这个服务的定义
      
      ~$ ll /etc/systemd/system/ | grep -i resolv
      lrwxrwxrwx  1 root root   44 Mar 31  2023 dbus-org.freedesktop.resolve1.service -> /lib/systemd/system/systemd-resolved.service
      $ cat /lib/systemd/system/systemd-resolved.service | grep ExecStart
      ExecStart=!!/lib/systemd/systemd-resolved
      
      这里的两个!!的原因是什么呢?豆包说是笔误,我不太相信。
    2. 正像豆包说的那样子,DNS协议可以形象的说是53
      DNS(域名系统)协议主要包括以下这些:

      DNS 查询和响应协议

      • 这是最基本的部分。当客户端(如浏览器)需要将域名转换为 IP 地址时,会发送 DNS 查询请求(Query)。通常是 UDP 协议,端口为 53。
      • 服务器收到查询后,返回 DNS 响应(Response),包含所请求域名对应的 IP 地址或其他相关资源记录(如 CNAME 记录,用于别名解析)。

      主从 DNS 协议(区域传送协议)

      • 用于主 DNS 服务器和从 DNS 服务器之间的数据同步。
      • 主 DNS 服务器维护域名空间的权威信息,从 DNS 服务器定期从主服务器获取数据更新,以提供冗余和负载均衡。
      • 包括完全区域传送(AXFR)和增量区域传送(IXFR)。完全区域传送是把整个区域文件内容传输,增量区域传送则只是传送变化的部分。

      DNS 安全扩展协议(DNSSEC)

      • 用于保证 DNS 数据的完整性和真实性。
      • 它通过数字签名等技术,防止 DNS 缓存污染、中间人攻击等安全问题,让客户端能够验证 DNS 响应是否被篡改。
      因为是使用UDP,所以是端口53,所以,就在这里侦听到的127.0.0.53,这个就是我当初被搞混乱的地方,最早大家都是说使用libc的库函数 我发现使用豆包比google有时候更加的好,因为豆包是自己总结过的答案,而google仅仅是给你指个路:You still have to walk the path yourself because Google can only show you the path!
    3. 我下载了systemd的源代码编译之后就是这个样子。看来这个问题是比较深入的,暂时不要去追究了。
    4. 豆包关于DNS缓存的一些问题还是让我信服的,总而言之,这个问题相当的复杂,看似简单,但是实际操作中不容易解决。比如你怎么知道哪些域名是需要国内的DNS服务而哪些需要国外的呢?我模糊记得openvpn上有提到所谓的split的做法,也许windows上那些push的先后循序可以,可是我怀疑,因为GFW很聪明的,它并不是在你解析DNS让你失败,而是当你实际去访问网站的时候卡住你,所以,你在解析域名的时候是成功的,这个会让你的缓存得到一个错误的结果,而且相当长的时间你不会自动刷新,这个才会迷惑很多人。
      
      ~$ resolvectl query google.com
      google.com: 142.250.189.206                    -- link: tun0
                  2607:f8b0:4005:803::200e           -- link: tun0
      
      -- Information acquired via protocol DNS in 330.8ms.
      -- Data is authenticated: no; Data was acquired via local or encrypted transport: yes
      -- Data from: network
      
      这个比使用dig或者nslookup更加的直接,因为你知道你的来源是哪一个设备,而不是傻傻的去127.0.0.53去问。
    5. 豆包上课了!
      这里是豆包给我上课教我所有的DNS记录类型:
      1. A 记录(Address Record)
        • 定义:A 记录是最常见的 DNS 记录类型,用于将域名(如www.example.com)映射到一个 IPv4 地址(例如192.168.1.1)。当用户在浏览器中输入域名时,DNS 服务器会查找 A 记录来确定要访问的服务器的 IP 地址。
        • 用途:主要用于正向 DNS 解析,让用户通过域名访问基于 IPv4 的网站、服务器等资源。例如,一个网站的服务器有一个 IPv4 地址,通过创建 A 记录,用户就可以使用友好的域名来访问该服务器。
        • 示例:如果一家公司的网站服务器的 IPv4 地址是203.0.113.10,要使域名www.company.com能够访问这个服务器,就需要在 DNS 服务器中为www.company.com创建一个 A 记录,将其指向203.0.113.10
      2. AAAA 记录(IPv6 Address Record)
        • 定义:与 A 记录类似,但是用于将域名映射到 IPv6 地址。随着 IPv6 的逐渐普及,AAAA 记录变得越来越重要。IPv6 地址的格式通常是类似于2001:0db8:85a3:0000:0000:8a2e:0370:7334
        • 用途:用于在 IPv6 网络环境下的正向 DNS 解析,确保支持 IPv6 的设备能够通过域名访问相应的服务器或服务。例如,对于支持 IPv6 的网站和服务器,需要创建 AAAA 记录来使这些设备能够正确访问。
        • 示例:假设一个服务器的 IPv6 地址是2001:db8::1,要让域名www.exampleipv6.com能够访问这个服务器,就需要为该域名创建一个 AAAA 记录,指向2001:db8::1
      3. CNAME 记录(Canonical Name Record)
        • 定义:CNAME 记录用于将一个域名(别名)指向另一个域名(规范名称)。例如,alias.example.com可以通过 CNAME 记录指向www.example.com
        • 用途:主要用于创建域名别名。当有多个域名需要指向同一个实际的服务器或服务时,或者在进行域名重定向等操作时非常有用。不过,CNAME 记录不能用于 DNS 命名空间的顶级节点(如example.com本身,不能直接用 CNAME 记录来设置别名)。
        • 示例:如果一家公司有不同的营销活动,创建了多个域名如campaign1.example.comcampaign2.example.com等,它们都可以通过 CNAME 记录指向www.example.com,这样所有这些域名都可以访问公司的主网站。
      4. NS 记录(Name Server Record)
        • 定义:NS 记录用于指定一个域名的权威 DNS 服务器。每个域名都有一组 NS 记录,这些记录告诉其他 DNS 服务器应该到哪里去查找该域名的更详细的 DNS 信息。
        • 用途:在域名注册和管理中非常重要,用于确定域名的解析由哪些 DNS 服务器负责。例如,当一个新的域名被注册时,域名注册商需要设置该域名的 NS 记录,将其指向域名托管商的 DNS 服务器。
        • 示例:如果一个域名example.com由某 DNS 服务提供商托管,该服务提供商的 DNS 服务器名称是ns1.provider.comns2.provider.com,那么就需要为example.com设置 NS 记录,分别指向ns1.provider.comns2.provider.com
      5. MX 记录(Mail Exchanger Record)
        • 定义:MX 记录用于指定处理一个域名的邮件的邮件服务器。它包含了邮件服务器的优先级和域名信息。
        • 用途:在电子邮件系统中起到关键作用,用于确定邮件的接收服务器。当发送邮件到一个域名时,发送方的邮件服务器会查找该域名的 MX 记录,然后将邮件发送到对应的邮件服务器。
        • 示例:对于域名example.com,如果邮件服务器是mail.example.com,并且优先级为 10,那么就需要为example.com创建一个 MX 记录,将优先级设置为 10,指向mail.example.com。如果有多个邮件服务器,可以设置不同的优先级,邮件首先会尝试发送到优先级最高的服务器。
      6. TXT 记录(Text Record)
        • 定义:TXT 记录是一种可以存储任意文本信息的 DNS 记录。它可以用于多种目的,如验证域名所有权、提供域名的附加说明等。
        • 用途:在安全领域,用于设置 SPF(Sender Policy Framework)和 DKIM(DomainKeys Identified Mail)等邮件验证机制。也可以用于一些自定义的文本信息存储,比如网站的安全策略说明等。
        • 示例:为了防止垃圾邮件发送者滥用域名发送邮件,可以设置 SPF TXT 记录。例如,一个域名example.com的 SPF TXT 记录可以是v=spf1 ip4:192.168.1.1 -all,这表示只有来自192.168.1.1这个 IP 地址的邮件才被允许使用example.com这个域名发送。
      7. CAA 记录(Certification Authority Authorization Record)
        • 定义:CAA 记录用于指定哪些证书颁发机构(CA)被允许为一个域名颁发 SSL/TLS 证书。
        • 用途:在网络安全领域,用于增强域名的证书管理安全性,防止未经授权的 CA 为域名颁发证书,从而降低中间人攻击等安全风险。
        • 示例:一个域名example.com可以设置 CAA 记录,如0 issue "letsencrypt.org",这表示只允许 Let's Encrypt 这个 CA 为该域名颁发证书。
      8. NAPTR 记录(Naming Authority Pointer Record)
        • 定义:NAPTR 记录用于在域名系统中实现基于正则表达式的重写和转换。它的结构相对复杂,涉及到多个字段,用于将一个域名或服务名称转换为另一个。
        • 用途:通常用于复杂的电信和互联网应用,如 ENUM(电话号码映射到域名系统)服务,用于将电话号码等信息转换为可以在互联网上访问的域名或服务。
        • 示例:在 ENUM 服务中,一个电话号码+1 - 202 - 555 - 0100可以通过 NAPTR 记录转换为一个域名,然后通过这个域名访问相关的电信服务。
      9. PTR 记录(Pointer Record)
        • 定义:PTR 记录主要用于反向 DNS 查询,它将 IP 地址映射回域名,与 A 记录和 AAAA 记录的正向查询相反。
        • 用途:在网络管理和安全领域有很多应用,比如邮件服务器验证、网络访问控制等。当收到一封邮件时,邮件服务器可能会通过反向 DNS 查询(使用 PTR 记录)来验证发送邮件的服务器的域名是否与 IP 地址匹配。
        • 示例:如果一个服务器的 IP 地址是192.168.1.1,对应的域名是server.example.com,那么在反向 DNS 区域中,需要为192.168.1.1创建一个 PTR 记录,将其指向server.example.com
      10. SOA 记录(Start of Authority Record)
      • 定义:SOA 记录包含了关于域名的权威信息,如域名的主名称服务器、管理员邮箱、序列号等。它是一个 DNS 区域的起始授权机构的标识。
      • 用途:在 DNS 区域管理中起到关键作用,用于确定 DNS 区域的基本参数和管理信息。例如,每次 DNS 区域数据更新时,序列号会增加,其他 DNS 服务器可以通过比较序列号来确定是否需要更新缓存。
      • 示例:一个典型的 SOA 记录可能包含主名称服务器ns1.example.com,管理员邮箱admin.example.com(通常写成admin.example.com.,最后的点表示完整的域名),以及序列号等信息。
      1. SPF 记录(Sender Policy Framework Record)
      • 定义:实际上 SPF 记录通常以 TXT 记录的形式存在,用于指定允许为一个域名发送邮件的 IP 地址或域名范围,是一种邮件验证机制。
      • 用途:帮助接收邮件的服务器验证邮件的发送者是否合法,防止邮件欺诈,如垃圾邮件和钓鱼邮件。
      • 示例:如前面提到的,v=spf1 ip4:192.168.1.1 -all表示只有来自192.168.1.1这个 IP 地址的邮件才被允许使用example.com这个域名发送。
      1. SRV 记录(Service Record)
      • 定义:SRV 记录用于指定特定服务在域名下的位置和端口号。它包含了服务名称、协议、域名、端口号和优先级等信息。
      • 用途:在一些网络服务中用于定位服务,比如在即时通讯服务、LDAP(轻型目录访问协议)服务等场景中,帮助客户端找到正确的服务服务器和端口。
      • 示例:对于一个即时通讯服务im.example.com,如果服务服务器的 IP 地址是192.168.1.2,端口是5222,可以创建一个 SRV 记录,指定服务_xmpp - client._tcp.im.example.com,优先级为 10,权重为 10,端口为5222,指向192.168.1.2
      1. ALIAS 记录(Amazon Route 53 特定记录类型)
      • 定义:类似于 CNAME 记录,但有一些特殊之处。ALIAS 记录允许将流量解析到 AWS 资源,如负载均衡器、CloudFront 分发、Amazon S3 存储桶等,并且可以在顶级域名上创建别名记录,这是 CNAME 记录不支持的。
      • 用途:在使用亚马逊 AWS 服务时,方便将域名直接指向 AWS 的各种资源,优化域名管理和资源配置。
      • 示例:在 AWS 环境中,可以为域名example.com创建一个 ALIAS 记录,直接指向一个 CloudFront 分发资源,使得用户访问example.com时能够直接通过 CloudFront 来获取内容。

    十一月二十七日 等待变化等待机会

    1. 又重新做了一下aws+openvpnas的实例,有这么几点反复忘记:
      1. 首先,还是软件的费用,我之前已经说的很清楚了,现在又忘记了。这个意味着我必须要注册登陆openvpn的网站获得一个启动码,作为自用它限制了链接数量为2,这个也是我想破解的,纯粹是有些痴心妄想。这里大概是一个固定的获取启动码的链接吧?
      2. 我想在新加坡创建instance,那么我把地区定位到新加坡来创建一系列的VPC的部件,其中我在subnet这个对象上犯迷糊,不知道为什么新加坡和别的地方不一样只能分配10.0.0.0/24的subnet,这个和其它区的172.x.x.x的subnet似乎不兼容,因为对于大多数应用来说这个是私有地址无关紧要,可是对于vpn来说我因为要让客户访问vpn服务器得到DNS解析就似乎是一个大问题,也许依靠别的配置可以解决,可是我感觉非常的头疼。所以,选择香港似乎更加的简单。
      3. 我之所以费劲折腾就是想省几个钱,豆包给了我错误的印象说新加坡便宜,看起来似乎不是这样子的。现在就以2个vCPU,1G内存的t3.micro的Linux/on-demand来比较价格
        regionprice
        HongKong$0.0146
        Jakarta$0.0132
        Malaysia$0.0119
        Mumbai$0.0112
        Seoul$0.013
        Singapore$0.0132
        N.Virginia$0.0104
        N.California$0.0124
        Canada(Centra)$0.0116
        USGovCloud(East)$0.0122
        看起来香港是一个最贵的地方,不应该设在这里。N.Virginia似乎比较好。而且流量也便宜。
      4. 另一个错误是我事先没有先配置elastic ip,结果openvpnas服务器总是使用之前的临时Ip,即便我后来更改了elastic ip,但是似乎web server显示的总是那个旧的IP。这样子导致我的profile文件总是旧的IP,这个是一个头疼的问题,似乎我以前也遇到过,不知道怎么解决的,也许是重新做instance吧?另外,openvpnas强调说时间必须正确,所以,安装ntp似乎是必须的工作。此外我修改了dns的命令。添加了flush-caches,希望能够快一点。
      5. 此外,在profile里我通常加上这个:
        
        script-security 2
        up /etc/openvpn/update-resolv-conf
        down /etc/openvpn/update-resolv-conf
        
        这两个脚本从哪里安装的我都忘记了。而在服务器端都选择允许client互相访问,并其client/server共用DNS配置。
      6. 我登陆的openvpn总是报错说
        /usr/bin/xauth: timeout in locking authority file /home/openvpnas/.Xauthority
        折腾了大半天也没有搞定x11,后来我想干脆不要折腾了,因为我之所以被迫迁移就是之前的服务器硬盘8G被用光了,我也没有好的办法把之前安装的包一个个卸载,干脆这个服务器不要多装东西了。所以,在/etc/ssh/sshd_config里直接设定X11Forwarding no
      至此,我终于可以简单的运行一个openvpnas服务器了。

    十一月二十八日 等待变化等待机会

    1. openvpn能够赚钱的一个原因是因为尽管它已经开源了,但是单单编译openvpn本身就是一个挑战,也许它是在成熟之后有意识的复杂吧?但这个是不能责怪的,因为任何软件开发者都会进行的完善。我尝试接受这个挑战决定从源代码编译openvpn,而它所依赖的众多的第三方软件也尽量从源代码编译,这个工作量相当的大。单单下载源码就是一个头疼的事情,比如gitlab现在莫名其妙的不但很慢还需要登陆才能下载,这个还叫做开源吗?因为单单修改密码就花了我无数的时间最后还出错,简直和github比已经是纯粹的商业化不再看顾开园社区了吧?大多数项目似乎都已经开始转变使用meson来配置,而这个libcap-ng还是使用很古老的autoconf,我出错因为我系统可能很久没有用过了吧?
      
      $ autoconf
      configure.ac:41: error: possibly undefined macro: AM_INIT_AUTOMAKE
            If this token and others are legitimate, please use m4_pattern_allow.
            See the Autoconf documentation.
      configure.ac:94: error: possibly undefined macro: AM_CONDITIONAL
      configure.ac:184: error: possibly undefined macro: AM_PATH_PYTHON
      
      其实,我始终就没有搞明白autoconf和automake的关系,总之,运行这个autoreconf --install就是了。
      projectgit address
      util-linuxhttps://github.com/util-linux/util-linux.git
      lz4https://lz4.github.io/lz4
      OpenSSLhttps://github.com/openssl/openssl
    2. git经常遇到这个错误:
      
      fetch-pack: unexpected disconnect while reading sideband packet
      fatal: early EOF
      fatal: fetch-pack: invalid index-pack output
      
      尝试这个临时性的解决办法
      
      $ git config --global core.compression 9 repack
      
      作者说完成后再恢复压缩率:
      
      $ git config --global core.compression -1
      
      结果不行!!!其他人建议git config --global core.compression 0我觉得应该是大同小异,都是不压缩,如果不行,我就扩大buffer:
      
      $ git config --global http.postBuffer 2M
      
      还有人建议扩大系统文件句柄数目,我准备buffer不行再实验这个注意系统故意设计这个ulimit让你只改变自己用户的参数,而你使用sudo改变整个系统参数会发现没有这个命令,因为limit会影响到整个系统的其他用户吧?
      
      ulimit -f 2097152
      ulimit -c 2097152
      #ulimit -n 2097152 // not in ubuntu!!!
      
      这个是临时改变,要永久改变需要修改这个文件: /etc/security/limits.conf 再结尾添加:(这里的数字我不太清楚)
      
      hard fsize 1000000
      soft fsize 1000000
      
      事实上我的git设置是可以这样子查看的:$ git config --global -e 另一个种思路是文件可能太大了,那么压缩率更高反而能够解决,所以,就有了git config --global core.compression 9 但是当所有的尝试都失败了就说明问题不是本地的问题而是网络的问题,首先,就是我在使用vpn这个是最可能的问题,关掉看看吧?所以,这个很有可能是openvpn的一个问题,不一定是它的bug,但是和它很可能是有关系的。要留心看看。虽然关闭vpn能够解决某些问题,但是当我关闭了vpn依然在clone openssl的时候出现类似 的问题,这个时候通过改变.gitconfig的参数解决了,这个似乎暗示这个问题本身不是vpn的问题,有可能还是git本身的buffer的问题。这里是一个很好的debug git的大全,很多方法可以一试。但是我首先增大git的postBuffer,这个名字听上去似乎是post有关,实际上它即便clone也会用到。 比如我这么测试
      
      GIT_TRACE_PACKET=1 GIT_TRACE=2 GIT_CURL_VERBOSE=1 git clone  https://github.com/openssl/openssl
      ...
      13:56:07.472149 http.c:845              == Info: HTTP/2 stream 0 was not closed cleanly: CANCEL (err 8)
      13:56:07.472157 http.c:845              == Info: stopped the pause stream!
      13:56:07.472168 http.c:845              == Info: Connection #0 to host github.com left intact
      error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: CANCEL (err 8)
      error: 817 bytes of body are still expected
      13:56:07.472274 pkt-line.c:85           packet:          git> 0002
      fetch-pack: unexpected disconnect while reading sideband packet
      fatal: early EOF
      fatal: fetch-pack: invalid index-pack output
      
      这里建议关闭http2改为http1.1
      
      git config --global http.version HTTP/1.1
      git clone https://github.com/openssl/openssl
      git config --global --unset http.version
      
      看起来它并不一定是https级别的协议的问题,可能是更加地层的问题,我看到这个类似在debug curl的帖子,这里有提到tshark,这个更加的头疼因为他在数据中心的云端比我更加的困难。可以借鉴一下,将来吧?帖子的最后提到他升级了curl解决了问题,而他得到启示的帖子说有可能是openssl/ubuntu22.04的问题?这个似乎是最权威的帖子,据说它的背景是这里。大概就是说这个本来是openssl1.1.1的一个bug,但是它漏报了对方的错误,如果要修正会导致很多正在工作的程序出错,所以,openssl推迟到openssl3才修正,所以,这个问题才出现。我的感觉之前那位博主提到过有些时候出错,有些时候不出错,我的感觉是关闭tls连接也许有一个时机问题吧?最后很多时候这个并不影响数据的传输,因为数据传完了,只是打扫卫生出了一点点错误,其实不太影响数据的完整,除非在关闭连接的一霎那服务端突然想起来自己的网络被劫持了,它用尽最后一点力气把放在窗口用来做信号的花盆扔到下面的大街上给同志们报警,可惜太晚了因为前来接头的情报员已经到门口了,算了,大不了大家一起坐牢就义就是了,还有一个伴!我看来脑子坏了。 很明显的这个罪魁祸首是openssl因为它升级到了高版本把之前的问题暴露出来了。也许github的openssl的服务端就是有这个问题而没有修正,而ubuntu22.04敢于冒险升级了openssl3得到了这个骂名:
      
      $ openssl version -a
      OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
      built on: Tue Aug 20 17:27:32 2024 UTC
      platform: debian-amd64
      options:  bn(64,64)
      compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -ffile-prefix-map=/build/openssl-aGUoHt/openssl-3.0.2=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_BUILDING_OPENSSL -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
      OPENSSLDIR: "/usr/lib/ssl"
      ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-3"
      MODULESDIR: "/usr/lib/x86_64-linux-gnu/ossl-modules"
      Seeding source: os-specific
      CPUINFO: OPENSSL_ia32cap=0x7ffaf3bfffebffff:0x98c007bc239c27eb
      
      我觉得我如果使用openssl1.1.1大家就都不会看到这个错误了。我突然有一个念头,如果这个问题不是源于github服务器的话,而是openvpn使用了openssl1.1.1的编译的库,这个问题是不是就是一个可以避免的错误,比如我重新编译openssl3的话? 实际上也许这个是可以在客户端解决的问题,问题是掩盖可能的黑客攻击是否是一个明智的选择,比如openssl的大牛提到了这个选项SSL_OP_IGNORE_UNEXPECTED_EOF,在这里说到:

      SSL_OP_IGNORE_UNEXPECTED_EOF

      Some TLS implementations do not send the mandatory close_notify alert on shutdown. If the application tries to wait for the close_notify alert but the peer closes the connection without sending it, an error is generated. When this option is enabled the peer does not need to send the close_notify alert and a closed connection will be treated as if the close_notify alert was received.

      You should only enable this option if the protocol running over TLS can detect a truncation attack itself, and that the application is checking for that truncation attack.

    3. 这里有极其复杂的关于SSL_shutdown的介绍,我看的都要吐了。

      SSL_shutdown() shuts down an active TLS/SSL connection. It sends the close_notify shutdown alert to the peer.

      SSL_shutdown() tries to send the close_notify shutdown alert to the peer. Whether the operation succeeds or not, the SSL_SENT_SHUTDOWN flag is set and a currently open session is considered closed and good and will be kept in the session cache for further reuse.

      Note that SSL_shutdown() must not be called if a previous fatal error has occurred on a connection i.e. if SSL_get_error() has returned SSL_ERROR_SYSCALL or SSL_ERROR_SSL.

      The shutdown procedure consists of two steps: sending of the close_notify shutdown alert, and reception of the peer's close_notify shutdown alert. The order of those two steps depends on the application.

      It is acceptable for an application to only send its shutdown alert and then close the underlying connection without waiting for the peer's response. This way resources can be saved, as the process can already terminate or serve another connection. This should only be done when it is known that the other side will not send more data, otherwise there is a risk of a truncation attack.

      When a client only writes and never reads from the connection, and the server has sent a session ticket to establish a session, the client might not be able to resume the session because it did not received and process the session ticket from the server. In case the application wants to be able to resume the session, it is recommended to do a complete shutdown procedure (bidirectional close_notify alerts).

      When the underlying connection shall be used for more communications, the complete shutdown procedure must be performed, so that the peers stay synchronized.

      SSL_shutdown() only closes the write direction. It is not possible to call SSL_write() after calling SSL_shutdown(). The read direction is closed by the peer.

      The behaviour of SSL_shutdown() additionally depends on the underlying BIO. If the underlying BIO is blocking, SSL_shutdown() will only return once the handshake step has been finished or an error occurred.

      If the underlying BIO is nonblocking, SSL_shutdown() will also return when the underlying BIO could not satisfy the needs of SSL_shutdown() to continue the handshake. In this case a call to SSL_get_error() with the return value of SSL_shutdown() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. The calling process then must repeat the call after taking appropriate action to satisfy the needs of SSL_shutdown(). The action depends on the underlying BIO. When using a nonblocking socket, nothing is to be done, but select() can be used to check for the required condition. When using a buffering BIO, like a BIO pair, data must be written into or retrieved out of the BIO before being able to continue.

      After SSL_shutdown() returned 0, it is possible to call SSL_shutdown() again to wait for the peer's close_notify alert. SSL_shutdown() will return 1 in that case. However, it is recommended to wait for it using SSL_read() instead.

      SSL_shutdown() can be modified to only set the connection to "shutdown" state but not actually send the close_notify alert messages, see SSL_CTX_set_quiet_shutdown(3). When "quiet shutdown" is enabled, SSL_shutdown() will always succeed and return 1. Note that this is not standard compliant behaviour. It should only be done when the peer has a way to make sure all data has been received and doesn't wait for the close_notify alert message, otherwise an unexpected EOF will be reported.

      There are implementations that do not send the required close_notify alert. If there is a need to communicate with such an implementation, and it's clear that all data has been received, do not wait for the peer's close_notify alert. Waiting for the close_notify alert when the peer just closes the connection will result in an error being generated. The error can be ignored using the SSL_OP_IGNORE_UNEXPECTED_EOF. For more information see SSL_CTX_set_options(3).

      First to close the connection

      When the application is the first party to send the close_notify alert, SSL_shutdown() will only send the alert and then set the SSL_SENT_SHUTDOWN flag (so that the session is considered good and will be kept in the cache). If successful, SSL_shutdown() will return 0.

      If a unidirectional shutdown is enough (the underlying connection shall be closed anyway), this first successful call to SSL_shutdown() is sufficient.

      In order to complete the bidirectional shutdown handshake, the peer needs to send back a close_notify alert. The SSL_RECEIVED_SHUTDOWN flag will be set after receiving and processing it.

      The peer is still allowed to send data after receiving the close_notify event. When it is done sending data, it will send the close_notify alert. SSL_read() should be called until all data is received. SSL_read() will indicate the end of the peer data by returning <= 0 and SSL_get_error() returning SSL_ERROR_ZERO_RETURN.

      Peer closes the connection

      If the peer already sent the close_notify alert and it was already processed implicitly inside another function (SSL_read(3)), the SSL_RECEIVED_SHUTDOWN flag is set. SSL_read() will return <= 0 in that case, and SSL_get_error() will return SSL_ERROR_ZERO_RETURN. SSL_shutdown() will send the close_notify alert, set the SSL_SENT_SHUTDOWN flag. If successful, SSL_shutdown() will return 1.

      Whether SSL_RECEIVED_SHUTDOWN is already set can be checked using the SSL_get_shutdown() (see also SSL_set_shutdown(3) call.

      我感觉这里的确是一个坑

      0

      The shutdown is not yet finished: the close_notify was sent but the peer did not send it back yet. Call SSL_read() to do a bidirectional shutdown.

      而openvpn的并没有触及这个底层逻辑,这个是asio的代码,这个难道说是asio的错吗?
      
      int engine::do_shutdown(void*, std::size_t)
      {
        int result = ::SSL_shutdown(ssl_);
        if (result == 0)
          result = ::SSL_shutdown(ssl_);
        return result;
      }
      
      我还是不要这么武断的下结论吧?
    4. 我再次做了一个实验,就是在openvpn情况下和不开的情况下是否有这个问题?结果是openvpn就出现了这个问题。也许真的是openvpn的问题。可是难道说你要挑战asio的权威吗?似乎不大可能出错的。 再次读这个openssl的bug的描述,我感觉这个就是在asio这一层根本不可能解决的问题,是openssl的bug毫无疑问,因为它的error number是0,这个是任何程序都无法解决的
      Previously, if a peer unexpectedly shutdown a connection an OpenSSL IO function (such as SSL_read()) would report an error and SSL_get_error() would report SSL_ERROR_SYSCALL and errno would be 0. This was considered a bug in 1.1.1 (you should never get SSL_ERROR_SYSCALL but with errno as 0). However fixing it in 1.1.1 broke some apps. We delayed the fix until the next major version (OpenSSL 3.0).
      也就是说这个不可能是openvpn的错误,更不要说是asio之类久经考验的库了。那么唯一能够避免的也许是升级openssl3吗?
    5. 我还是先编译一下openssl3看看有什么困难吧? 首先是一个白痴的老问题,我始终不知道怎么自动跟上remote的branch,这个看起来实在是弱智的问题。如果没有switch,我总是不放心,直到看到这个log里的 内容
      
      $ git branch -a | grep openssl-3
        remotes/origin/openssl-3.0
        remotes/origin/openssl-3.1
        remotes/origin/openssl-3.2
        remotes/origin/openssl-3.3
        remotes/origin/openssl-3.4
      $ git switch -c openssl-3.4 origin/openssl-3.4
      branch 'openssl-3.4' set up to track 'origin/openssl-3.4'.
      Switched to a new branch 'openssl-3.4'
      $ git log 
      commit 74b3781fb4e92fef51fed57fcb1d5795844604d4 (HEAD -> openssl-3.4, origin/openssl-3.4)
      ...
      
      在老版本的git里这个似乎是无脑行为自动就做到了,可是我总是担心本地的branch和remote的冲突怎么办?因为git很聪明的是即便我的命令是
      
      $ git switch openssl-3.4
      
      也会自动的创建这个效果,可是这个毕竟有些让人惴惴不安啊!
    6. 可是查看openssl的branch,让我大吃一惊,因为1.1.1是一个陈旧的分支,默认都是3.x分支,就是说编译openvpn的时候默认都是使用openssl-3.x的,那么怎么可能

    十一月三十日 等待变化等待机会

    1. 我之前对于webm格式所知甚少,突然才意识到m4v格式对于流媒体支持不好,问豆包得到证实。总之,选择webm也许比mp4更好一些因为它的压缩率更高,而且是html5的内生的视频格式。那么使用ffmpeg是否可以得到vp9+aac的组合呢因为原生的音频格式是opus等等。总之,这些新的格式我很陌生。另外豆包提到能够利用NVidia硬件加速ffmpeg,但是这个需要在编译源代码的时候设置,也许还有驱动和版本的问题,我决定尝试一下。

    十二月十日 等待变化等待机会

    1. 这个是豆包给我的安装nvidia驱动的指示,尝试看看。它让我禁用nouveau驱动,这个可能是必要的吧?大体上是两个思路: 一个是ubuntu平台预编译的,不论是官方正式还是PPA大都是如此,不管是否内含了nvidia的二进制的固件。另一个是nvidia原生的驱动去动态编译,这个我感觉是我每次都黑屏的原因吧?决定尝试前一种。使用ubuntu-drivers devices可以自动查找驱动,省的我自己使用硬件型号lspci去匹配。
    2. 我对于安装conda是有抵触的因为我印象中上一次就是把系统搞乱了,不过还是豆包说服了我一点。运行conda我不敢再让它修改我的bashrc,所以,每次要先运行:
      
      $ eval "$(/home/nick/anaconda3/bin/conda shell.bash hook)"
      $ conda init --all
      
      但是这个所谓的运行环境很重要,我对于python的运行机制一窍不通,只知道要在安装运行比如facefusion的时候先要激活conda的环境,比如source ~/anaconda3/bin/activate

    十二月十二日 等待变化等待机会

    1. 朋友推荐腾讯的元宝,我做了一个比较发现至少在c++编程知识领域里豆包完胜元宝,因为针对这一个问题,元宝的回答完全是有一点点古早穴居人的感觉。诚然,很多旧时代的程序员大概在二三十年前还是认为lambda需要一个万能的模板类来包装一下才能使用,可是现代的lambda内部是已经有编译时候产生的内置的函数指针成员变量,而且是static的不是成员函数,并且一个非常贴心的一个特定的类型转换成员函数可以自动返回这个私有的static的成员,因此你是可以使用这个函数指针,因为它是唯一的lambda的实例地址的,这个多么完美的设计居然元宝不知道还说要用古老的std::function来进行转换才能使用。这就是高下立判,我并不是咬文嚼字说类型转换,我仅仅从一个普通使用者的角度问能不能转换,哪怕是强制转换也无不可,元宝怎么能够回答不可以呢?我记得我对于这个问题已经讨论过好几次了,这里的这个cppinsights.io就是再次从另一个角度的解说,对于cppinsights如何产生这种神奇的代码的,我曾经管窥过一次,好像都是硬编码的吧?
    2. 我总是看到这个错误,我以前一直认为是DNS的cache的问题,现在我开始怀疑了。
      TLDR;我做了一些似乎没有结果的探索
      1. 它表现的现象是:
        
        Websites prove their identity via certificates. Firefox does not trust this site because it uses a certificate that is not valid for www.google.com. The certificate is only valid for the following names: *.facebook.com, *.facebook.net, *.fbcdn.net, *.fbsbx.com, *.m.facebook.com, *.messenger.com, *.xx.fbcdn.net, *.xy.fbcdn.net, *.xz.fbcdn.net, facebook.com, messenger.com
         
        Error code: SSL_ERROR_BAD_CERT_DOMAIN 
        
        为什么我访问的是谷歌的搜索网页给出的证书却是facebook.com之类呢?
      2. 我拷贝了这个证书如下:
        
        https://www.google.com/
        
        Unable to communicate securely with peer: requested domain name does not match the server’s certificate.
        
        HTTP Strict Transport Security: false
        HTTP Public Key Pinning: true
        
        Certificate chain:
        
        -----BEGIN CERTIFICATE-----
        MIIGmTCCBYGgAwIBAgIQCcU6goKeAO3NorvB+50XsDANBgkqhkiG9w0BAQsFADBw
        MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
        d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
        dXJhbmNlIFNlcnZlciBDQTAeFw0yNDA5MTkwMDAwMDBaFw0yNDEyMTgyMzU5NTla
        MG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRMwEQYDVQQHEwpN
        ZW5sbyBQYXJrMR0wGwYDVQQKExRNZXRhIFBsYXRmb3JtcywgSW5jLjEXMBUGA1UE
        AwwOKi5mYWNlYm9vay5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARghYHm
        9gsC1Pt9Z7bIQZFF52vEo8nYIcizwBGzTe3OIgyu64z1UdO32uGvtUtm1K0Shbaj
        1VRmp5l0o7pPIEeco4ID+TCCA/UwHwYDVR0jBBgwFoAUUWj/kK8CB3U8zNllZGKi
        ErhZcjswHQYDVR0OBBYEFOgx48T/9eUPFcbhaz72p7n0/Iq2MIG1BgNVHREEga0w
        gaqCDiouZmFjZWJvb2suY29tgg4qLmZhY2Vib29rLm5ldIILKi5mYmNkbi5uZXSC
        CyouZmJzYnguY29tghAqLm0uZmFjZWJvb2suY29tgg8qLm1lc3Nlbmdlci5jb22C
        DioueHguZmJjZG4ubmV0gg4qLnh5LmZiY2RuLm5ldIIOKi54ei5mYmNkbi5uZXSC
        DGZhY2Vib29rLmNvbYINbWVzc2VuZ2VyLmNvbTA+BgNVHSAENzA1MDMGBmeBDAEC
        AjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
        VR0PAQH/BAQDAgOIMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNV
        HR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1z
        ZXJ2ZXItZzYuY3JsMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hh
        Mi1oYS1zZXJ2ZXItZzYuY3JsMIGDBggrBgEFBQcBAQR3MHUwJAYIKwYBBQUHMAGG
        GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBNBggrBgEFBQcwAoZBaHR0cDovL2Nh
        Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkhpZ2hBc3N1cmFuY2VTZXJ2
        ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADCCAX8GCisGAQQB1nkCBAIEggFvBIIBawFp
        AHYA7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZsAAAGSB6m2jwAABAMA
        RzBFAiEAhBPg8xkoXY74jFNhehmMOJ9BSfpy7xVU1EbFHzyTN9ACIDINAs7K8ZbH
        GGLKOS1Z6crkbVk2/GUnXCkKvpEN8v2jAHYA2ra/az+1tiKfm8K7XGvocJFxbLtR
        hIU0vaQ9MEjX+6sAAAGSB6m2UgAABAMARzBFAiEAtZVuPynZEd44RcpCYMo7g5Sk
        Ln4WIOlWPG6fMjs3F/ICIFrGs4WeC5tUr5rFoSXYBcAPfDGeIEv7/NJ8Yc8sIP3i
        AHcAPxdLT9ciR1iUHWUchL4NEu2QN38fhWrrwb8ohez4ZG4AAAGSB6m27gAABAMA
        SDBGAiEA1kT/bDv5PZQJCv9GCOBSw9+HB3sZ7vldQP3Z4c7wR2ACIQDPTYC1A/4g
        t6axcwGd/31J1BMhva7xYrQqToRomate9jANBgkqhkiG9w0BAQsFAAOCAQEAkjLa
        KGWVNY0wSK5pSUfPAFfRKg85NdWFK+XMGNeFCDLEoaxRO3VsmyYCz9Qa2oSllgwL
        +peqckBQZzgPlUy7M1ZBVlnUvMjS247p/EenPcMuZ90rnrXPnWAd7J9ofWA+dsHa
        eelcGF3nMcICN3gp1N7xeLJoSDAEUYe+1y0o4l9JBA5zDbV15zw6NRsWAKpVuulz
        E1rQ9/ctc3T3GU00yuGzkYBLwBnABO5+wBd6nWFNuRw720MtVL23VDh8x3z2MYsG
        XZh2556IYXpsTY/4MaFAAW3SQu0Noln+GQtUihUDKT+IhjNPEx8s2YACkOVdDpjT
        0yyJgwaOajSS1UW0ww==
        -----END CERTIFICATE-----
        -----BEGIN CERTIFICATE-----
        MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs
        MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
        d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
        ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL
        MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
        LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy
        YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2
        4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC
        Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1
        itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn
        4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X
        sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft
        bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA
        MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
        NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
        dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t
        L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG
        BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ
        UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D
        aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd
        aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH
        E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly
        /D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu
        xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF
        0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae
        cPUeybQ=
        -----END CERTIFICATE-----
        
        使用命令行工具的确证书是facebook的
        
        ~$ openssl x509 -in /tmp/cert.txt -text -noout
        Certificate:
            Data:
                Version: 3 (0x2)
                Serial Number:
                    09:c5:3a:82:82:9e:00:ed:cd:a2:bb:c1:fb:9d:17:b0
                Signature Algorithm: sha256WithRSAEncryption
                Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
                Validity
                    Not Before: Sep 19 00:00:00 2024 GMT
                    Not After : Dec 18 23:59:59 2024 GMT
                Subject: C = US, ST = California, L = Menlo Park, O = "Meta Platforms, Inc.", CN = *.facebook.com
                Subject Public Key Info:
                    Public Key Algorithm: id-ecPublicKey
                        Public-Key: (256 bit)
                        pub:
                            04:60:85:81:e6:f6:0b:02:d4:fb:7d:67:b6:c8:41:
                            91:45:e7:6b:c4:a3:c9:d8:21:c8:b3:c0:11:b3:4d:
                            ed:ce:22:0c:ae:eb:8c:f5:51:d3:b7:da:e1:af:b5:
                            4b:66:d4:ad:12:85:b6:a3:d5:54:66:a7:99:74:a3:
                            ba:4f:20:47:9c
                        ASN1 OID: prime256v1
                        NIST CURVE: P-256
                X509v3 extensions:
                    X509v3 Authority Key Identifier: 
                        51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B
                    X509v3 Subject Key Identifier: 
                        E8:31:E3:C4:FF:F5:E5:0F:15:C6:E1:6B:3E:F6:A7:B9:F4:FC:8A:B6
                    X509v3 Subject Alternative Name: 
                        DNS:*.facebook.com, DNS:*.facebook.net, DNS:*.fbcdn.net, DNS:*.fbsbx.com, DNS:*.m.facebook.com, DNS:*.messenger.com, DNS:*.xx.fbcdn.net, DNS:*.xy.fbcdn.net, DNS:*.xz.fbcdn.net, DNS:facebook.com, DNS:messenger.com
                    X509v3 Certificate Policies: 
                        Policy: 2.23.140.1.2.2
                          CPS: http://www.digicert.com/CPS
                    X509v3 Key Usage: critical
                        Digital Signature, Key Agreement
                    X509v3 Extended Key Usage: 
                        TLS Web Server Authentication, TLS Web Client Authentication
                    X509v3 CRL Distribution Points: 
                        Full Name:
                          URI:http://crl3.digicert.com/sha2-ha-server-g6.crl
                        Full Name:
                          URI:http://crl4.digicert.com/sha2-ha-server-g6.crl
                    Authority Information Access: 
                        OCSP - URI:http://ocsp.digicert.com
                        CA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt
                    X509v3 Basic Constraints: critical
                        CA:FALSE
                    CT Precertificate SCTs: 
                        Signed Certificate Timestamp:
                            Version   : v1 (0x0)
                            Log ID    : EE:CD:D0:64:D5:DB:1A:CE:C5:5C:B7:9D:B4:CD:13:A2:
                                        32:87:46:7C:BC:EC:DE:C3:51:48:59:46:71:1F:B5:9B
                            Timestamp : Sep 19 00:23:35.823 2024 GMT
                            Extensions: none
                            Signature : ecdsa-with-SHA256
                                        30:45:02:21:00:84:13:E0:F3:19:28:5D:8E:F8:8C:53:
                                        61:7A:19:8C:38:9F:41:49:FA:72:EF:15:54:D4:46:C5:
                                        1F:3C:93:37:D0:02:20:32:0D:02:CE:CA:F1:96:C7:18:
                                        62:CA:39:2D:59:E9:CA:E4:6D:59:36:FC:65:27:5C:29:
                                        0A:BE:91:0D:F2:FD:A3
                        Signed Certificate Timestamp:
                            Version   : v1 (0x0)
                            Log ID    : DA:B6:BF:6B:3F:B5:B6:22:9F:9B:C2:BB:5C:6B:E8:70:
                                        91:71:6C:BB:51:84:85:34:BD:A4:3D:30:48:D7:FB:AB
                            Timestamp : Sep 19 00:23:35.762 2024 GMT
                            Extensions: none
                            Signature : ecdsa-with-SHA256
                                        30:45:02:21:00:B5:95:6E:3F:29:D9:11:DE:38:45:CA:
                                        42:60:CA:3B:83:94:A4:2E:7E:16:20:E9:56:3C:6E:9F:
                                        32:3B:37:17:F2:02:20:5A:C6:B3:85:9E:0B:9B:54:AF:
                                        9A:C5:A1:25:D8:05:C0:0F:7C:31:9E:20:4B:FB:FC:D2:
                                        7C:61:CF:2C:20:FD:E2
                        Signed Certificate Timestamp:
                            Version   : v1 (0x0)
                            Log ID    : 3F:17:4B:4F:D7:22:47:58:94:1D:65:1C:84:BE:0D:12:
                                        ED:90:37:7F:1F:85:6A:EB:C1:BF:28:85:EC:F8:64:6E
                            Timestamp : Sep 19 00:23:35.918 2024 GMT
                            Extensions: none
                            Signature : ecdsa-with-SHA256
                                        30:46:02:21:00:D6:44:FF:6C:3B:F9:3D:94:09:0A:FF:
                                        46:08:E0:52:C3:DF:87:07:7B:19:EE:F9:5D:40:FD:D9:
                                        E1:CE:F0:47:60:02:21:00:CF:4D:80:B5:03:FE:20:B7:
                                        A6:B1:73:01:9D:FF:7D:49:D4:13:21:BD:AE:F1:62:B4:
                                        2A:4E:84:68:99:AB:5E:F6
            Signature Algorithm: sha256WithRSAEncryption
            Signature Value:
                92:32:da:28:65:95:35:8d:30:48:ae:69:49:47:cf:00:57:d1:
                2a:0f:39:35:d5:85:2b:e5:cc:18:d7:85:08:32:c4:a1:ac:51:
                3b:75:6c:9b:26:02:cf:d4:1a:da:84:a5:96:0c:0b:fa:97:aa:
                72:40:50:67:38:0f:95:4c:bb:33:56:41:56:59:d4:bc:c8:d2:
                db:8e:e9:fc:47:a7:3d:c3:2e:67:dd:2b:9e:b5:cf:9d:60:1d:
                ec:9f:68:7d:60:3e:76:c1:da:79:e9:5c:18:5d:e7:31:c2:02:
                37:78:29:d4:de:f1:78:b2:68:48:30:04:51:87:be:d7:2d:28:
                e2:5f:49:04:0e:73:0d:b5:75:e7:3c:3a:35:1b:16:00:aa:55:
                ba:e9:73:13:5a:d0:f7:f7:2d:73:74:f7:19:4d:34:ca:e1:b3:
                91:80:4b:c0:19:c0:04:ee:7e:c0:17:7a:9d:61:4d:b9:1c:3b:
                db:43:2d:54:bd:b7:54:38:7c:c7:7c:f6:31:8b:06:5d:98:76:
                e7:9e:88:61:7a:6c:4d:8f:f8:31:a1:40:01:6d:d2:42:ed:0d:
                a2:59:fe:19:0b:54:8a:15:03:29:3f:88:86:33:4f:13:1f:2c:
                d9:80:02:90:e5:5d:0e:98:d3:d3:2c:89:83:06:8e:6a:34:92:
                d5:45:b4:c3
        
        
      3. 这个的确是让我大吃一惊,为什么会这样子呢?如果我们不使用浏览器,直接使用openssl工具看看
        
        $ openssl s_client -connect www.google.com:443
        CONNECTED(00000003)
        depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
        verify return:1
        depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
        verify return:1
        depth=0 C = US, ST = California, L = Menlo Park, O = "Meta Platforms, Inc.", CN = *.facebook.com
        verify return:1
        ---
        Certificate chain
         0 s:C = US, ST = California, L = Menlo Park, O = "Meta Platforms, Inc.", CN = *.facebook.com
           i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
           a:PKEY: id-ecPublicKey, 256 (bit); sigalg: RSA-SHA256
           v:NotBefore: Sep 19 00:00:00 2024 GMT; NotAfter: Dec 18 23:59:59 2024 GMT
         1 s:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
           i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
           a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
           v:NotBefore: Oct 22 12:00:00 2013 GMT; NotAfter: Oct 22 12:00:00 2028 GMT
        ---
        Server certificate
        -----BEGIN CERTIFICATE-----
        MIIGmTCCBYGgAwIBAgIQCcU6goKeAO3NorvB+50XsDANBgkqhkiG9w0BAQsFADBw
        MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
        d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
        dXJhbmNlIFNlcnZlciBDQTAeFw0yNDA5MTkwMDAwMDBaFw0yNDEyMTgyMzU5NTla
        MG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRMwEQYDVQQHEwpN
        ZW5sbyBQYXJrMR0wGwYDVQQKExRNZXRhIFBsYXRmb3JtcywgSW5jLjEXMBUGA1UE
        AwwOKi5mYWNlYm9vay5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARghYHm
        9gsC1Pt9Z7bIQZFF52vEo8nYIcizwBGzTe3OIgyu64z1UdO32uGvtUtm1K0Shbaj
        1VRmp5l0o7pPIEeco4ID+TCCA/UwHwYDVR0jBBgwFoAUUWj/kK8CB3U8zNllZGKi
        ErhZcjswHQYDVR0OBBYEFOgx48T/9eUPFcbhaz72p7n0/Iq2MIG1BgNVHREEga0w
        gaqCDiouZmFjZWJvb2suY29tgg4qLmZhY2Vib29rLm5ldIILKi5mYmNkbi5uZXSC
        CyouZmJzYnguY29tghAqLm0uZmFjZWJvb2suY29tgg8qLm1lc3Nlbmdlci5jb22C
        DioueHguZmJjZG4ubmV0gg4qLnh5LmZiY2RuLm5ldIIOKi54ei5mYmNkbi5uZXSC
        DGZhY2Vib29rLmNvbYINbWVzc2VuZ2VyLmNvbTA+BgNVHSAENzA1MDMGBmeBDAEC
        AjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
        VR0PAQH/BAQDAgOIMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNV
        HR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1z
        ZXJ2ZXItZzYuY3JsMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hh
        Mi1oYS1zZXJ2ZXItZzYuY3JsMIGDBggrBgEFBQcBAQR3MHUwJAYIKwYBBQUHMAGG
        GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBNBggrBgEFBQcwAoZBaHR0cDovL2Nh
        Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkhpZ2hBc3N1cmFuY2VTZXJ2
        ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADCCAX8GCisGAQQB1nkCBAIEggFvBIIBawFp
        AHYA7s3QZNXbGs7FXLedtM0TojKHRny87N7DUUhZRnEftZsAAAGSB6m2jwAABAMA
        RzBFAiEAhBPg8xkoXY74jFNhehmMOJ9BSfpy7xVU1EbFHzyTN9ACIDINAs7K8ZbH
        GGLKOS1Z6crkbVk2/GUnXCkKvpEN8v2jAHYA2ra/az+1tiKfm8K7XGvocJFxbLtR
        hIU0vaQ9MEjX+6sAAAGSB6m2UgAABAMARzBFAiEAtZVuPynZEd44RcpCYMo7g5Sk
        Ln4WIOlWPG6fMjs3F/ICIFrGs4WeC5tUr5rFoSXYBcAPfDGeIEv7/NJ8Yc8sIP3i
        AHcAPxdLT9ciR1iUHWUchL4NEu2QN38fhWrrwb8ohez4ZG4AAAGSB6m27gAABAMA
        SDBGAiEA1kT/bDv5PZQJCv9GCOBSw9+HB3sZ7vldQP3Z4c7wR2ACIQDPTYC1A/4g
        t6axcwGd/31J1BMhva7xYrQqToRomate9jANBgkqhkiG9w0BAQsFAAOCAQEAkjLa
        KGWVNY0wSK5pSUfPAFfRKg85NdWFK+XMGNeFCDLEoaxRO3VsmyYCz9Qa2oSllgwL
        +peqckBQZzgPlUy7M1ZBVlnUvMjS247p/EenPcMuZ90rnrXPnWAd7J9ofWA+dsHa
        eelcGF3nMcICN3gp1N7xeLJoSDAEUYe+1y0o4l9JBA5zDbV15zw6NRsWAKpVuulz
        E1rQ9/ctc3T3GU00yuGzkYBLwBnABO5+wBd6nWFNuRw720MtVL23VDh8x3z2MYsG
        XZh2556IYXpsTY/4MaFAAW3SQu0Noln+GQtUihUDKT+IhjNPEx8s2YACkOVdDpjT
        0yyJgwaOajSS1UW0ww==
        -----END CERTIFICATE-----
        subject=C = US, ST = California, L = Menlo Park, O = "Meta Platforms, Inc.", CN = *.facebook.com
        issuer=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA
        ---
        No client certificate CA names sent
        Peer signing digest: SHA256
        Peer signature type: ECDSA
        Server Temp Key: X25519, 253 bits
        ---
        SSL handshake has read 3236 bytes and written 380 bytes
        Verification: OK
        ---
        New, TLSv1.3, Cipher is TLS_CHACHA20_POLY1305_SHA256
        Server public key is 256 bit
        Secure Renegotiation IS NOT supported
        Compression: NONE
        Expansion: NONE
        No ALPN negotiated
        Early data was not sent
        Verify return code: 0 (ok)
        ---
        ---
        Post-Handshake New Session Ticket arrived:
        SSL-Session:
            Protocol  : TLSv1.3
            Cipher    : TLS_CHACHA20_POLY1305_SHA256
            Session-ID: 169B2DBEF1E1767C4E7E8754E217FE546AC136F0FC93DDCC195796AFA98D09D8
            Session-ID-ctx: 
            Resumption PSK: 28BB702C181947835600E1C0F28FF57B3BFFC75C664240F571130C04458DDD1F
            PSK identity: None
            PSK identity hint: None
            SRP username: None
            TLS session ticket lifetime hint: 172800 (seconds)
            TLS session ticket:
            0000 - d9 cf 43 49 26 1f a6 5d-1b 71 23 81 a5 c8 ad 48   ..CI&..].q#....H
            0010 - 8a 66 1c b8 e5 24 73 da-68 c1 36 0d fe cf 2b 3b   .f...$s.h.6...+;
            0020 - 00 00 00 00 4a ef 8e f1-90 83 04 ea a0 84 b2 68   ....J..........h
            0030 - b7 b3 e5 28 a1 2f f7 f4-3d 50 c4 29 b7 b9 10 64   ...(./..=P.)...d
            0040 - 6c b4 88 ab 85 30 51 a8-cb 3e c8 12 29 d1 80 dc   l....0Q..>..)...
            0050 - 5d 7c 94 84 a5 5c 16 c8-7c bc 3b 90 cf 87 eb fb   ]|...\..|.;.....
            0060 - 99 a1 cd f3 d4 1c f5 a6-5e b1 c5 8f e3 c8 8d 45   ........^......E
            0070 - 92 ea 19 92 52 08 04 c2-63 c4 cd 3e 0c b4 dc 28   ....R...c..>...(
            0080 - da 82                                             ..
        
            Start Time: 1733962688 
            Timeout   : 7200 (sec)
            Verify return code: 0 (ok)
            Extended master secret: no
            Max Early Data: 0
        ---
        read R BLOCK
        
        结果是一样的!
      4. 除非我的vpn被做了手脚,在vpn上看看:
        
        $ openssl s_client -connect www.google.com:443
        CONNECTED(00000003)
        depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1
        verify return:1
        depth=1 C = US, O = Google Trust Services, CN = WR2
        verify return:1
        depth=0 CN = www.google.com
        verify return:1
        ---
        Certificate chain
         0 s:CN = www.google.com
           i:C = US, O = Google Trust Services, CN = WR2
           a:PKEY: id-ecPublicKey, 256 (bit); sigalg: RSA-SHA256
           v:NotBefore: Nov  4 08:39:37 2024 GMT; NotAfter: Jan 27 08:39:36 2025 GMT
         1 s:C = US, O = Google Trust Services, CN = WR2
           i:C = US, O = Google Trust Services LLC, CN = GTS Root R1
           a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
           v:NotBefore: Dec 13 09:00:00 2023 GMT; NotAfter: Feb 20 14:00:00 2029 GMT
         2 s:C = US, O = Google Trust Services LLC, CN = GTS Root R1
           i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
           a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
           v:NotBefore: Jun 19 00:00:42 2020 GMT; NotAfter: Jan 28 00:00:42 2028 GMT
        ---
        Server certificate
        -----BEGIN CERTIFICATE-----
        MIIEWDCCA0CgAwIBAgIQAoSxBBAXbikSDTlMCHenzzANBgkqhkiG9w0BAQsFADA7
        MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMQww
        CgYDVQQDEwNXUjIwHhcNMjQxMTA0MDgzOTM3WhcNMjUwMTI3MDgzOTM2WjAZMRcw
        FQYDVQQDEw53d3cuZ29vZ2xlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
        BIkExubCQdtCqvrCz0ECKXYIqwCOwXZsSRHlvPdgL1nKJ0IXRpKV+fV3liW6DPOE
        5Xz9EjoKix22mK4PL8eiWCOjggJDMIICPzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0l
        BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUhstHZ0eJiOWn
        8YrYTKXxL8NreMQwHwYDVR0jBBgwFoAU3hse7XkV1D43JMMhu+w0OW1CsjAwWAYI
        KwYBBQUHAQEETDBKMCEGCCsGAQUFBzABhhVodHRwOi8vby5wa2kuZ29vZy93cjIw
        JQYIKwYBBQUHMAKGGWh0dHA6Ly9pLnBraS5nb29nL3dyMi5jcnQwGQYDVR0RBBIw
        EIIOd3d3Lmdvb2dsZS5jb20wEwYDVR0gBAwwCjAIBgZngQwBAgEwNgYDVR0fBC8w
        LTAroCmgJ4YlaHR0cDovL2MucGtpLmdvb2cvd3IyLzlVVmJOMHc1RTZZLmNybDCC
        AQYGCisGAQQB1nkCBAIEgfcEgfQA8gB3AM8RVu7VLnyv84db2Wkum+kacWdKsBfs
        rAHSW3fOzDsIAAABkvaLUIIAAAQDAEgwRgIhAPDTtjsUa6F6HRn0dqE3Tu+83uBJ
        fFyU22OI+GZoVN9qAiEAtC3xUaCftdNCSgwJy4/fcwzCJGfCwwBa26zcRVK/Gx8A
        dwDm0jFjQHeMwRBBBtdxuc7B0kD2loSG+7qHMh39HjeOUAAAAZL2i1BlAAAEAwBI
        MEYCIQD3DllNLf9dzZoGCnA0wA7eEhrwfkSQOdjSnBivbTItqQIhAO/XQE0i02LS
        3h6UsvXZ1sKhbw7xrf/E/2RXZYFyAG0QMA0GCSqGSIb3DQEBCwUAA4IBAQB7jBGa
        Asu92E4YWQxq9y9UK+5+fsLXe1AGCI6HA7FlHFS3dDTWjQEV+07ewgEkPywZs3Qs
        sX3NrcRIPM5SyJaLAoNgKBjFWLyWuWfMq6hpyI0R6UgZ0IupK/bmT8qT2hAqQN8J
        pWGF0p853LuVmBwKkSjvtYh4ZJjJxSaUZKCtfl43SxMam0z9hk2MviPDPmy+fWuA
        dFhvfiW1J9uqNxvzgWNP8YVDuK1j8z049x7DI/x0yHmDs9/8vxbiUoFU9BUqyMjP
        mt3P0DLbkif+NH6uqjLPVzMFqhUxe3E0nutarul3BFEvwpW480yHIyLnHUqnBL2Z
        YUaLxsyeu3X1RV/l
        -----END CERTIFICATE-----
        subject=CN = www.google.com
        issuer=C = US, O = Google Trust Services, CN = WR2
        ---
        No client certificate CA names sent
        Peer signing digest: SHA256
        Peer signature type: ECDSA
        Server Temp Key: X25519, 253 bits
        ---
        SSL handshake has read 4109 bytes and written 396 bytes
        Verification: OK
        ---
        New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
        Server public key is 256 bit
        Secure Renegotiation IS NOT supported
        Compression: NONE
        Expansion: NONE
        No ALPN negotiated
        Early data was not sent
        Verify return code: 0 (ok)
        ---
        80AB0FF1CF700000:error:0A000126:SSL routines:ssl3_read_n:unexpected eof while reading:../ssl/record/rec_layer_s3.c:317:
        
        
        那么看起来我的vpn本身是直接连接的,但是vpn连接也许是被做了手脚吧?我能这么说吗?
      5. 我现在先解决我是怎么访问谷歌的:
        
        $ sudo traceroute -T -p 443 www.google.com
        [sudo] password for nick: 
        traceroute to www.google.com (31.13.88.169), 30 hops max, 60 byte packets
         1  172.27.236.1 (172.27.236.1)  210.412 ms  210.452 ms  210.432 ms
         2  * * *
         3  240.3.12.65 (240.3.12.65)  210.464 ms 240.3.84.71 (240.3.84.71)  210.446 ms *
         4  242.8.90.151 (242.8.90.151)  211.104 ms * 242.12.42.5 (242.12.42.5)  211.672 ms
         5  240.2.172.15 (240.2.172.15)  225.421 ms  225.668 ms  225.391 ms
         6  peer-as16509.pr05.atl1.tfbnw.net (157.240.73.223)  225.507 ms 242.0.170.81 (242.0.170.81)  222.612 ms *
         7  240.2.172.15 (240.2.172.15)  224.860 ms 99.83.113.37 (99.83.113.37)  225.570 ms ae16.pr05.atl1.tfbnw.net (157.240.73.222)  224.979 ms
         8  99.83.113.36 (99.83.113.36)  224.599 ms po201.asw01.atl1.tfbnw.net (129.134.100.42)  224.608 ms peer-as16509.pr05.atl1.tfbnw.net (157.240.73.223)  225.224 ms
         9  psw01.atl3.tfbnw.net (173.252.66.12)  225.628 ms psw04.atl3.tfbnw.net (173.252.65.163)  225.167 ms psw02.atl3.tfbnw.net (173.252.66.11)  225.201 ms
        10  msw1ao.02.atl3.tfbnw.net (129.134.114.165)  226.591 ms * *
        11  po203.asw01.atl1.tfbnw.net (129.134.100.48)  224.900 ms edge-z-p4-shv-02-atl3.facebook.com (31.13.88.169)  225.009 ms  224.354 ms
        
        为什么最终我又去了facebook呢?是DNS被劫持了吗?
      6. 在vpn服务器上我发现它使用的traceroute版本和我的ubuntu不同,查看它的source.list我发现它使用的是亚马逊上的自建的bucket,这个是正常的吗?
        
        deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ jammy universe
        # deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ jammy universe
        deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ jammy-updates universe
        # deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ jammy-updates universe
        
        我发现这个注册信息,似乎也没有什么不对吧?因为子域名的解析是不可能被劫持的吧?
        
        $ dig us-east-1.ec2.archive.ubuntu.com
        
        ; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> us-east-1.ec2.archive.ubuntu.com
        ;; global options: +cmd
        ;; Got answer:
        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14453
        ;; flags: qr rd ra; QUERY: 1, ANSWER: 10, AUTHORITY: 3, ADDITIONAL: 5
        
        ;; OPT PSEUDOSECTION:
        ; EDNS: version: 0, flags:; udp: 65494
        ;; QUESTION SECTION:
        ;us-east-1.ec2.archive.ubuntu.com. IN	A
        
        ;; ANSWER SECTION:
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	18.232.150.247
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	34.237.137.22
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	52.207.133.243
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	3.209.10.109
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	54.144.148.213
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	54.87.19.168
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	34.201.250.36
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	52.91.65.63
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	54.165.17.230
        us-east-1.ec2.archive.ubuntu.com. 144 IN A	3.87.126.146
        
        ;; AUTHORITY SECTION:
        ubuntu.com.		144	IN	NS	ns2.canonical.com.
        ubuntu.com.		144	IN	NS	ns1.canonical.com.
        ubuntu.com.		144	IN	NS	ns3.canonical.com.
        
        ;; ADDITIONAL SECTION:
        ns2.canonical.com.	144	IN	AAAA	2620:2d:4000:1::44
        ns1.canonical.com.	144	IN	A	185.125.190.65
        ns1.canonical.com.	144	IN	AAAA	2620:2d:4000:1::43
        ns3.canonical.com.	144	IN	A	91.189.91.139
        
        ;; Query time: 0 msec
        ;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
        ;; WHEN: Thu Dec 12 09:02:47 +08 2024
        ;; MSG SIZE  rcvd: 373
        
      7. 正当我茫然不知所措的时候,似乎连接又正常了,我看到的traceroute又变化了:
        
        $ sudo traceroute -T -p 443 www.google.com
        traceroute to www.google.com (199.59.148.20), 30 hops max, 60 byte packets
         1  172.27.232.1 (172.27.232.1)  221.888 ms  222.377 ms  222.400 ms
         2  * * *
         3  240.3.12.99 (240.3.12.99)  222.405 ms 240.3.12.96 (240.3.12.96)  222.391 ms 240.3.12.97 (240.3.12.97)  222.376 ms
         4  * 242.2.112.71 (242.2.112.71)  223.779 ms *
         5  240.0.236.2 (240.0.236.2)  1165.732 ms 240.0.184.3 (240.0.184.3)  223.324 ms 240.0.236.2 (240.0.236.2)  1165.622 ms
         6  * * 242.2.213.67 (242.2.213.67)  224.208 ms
         7  100.100.36.42 (100.100.36.42)  223.574 ms 100.100.36.34 (100.100.36.34)  223.509 ms 100.100.36.90 (100.100.36.90)  224.150 ms
         8  99.83.112.43 (99.83.112.43)  223.557 ms  223.562 ms  224.515 ms
         9  100.100.36.80 (100.100.36.80)  224.037 ms * *
        10  99.83.112.43 (99.83.112.43)  223.643 ms 100.100.36.36 (100.100.36.36)  223.598 ms 99.83.112.41 (99.83.112.41)  223.081 ms
        11  * * *
        12  * * *
        13  * * *
        14  * * *
        15  100.100.36.92 (100.100.36.92)  223.979 ms 100.100.36.46 (100.100.36.46)  224.005 ms *
        16  * * *
        17  * * *
        18  * * *
        19  * * *
        20  * * *
        21  * * *
        22  * * *
        23  * * *
        24  * * *
        25  * * *
        26  * * *
        27  * * *
        28  * * *
        29  * * *
        30  * * *
        
        我不再去facebook了,难道被劫持的dns缓存刷新了?
      8. 不管怎么说我看到的都是让我感到胆战心惊的:
        
        $ ping -4 www.google.com
        PING www.google.com (31.13.94.49) 56(84) bytes of data.
        64 bytes from edge-z-p3-shv-01-eze1.facebook.com (31.13.94.49): icmp_seq=1 ttl=49 time=377 ms
        ...
        
        这说明了什么?
        
        $ traceroute  edge-z-p3-shv-01-eze1.facebook.com
        traceroute to edge-z-p3-shv-01-eze1.facebook.com (31.13.94.41), 30 hops max, 60 byte packets
         1  172.27.236.1 (172.27.236.1)  210.092 ms  210.634 ms  210.657 ms
         2  244.5.0.181 (244.5.0.181)  217.800 ms 216.182.226.40 (216.182.226.40)  231.425 ms ec2-3-236-60-11.compute-1.amazonaws.com (3.236.60.11)  214.968 ms
         3  100.66.9.236 (100.66.9.236)  230.784 ms 100.65.120.240 (100.65.120.240)  211.342 ms 100.66.12.4 (100.66.12.4)  258.891 ms
         4  242.2.113.67 (242.2.113.67)  211.779 ms 242.12.42.3 (242.12.42.3)  211.743 ms 242.8.91.151 (242.8.91.151)  215.657 ms
         5  240.0.184.1 (240.0.184.1)  211.340 ms * *
         6  242.3.85.193 (242.3.85.193)  211.296 ms 242.2.213.195 (242.2.213.195)  213.119 ms 241.0.4.243 (241.0.4.243)  210.794 ms
         7  100.100.4.78 (100.100.4.78)  211.755 ms 100.100.4.66 (100.100.4.66)  211.750 ms 242.0.171.85 (242.0.171.85)  211.224 ms
         8  195.22.206.60 (195.22.206.60)  211.451 ms  211.457 ms  211.453 ms
         9  100.100.4.74 (100.100.4.74)  211.660 ms 185.70.203.99 (185.70.203.99)  372.353 ms 100.100.4.72 (100.100.4.72)  211.669 ms
        10  185.70.203.109 (185.70.203.109)  364.796 ms  368.024 ms  366.114 ms
        11  po222.asw04.eze1.tfbnw.net (129.134.52.234)  367.258 ms 241.0.4.66 (241.0.4.66)  211.033 ms po222.asw04.eze1.tfbnw.net (129.134.52.234)  368.163 ms
        12  psw02.eze1.tfbnw.net (129.134.63.4)  365.204 ms  370.318 ms psw04.eze1.tfbnw.net (129.134.62.254)  367.805 ms
        13  157.240.63.150 (157.240.63.150)  368.648 ms msw1ab.01.eze1.tfbnw.net (129.134.88.91)  367.764 ms 240.0.184.1 (240.0.184.1)  211.955 ms
        14  * * 242.3.84.65 (242.3.84.65)  211.971 ms
        15  * * *
        16  * * *
        17  * * *
        18  * * 185.70.203.109 (185.70.203.109)  374.976 ms
        19  * * *
        20  * * *
        21  msw1ad.01.eze1.tfbnw.net (129.134.88.49)  363.238 ms * *
        22  * * *
        23  * * *
        24  * * *
        25  * * *
        26  * * *
        27  * * *
        28  * * *
        29  * * *
        30  * * *
        
        
        这个tfbnw.net毫无疑问是据说facebook内部测试人员使用的,我感觉这个不像是facebook上用户的行文,似乎是facebook公司开发人员的行为吧?
      9. 我开始有点明白了:
        
        $ dig www.google.com
        
        ; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> www.google.com
        ;; global options: +cmd
        ;; Got answer:
        ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8129
        ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 9
        
        ;; OPT PSEUDOSECTION:
        ; EDNS: version: 0, flags:; udp: 65494
        ;; QUESTION SECTION:
        ;www.google.com.			IN	A
        
        ;; ANSWER SECTION:
        www.google.com.		583	IN	A	31.13.88.169
        
        ;; AUTHORITY SECTION:
        google.com.		583	IN	NS	ns2.google.com.
        google.com.		583	IN	NS	ns1.google.com.
        google.com.		583	IN	NS	ns4.google.com.
        google.com.		583	IN	NS	ns3.google.com.
        
        ;; ADDITIONAL SECTION:
        ns4.google.com.		583	IN	A	216.239.38.10
        ns1.google.com.		583	IN	AAAA	2001:4860:4802:32::a
        ns2.google.com.		583	IN	AAAA	2001:4860:4802:34::a
        ns2.google.com.		583	IN	A	216.239.34.10
        ns3.google.com.		583	IN	AAAA	2001:4860:4802:36::a
        ns4.google.com.		583	IN	AAAA	2001:4860:4802:38::a
        ns1.google.com.		583	IN	A	216.239.32.10
        ns3.google.com.		583	IN	A	216.239.36.10
        
        ;; Query time: 0 msec
        ;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
        ;; WHEN: Thu Dec 12 10:05:08 +08 2024
        ;; MSG SIZE  rcvd: 307
        
        nslookup看到的更加清晰
        
        $ nslookup www.google.com
        Server:		127.0.0.53
        Address:	127.0.0.53#53
        
        Non-authoritative answer:
        Name:	www.google.com
        Address: 31.13.88.169
        Name:	www.google.com
        Address: 2001::1
        
        
        但是这个地址31.13.88.169是谁的?
        
        $ dig edge-z-p4-shv-02-atl3.facebook.com +noall +answer
        edge-z-p4-shv-02-atl3.facebook.com. 24 IN A	108.160.172.200
        
        不过这个奇怪的现象很快就消失了,我看到的又是另一个结果
        
        $ ping www.google.com
        PING www.google.com (157.240.17.35) 56(84) bytes of data.
        64 bytes from edge-star-mini-shv-01-zrh1.facebook.com (157.240.17.35): icmp_seq=1 ttl=47 time=310 ms
        ...
        
        $ dig  www.google.com +noall +answer
        www.google.com.		186	IN	A	157.240.17.35
        $ dig edge-star-mini-shv-01-zrh1.facebook.com +noall +answer
        edge-star-mini-shv-01-zrh1.facebook.com. 3600 IN A 157.240.17.35
        
        劫持似乎发生了,究竟是谁干的?
      10. 开始接触所谓的DNS leaking的概念,这个检测网站有一些解释:

        What are DNS leaks?

        In this context, with "DNS leak" we mean an unencrypted DNS query sent by your system OUTSIDE the established VPN tunnel.

        那么怎么检测呢?我使用这个检测网站,但是也是不得要领。
      11. 另一个是什么

        What are WebRTC leaks?

        WebRTC implement STUN (Session Traversal Utilities for Nat), a protocol that allows to discover the public IP address. To disable it:

        • Mozilla Firefox: Type "about:config” in the address bar. Scroll down to “media.peerconnection.enabled”, double click to set it to false.
        • Google Chrome: Install Google official extension WebRTC Network Limiter.
        • Opera: Type "about:config" in the address bar or go to "Settings". Select "Show advanced settings" and click on "Privacy & security". At "WebRTC" mark select "Disable non-proxied UDP".
    3. 我开始对于openvpn的access server产生了极度怀疑,那么如果我不使用它的AMI,而是直接使用开源的简单的程序如何呢?
      
      $ aptitude search openvpn | grep -e i\\s
      i A network-manager-openvpn - network management framework (OpenVPN plugin core)
      i  network-manager-openvpn-gnome - network management framework (OpenVPN plugin GNOME GUI)
      i  openvpn - virtual private network daemon
      i  openvpn-systemd-resolved - integrates OpenVPN with systemd-resolved
      
      这个是我当前安装的几个包,如果我只需要服务器这个daemon,是不是我就可以了呢?

    十二月十四日 等待变化等待机会

    1. 我开始尝试openvpn2.5版本的一些最最基本的测试范例,这个牵扯到制作证书的基本操作,这个是一个复习吗?
      1. 开始之前都要做的就是分配密钥:
        
        $ openssl genrsa -out server.key 2048
        
      2. 创建一个证书的请求,我以前一直不理解为什么要这么做,现在才理解了因为制作证书可以是用自己的密钥做自签名,也可以是让别人用它的密钥做认证式的制作,它们都需要最基本的请求内容:
        
        $ openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=FJ/L=XM/O=PARC22/OU=sager1/CN=sager"
        
        这里我们设定的部分就是Country/State/Location/Organization/OrganizationUnit/OrgnaizationName/CommonName
      3. 如果选择自签名的话:
        
        $ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
        
      4. 这个制作好的证书也可以作为根证书来给别人背书:
        
        $ openssl genrsa -out client.key 2048
        $ openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=FJ/L=XM/O=PARC22/OU=zbook1/CN=zbook"
        $ openssl x509 -req -days 3650 -in client.csr -CA server.crt -CAkey server.key -CAcreateserial -out client.crt
        
      另一个关于Diffie Hellman密钥交换的概念,我本来一直以为需要服务器和客户端必须要先部署,这个实际上是无意义的,公钥交换就是为了解决这个问题,如果能够事先部署就根本不是问题,所以,你只需要设定它的参数就可以了:
      
      $ openssl dhparam -out dh2048.pem 2048
      
    2. 然后我在实验完全TLS/SSL的通讯时候发现总是报错,openvpn总是说DCO的data-cipher谈判失败,哪怕我给了参数--data-ciphers AES-256-CBC也不行。在log里看到 Note: cipher 'AES-256-CBC' in --data-ciphers is not supported by ovpn-dco, disabling data channel offload.,看这里据说是一个高级的性能提升。
      1. Data packets arrive in the kernel space.

      2. OpenVPN copies the packets to the user space.

      3. OpenVPN decrypts and encrypts packets in the user space.

      4. OpenVPN copies those packets back to the kernel space.

      5. The data packets then get sent to their destination.

      如果使用DCO就减少了一个context switch的过程
      1. Data packets arrive in the kernel space.

      2. OpenVPN DCO processes the packets in the kernel space.

      3. The data packets then get sent to their destination.

      安装似乎有些麻烦,我看到这个解说需要把key的属性设为root的才行:
      
      sudo mkdir -p /etc/apt/keyrings      # it already exists, but just in case 
      curl -sSfL https://packages.openvpn.net/packages-repo.gpg > openvpn.asc
      sudo mv openvpn.asc /etc/apt/keyrings/
      sudo chown root:root /etc/apt/keyrings/openvpn.asc 
      
      而这个配置文件是新的需求
      
      $ cat /etc/apt/sources.list.d/openvpn3.list 
      deb [signed-by=/etc/apt/keyrings/openvpn.asc] https://packages.openvpn.net/openvpn3/debian jammy main
      

    十二月十五日 等待变化等待机会

    1. 我都忘记实验一下元宝了。没时间了。
    2. openvpn的配置的确是有些复杂,这个傻瓜化的安装脚本是一个不错的入门选择,因为我之前按照文档发现都是比较老的版本,不再支持了。有一个非常好的学习机会是我终于找到ec2开防火墙的策略实践了一下,就是说在security group里增加一个规则,这个说来简单,但是我之前找了好久没有找到。真是惭愧。
    3. 当我按照这个脚本制作了新的openvpn的服务器之后再去连接发现不再有ping把谷歌导向脸书的问题了,这个看来是证明了之前的那个AMI是有问题的,因为这个脚本唯一下载的就是一个openvpn的easyrsa的小工具,无非就是利用openssl来制作证书生成密钥的简单的工作,而脚本仅仅是配置openvpn的服务端客户端,用的也不过就是原本的openvpn的服务,我注意到这里有一个我还不太明白的systemd的服务,就是加了@的服务,比如
      
      ubuntu@ip-172-31-29-227:~$ sudo systemctl status openvpn | grep Active --color=auto
           Active: active (exited) since Thu 2024-12-12 06:43:49 UTC; 2 days ago
      ubuntu@ip-172-31-29-227:~$ sudo systemctl status openvpn@server | grep Active --color=auto
           Active: active (running) since Sun 2024-12-15 03:22:43 UTC; 36min ago
      
      这个说明了什么?它启动了另外一个服务,而不是我本身自带的服务,这个脚本写的真的是不错,我决定下载保存作为学习的参考。
    1. 这个是一篇非常好的文章!不仅它是针对wireguard的dns的问题解决,更主要的是一种探究问题根源的思路和手段。
      1. 首先,我如何能够知道我的本地的域名解析服务在工作?这个长久就困扰我的问题: 这个是在我的aws的ubuntu服务器上显示我根本没有DNS服务在运行,而这个systemd-resolve是所谓的
        the ss command shows a DNS resolver running, this resolver is actually the systemd-resolved daemon listening on the server’s own loopback interface — which isn’t accessible to external connections
        
        $ sudo ss -punl sport :53
        State           Recv-Q          Send-Q                   Local Address:Port                   Peer Address:Port          Process          
        UNCONN          0               0                        127.0.0.53%lo:53                          0.0.0.0:*              users:(("systemd-resolve",pid=335,fd=13))
        
      2. 作者问了很多问题,就是是否需要使用wireguard专门的dns服务,这个问题对于很多国家的用户是不需要的。
    2. 我决定要架设自己的DNS服务,这个是一个指导如何设置bind9

    十二月二十四日 等待变化等待机会

    1. 依然处于一种黑暗的摸索中。我觉得应该先解决DNS在aws/ec2的配置问题,那么我想一个方法是比较openvpn和普通的ubuntu的配置的区别来理解。
    2. 意外的发现一些线索,按照aws/ec2的配置来理解。结果我发现我本地的配置总是要被openvpn放置在第一个以至于后面的都不能用了。
      
      $ tail /run/systemd/resolve/resolv.conf 
      #
      # See man:systemd-resolved.service(8) for details about the supported modes of
      # operation for /etc/resolv.conf.
      
      nameserver 172.31.0.2
      nameserver 218.85.157.99
      nameserver 8.8.4.4
      # Too many DNS servers configured, the following entries may be ignored.
      nameserver 8.8.8.8
      search .
      
      这里应该就是使用resolvectl来配置的吧?因为在/etc/systemd/resolved.conf里是没有配置的: The /etc/resolv.conf file is a symlink to the /run/systemd/resolve/stub-resolv.conf file. 而真正的配置文件/run/systemd/resolve/stub-resolv.conf 是这样配置的
      
      $ tail -n 3 /run/systemd/resolve/stub-resolv.conf
      nameserver 127.0.0.53
      options edns0 trust-ad
      search .
      
    3. 这里说
      Note: If you don't set the Domains=~. option, then systemd-resolved might use the per-link DNS servers set in the per-link configuration. The Domains=~. option doesn't affect queries of domain names that match the more specific search domains that are specified in per-link configurations. When domain names resolve, they use their respective per-link DNS servers.
      我想我的配置是search .也许说明了系统会按照网卡来分别搜索吧?我看到了一个网卡的设置:
      
      $ sudo cat /run/systemd/resolve/netif/2
      # This is private data. Do not parse.
      LLMNR=yes
      MDNS=no
      DEFAULT_ROUTE=yes
      SERVERS=218.85.157.99
      DOMAINS=~.
      
      就是说我的系统的internet provider(中国电信)的DNS配置是排他的设置了?
    4. 客户端的配置已经让我感到昏天黑地了,服务器端更加的糊涂了。不过之前我已经发现openvpn并不是通过修改ip routedefault因为这个修改是致命的,我依赖于原有的系统route,而是把自己放在第一位
      
      $ ip route
      0.0.0.0/1 via 172.27.232.1 dev tun0 
      default via 192.168.1.1 dev enp0s31f6 proto dhcp metric 100 
      34.199.45.31 via 192.168.1.1 dev enp0s31f6 
      128.0.0.0/1 via 172.27.232.1 dev tun0 
      169.254.0.0/16 dev enp0s31f6 scope link metric 1000 
      172.27.232.0/22 dev tun0 proto kernel scope link src 172.27.232.17 
      192.168.1.0/24 dev enp0s31f6 proto kernel scope link src 192.168.1.6 metric 100 
      
      这个route规则0.0.0.0/1 via 172.27.232.1 dev tun0 在default之前才能够导致我所有的请求都通过openvpn创建的tun0设备。我现在也知道了为什么ping谷歌转到脸书的直接原因了,因为这一条 34.199.45.31 via 192.168.1.1 dev enp0s31f6 因为
      
      $ dig +noall +authority 34.199.45.31
      .			86368	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2024122303 1800 900 604800 86400
      
      这个应该是openvpn自己加上的,因为我停掉openvpn之后ip route是没有这个的
      
      $ ip route
      default via 192.168.1.1 dev enp0s31f6 proto dhcp metric 100 
      169.254.0.0/16 dev enp0s31f6 scope link metric 1000 
      192.168.1.0/24 dev enp0s31f6 proto kernel scope link src 192.168.1.6 metric 100
      
      所以,openvpn这个启动脚本或者说它的那个openvpnas服务器的python脚本也许加了这个规则作为引流或者其他不可告人的目的的手脚这个是我的幻觉,因为这个34.199.45.31 是我的openvpnas的地址,这个并不是李代桃僵的facebook的代理地址,我错怪了openvpnas吧?。而这个也是我迫切想要wireguard来替代这个的直接原因。
    5. 下载这个Military and Security Developments Involving the People’s Republic of China(2024中国军力报告)。本地保留一个拷贝

    十二月二十七日 等待变化等待机会

    1. 我现在大概明白这个pyovpn是安装在这个目录/usr/local/openvpn_as/lib/python下吧?这个也许是opvenas的一部分?然后关于神秘的route的其实一点都不神秘,这个是完全意料之中的,因为我在我的openvpnas服务器端要求了配置文件里设置了
      
      $ sudo sacli ConfigQuery
      {
        "admin_ui.https.ip_address": "all",
        "admin_ui.https.port": "943",
        "aui.eula_version": "3",
        "auth.local.0.enable": "true",
        "auth.module.type": "local",
        "cs.https.ip_address": "all",
        "cs.https.port": "943",
        "host.name": "34.199.45.31",
        "subscription.bundle": "ewogICJub25jZSIgOiAiMkU1QzFGMzBGQTEwODM4REVFMzBCQjFGMDVBRUJDMUMzMTY2MkY3RkZFNEE2NzMxNjcxN0JEREI3NUQ3REM2RCIsCiAgInN1YmtleSIgOiAiQVNVVkpTRmZaUHBLU05LSGdUa1hNQUtfQVNSUHhwQmdObFhwUkFSVnV1cVNuaUxCdWNIZ2N6VmdfMWEyNGQ2NTAwMTc1NzVmZDA0M2U5YjUyMzUzMzA1NTNmN2M4NGVhOSIsCiAgImlhdCIgOiAxNzA0NzYxMzI2Cn0=",
        "subscription.saved_state": "SUBSCRIPTION_OK,tnYefo0Uo3KmKP+aXdloYB4zdWIKrMmICcKe/5Q2hqGqsOLULXObzMA/DH+Vu/AJO6CWd5b/ipJC\n/UcJuqUocDLzTA2zwPQo3Ays1NW2vgKuUligdSnpTb2cy9TKFzxg68qzp0aUR/V5O2T/cFiIH4PN\nXSjk3KK4gU34hYfghXMuVWE52wqJxldpLIITLn3zt9njEyUA4KA0vcnXZxeTdVzYmGn7qAZOOCmN\nfA5dXBvBc0HrOE8br7AZQtOj62zkp7g0kVxkCRTo5toE7Alddk/4fHm6cFAIR7y31wqrt0E4F4+Q\ni9qe6suyoTtxGQ1qK4ngx3dg6HrfEELnNzhAuGj+sTYiW974gjA/ZJSji0EvjVr0mUV9YwfQhxiw\nAMKQjJAr7P43ahxnsO2QXQdMxXQes3jm4d3U1LtSKF0YOrpo2CmJXOSVcx/AJxIoNp06F6ZK8vx4\n7C8vikrPbK4xP3zkc9E6mpCHQlZ29PCP56Zx0zKqPL6saqz8EMCwrEvW7LSHEoDuzuf5OiDCwfuZ\nwoameK4gr3m6d/6ek8Ar3sUjVBZFEA==\n",
        "upgrade.current_version": "2.13.1",
        "upgrade.initial_version": "2.13.1",
        "vpn.client.routing.inter_client": "true",
        "vpn.client.routing.reroute_dns": "true",
        "vpn.client.routing.reroute_gw": "true",
        "vpn.daemon.0.listen.ip_address": "all",
        "vpn.daemon.0.listen.port": "443",
        "vpn.daemon.0.server.ip_address": "all",
        "vpn.server.daemon.tcp.n_daemons": "2",
        "vpn.server.daemon.tcp.port": "443",
        "vpn.server.daemon.udp.n_daemons": "2",
        "vpn.server.enable_cipher_fallback": "true",
        "vpn.server.routing.allow_private_nets_to_clients": "true",
        "vpn.server.routing.private_access": "route",
        "vpn.server.routing.private_network.0": "172.31.0.0/16",
        "vpn.server.tls_cc_security": "tls-crypt"
      }
      
      
      谷歌的智能解释是这样子的:
      In an OpenVPN Access Server user configuration, "vpn.client.routing.reroute_dns" is a setting that determines whether the client's DNS requests will be routed through the VPN tunnel, essentially forcing all DNS lookups to use the DNS servers specified on the VPN server instead of the client's local DNS providers; setting it to "true" means all DNS will be rerouted through the VPN.

      Key points about "vpn.client.routing.reroute_dns":

      • Function: Controls whether DNS resolution is handled through the VPN tunnel.
      • Value options:
        • "true": All DNS requests will be sent through the VPN.
        • "false": DNS requests will use the client's local DNS settings.

      Why use "reroute_dns":

      • Privacy: By forcing DNS queries through the VPN, your actual IP address is masked when resolving domain names, enhancing privacy.
      • Security: If the VPN server is configured with trusted DNS providers, it can help protect against DNS hijacking or poisoning attacks.

      Important considerations:

      • Full tunnel vs. split tunneling: If you are using split tunneling, you may need to adjust the routing settings to ensure only specific traffic is routed through the VPN, including DNS requests if you want to utilize "reroute_dns".
      • Performance impact: Rerouting DNS can sometimes introduce slight latency increases depending on the VPN server location and network conditions.

      How to configure "reroute_dns" in OpenVPN Access Server:

      • Access the user configuration settings in the OpenVPN Access Server management interface.
      • Locate the "vpn.client.routing.reroute_dns" option and set it to "true" to enable DNS rerouting or "false" to disable it.
      它的效果是怎么样子的呢?
      
      $ dig www.google.com
      ;; communications error to 172.31.0.2#53: timed out
      ;; communications error to 172.31.0.2#53: timed out
      ;; communications error to 172.31.0.2#53: timed out
      ...
      
      随后出现的应该是DNS缓存的内容,那么这个说明了我本地的确是试图通过oepnvpnas服务器来做dns查询,但是服务器端没有人侦听,这个可以做验证,这个结果说明了openvpnas端没有配置dns服务。那么能不能说设置facebook代理的dns劫持不是openvpnas做的,而是我之前的dns缓存里的结果呢?
    2. 所以,我的计划是给openvpnas设置bind9服务,这样子看能不能解决李代桃僵的问题?吃晚饭再说吧?
    3. 这里是官方的说明文件可以设置某一个特定的用户的DNS,但是这个是高级功能,我不需要。让我们看看一般的设置。
    4. 这里讲的很深奥,我不太理解,不过似乎很有关系。
    5. bind9不允许recursion?我不明白为什么?但是至少可以在客户端使用+norec把这个警告WARNING: recursion requested but not available掩盖起来。
    6. bind9有一个误区吧?我最后只有设定为any才能让外来的dns请求被允许。
    7. 我观察到一个问题,就是我的网卡也是设置了domain '~.',这个可能会掩盖我的openvpn的设备设置,所以我加了一句:
      
      sudo resolvectl domain enp0s31f6 ''
      
      这样子在配置的基础上我删除了干扰的部分。
      
      #!/bin/bash
      
      sudo resolvectl flush-caches
      sudo resolvectl domain tun0 '~.'
      sudo resolvectl domain enp0s31f6 ''
      sudo resolvectl default-route tun0 true
      sudo resolvectl dns tun0 8.8.4.4 8.8.8.8
      
      此外我在服务器端添加了这两个属性:
      
      $ sudo sacli configquery | grep dns
        "vpn.client.routing.reroute_dns": "true",
        "vpn.server.dhcp_option.dns.0": "8.8.8.8",
        "vpn.server.dhcp_option.dns.1": "8.8.4.4",
      
      这个是否起作用我并不清楚。
    8. 在aws的服务器端我看到了多个设备:
      
      $ ifconfig | grep 'inet '
              inet 172.27.224.1  netmask 255.255.252.0  destination 172.27.224.1
              inet 172.27.228.1  netmask 255.255.252.0  destination 172.27.228.1
              inet 172.27.232.1  netmask 255.255.252.0  destination 172.27.232.1
              inet 172.27.236.1  netmask 255.255.252.0  destination 172.27.236.1
              inet 172.31.32.202  netmask 255.255.240.0  broadcast 172.31.47.255
              inet 127.0.0.1  netmask 255.0.0.0
      
      问题是我在vpn运行状态下智能访问其中一个设备的ip地址 172.27.224.1。而我本地的地址如下
      
      $ ifconfig | grep 'inet '
              inet 192.168.1.6  netmask 255.255.255.0  broadcast 192.168.1.255
              inet 127.0.0.1  netmask 255.0.0.0
              inet 172.27.232.5  netmask 255.255.252.0  destination 172.27.232.5
      
      后来我感觉这个是在web界面vpn settings定义的
      
      Dynamic IP Address Network
      When a user does not have a specific VPN IP address configured on the User Permissions page, the user's VPN client is assigned an address from this network.
      Network Address                                           # of Netmask bits
      172.27.224.0                                                  20
      
      我曾经试图修改这个值但是似乎不允许。但是不管怎么样子我证明了一件事情,那就是我现在设定了openvpnas服务器的bind9作为dns解析服务,然后我就可以在客户端使用它来做dns解析,比如
      
      $ dig @172.27.224.1 txt www.google.com
      
      ; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> @172.27.224.1 txt www.google.com
      ; (1 server found)
      ;; global options: +cmd
      ;; Got answer:
      ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10965
      ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
      
      ;; OPT PSEUDOSECTION:
      ; EDNS: version: 0, flags:; udp: 1232
      ; COOKIE: 64dd8d80cd67b61301000000676ebe603f34e337a9b17e49 (good)
      ;; QUESTION SECTION:
      ;www.google.com.			IN	TXT
      
      ;; AUTHORITY SECTION:
      google.com.		60	IN	SOA	ns1.google.com. dns-admin.google.com. 707854421 900 900 1800 60
      
      ;; Query time: 247 msec
      ;; SERVER: 172.27.224.1#53(172.27.224.1) (UDP)
      ;; WHEN: Fri Dec 27 22:49:04 +08 2024
      ;; MSG SIZE  rcvd: 121
      
      而可以证明的是如果我把服务器的bind9服务停掉$ sudo systemctl stop bind9,那么这个是不能得到结果的。
      
      ~$ dig  @172.27.224.1 txt www.google.com
      ;; communications error to 172.27.224.1#53: connection refused
      ;; communications error to 172.27.224.1#53: connection refused
      ;; communications error to 172.27.224.1#53: connection refused
      
      ; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> @172.27.224.1 txt www.google.com
      ; (1 server found)
      ;; global options: +cmd
      ;; no servers could be reached
      
      而这一点很重要,因为如果我得到的dns解析和ping得到的是一致的话,就不会有所谓的dns-leaking或者hijacking的问题了。
      
      $ ping -4 www.google.com
      PING  (142.250.31.104) 56(84) bytes of data.
      64 bytes from bj-in-f104.1e100.net (142.250.31.104): icmp_seq=1 ttl=57 time=235 ms
      64 bytes from bj-in-f104.1e100.net (142.250.31.104): icmp_seq=3 ttl=57 time=241 ms
      ...
      
      这个所谓i的bj-in-f104.1e100.net是真的谷歌,因为
      
      $ whois 1e100.net | grep -i google
         Name Server: NS1.GOOGLE.COM
         Name Server: NS2.GOOGLE.COM
         Name Server: NS3.GOOGLE.COM
         Name Server: NS4.GOOGLE.COM
      Registrant Organization: Google LLC
      Admin Organization: Google LLC
      Tech Organization: Google LLC
      Name Server: ns3.google.com
      Name Server: ns4.google.com
      Name Server: ns1.google.com
      Name Server: ns2.google.com
      
    9. 总而言之,我感觉被dns-hijack的我不应该怀疑openvpnas的可靠性,倒是应该问一下国内的dns服务器是否在做手脚,因为那个facebook的代理都是从dns-caches里提取的,说明了不是来自于vpn。
    10. 顺便说一下在aws上的dns设置是这样子的:
      
      $ resolvectl status | grep -i ' dns '
      Current DNS Server: 172.31.0.2
             DNS Servers: 172.31.0.2
              DNS Domain: ec2.internal
      
      而这个默认的dns服务器似乎是不能为外界访问的,但是我可以证明它是起作用的,这个几乎就是不言而喻的,我其实对于recursion不理解,甚至于bind9的forwarder也不理解,它是否依赖于这个dns还是直接使用我配置的8.8.8.8服务呢?
      
      $ dig +noall +stats @172.31.0.2 www.google.com
      ;; Query time: 1 msec
      ;; SERVER: 172.31.0.2#53(172.31.0.2) (UDP)
      ;; WHEN: Fri Dec 27 15:10:57 UTC 2024
      ;; MSG SIZE  rcvd: 139
      
      这里说的是你不能简单的使用ping来证明连通性,因为icmp被过滤了,是ping不到的。它只接受dns请求(53)。

    十二月二十九日 等待变化等待机会

    1. 现在我从头来过,就是在aws/ec2的ubuntu22.04先安装bind9,为了调试方便,需要配置log输出。 要在配置文件/etc/bind/named.conf.options里最后加上:
      
      logging {
      channel b_query {
      file "/var/log/named/query.log" versions 2 size 1m;
      print-time yes;
      severity info;
      };
      category queries { b_query; };
      };
      
      居然还要我自己手动来创建目录和log文件啊?
      
      sudo mkdir /var/log/named
      sudo chown bind:bind /var/log/named
      sudo touch /var/log/named/query.log
      sudo chown bind:bind /var/log/named/query.log
      
      这里也有一个小问题,服务是named.service,用户名是bind,我可以使用bind9查到服务:sudo systemctl status bind9,这个是搜索关键字吗?还是服务的别名?
      
      $ ll /etc/systemd/system/ | grep name
      lrwxrwxrwx  1 root root   33 Dec 19 08:08 bind9.service -> /lib/systemd/system/named.service
      
      的确是别名!

    十二月三十日 等待变化等待机会

    1. 我不知道在哪里设置了dns导致
      
      $ sudo resolvectl status
      Global
               Protocols: -LLMNR -mDNS DNSOverTLS=opportunistic DNSSEC=no/unsupported
        resolv.conf mode: foreign
      Current DNS Server: 10.0.0.1
             DNS Servers: 10.0.0.1
      
      这个要怎么删除呢?事实上这个在/etc/resolv.conf能看到,但是它是这个/run/resolvconf/resolv.conf的软链接:
      
      $ ll /etc/resolv.conf 
      lrwxrwxrwx 1 root root 29 Dec 18 09:23 /etc/resolv.conf -> ../run/resolvconf/resolv.conf
      
      我出于无奈只好手动去修改/run/resolvconf/resolv.conf,然后重启服务resolvconf。但是这个所谓的foreign是谁写入的呢?这个resolvconf实际上是一个脚本,看的我也头疼。
    2. 我刚刚想起来有一个关于openvpn的启动脚本的问题,它需要安装一个包openvpn-systemd-resolved,它自带的两个启动/关闭脚本/etc/openvpn/update-systemd-resolved似乎是有作用的。
    3. 看来只能深入debug wireguard,这就需要使用tcpdump

    Smiley face