Openwrt系统简述

1. OpenWrt简介

OpenWrt是一个嵌入式设备的Linux发行版,以GPL许可协议发行。

1.1 主要特点

  1. 代码里不含第三方开源包,只包含开源包地址链接。
  2. 编译时自动下载源代码、打补丁来满足指定平台要求,并编译。还可以修改Makefile来下载最新的软件包。
  3. 使用LuCI作为最终用户管理界面。LuCI以Apache许可协议发布Web管理功能代码。
  4. UCI通用配置管理方法。
  5. 通过脚本来调用iptables来实现防火墙功能,配置保存在UCI文件中。
  6. 开放和可扩展的OPKG格式安装升级包

1.2 开源嵌入式操作系统比较

AndroidOpenWrt
内核Linux 内核Linux 内核
许可协议Apache2.0GNU License
使用场景面向终端用户,手持设备。用户接口采用JAVA 提供图形用户界面GUI服务器、家庭路由器等,用户接口默认为 UCI 命令行提供,也支持通过 Web 方式来管理
开发主导模式由谷歌公司主导开发由 OpenWrt.org 社区主导,社区由个人组成,更开放

1.3 开发环境及编译分析

  1. 准备一台Ubuntu系统环境
  2. 安装编译工具:
sudo apt-get install subversion //版本管理系统
sudo apt-get install g++ flex patch // g++是GNU工程的C/C++编译工具,FLEX(The Fast Lexical Analyzer)一个快速词法分析工具,patch是将diff文件应用到原始文件的工具,用于在程序开发过程中提交代码,是应用差异文件的工具。
sudo apt-get install libncurses5-dev zlib1g-dev // libncurses5-dev用于屏幕终端控制,zlib1g-dev是压缩及解压缩开发。
sudo apt-get install git-core //用于大型工程的分布式版本管理工具
sudo apt-get install libssl-dev // openssl开发库,用于加密解密、计算哈希和数据签名等。
sudo apt-get install gawk // GNU工程实现的AWK语言工具,是文本模式扫描和处理的工具。
sudo apt-get install xz-util // xz格式的压缩工具集,有非常高的压缩比率。
  1. 下载代码:git clone git://git.openwrt.org/15.05/openwrt.git cc
  2. 配置和编译:
    (1)更新、安装所有可选的软件包:
./scripts/feeds update 更新最新的包定义 
./scripts/feeds install -a 安装所有的包

(2)编译配置:make defconfig。OpenWrt提供模块化选择编译,每一个模块通常都有3个选项[Y|N|M]可供选择,输入Y该模块将包含在固件中;输入M将作为一个模块来编译,可以后续再进行安装;输入N将不编译该模块。还有一些是单选选项菜单,按空格键进行选择,再次按空格键则取消选择。
(3)编译:make

make V=s 可以输出编译过程中每一步的执行动作,出错后显示详细的错误信息。
make -j2 使用2个线程进行并行编译。
注:OpenWrt采用补丁包方式来管理代码,第三方的代码不放在它自己的代码库中,仅在编译前从第三方服务器下载。
单编应用:
(1)在qsdk目录下执行make menuconfig将工具加入config;
(2)make package/xxx /compile V=s

1.4 编译扩展机制feeds

feeds是OpenWrt开发所需要的软件包套件的工具及更新地址集合,这些软件包通过一个统一的接口地址进行访问。这样用户可以不用关心扩展包的存储位置,可以减少扩展软件包和核心代码部分的耦合。它由两部分组成,即扩展包位置配置文件feeds.conf.default和脚本工具feeds。目前在配置文件中保存最重要的扩展软件包集合有以下4个:

(1)“LuCI” OpenWrt默认的Web浏览器图形用户接口。

(2)“routing”一些额外的基础路由器特性软件,包含动态路由Quagga等。

(3)“telephony” IP电话相关的软件包,例如freeswitch和Asterisk等。

(4)“management” TR069等各种管理软件包。

利用feeds提供的接口将OpenWrt所需的全部扩展软件包进行下载并安装:

./scripts/feeds update –a 
./scripts/feeds install -a
  1. feeds工具用法如下:
