【Busybox】Busybox源码分析-01 | 源码目录结构和程序入口
Busybox是一个开源项目,遵循GPL v2协议。结构其本质是源码海豚支付系统源码将多个UNIX命令集合成一个小型可执行程序,适用于构建轻量级根文件系统,分支特别是结构嵌入式系统设计中。版本1..0的源码Busybox体积小巧,仅为几百千字节至1M左右,分支动态链接方式下大小更小。结构其设计模块化,源码可灵活添加、分支去除命令或调整选项。结构
Busybox程序主体在Linux内核启动后加载运行,源码入口为main()函数,位于libbb/appletlib文件末尾。通过条件分支处理,决定以库方式构建。在函数体中,使用mallopt()调整内存分配参数以优化资源使用。接着通过条件宏定义,控制代码编译逻辑,如在Linux内核启动后期加载并运行Busybox构建的init程序。命令行输入时,Busybox会解析参数,go 1.4源码执行对应操作。
在源码中,通过char * applet_name表示工具名称,调用lbb_prepare()函数设置其值为“busybox”。之后解析命令行参数,如在mkdir iriczhao命令中,解析到mkdir命令传递给applet_name。配置了FEATURE_SUID_CONFIG宏定义时,会从/etc/busybox.conf文件中解析配置参数。最后,执行run_applet_and_exit()函数,根据NUM_APPLETS值决定执行命令或报错。
在命令行下键入命令后,执行关键操作的函数是find_applet_by_name()和run_applet_no_and_exit()。编译构建并安装Busybox后,可执行程序和命令链接分布在安装目录下。从源码角度,命令有一一对应的执行函数,通过命令表管理命令入口函数。在代码执行逻辑中,首先调用find_applet_by_name()获取命令表数组下标,再传递给run_applet_no_and_exit()执行对应命令。
golang chan 最详细原理剖析,全面源码分析!看完不可能不懂的html钟表源码!
大纲
概述
chan 是 golang 的核心结构,是与其他高级语言区别的显著特色之一,也是 goroutine 通信的关键要素。尽管广泛使用,但对其深入理解的人却不多。本文将从源码编译器的视角,全面剖析 channel 的用法。
channel 的本质
从实现角度来看,golang 的 channel 实质上是环形队列(ringbuffer)的实现。我们将 chan 称为管理结构,channel 中可以放置任何类型的对象,称为元素。
channel 的使用方法
我们从 channel 的使用方式入手,详细介绍 channel 的使用方法。
channel 的创建
创建 channel 时,用户通常有两种选择:创建带有缓冲区和不带缓冲区的 channel。这对应于 runtime/chan.go 文件中的 makechan 函数。
channel 入队
用户使用姿势:对应函数实现为 chansend,位于 runtime/chan.go 文件。
channel 出队
用户使用姿势:对应函数分别是 chanrecv1 和 chanrecv2,位于 runtime/chan.go 文件。
结合 select 语句
用户使用姿势:对应函数实现为 selectnbsend,位于 runtime/chan.go 文件中。
结合 for-range 语句
用户使用姿势:对应使用函数 chanrecv2,位于 runtime/chan.go 文件中。android go 源码
源码解析
以上,我们通过宏观的用户使用姿势,了解了不同使用姿势对应的不同实现函数,接下来将详细分析这些函数的实现。
makechan 函数
负责 channel 的创建。在 go 程序中,当我们写类似 v := make(chan int) 的初始化语句时,就会调用不同类型对应的初始化函数,其中 channel 的初始化函数就是 makechen。
runtime.makechan
定义原型:
通过这个,我们可以了解到,声明创建一个 channel 实际上是得到了一个 hchan 的指针,因此 channel 的核心结构就是基于 hchan 实现的。
其中,t 参数指定元素类型,size 指定 channel 缓冲区槽位数量。如果是带缓冲区的 channel,那么 size 就是槽位数;如果没有指定,那么就是 0。
makechan 函数执行了以下两件事:
1. 参数校验:主要是越界或 limit 的校验。
2. 初始化 hchan:分为三种情况:
所以,我们看到除了 hchan 结构体本身的内存分配,该结构体初始化的关键在于四个字段:
hchan 结构
makechan 函数负责创建了 chan 的核心结构-hchan,接下来我们将详细分析 hchan 结构体本身。
在 makechan 中,刺客引擎 源码初始化时实际上只初始化了四个核心字段:
我们使用 channel 时知道,channel 常常会因为两种情况而阻塞:1)投递时没有空间;2)取出时还没有元素。
从以上描述来看,就涉及到 goroutine 阻塞和 goroutine 唤醒,这个功能与 recvq,sendq 这两个字段有关。
waitq 类型实际上是一个双向列表的实现,与 linux 中的 LIST 实现非常相似。
chansend 函数
chansend 函数是在编译器解析到 c <- x 这样的代码时插入的,本质上就是把一个用户元素投递到 hchan 的 ringbuffer 中。chansend 调用时,一般用户会遇到两种情况:
接下来,我们看看 chansend 究竟做了什么。
当我们在 golang 中执行 c <- x 这样的代码,意图将一个元素投递到 channel 时,实际上调用的是 chansend 函数。这个函数分几个场景来处理,总结来说:
关于返回值:chansend 返回值标明元素是否成功入队,成功则返回 true,否则 false。
select 的提前揭秘:
golang 源代码经过编译会变成类似如下:
而 selectnbasend 只是一个代理:
小结:没错,chansend 功能就是这么简单,本质上就是一句话:将元素投递到 channel 中。
chanrecv 函数
对应的 golang 语句是 <- c。该函数实现了 channel 的元素出队功能。举个例子,编译对应一般如下:
golang 语句:
对应:
golang 语句(这次的区别在于是否有返回值):
对应:
编译器在遇到 <- c 和 v, ok := <- c 的语句时,会换成对应的 chanrecv1,chanrecv2 函数,这两个函数本质上都是一个简单的封装,元素出队的实现函数是 chanrecv,我们详细分析这个函数。
chanrecv 函数的返回值有两个值,selected,received,其中 selected 一般作为 select 结合的函数返回值,指明是否要进入 select-case 的代码分支,received 表明是否从队列中成功获取到元素,有几种情况:
selectnbsend 函数
该函数是 c <- v 结合到 select 时的函数,我们使用 select 的 case 里面如果是一个 chan 的表达式,那么编译器会转换成对应的 selectnbsend 函数,如下:
对应编译函数逻辑如下:
selectnbsend 本质上也就是个 chansend 的封装:
chansend 的内部逻辑上面已经详细说明过,唯一不同的就是 block 参数被赋值为 false,也就是说,在 ringbuffer 没有空间的情况下也不会阻塞,直接返回。划重点:chan 在这里不会切走执行权限。
selectnbrecv 函数
该函数是 v := <- c 结合到 select 时的函数,我们使用 select 的 case 里面如果是一个 chan 的表达式,那么编译器会转换成对应的 selectnbsrecv 函数,如下:
对应编译函数逻辑如下:
selectnbrecv 本质上也就是个 chanrecv 的封装:
chanrecv 的内部逻辑上面已经详细说明过,在 ringbuffer 没有元素的情况下也不会阻塞,直接返回。这里不会因此而切走调度权限。
selectnbrecv2 函数
该函数是 v, ok = <- c 结合到 select 时的函数,我们使用 select 的 case 里面如果是一个 chan 的表达式,那么编译器会转换成对应的 selectnbrecv2 函数,如下:
对应编译函数逻辑如下:
selectnbrecv2 本质上是个 chanrecv 的封装,只不过返回值不一样而已:
chanrecv 的内部逻辑上面已经详细说明过,在 ringbuffer 没有元素的情况下也不会阻塞,直接返回。这里不会因此而切走调度权限。selectnbrecv2 与 selectnbrecv 函数的不同之处在于还有一个 ok 参数指明是否获取到了元素。
chanrecv2 函数
chan 可以与 for-range 结合使用,编译器会识别这种语法。如下:
这个本质上是个 for 循环,我们知道 for 循环关键是拆分成三个部分:初始化、条件判断、条件递进。
那么在我们 for-range 和 chan 结合起来之后,这三个关键因素又是怎么理解的呢?简述如下:
init 初始化:无
condition 条件判断:
increment 条件递进:无
当编译器遇到上面 chan 结合 for-range 写法时,会转换成 chanrecv2 的函数调用。目的是从 channel 中出队元素,返回值为 received。首先看下 chanrecv2 的实现:
chan 结合 for-range 编译之后的伪代码如下:
划重点:从这个实现中,我们可以获取一个非常重要的信息,for-range 和 chan 的结束条件只有这个 chan 被 close 了,否则一直会处于这个死循环内部。为什么?注意看 chanrecv 接收的参数是 block=true,并且这个 for-range 是一个死循环,除非 chanrecv2 返回值为 false,才有可能跳出循环,而 chanrecv2 在 block=true 场景下返回值为 false 的唯一原因只有:这个 chan 是 close 状态。
总结
golang 的 chan 使用非常简单,这些简单的语法糖背后其实都是对应了相应的函数实现,这个翻译由编译器来完成。深入理解这些函数的实现,对于彻底理解 chan 的使用和限制条件是必不可少的。深入理解原理,知其然知其所以然,你才能随心所欲地使用 golang。
代ç å伪代ç ä»ä¹åºå«
åºå«ï¼1ï¼é¢å¯¹å¯¹è±¡ä¸åï¼ä¼ªä»£ç æ¯æ¹ä¾¿ç¨åºå便äºç解ï¼æºä»£ç æ¯é¢å¯¹çµèï¼ä½¿çµèç¼è¯ã
2ï¼ç¼è¯æ¹å¼ä¸åï¼ä¼ªä»£ç æ 被çµèç¼è¯ï¼æºä»£ç å¯ä»¥è¢«çµèç¼è¯ã
3ï¼ç¼åæ¹å¼ä¸åï¼å¨ä¼ªä»£ç ä¸ï¼æ¯ä¸æ¡æ令å ä¸ è¡ï¼æ令åä¸éä»»å符å·ï¼æºä»£ç ä¸æ¡æ令客æ å¤è¡ï¼å¯å 符å·ã
å¨ç°ä»£ç¨åºè¯è¨ä¸ï¼æºä»£ç å¯ä»¥æ¯ä»¥ä¹¦ç±æè ç£å¸¦çå½¢å¼åºç°ï¼ä½æ为常ç¨çæ ¼å¼æ¯ææ¬æ件ï¼è¿ç§å ¸åæ ¼å¼çç®çæ¯ä¸ºäºç¼è¯åºè®¡ç®æºç¨åºã
æ©å±èµæï¼
伪代ç ï¼æ¯ç¨ä»äºèªç¶è¯è¨å计ç®æºè¯è¨ä¹é´çæåå符å·ï¼å æ¬æ°å¦ç¬¦å·ï¼æ¥æè¿°ç®æ³ã
伪代ç ç®å示ä¾ï¼è¾å ¥3个æ°ï¼æå°è¾åºå ¶ä¸æ大çæ°ãå¯ç¨å¦ä¸ç伪代ç 表示ï¼
Beginï¼ç®æ³å¼å§ï¼
è¾å ¥ Aï¼Bï¼C
IF A>B å AâMax
å¦å BâMax
IF C>Max å CâMax
Print Max
End ï¼ç®æ³ç»æï¼
伪代ç (Pseudocode)æ¯ä¸ç§ç®æ³æè¿°è¯è¨ãå®ä¸æ¯ä¸ä¸ç§ç°å®åå¨çç¼ç¨è¯è¨ã使ç¨ä¸ºä»£ç çç®çæ¯ä¸ºäºä½¿è¢«æè¿°çç®æ³å¯ä»¥å®¹æå°ä»¥ä»»ä½ä¸ç§ç¼ç¨è¯è¨(Pascal, Cï¼Java, etc) å®ç°ã
æºç¨åº(source code) å³ä»£ç æ¯ææªç¼è¯çæç §ä¸å®çç¨åºè®¾è®¡è¯è¨è§è书åçææ¬æ件ãæºä»£ç (ä¹ç§°æºç¨åº)ï¼æ¯æä¸ç³»å人类å¯è¯»ç计ç®æºè¯è¨æ令ã
åèèµææ¥æºï¼ç¾åº¦ç¾ç§-伪代ç
链桨PaddleDTX系列 - xdb源码分析(一)
本文基于链桨开源master分支,分析xdb模块代码。最新commit为4eee7caeebc0febdc。
xdb是基于区块链的去中心化存储系统,它实现了文件存储、文件摘要上链、副本保持证明、健康状态监控、文件迁移等功能。在代码结构上,主要包含以下部分:
blockchain模块:此模块负责实现xdb与区块链网络的交互。目前,xdb支持的区块链网络包括Xuperchain(xchain)和Fabric。以Xuperchain为例,xchain模块中的xchain.go文件包含了初始化xchain客户端的相关功能。
client模块:作为xdb的客户端工具,client模块使得用户可以通过与xdb server进行交互来实现文件操作。具体实现细节在client/http/http.go文件中。
cmd模块:这是xdb的命令行工具,通过client请求server服务,支持的功能包括文件上传、下载、查询等操作。关于cmd模块的详细用法,参考cmd/client/README.md文档。
总的来说,xdb模块的代码结构清晰,功能全面,为去中心化存储提供了强大支持。通过本文的分析,可以更直观地理解xdb模块的实现原理和使用方法。
什么是程序的三种基本结构?
答:第一种:顺序结构
顺序结构表示程序中的各个操作时按照它们在源代码中的排列顺序依次执行的,其流程如图所示。
图中的S1和S2表示;两个处理步骤,这些处理步骤可以是一个非转移操作或多个非转移操作,甚至可以是空操作,也可以是三种基本操作中的任意一种结构,整个顺序结构只有一个入口点a和一个出口点b。这种结构的特点是:程序从a出开始,按顺序执行所有操作,知道出口b处,所以称为顺序结构。
第二种:选择结构 选择结构表示程序处理需要根据某个特定条件选择其中一个分支执行。选择结构有单选择、双选择、多选择。其流程如图所示。
第三种:循环结构 循环结构表示程序反复执行某个或某些操作,直到满足特定条件时结束,循环结构有两种基本形式:当型循环和直到型循环,其流程如图所示。
2024-12-24 00:13
2024-12-23 22:55
2024-12-23 22:40
2024-12-23 22:16
2024-12-23 21:45