无垠之码

深度剖析代码之道


现代编译构建工具-Ninja语言

在传统的C/C++项目中,通常采用make系统,使用Makefile约束进行整个项目的编译构建。Makefile指定的编译依赖规则会使编译流程简单,但是make的依赖大而且复杂,在大型项目编译时,使用的模块越来越多,Makefile组织的项目编译时间越来越长,这个对于编译效率来说是一个极大的浪费。在执行增量或无操作时Make相较于Ninja构建时很慢,特别诸如Google Chrome这种将40,000个输入文件编译为单个可执行文件的工程,这可能会大大降低开发人员在大型项目上的工作速度。

根据Chromium的实际测试:在超过30,000个源文件的情况下,Ninja也能够在1秒钟内开始进行真正的构建。与之相比,通过资深工程师进行编写的Makefiles文件也需要10-20秒才能开始构建。

Ninja是由Google员工Evan Martin开发的小型构建系统。Ninja注重速度,Ninja被设计为使其输入文件由更高级别的构建系统生成,并且其被设计为尽可能快地运行构建。同时与Make相比,Ninja缺少诸如字符串操作比较、隐式规则、函数和遍历搜索等的功能,因为Ninja生成文件不需要手工编写。相反,应使用"生成器"生成Ninja生成文件。CMake,Meson,Gyp(Google早期用来维护chromium项目的构建系统,GN则是用来替代GYP的工具)和Gn是流行的构建管理软件工具,这些更高层次的工具,支持生成Ninja文件构建工程项目。

Ninja特有功能:

  1. Ninja特别支持在构建时发现额外的依赖项,从而可以轻松地为C/C++代码获取正确的头文件
  2. 更改编译标志将导致输出重新生成,因为输出隐式依赖于用于生成它们的命令行(等等,啥意思?)
  3. 规则可以提供正在运行的命令的简短描述,因此您可以在构建时打印例如CC foo.o而不是长命令行
  4. 构建工作始终并行运行。默认情况下,根据系统具有的CPU核数决定并行执行的线程数
  5. 命令输出始终是缓冲的。这意味着并行运行的命令不会交错其输出,当命令失败时可以将其失败输出打印在产生故障的完整命令行旁边

疑问: 特有功能1中,gcc -M 输出用于make系统的规则,该规则描述了源文件的依赖关系,makefile也可以轻松地为C/C++代码获取正确的头文件(见文章Gcc部分参数说明)

接下来将通过具体示例,探索GN、Meson和CMake这些现代构建工具如何生成Ninja文件,并分析它们在实际项目中的应用优势。

0.GN


GN是专为生成Ninja文件而设计的,运行速度极快,尤其适合大型项目,已在Chromium项目验证。其语法简单直接,避免了复杂的逻辑处理,专注于配置构建规则并且内置对依赖项的高效处理和验证机制,减少了构建错误,能够很好地应对超大规模代码库。但是其由Google内部主导开发,社区生态和扩展能力并没有Cmake和Meson好,并且缺少某些高级特性,如内置的包管理和复杂的自定义逻辑支持。

gn把.gn文件转换成.ninja文件,然后Ninja根据.ninja文件将源码生成目标程序

GN工具安装

apt-get install -y ninja-build
git clone https://gn.googlesource.com/gn
cd gn && python3 build/gen.py --allow-warning
out/gn_unittests

GN构建样例

gn-sample.tar.gz是关于gn构建的入门样例

tree -a
.
├── build
│   ├── BUILDCONFIG.gn
│   └── config
│       └── toolchains
│           └── BUILD.gn
├── BUILD.gn
├── .gn
└── src
    ├── BUILD.gn
    ├── main.c
    ├── tools.c
    └── tools.h
  1. .gn文件: gn命令执行时最先加载的配置文件,.gn置于根目录,buildconfig参数指定构建配置文件位置. 例子中指定的是//build/BUILDCONFIG.gn
  2. build/BUILDCONFIG.gn: 针对项目的一些设置包括不同操作系统的设置,默认编译工具的设置,例子中通过set_default_toolchain,指定//build/config/toolchains:gcc
  3. build/config/toolchains/BUILD.gn: 这个是针对工具链的一些设置
  4. BUILD.gn: 一般进行配置的文件,主要是配置需要编译的文件,库,最终的可执行文件,本例中依赖子目录src中的a.out目标