./scripts/feeds 
Usage: ./scripts/feeds <command> [options]
Commands: 
list [options]: List feeds, their content and revisions (if installed) 
Options: -n : List of feed names. 
-s : List of feed names and their URL. 
-r <feedname>: List packages of specified feed. 
-d <delimiter>: Use specified delimiter to distinguish rows (default: spaces)
install [options] <package>: Install a package
Options: -a : Install all packages from all feeds or from the specified feed using the -p option. 
-p <feedname>: Prefer this feed when installing packages. 
-d <y|m|n>: Set default for newly installed packages.
-f : Install will be forced even if the package exists in core OpenWrt (override) 
search [options] <substring>: Search for a package 
Options: -r <feedname>: Only search in this feed 
uninstall -a|<package>: Uninstall a package
Options: -a : Uninstalls all packages.
update -a|<feedname(s)>: Update packages and lists of feeds in feeds.conf . 
Options: -a : Update all feeds listed within feeds.conf. Otherwise the specified feeds will be updated.
                -i : Recreate the index only. No feed update from repository is performed. 
clean: Remove downloaded/generated files. 
  • Update:下载在feeds.conf或feeds.conf.default文件中的软件包列表并创建索引。-a表示更新所有的软件包。只有更新后才能进行后面的操作。
  • list:从创建的索引文件“feed.index”中读取列表并显示。只有进行更新之后才能查看列表。
  • install:安装软件包以及它所依赖的软件包,从feeds目录安装到package目录,即在“package/feeds”目录创建软件包的软链接。只有安装之后,在后面执行“make menuconfig”时,才可以对相关软件包是否编译进行选择。
    例如安装luci-app-firewall:./scripts/feeds install luci-app-firewall
  • search:按照给定的字符串来查找软件包,需要传入一个字符串参数。
  • uninstall:卸载软件包,但它没有处理依赖关系,仅仅删除本软件包的软链接。
  • clean:删除update命令下载和生成的索引文件,但不会删除install创建的链接。
  1. feeds代码处理过程
    首先读取并解析feeds.conf配置文件,然后执行相应的命令,例如install时,将安装应用程序包和它所有直接或间接依赖的所有软件包。安装时将创建一个符号链接,从packages/feeds/ f e e d n a m e / feed_name/ feedname/package_name指向feeds/ f e e d n a m e / feed_name/ feedname/package_name, 这样在“make menuconfig”时,feeds的软件包就可以被处理到,就可以选择编译了

1.5 OpenWrt 包管理系统

OPKG(Open/OpenWrt Package)是一个轻量快速的软件包管理系统,是IPKG的克隆,目前已成为开源嵌入式系统领域的事实标准。OPKG常用于路由、交换机等嵌入式设备中,用来管理软件包的下载、安装、升级、卸载和查询等,并处理软件包的依赖关系。功能和桌面Linux操作系统Ubuntu中的apt-get、Redhat中的yum类似。 OPKG 是一个针对根文件系统全功能的软件包管理器。它不仅仅是在独立的目录安装软件,还可以用于安装内核模块和驱动等。OPKG在安装时会自动解决安装软件时的包依赖关系,如果遇见错误,就中止安装。

1.5.1 OpenWrt 包管理系统

当执行“opkg update”命令进行软件列表的更新时,OPKG首先会读取配置文件/ etc/opkg.conf,这个文件保存了OPKG的全局配置信息。紧接着,OPKG会根据配置地址位置下载软件包列表文件Packages.gz到/var/opkg-list目录下,这个文件是软件仓库中所有软件列表及其依赖关系的清单,是使用gzip压缩的文件,这样在网络传输时所占用网络流量比较小。其后任何安装命令均需首先读取这两个文件。

软件安装之后的信息会保存在目录/usr/lib/opkg/下面,这里就相当于Windows操作系统中的注册表。它包含状态文件,OPKG通过访问这个状态文件确定该软件是否已安装、安装的版本,以及依赖关系是否满足等,从而可以确定安装软件的版本、文件路径等信息。

OPKG命令执行会读取以下3部分的信息:配置文件、已安装软件包信息和软件仓库的软件包信息。

  • 配置文件默认位置为/etc/opkg.conf。

  • 已安装软件包状态信息保存在/usr/lib/opkg目录下。

  • 软件仓库的软件包信息保存在/var/opkg-lists目录下。

1.5.2 OPKG命令

OPKG必须带有一个子命令,如果不带有子命令,将输出OPKG的详细使用提示信息。首先是提示必须有一个子命令参数,然后是命令格式提示信息,最后是各个子命令和选项信息含义描述。

opkg must have one sub-command argument: 
usage: opkg [options...] sub-command [arguments...] where sub-command is one of: 
Package Manipulation: 
update   Update list of available packages 
upgrade <pkgs>   Upgrade packages 
install <pkgs>   Install package(s) 
configure <pkgs>   Configure unpacked package(s) 
remove <pkgs|regexp>   Remove package(s) 
flag <flag> <pkgs>   Flag package(s) 
<flag>=hold|noprune|user|ok|installed|unpacked (one per invocation) 
Informational Commands:
List   List available packages 
list-installed   List installed packages 
list-upgradable   List installed and upgradable packages 
list-changed-conffiles   List user modified configuration files 
files <pkg>   List files belonging to <pkg> 
search <file|regexp>   List package providing <file> 
find <regexp>   List packages whose name or description matches <regexp> 
info [pkg|regexp]   Display all info for <pkg> 
status [pkg|regexp]   Display all status for <pkg> 
download <pkg>   Download <pkg> to current directory 
compare-versions <v1> <op> <v2>   compare versions using <= < > >= = << >>

OPKG的功能主要分两类,一种是软件包的管理命令,另外一种是软件包的查询命令。另外还有很多可以修饰的选项。

  1. 软件包的管理
    软件包的管理是OPKG最重要的功能,主要包含更新软件包列表、安装、卸载和升级等功能。

(1)opkg update。该命令用于更新可以安装的软件包列表。该命令不需要参数,执行时从服务器地址下载软件包列表文件并存储在/var/opkg-lists/目录下。OPKG在安装或升级时需要读取这个文件,这个文件代表当前仓库中所有可用的软件包。也可以删除该文件来释放存储空间,在安装软件前需要重新获取这个文件。

(2)opkg install。该命令用于安装软件包,需要一个参数,传递一个软件包名称。如果软件包之间有依赖关系,会自动下载所有被依赖的软件包,并依次将所有被依赖的软件包安装上。

(3)opkg remove。该命令用于卸载软件包,需要一个参数,传递一个软件包名称。需要注意的是,在安装时自动安装的软件包并不会删除,需要自己手动删除,或者在卸载软件包的同时增加(–autoremove)参数将不需要的安装包也删除。

(4)opkg upgrade。该命令用于升级软件包。如果软件包没有安装,该命令执行之后和“opkg install”效果相同。如果升级多个软件包,以空格分隔列在命令之后即可。例如使用opkg upgrade ip wget来升级两个软件包。

  1. 查询信息
    OPKG查询命令可以在软件仓库中查询,也可以在运行的系统中查询。OPKG提供了软件包的双向查询功能:正向查询,即从软件包来查询所包含的文件列表;也可以反向查询,从系统中所安装的文件查询所属的软件包。

(1)opkg list。该命令用于列出所有可使用的软件包,列出内容格式为: 软件包名称 – 版本 – 描述。

(2)opkg list-installed。该命令用于列出系统中已经安装的软件包。

(3)opkg list-changed-conffiles。该命令用于列出用户修改过的配置文件。

(4)opkg files 。该命令用于列出属于这个软件包()中的所有文件,这个软件包必须已经安装。

(5)opkg search 。该命令用于列出提供的软件包,注意:需要传递文件的绝对路径。

(6)opkg find 。该命令用于列出软件包名称和匹配的软件包。是一个正则表达式,可以精确匹配,也可以使用星号来模糊匹配,例如使用“net”或者“ net*”,均可以匹配NetCat。

(7)opkg info [pkg]。该命令用于显示已安装[pkg]软件包的信息,包含软件包名称、版本、所依赖的软件包名称、安装状态和安装时间等。如果没有指定参数则输出所有已安装软件包的信息。“opkg status”和这个命令功能完全相同。

(8)opkg download 。该命令用于将软件包下载到当前目录。