buildconfig = "//build/BUILDCONFIG.gn"
group("default") {
  deps = [ "//src:a.out" ]
}
set_default_toolchain("//build/config/toolchains:gcc")
import("//build/config/component.gni")  // 允许使用component模板
declare_args() {
  is_debug = false
  is_component_build = false   // 配置[静态编译,动态编译];如果为true的话,就全部是动态库,否则就是静态库
  is_mac = false
}
executable("a.out") {
  sources = [
    "main.c",
  ]
  include_dirs = [
    ".",
  ]
  deps = [
    ":tools",
  ]
  libs = [ "json-c" ]
  if (is_debug) {
    cflags = [ "-g", "-O0" ]
  }

shared_library("tools") {
  sources = [
    "tools.c",
  ]
  if (is_debug) {
    cflags = [ "-g", "-O0" ]
  }

component("test") {
  sources = [ "tools.c" ]
  if (is_debug) {
    cflags = [ "-g", "-O0" ]
  }
}
toolchain("gcc") {
  tool("cc") {
    depfile = "{{output}}.d"
    command = "gcc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -{{output}}"
    depsformat = "gcc"
    description = "CC {{output}}"
    outputs =
        [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
  }
  tool("cxx") {
    depfile = "{{output}}.d"
    command = "g++ -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -{{output}}"
    depsformat = "gcc"
    description = "CXX {{output}}"
    outputs =
        [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
  }
  tool("alink") {
    command = "ar rcs {{output}} {{inputs}}"
    description = "AR {{target_output_name}}{{output_extension}}"
    outputs =
        [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
    default_output_extension = ".a"
    output_prefix = "lib"
  }
  tool("solink") {
    soname = "{{target_output_name}}{{output_extension}}"
    sofile = "{{output_dir}}/$soname"
    rspfile = soname + ".rsp"
    if (is_mac) {
      os_specific_option = "-install_name @executable_path/$sofile"
      rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
    } else {
      os_specific_option = "-Wl,-soname=$soname"
      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
    }
    command = "g++ -shared {{ldflags}} -o $sofile $os_specific_option @$rspfile"
    description = "SOLINK $soname"
    default_output_extension = ".so"
    default_output_dir = "{{root_out_dir}}"
    outputs = [ sofile ]
    link_output = sofile
    depend_output = sofile
    output_prefix = "lib"
  }
  tool("link") {
    outfile = "{{target_output_name}}{{output_extension}}"
    rspfile = "$outfile.rsp"
    if (is_mac) {
      command = "g++ {{ldflags}} -o $outfile @$rspfile {{solibs}} {{libs}}"
    } else {
      command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
    }
    description = "LINK $outfile"
    default_output_dir = "{{root_out_dir}}"
    rspfile_content = "{{inputs}}"
    outputs = [ outfile ]
  }
  tool("stamp") {
    command = "touch {{output}}"
    description = "STAMP {{output}}"
  }
  tool("copy") {
    command = "cp -af {{source}} {{output}}"
    description = "COPY {{source}} {{output}}"
  }
}
template("component") {
  if (is_component_build) {
    _component_mode = "shared_library
    if (!defined(invoker.output_name)) {
      _output_name = get_label_info(":$target_name", "label_no_toolchain")
      _output_name = string_replace(_output_name, "$target_name:$target_name", target_name)
      _output_name = string_replace(_output_name, "//", "")
      _output_name = string_replace(_output_name, "/", "_")
      _output_name = string_replace(_output_name, ":", "_")
    }
  } else if (defined(invoker.static_component_type)) {
    assert(invoker.static_component_type == "static_library" || invoker.static_component_type == "source_set")
    _component_mode = invoker.static_component_type
  } else if (!defined(invoker.sources) || invoker.sources == []) {
    _component_mode = "source_set"
  } else {
    _component_mode = "static_library"
  }
  target(_component_mode, target_name) {
    if (defined(_output_name)) {
      output_name = _output_name
    }
    if (is_component_build) {
      output_extension = "so"
    }
    forward_variables_from(invoker, ["testonly", "visibility",])
    forward_variables_from(invoker, "*", ["testonly", "visibility",])
  }
}

GN相关概念

1.GN命令

Read more...

cicd-持续集成与持续交付

在快节奏的数字化时代,软件交付的速度和质量直接决定了企业的竞争力。传统的开发模式中,手动构建、测试和部署不仅效率低下,还容易引入人为错误,导致交付延迟或线上故障。持续集成与持续交付/部署的出现,彻底改变了这一局面。其通过自动化构建、测试和部署流程,让开发团队能够快速、安全、可靠地交付软件。无论是小型创业公司还是大型企业,采用CI/CD都能显著提升开发效率,减少人为失误,并加快产品迭代速度

0.CI/CD概况


CI/CD是敏捷开发和精益理念在DevOps中的实践,是一种以持续集成/交付/部署为手段,实现高效、安全、快速的软件交付方式

  • CI-Continuous Integration:持续集成是在源代码变更后自动检测、拉取、构建和在大多数情况下进行单元测试的过程
  • CD-Continuous Delivery和Continuous Deployment:持续交付和持续部署,持续交付,完成ci中构建及单元测试和集成测试的自动化流程后,可自动将已验证的代码发布到服务器,持续部署,意味着所有的变更都会被自动部署到生产环境中,当然出于业务考虑,可以选择不部署

补充概念:

敏捷开发是一种强调快速响应变化与持续交付价值的软件开发方法论,强调人际互动、快速和频繁的交付、增量价值交付、分散价值、客户协作反馈循环和持续改进

敏捷开发方法框架是指一组具体的流程、规则、角色和工具集合,用来指导团队按照敏捷原则进行软件开发和项目管理。它是敏捷开发理念的具体落地形式,其中包括Scrum,Kanban,Lean,LeSS,SAFe,Jira原生支持Scrum和Kanban,也可通过插件支持其他的敏捷开发框架

  1. Scrum是一种敏捷开发方法框架,专门用来管理复杂项目中的软件开发过程。它提供了一套明确的角色、事件和工件(工作产出),帮助团队在短周期内不断迭代、持续交付可用的软件
  2. Kanban是一种可视化、渐进式的工作管理方法,起源于丰田生产系统(TPS),后来被引入软件开发及知识工作领域,成为敏捷开发的重要实践之一。它强调持续改进(Kaizen)、限制在制品(WIP)和优化工作流程,帮助团队提高效率并减少浪费

image

工具选择

要具体落实CI/CD开发模式,通常需要在代码托管、持续集成、持续部署、构建发布、环境管理等各个环节,分别选择合适的工具组合进行实施。

  1. 代码托管

    代码托管工具五花八门,比如GitHub、GitLab、Gitea、Bitbucket、SVN等,并且其中很多现代代码托管平台如GitLab、GitHub不仅仅是代码仓库,还内置了CI/CD功能,提供一体化的DevOps支持

  2. 持续集成

    • Jenkins image
      • 优点:
        1. web界面管理,社区强大
        2. 插件丰富,文档丰富
        3. 历史久远(Hudson Java编写2005发布->Sun收购),应用广泛
        4. 分布式任务,功能强大
      • 缺点
        1. 插件及自生安装复杂
        2. 启动慢,运行慢,资源消耗高
        3. 插件编写语言只支持Groovy和Java
        4. 学习成本高,专人维护
    • Github Action
      • 优点:
        1. 轻量级,启动快,资源占用少,运行快
        2. 云原生支持docker,新兴CI/CD工具(Github 2019)
        3. 编写yaml文件,Pipeline语法比Jenkins-File语法简单
        4. 本机直接测试Pipeline是否正确
        5. 插件编写简单
      • 缺点:
        1. 插件有限, 需要公网环境
        2. 大型项目复杂的工作流程, 不如Jenkins
    • Drone image
      • 优点:
        1. 云原生设计,轻量易部署,适合K8s和GitOps场景
        2. Gitea/GitLab等集成良好
        3. YAML编排简单直观,插件机制灵活
        4. 支持Secrets管理、触发器等CI常见功能
      • 缺点:
        1. 文档较简略,相比Jenkins/GitLab CI
        2. UI功能简单,权限管理能力较弱
    • Travis-ci
    • GitLab
    • Gitea
  3. 持续部署

    持续部署工作通常利用持续集成平台的相关插件完成,插件帮助自动化地将代码部署到不同环境

    • drone的插件市场https://plugins.drone.io/,列出其支持的部署插件,及其详细用法
    • jenkins内置的插件管理界面,可以安装最新的持续部署相关插件
    • github市场的插件更为丰富https://github.com/marketplace

1.应用案例


Gitea案例

Gitea工作流

Gitea Actions的配置文件叫做workflow文件,存放在代码仓库的.gitea/workflows目录。workflow文件采用YAML格式,文件名可以任意取,但是后缀名统一为.yml。一个库可以有多个workflow文件。Gitea只要发现.gitea/workflows目录里面有.yml文件,就会自动运行该文件。Gitea Actions的语法与GitHub Actions以及Drone Pipeline高度兼容,因此可以直接参考GitHub的工作流规范来编写Gitea的工作流。

Read more...

般若波罗蜜多心经

译文:

观自在菩萨,行深般若波罗蜜多时,照见五蕴皆空,度一切苦厄。舍利子,色不异空,空不异色;色即是空,空即是色。受、想、行、识,亦复如是。舍利子,是诸法空相,不生不灭,不垢不净,不增不减,是故空中无色,无受、想、行、识;无眼、耳、鼻、舌、身、意;无色、声、香、味、触、法;无眼界,乃至无意识界;无无明,亦无无明尽;乃至无老死,亦无老死尽。无苦、集、灭、道,无智亦无得。以无所得故,菩提萨埵,依般若波罗蜜多故,心无罣碍。无罣碍故,无有恐怖,远离颠倒梦想,究竟涅槃。三世诸佛,依般若波罗蜜多故,得阿耨多罗三藐三菩提。故知般若波罗蜜多,是大神咒,是大明咒,是无上咒,是无等等咒,能除一切苦,真实不虚。故说般若波罗蜜多咒,即说咒曰:“揭谛、揭谛,波罗揭谛,波罗僧揭谛,菩提萨婆诃。”

梵语:

आर्यावलोकितेश्वरो बोधिसत्त्वो गंभीरायां प्रज्ञापारमितायां चर्यां चरमाणो व्यवलोकयति स्म । पंचस्कन्धाः । तांश्च स्वभावशून्यान्पश्यति स्म । इह शारिपुत्र रूपं शून्यता शून्यतैव रूपं रूपान्न पृथक्शून्यता शून्यताया न पृथग्रूपं यद्रूपं सा शून्यता या शून्यता तद्रूपं । एवमेव वेदनासंज्ञासंस्कारविज्ञानानि । इह शारिपुत्र सर्वधर्माः शून्यतालक्षणा अनुत्पन्ना अनिरुद्धा अमला न विमला नोना न परिपूर्णाः । तस्माच्छारिपुत्र शून्यतायां न रूपं न वेदना न संज्ञा न संस्कारा न विज्ञानानि । न चक्षुःश्रोत्रघ्राणजिह्वाकायमनांसी । न रूपशब्दगंधरसस्प्रष्टव्यधर्माः । न चक्षुर्धातुर्यावन्न मनोविज्ञानधातुः । न विद्या नाविद्या न विद्याक्षयो नाविद्याक्षयो यावन्न जरामरणं न जरामरणक्षयो न दुःखसमुदयनिरोधमार्गा न ज्ञानं न प्राप्तिः ॥ तस्मादप्राप्तित्वाद्बोधिसत्त्वाणां प्रज्ञापारमितामाश्रित्य विहरत्यचित्तावरणः । चित्तावरणनास्तित्वादत्रस्तो विपर्यासातिक्रान्तो निष्ठनिर्वाणः ।। त्र्यध्वव्यवस्थिताः सर्वबुद्धाः प्रज्ञापारमितामाश्रित्यानुत्तरां सम्यक्सम्बोधिमभिसंबुद्धाः ।। तस्माज्ज्ञातव्यं प्रज्ञापारमिता महामन्त्रो महाविद्यामन्त्रो ऽनुत्तरमन्त्रो ऽसमसममन्त्रः सर्वदुःखप्रशमनः । सत्यममिथ्यत्वात् । प्रज्ञपारमितायामुक्तो मन्त्रः । तद्यथा गते गते पारगते पारसंगते बोधि स्वाहा ।। इति प्रज्ञापारमिताहृदयं समाप्तम्

Read more...

terraform-从入门到放弃

注: 本文只简单的介绍Terraform的安装以及基本使用,文章基本可看作对Terraform官方文档的翻译以及部分自我理解。

Terraform旨在任何云以及数据中心上可以自动化的配置和管理资源,Terraform将云API或数据中心接口编码为声明性配置文件,实现基础设施即代码。(Infrastructure as Code,简称IaC,是一种管理和提供计算基础设施的方式,利用代码来自动化基础设施的部署和管理)

网站中描述Terraform的几点优势:

  • 使用HCL(HashiCorp Configuration Language)在Terraform文件中将基础设施编写为代码,以便从任何基础设施提供商处调配资源。
  • 构建基础设施自动化工作流程,以跨IT运营和开发团队编写、协作、重用和配置基础设施作为代码。
  • 通过基于角色的访问控制、策略实施和审计,为安全性、合规性和成本管理建立护栏,实现标准化。
  • 使用自助服务和基础设施作为代码将工作流自动化扩展到组织中的所有团队,并与VCS、ITSM(IT服务管理,如ServiceNow、Jira)和CI/CD集成。

使用场景:

  • IaC,使用Terraform可以将多云管理编码为声明书代码,Terraform提供的插件生态使得可以使用统一、易于学习的配置语言来定义资源,代码被编码、共享、版本化,并且通过一致的工作流在所有环境中执行。
  • 多云部署,Terraform采用单一的自动化工作流来管理多个基础设施和SaaS提供商,并处理跨云依赖关系,简化多云基础设施的生命周期管理和编排,适用于任何规模的环境。
  • Terraform支持K8S,可以通过单一工作流程配置Kubernetes集群、周边服务和应用程序资源。
  • 采用Consul-Terraform-Sync的网络基础设施自动化(NIA)可确保网络和安全基础设施安全地适应变化。Consul-Terraform-Sync根据Consul观察到的变化并通过Terraform进行管理,自动执行各种网络任务和工作流。这些任务可以由不同的事件触发,例如服务实例的扩展、服务地址或端口号的更改、服务标签、元数据或健康状况的更新等。这可以缩短交付时间并大大降低配置错误的可能性。
  • HCP Terraform提供灵活的远程运行环境,可以轻松集成到现有的版本控制系统、CI/CD管道和IT服务管理界面中。这种与现有工作流的深度集成,减少了工具更改的需求,简化了启动和运行的过程,同时确保了平台团队和开发人员的工作一致性。
  • HCP Terraform可以帮助您对团队可部署的基础设施配置实施策略。基于工单的审查流程通常会成为瓶颈,导致开发速度变慢。相反,您可以使用HashiCorp Sentinel(一种策略即代码框架),在Terraform进行基础设施变更之前,自动实施合规性和治理策略。
  • HashiCorp Vault将每个动态密钥与租约(lease)关联,并在租约到期时自动销毁这些凭据。Vault支持与多种系统集成的动态密钥,并且可以通过插件轻松扩展功能。
  • HCP Packer是用于云镜像及云镜像版本跟踪管理,并通过API提供这些信息。通过将常用的基础镜像可以被标准化、安全化,并通过自动化进行更新。

0.Terraform安装


这里只粘贴ubuntu系统的安装方法,Terraform也支持windows,mac,freebsd甚至solaris系统的安装,其他的方法参见文献1。

wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

1.入门样例


把大象放进冰箱一般情况只需要三个步骤,同样Terraform工作流核心包括三个阶段:

  1. 写下你的梦想。使用HCL语言编写声明式配置文件定义资源,资源可跨越多个云平台或数据中心。
  2. 规划。创建一个执行计划,描述根据现有基础设施和定义的配置来创建、更新或销毁的基础设施。
  3. 让梦想成真。Terraform按照预订的顺序执行相关操作,帮您圆梦。

image

啊,上帝赐给我一个像朱颖一样漂亮的女朋友

什么?定义她?哦,她姓朱,瓜子脸…哦,对了她坐我斜对面…

HCL可以被视为一种领域特定语言(DSL)。它被设计用于描述基础设施资源的配置和管理,特别是与HashiCorp的工具(如Terraform)一起使用时。

resource "aws_vpc" "main" {
  cidr_block = var.base_cidr_block
}

<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
  # Block body
  <IDENTIFIER> = <EXPRESSION> # Argument
}

块是用于包含其他内容的容器,通常表示某种对象的配置,例如资源。块有块类型,可以有零个或多个标签,并且有一个包含多个参数和嵌套块的主体。大多数Terraform的功能是通过配置文件中的顶级块来控制的。 IDENTIFIER表示参数名,它们出现在块内。EXPRESSION表示一个值,可以是字面值,也可以通过引用和组合其他值来得到。

Read more...

科学上网-去你妈的gfw

0.代理方案


代理是一种网络技术,允许用户或应用通过中间服务器(代理服务器)与目标服务器进行通信,从而实现数据传输的控制、过滤、加速等功能。普通的HTTP/HTTPS代理、SOCKS代理、VPN等传统代理通常不专门设计来对抗防火墙和审查系统,其功能主要是聚焦于流量转发、访问控制、加密通信等。专用于GFW的代理在设计时就考虑到深度包检查、流量分析和阻断,其通过强加密、流量伪装与混淆等方式对抗网络封锁,一般都会采用客户端服务器模式

1.NavieProxy

NaïveProxy是一个基于Chromium网络栈(net 模块)开发的轻量级代理工具,其核心设计目标是模拟普通HTTPS流量,以规避深度包检测和流量审查。其客户端部分使用github-action构建项目,服务端使用Caddy充当代理服务器。

[Browser → Naïve client] ⟶ Censor ⟶ [Frontend → Naïve server] ⟶ Internet

代理过程中客户端浏览器的HTTPS请求被Naïve客户端接收(其用一个伪造的证书(如自动申请的免费证书)来和客户端完成TLS握手,拿到明文数据),随后客户端把明文包装成自己的HTTPS请求,再通过另一个 TLS连接发往后端真实代理服务器,代理服务器收到再解密、还原原始请求,再访问真实目标网站,数据的返程封装与请求过程一致。

安装

二进制安装,https://github.com/klzgrad/forwardproxy/releases中选择对应平台的二进制进程直接绿色安装

配置

服务器配置:
{
    order forward_proxy before file_server
}
:443, hostdare.diyao.me {
    tls hostdare@diyao.me
    forward_proxy {
        basic_auth diyao cauc.peter@gmail.com
        hide_ip
        hide_via
        probe_resistance
    }
    file_server {
        root /var/www/html
    }
}

客户端配置:
{
    "listen": "socks://127.0.0.1:1080",
    "proxy": "https://diyao:cauc.peter@gmail.com@hostdare.diyao.me"
}

2.Tuic

Tuic(全称为TUIC - Tailscale UDP In Congestion control)是一个现代、高性能、低延迟的UDP传输协议,专为代理和绕过网络限制场景而设计,尤其适合替代传统的TCP/TLS + HTTP/HTTPS代理技术。它本质上是一个基于QUIC协议的应用层代理协议。QUIC内建的多路复用,加密(TLS 1.3),快速握手,自带拥塞控制和重传机制,使得Tuic天生就具备低延迟、无HOL(头阻塞)和NAT穿透能力强等优势

Read more...

你会打补丁吗?

上个月揶揄室友内裤上打了个补丁,他嬉皮笑脸的跟我说,“这有啥,新三年旧三年缝缝补补又三年”。“缝缝补补又三年"这句俏皮话放在程序员的世界里,也再合适不过。代码世界中,打补丁不仅是一种日常操作,更是一种技术艺术。就像给衣服补上一个精致的补丁可以延续它的寿命,代码补丁(patch)则是为了修复漏洞、优化性能或引入新功能,使系统在不断演进中保持稳定和高效。

根据补丁所作用的对象,这里简单地将其分为源码补丁和二进制补丁,前者针对软件的源代码,后者则针对编译后的二进制文件。

0.源码补丁


源码补丁通常以文本形式存在,描述了原始代码和修改后代码之间的差异,常用的格式是diff或unified diff,以.diff或.patch文件保存。由于是文本文件,开发者可以直观地查看修改内容,可读性高,同时便于审查和共享代码更改。

下面介绍几种目前业界主流的源码补丁工具或方案。

Quilt

Quilt是一个用于管理大量补丁的工具,通过跟踪每个补丁所做的更改来管理补丁集。补丁可以被应用、撤销、更新等。其核心理念是,你的主要输出是补丁。

quilt --help

Usage: quilt [--trace[=verbose]] [--quiltrc=XX] command [-h] ...
       quilt --version
Commands are:
        add       fold    mail      refresh  snapshot
        annotate  fork    new       remove   top
        applied   graph   next      rename   unapplied
        delete    grep    patches   revert   upgrade
        diff      header  pop       series
        edit      import  previous  setup
        files     init    push      shell

Global options:

--trace
        Runs the command in bash trace mode (-x). For internal debugging.

--quiltrc file
        Use the specified configuration file instead of ~/.quiltrc (or
        /etc/quilt.quiltrc if ~/.quiltrc does not exist).  See the pdf
        documentation for details about its possible contents.  The
        special value "-" causes quilt not to read any configuration
        file.

--version
        Print the version number and exit immediately.

使用样例

Read more...

qemu虚拟机构建

目前构建qemu虚拟机常见有: 下载成品镜像|源码编译内核和根文件系统|光盘安装 三种方式, 以下分别通过构建 riscv64|aarch64|sparc64 虚拟机介绍三种Qemu虚拟机构建方式

0.构建riscv64虚拟机


一些Linux发行版提供了成品虚拟机镜像,Qemu用户可以直接加载使用。例如,Ubuntu提供了多个架构的preinstalled-server镜像,可从cdimage.ubuntu.com下载,经过解压和简单配置即可启动。

sudo apt-get install u-boot-qemu opensbi qemu-system qemu
wget https://cdimage.ubuntu.com/releases/20.04.2/release/ubuntu-20.04.2-preinstalled-server-riscv64.img.xz
xz -dk ubuntu-20.04.2-preinstalled-server-riscv64.img.xz 
qemu-img resize -f raw ubuntu-20.04.2-preinstalled-server-riscv64.img +5G
sudo qemu-system-riscv64 \
        -machine virt \
        --nographic \
        -m 2G \
        -smp 2 \
        -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf \
        -kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
        -device virtio-net-device,netdev=eth0 \
        -netdev user,id=eth0,hostfwd=tcp::2222-:22 \
        -drive file=ubuntu-20.04.2-preinstalled-server-riscv64.img,format=raw,if=virtio
        # -writeconfig qemu.conf
sudo qemu-system-riscv64 -readconfig qemu.conf

首次登录用户名密码:ubuntu/ubuntu

1.构建aarch64虚拟机


备忘: ARMv8是继ARMv7之后的一次重要架构升级,其主要变化之一是引入64位架构。在此之前,ARM的产品均为32位架构。具体来说,ARM架构分为以下几种浮点运算模式:ARMEL为32位软浮点,ARMHF为32位硬浮点,而 ARM64(Apple的命名方式)或AArch64(GNU/GCC 的命名方式)为64位硬浮点。(32位和64位架构,并不是单纯指总线宽度,而是指寄存器宽度和指令集的宽度)

sudo qemu-system-aarch64 \
        -machine virt \
        -cpu max \
        --nographic \
        -kernel ./image \
        -nic none \
        -device virtio-net-device,netdev=eth0 \
        -netdev user,id=eth0,hostfwd=tcp::2222-:22 \
        -hda ./rootfs.qcow2 \
        -append 'root=/dev/vda rw nokaslr console=ttyS0'

本小节介绍DIY方式制作一个Qemu虚拟机: step0.源码编译内核 step1.根文件系统 step2.内核模块安装

Read more...

槟榔+烟

注: 本文大部分内容来源于flex和bison官方网站文档,如需系统学习直接阅读官方手册更合适

1.槟榔(flex)


官方手册定义flex为scanner生成工具(scanner指文本的词法分析程序)。flex从后缀.lex文件或标准输入中读取关于词法分析程序的定义(正则表达式,c语言片段)生成scanner的源码(默认lex.yy.c)。scanner的源码可以编译或链接至可执行文件中,运行该可执行文件时,它会分析输入内容中是否出现指定的正则表达式,当找到匹配的表达式时,执行词法分析器中预设的c代码片段。

槟榔的初体验

通过简单的文本字符和行数统计程序,感受槟榔的浓烈辛香。

  1. flex demo.lex编译生成scanner的源,默认lex.yy.c文件
  2. 运行gcc -g -O0 lex.yy.c编译生成文本词法分析程序a.out
  3. 执行程序./a.out后,按Ctrl+D结束输入或使用echo 'hello,world\r\n' | ./a.out运行程序
%option noyywrap

%{
int num_lines = 0, num_chars = 0;
%}

%%
\n ++num_lines; ++num_chars;
. ++num_chars;
%%

int main() {
  yylex();
  printf("# of lines = %d, # of chars = %d\n", num_lines, num_chars);
}

涩口的邀请,麻辣的回响

flex程序分为由%%分割的三部分组成:

1.定义部分

定义部分包含简单名称定义的声明,用于简化scanner规则部分的代码,以及起始条件的声明。名称定义使用name definition的格式,其中,name是一个以字母或下划线开头,后跟零个或多个字母、数字、下划线或短横线的单词。definition是从名称后第一个非空白字符开始,直到行尾的内容。规则定义可以通过{name} 的形式引用,并将展开为(definition)

DIGIT    [0-9]                   // DIGIT定义为匹配单个数字的正则表达式
ID       [a-z][a-z0-9]*          // ID定义为匹配一个字母后跟零个或多个字母或数字的正则表达式

%top {
/* 此代码会出现在生成文件的顶部。 */
  #include <stdint.h>
  #include <inttypes.h>
}

%%
/*
 * 展开:([0-9])+"."([0-9])*  *
 */

{DIGIT}+"."{DIGIT}*              // 匹配一个或多个数字,后跟一个.,然后是零个或多个数字

未缩进的注释(即以/*开头的行)会被原样复制到输出中,直到遇到下一个*/。任何缩进的文本或由%{和%}包围的文本也会被原样复制到输出中会移除%{和%}符号)。%{和%} 必须单独占据一行且不能缩进。%top块与%{ … %}块类似,但%top块中的代码会被移动到生成文件的顶部,在任何定义之前。%top块适用于需要定义某些预处理器宏或在生成代码前包含某些文件的情况。允许使用多个%top块,且它们的顺序会被保留。

Read more...

jq工具-菜刀在手天下我有

jq命令是一款功能强大的命令行json解析工具。支持诸如: 选择,迭代,归约(reduce)等各种操作方式去处理json文档。jq也可以接受文本输入,但默认情况下,jq从标准输入(stdin)读取一系列json实体(包括数字和其他字面量)。可以将一个jq程序看作一个过滤器,处理输入数据,按照规则过滤数据,产生输出数据。jq内置许多标准的过滤器,可以过滤json对象的特定字段,数据类型转换,或其他各种标准任务。各个filter之间支持级联,也支持将多个filter的输出归并至一个数组中。

基本用法


jq命令基本形式: jq + 过滤规则 + json文件或json字符串。

  • 文件读取,jq . test.json或者jq . -f test.json
  • 标准输入,echo '{"prices":[1,2]}' | jq .

常用参数

  • 参数–slurp|-s: 当输入内容是多个json对象时,将多个json对象视为1个json数组
  • 参数-n: 不读取任何内容,使用 jq 构造数据时可以使用
  • 参数-c: 表示紧凑的打印输出内容
  • 参数–color-output|-C: 强制高亮显示,默认只在终端输出才会高亮显示
  • 参数-e|–exit-status: 根据获取到的结果决定退出码是多少
  • 参数–tab: 使用tab作为缩进符,而不是使用2个空格符号
  • 参数–indent: 使用指定数量的空格符作为缩进符
  • 参数–arg: 输入key-value参数,在过滤表达式中引用($key)
  • 参数–args: 位置参数,在过滤表达式中引用($ARGS.positional[])
  • 参数–argjson: 与arg类似,value接受一个json字符串
  • 参数–jsonargs: value接受一个json数组,在过滤表达式中引用($ARGS.positional[])
  • 参数–slurpfile: 形式key-file,将指定文件读入保存在指定的变量

特殊符号

句号(.)作为最特殊的过滤器,作用是将输入的json字符串原样输出。逗号(,)的作用是将两个过滤器的结果连接起来。被逗号分隔的两个过滤器会从相同的输入数据中生成各自的输出值流,然后依次将这两个过滤器的输出结果串联起来:先输出左侧表达式的所有结果,然后输出右侧表达式的所有结果。管道(|)的作用与shell中的管道作用一致,连接两个过滤器,将前一个过滤器的结果作为后者的输入。括号(())的作用与大多数编程语言中的括号类似,用作分组操作符。$ENV可以用来在过滤器中引用环境变量

基本运算符

jq支持常见的算数运算符号,+-*/%的同时也支持逻辑运算符号and|or|not

  1. +, echo '{"a": 7}' | jq '.a + 1'
  2. -, echo '["xml", "yaml", "json"]' | jq '. - ["xml", "yaml"]'
  3. */%, jq -n '3/2*5%2'
  4. and,jq '(true, true) and (true, false)'
  5. or,jq -n '(true, false) or false'
  6. not,jq -n '[true, false | not]'
  7. 切片,echo '[1,2,3,4,5,6,7,8,9]' | jq '.[1:5]'

类型构造

使用当前的json字符串构造生成新的符合要求的json对象也是常见使用场景

Read more...

链接脚本-我的地盘我做主

基本概念

PROVIDE_HIDDEN

入门样例

#include <stdio.h>

int a;

int main(void) {
  printf("&a = %p", &a);
  return 0;
}
ENTRY(_start)

SECTIONS
{
  . = 0x01000000;
  .text : { 
    *(.text) 
  }
  
  . = 0x08000000;
  .data : { 
    HIDDEN(data_section_start = .);
    *(.data) 
    HIDDEN(data_section_end = .);
  }
  
  . = 0x16000000;
  .bss : { 
    *(.bss) 
  }

  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
}

Previous Page 3 of 4 Next Page