(9)opkg print-architecture。该命令用于列出安装包的架构。

(10)opkg whatdepends [-A] [pkg]。该命令用于针对已安装的软件包,输出依赖这个软件包的软件包。示例3-4所示代码用于查询依赖libmagic的软件包。

  1. 常用选项
    OPKG有很多选项可以使用,这里只列出几个最常用的选项。

(1)-A:查询所有的软件包,包含未安装的软件包。

(2)-d <dest_name>:使用<dest_name>作为软件包的安装根目录。<dest_name>是配置文件中定义的目录名称。

(3)-f <conf_file>:指定使用<conf_file>作为opkg的配置文件。如不指定,默认配置文件是/etc/opkg.conf。

(4)–nodeps:不按照依赖来安装,只安装软件包自己。这可能会导致缺少依赖文件,导致程序不能执行。

(5)–autoremove:卸载软件包时自动卸载不再使用的软件包(在安装时依赖会自动安装上)。

(6)–force-reinstall:强制重新安装软件包,在软件包版本未修改时不会再次安装,增加该选项来强制重新安装。

1.5.3 OPKG配置

OPKG需要一个配置文件来保存全局配置,例如软件从哪里下载、安装到哪里等。

  1. 调整软件仓库地址
    OPKG配置文件默认是/etc/opkg.conf。内容参考如下。
src/gz attitude_adjustment 
http://192.168.1.106:8080/openwrt 
dest root /
dest ram /tmp 
lists_dir ext /var/opkg-lists 
option overlay_root /overlay
  1. 调整安装目录
    OPKG的一个非常有用的特性,是有能力指定任何安装包的安装目录。安装目录在配置文件/etc/opkg.conf中定义。配置文件中目的地址格式是以dest开头,紧跟着目的地址的名称,最后是目录路径,必须从根目录开始。
dest root /
dest ram /tmp 
dest usb /opt

安装目录定义之后,目的地址名称就可以在安装命令中引用了。安装时目的地址名称只能引用在/etc/opkg.conf中定义的地址名称,例如“-d ram”表示软件将安装到临时目录/tmp下。安装命令类似如下格式: opkg install -d <目的地址名称>

  1. 代理设置
    OPKG通过下载软件包来安装,如果你通过HTTP代理服务器来上网,那就不能直接连接到服务器地址,这时就需要设置代理服务器地址。在/etc/opkg.conf中加入以下设置: option http_proxy http://proxy.example.org:3128/
    如果代理服务器需要认证,则需要增加以下认证信息:
option proxy_username xxxxxx 
option proxy_password xxxxxx 

如果使用busybox的wget命令,这个工具不支持认证功能,下载时将认证失败。可以改为在URL中传递用户名和密码:

option http_proxy http://username:password@proxy.example.org:3128/

2. OpenWrt配置

MVC(Model-View-Control)模式是经典的Web开发编程模式,OpenWrt也采用该设计模式。该设计模式为分层设计,模型层负责数据的持久化操作。OpenWrt的模型层采用统一配置接口(Unified Configuration Interface,UCI)。

2.1 UCI简介

2.1.1 文件语法

配置文件由配置节(section)组成,配置节由多个“name/values”选项对组成。每一个配置节都需要有一个类型标识,但不一定需要名称。每一个选项对都有名称和值,写在其所属于的配置节中。语法如下:

config <type> ["<name>"] # Section 
option <name> "<value>" # Option
注:
1.没有名称标识的配置节称为匿名配置节。
2.通常选项在配置文件中都是使用空格或制表符缩进来标识,但这个并非是语法要求,仅仅是为了增加配置文件的可读性。
3.在开始带有list关键字的选项,表示有多个值被定义,所有的语句有同一个选项名称。
4.通常不需要使用引号引上类型标识符或值,引号只在封闭的值包含空格或制表符的情况下需要。如:option 'example' "some value with space"
5. UCI标识符和配置文件的名称只能包含字母a~z、0~9和_。例如连字符(-)是不允许的。选项的值可以包含任何字符,但需要将它们正确地加上引号。
下面是所有的合法UCI文件选项语法示例:
option example value 
option 'example' value 
option example "value" 
option "example" 'value' 
option 'example' "value" 
option 'example' "some value with space"
下面的例子是错误的UCI文件语法:
option 'example" "value' (引号没有配对) 
option example some value with space (带有空格的值缺少引号)

2.1.2 配置原理

可以用一个文本编辑器修改,或用命令行实用程序UCI编辑配置文件。也可通过各种编程API(如shell、Lua和C等)来修改,这也是Web接口例如LuCI修改UCI文件的方式。 无论是通过一个文本编辑器还是命令行工具修改配置文件,在改变一个UCI的配置文件后,受影响的服务或可执行程序必须由init.d进行重启,这样更新UCI配置才会真正生效。init.d将UCI配置转换为它们软件特定的配置文件。init.d首先在该软件预期的位置生成这样的一个配置文件,它通过重新启动可执行程序再次读入配置。注意:如果只是直接启动可执行文件,没有通过init.d调用,将不会将一个UCI配置文件更新到特定程序相应的配置文件位置,在/etc/config/的修改将不会对现有进程有任何影响。

例如,在修改UCI配置文件时,如果你想将局域网网关IP地址从默认地址192.168.1.1修改为192.168.6.1,可以使用vi编辑器修改/etc/config/network。这里我们使用uci命令来修改。

 uci set network.lan.ipaddr=192.168.6.1 uci commit network 

下一步通过运行以下命令使修改生效。

 /etc/init.d/network restart

2.1.3 UCI工具

UCI工具选项的含义和基本使用方法:

uci 
Usage: uci [<options>] <command> [<arguments>]
Commands: 
batch 
export [<config>] //导出一个机器可读格式的配置。它是作为操作配置文件的shell脚本而在内部使用,导出配置内容时会在前面加“package”和文件名
import [<config>] //以UCI语法导入配置文件
changes [<config>] //列出配置文件分阶段修改的内容,即未使用“uci commit”提交的修改。如果没有指定配置文件,则指所有的配置文件的修改部分
commit [<config>] //对给定的配置文件写入修改,如果没有指定参数则将所有的配置文件写入文件系统。所有的“uci set”“uci add”“uci rename”和“uci delete”命令将配置写入一个临时位置,在运行“uci commit”时写入实际的存储位置
add <config> <section-type> //增加指定配置文件的类型为section-type的匿名区段
add_list <config>.<section>.<option>=<string> //对已存在的list选项增加字符串
del_list <config>.<section>.<option>=<string> 
show [<config>[.<section>[.<option>]]] //显示指定的选项、配置节或配置文件。以精简的方式输出,即key=value的方式输出
get <config>.<section>[.<option>] //获取指定区段选项的值
set <config>.<section>[.<option>]=<value> //设置指定配置节选项的值,或者是增加一个配置节,类型设置为指定的值
delete <config>[.<section>[[.<option>][=<id>]]] //删除指定的配置节或选项
rename <config>.<section>[.<option>]=<name> //对指定的选项或配置节重命名为指定的名字
revert <config>[.<section>[.<option>]] //恢复指定的选项,配置节或配置文件
reorder <config>.<section>=<position> 
Options: 
-c <path> set the search path for config files (default: /etc/ config) 
-d <str> set the delimiter for list values in uci show 
-f <file> use <file> as input instead of stdin 
-m when importing, merge data into an existing package 
-n name unnamed sections on export (default) 
-N don't name unnamed sections 
-p <path> add a search path for config change files 
-P <path> add a search path for config change files and use as default 
-q quiet mode (don't print error messages) 
-s force strict mode (stop on parser errors, default) 
-S disable strict mode 
-X do not use extended syntax on 'show'

例如设置值,如果更改系统局域网的网关地址,从默认值“192.168.1.1”修改为“192.168.56.11”:

~# uci set network.lan.ipaddr=192.168.56.11 
~# uci commit network 
~# /etc/init.d/network restart

当有多个配置节类型相同或者为匿名配置节时,UCI使用数组数字引用它们。OpenWrt系统默认有3个网卡接口,可以通过network.@interface[0]来引用第一个,通过network.@ interface[1]来引用第二个,通过network.@interface[2]来引用第三个。也可以使用负索引,例如network.@interface[−1],其中“−1”指的是最后一个,“−2”指的是倒数第二个,等等。

有些运行中的状态值没有保存在/etc/config目录下,而是保存在/var/state下,这时可以使用“-P”参数来查询当前状态值。

当为链表配置时,操作方法有所不同:
#增加到链表中一个配置项

~#>uci add_list system.ntp.server='ntp.bjbook.net' 
#删除链表中的一个配置项
~#>uci del_list system.ntp.server='ntp.ntp.org' 
#删除链表中的所有配置项
~#>uci delete system.ntp.server

:“uci add”仅可以创建匿名配置节,不能完成创建命名配置的目标,要使用“uci set”命令来完成增加一个配置节。

2.2 系统内核设置

2.2.1 sysctl.conf

这个文件是系统启动预加载的内核配置文件,通过sysctl命令读取和设置到系统当中。配置文件语法格式如下:

# comment
; comment token = value 

以“#”和分号开头的行均为注释行,并忽略空白行,配置值以key=value形式进行设置。例如,设置打开报文转发为net.ipv4.ip_forward=1。

2.2.2 sysctl

sysctl是用于修改运行中的内核参数的命令,所有可用的内核参数均在/proc/sys目录下。运行sysctl需要procfs文件系统支持。可以用sysctl读取和修改内核参数数据。参数以key= value形式进行设置。

-n:查询时输出配置项的值,但不输出配置项。 
-e:当碰到不认识的配置项时,忽略错误。 
-w:使用这个选项来修改系统设置。 
-p:从指定的配置文件中加载配置,如果没有指定则使用默认的配置文件/etc/sysctl. conf。 
-a:显示当前所有可用的值。

常用命令举例如下:

/sbin/sysctl -a,显示所有的内核配置; 
/sbin/sysctl -n kernel.hostname,查询kernel.hostname的值; 
/sbin/sysctl -w kernel.hostname ="host",修改系统主机名称为host; 
/sbin/sysctl -p /etc/sysctl.conf,加载配置。 

内核的参数配置在启动时由sysctl工具加载,默认加载/etc/sysctl.conf。启动之后均可在/proc/sys下查询,例如直接查询是否打开路由转发:cat /proc/sys/net/ipv4/ip_forward

内核参数也可以通过直接修改/proc/sys下的文件来生效。例如打开路由转发设置,可以执行以下命令:echo "1" > /proc/sys/net/ipv4/ip_forward

  • 25
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
OpenWrt是一个开源的嵌入式操作系统,它可以被用于路由器和其他网络设备,支持VLAN(Virtual Local Area Network)功能。VLAN可以将一个物理网络划分为多个逻辑网络,从而实现不同网络之间的隔离和通信。 在OpenWrt中,VLAN的实现原理是基于交换机芯片的功能。交换机芯片有多个物理接口,每个接口可以被配置成不同的VLAN成员。每个VLAN有一个唯一的标识符(VLAN ID),用于区分不同的VLAN。当数据包进入交换机芯片时,根据数据包的VLAN ID,交换机芯片可以决定将数据包转发到哪个VLAN成员接口。通过这种方式,OpenWrt可以实现VLAN的划分和隔离。 在OpenWrt中配置VLAN需要以下步骤: 1. 确定需要划分的VLAN数量和每个VLAN的成员接口。 2. 在OpenWrt的配置文件中添加VLAN的定义和成员接口的配置。 3. 根据实际需求,配置VLAN接口的IP地址和其他网络参数。 4. 保存配置并重新启动路由器,使配置生效。 通过以上步骤,OpenWrt可以实现基于VLAN的网络划分和隔离,从而满足不同网络的需求。 需要注意的是,对于OpenWrt的VLAN配置,具体的操作可能会根据不同的硬件平台和路由器固件版本有所不同。因此,在进行VLAN配置之前,建议查阅相关文档或咨询技术支持,以确保正确配置和使用VLAN功能。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [免费好用的IPv6之一个有趣的需求——浅谈Openwrt的VLAN](https://blog.csdn.net/weixin_43593122/article/details/104471273)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [MT7621A-Board.pdf](https://download.csdn.net/download/caibaihui/11489807)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四儿家的小祖宗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值