2.6内核编译小结

一、编译命令

 

 #make mrproper                             ## 与make clean 不同在于会清除.config文件

 

#make menuconfig                           ## gconfig、xconfig等都可以load .config文件

 

#make                                             ## make = make bzImage + make modules

 

#make modules_install                    ## /lib/modules/ 目录下生成模块目录

 

#make install                                  ## 自动处理bzImage、vmlinuz、initrd.img、system.map

                                                     ## 等,并自动配置grub.conf,可参考Makefile

 

 

二、grub.conf配置

 

1 hiddenmenu              ## 隐藏GRUB的启动菜单,可选,可#号注释掉

 

2. splashimage=(hd0,7)/boot/grub/splash.xpm.gz  ##GRUB背景画面,可选,可#号注释掉

 

3. kernel /vmlinuz-... ro root=LABEL=/ rdgb quite

            ## boot为独立分区,ro:read only,root=LABEL=/ 来表示Linux的根所处的分区

            ## rhgb:redhat graphics boot,图形模式启动,启动后可用dmesg查看启动信息

            ## quiet:表示在启动过程中只有重要信息显示,类似硬件自检的消息不会显示


rhgb = redhat graphical boot - This is a GUI mode booting screen with
most of the information hidden while the user sees a rotating activity
icon spining and brief information as to what the computer is doing.


quiet = hides the majority of boot messages before rhgb starts. These
are supposed to make the common user more comfortable. They get alarmed
about seeing the kernel and initializing messages, so they hide them for
their comfort.

 

 

 

内核编译参考:

 

 

 

 

Article 1:  http://www.cublog.cn/u/13991/showart.php?id=79823

 

摘自《编译 Linux2.6 内核总结》

 

 

编译 Linux2.6 内核总结

 

在X86, Ubuntu 上编译Linux2.6内核,总结如下:

基础知识

在介绍如何编译内核之前, 需要对内核相关概念有一定的了解. 关于Linux kernel的介绍浩如烟海, 这里只介绍系统中相关的目录, 文件及命令.

/boot

/boot/vmlinuz-<version>        : 用于启动的压缩内核镜像, 它也就是/arch/<arch>/boot中的压缩镜像.
/boot/system.map-<version>  : 存储内核符号地址.
/boot/initrd.img-<version>     : 初始化RAM硬盘时, 用来存储挂载根文件系统所需的模块.
/boot/grub/menu.lst                : grub的配置文件. (不同的发行版中它可能位于不同位置.


/lib/modules

该目录包含了内核模块及其他文件. 注意, modules中一般会有多个目录: 系统自带的内核模块在这里, 你编译自己的内核模块后, 它们也会被安装到这里. 不同的目录由内核版本号来区分. 即modules里目录的名称是内核版本号. (使用$ uname -r 可知当前系统内核所用的模块位于哪个目录).

/lib/modules/<kernel-version>/build
储存为该版本的内核编译新模块所需的文件. 包括Makefile, .config, module.symVers(模块符号信息), 内核头文件(位于include/, include/asm/中)

/lib/modules/<kernel-version>/kernel
储存内核目标文件(以.ko为后缀). 它的目录组织和内核源代码中kernel的目录组织相同.

/lib/modules/<kernel-version>/中:
modules.alias       : 模块别名定义. 模块加载工具使用它来加载相应的模块.
modules.dep        : 定义了模块间的依赖关系.
modules.symbols : 指定符号属于哪个模块.

这些文件都是文本文件, 可以查看它们.

$ uname -r

uname(1)被用来查看系统信息, 这里对我们有用的是它的"-r"选项, 它显示内核版本信息.


下载内核, 验证签名, 解压缩

http://www.kernel.org/pub/linux/kernel/下载最新版本的2.6内核. 速度还比较快. 这里以linux-2.6.17.13为例:

1, 下载内核压缩包

bzip2格式比gzip压缩效率更高, 一般就下载bz2的压缩包. 下载了内核压缩包之后, 还可下载对应的sign文件. 它被用来验证内核压缩文档的openPGP签名. 详细信息可参考 这里.

$ wget -c http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.17.13.tar.bz2
$ wget -c http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.17.13.tar.bz2.sign

2, 验证签名

首先从pgp的服务器获取签名公匙, linux内核包的公匙编号是0x517D0F0E. 再利用sign文件来验证.bz2压缩包的签名.  如果输出中有类似gpg: Good signature from "Linux Kernel Archives Verification Key <ftpadmin@kernel.org>" 的内容, 说明该包是有效的. 后面给出的警告信息可以忽略.

$ gpg --keyserver wwwkeys.pgp.net --recv-keys 0x517D0F0E
$ gpg --verify linux-2.6.17.13.tar.bz2.sign linux-2.6.17.13.tar.bz2

GPG签名只是保证镜像网站提供的压缩包和kernel.org所提供的是相同的, 如果你在kernel.org下载, 不需要验证签名.

3, 解压缩

解压缩之前, 有个问题值得思考: 要将压缩包解压到何处? 即要在哪个目录进行Linux内核源代码的编译?

内核源码树的 README中有这样一段话:

Do NOT use the /usr/src/linux area! This area has a (usually incomplete) set of kernel headers that are used by the library header files.  They should match the library, and not get messed up by whatever the kernel-du-jour happens to be.

实际上, 在我的Ubuntu系统中, /usr/src/ 目录中最初是没有linux目录的. 你可以在/usr/src中新建一个目录, 用内核版本命名, 比如/usr/src/linux-2.6.17.13. 这样, 即便之前在/usr/src中安装了linux的头文件, 也不会对它们造成影响.

我采用的方法是: 在/usr/local/src/kernel目录中进行.

编译内核时候, 若在make 后添加 "O=<complete_dir>"将会使生成的目标文件(包括.config)被放置到指定的目录. 否则, 生成的目标文件默认地被放到内核源码目录. 我们就采用默认的方法. 这是安全的.

4, 打补丁

对于kernel.org中的内核, 我个人认为没必要下载patch, 再打补丁. 费那事干嘛, 直接下载bz2包不就行了. 特定的补丁只能针对紧随其前的一个版本. 比如你想从2.6.17.1升级到2.6.17.13. 你得打12次补丁, 忒麻烦了.

但是, 有时候需要对"官方内核"添加补丁, 以支持特定的系统. 比如ARMLinux, 它往往不是发布完整的内核, 而是发布针对特定版本的补丁包. 这种情况下就要知道如何打补丁了. 方法很简单: 把补丁下载, 解压. 得到patch-<version>. 将它放到解压后的内核目录树的父目录中(也就是补丁和内核目录在同一目录). 然后cd到内核目录树中运行:

$ patch -p1 <../patch-<version>


配置内核

1, 前提: 构建编译环境

显然, 需要make, gcc等工具, 在Ubuntu中, 只需一条简单命令就可安装所有的源代码编译工具:
# apt-get install build-essential

当然, 如果你的内核是要安装到不同体系结构的目标系统中, 还需要构建cross编译环境.


2, 内核配置工具介绍

Linux提供了多种内核配置工具, 最基础的是 make config, 它列出每个编译选项, 而且是基于文本的, 一般不用它.

menuconfig (make menuconfig)
menuconfig是比较主流的配置工具, 它需要curse库的支持, 在Ubuntu中默认是没有的, 先安装它:
# apt-get install libncurses5-dev

xconfig ( make xconfig)
xconfig基于X11, 使用qt库, 在Ubuntu中先安装qt库:
# apt-get install libqt3-headers libqt3-mt-dev

3, 内核配置相关

.config配置文件
在内核树的根目录中,有一个.config文件,它记录了内核的配置选项,可直接对它进行修改,再运行( 若.config不存在,对内核进行配置后会生成它,这种情况下当然不能开始就运行oldconfig ). 实际上, 如果你手头有合适的 .config 文件, 可以运行 make oldconfig  直接按 .config 的内容来配置
$ sudo make oldconfig

对内核的配置都是围绕 .config 来展开的.  即便开始 .config 文件不存在, 进行配置后会创造它.

其实可以直接在menuconfig中加载已有的配置文件, 不要将它改名为.config. 否则完成配置, 退出menuconfig时会提示你运行 make mrproper. 上面提到的方法只是比较适合于oldconfig!

make相关命令
$ make oldconfig    : 基于已有的.config进行配置, 若有新的符号, 它将询问用户.
$ make defconfig    : 按默认选项对内核进行配置(386的默认配置是Linus做的).
$ make allnoconfig : 除必须的选项外, 其它选项一律不选. (常用于嵌入式系统).

$ make clean        : 删除生成的目标文件, 往往用它来实现对驱动的重新编译.
$ make mrproper : 删除包括.config在内的生成的目标文件.

可以查看内核源码树中的README和Makefile了解上述配置方法.

4, 开始配置

1, 修改Makefile (可选)
在Makefile中, 有这样的内容:

VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 16
EXTRAVERSION = .20
NAME=Sliding Snow Leopard

我们在编译内核之前, 可以先修改Makefile中的版本信息(一般是修改EXTRAVERSION, 比如EXTRAVERSION =-zp). 这样就能将自己编译的内核同别人编译的相同版本内核区分开来. 修改, 编译之后, 可使用 $ uname -r 查看内核版本信息.

但实际上, 从2.6.8的版本起可在内核版本号后面添加个性化字符串. 所以也就没有必要修改Makefile了: ()  Local version - append to kernel release

如果你即修改了Makefile中的EXTRAVERSION, 又在配置时定义了local version. 那么local version所定义的字符串将位于末尾, 紧跟在EXTRAVERSION的值之后.

另外, 如果要用到ccache, 也需要修改Makefile. 参考后面的内容!

2, 准备一个.config文件.
内核配置选项众多, 一个个去配置相当麻烦. 建议使用手头已有的配置文件. 如果你手头没有,  有多种方法获得它:

(1) 使用make defconfig将在源码树的根目录得到.config.
(2) 使用当前系统内核的配置文件, 一般位于/boot目录中. 它的名称并不是.config.
(3) 使用别的发行版提供的配置文件(网上去下载).

slackware的.config是个不错的起点. 在它的配置文件基础上作出适合自己系统的修改, 比较方便.  也可以拷贝发行版提供商的.config文件.

3, 建议配置步骤:
(1) 将配置文件(不要将它命名为.config!)拷贝到内核源码树根目录.
(2) make menuconfig, 然后将上述的配置文件加载进去.
(3) 配置完成后, 将生成的配置文件备份(.config, 也可以在menuconfig中指定生成的配置文件名).


配置选项是最头疼的问题
: 配置时候注意驱动的问题, 尤其是网络驱动. 使用 pppoe 的话, 要选上 ppp 相关的选项. 网卡驱动也要注意, 我刚开始配置的时候, 只加上了 lspci | grep Ethernet 对应的网卡, 但是重启后找不到eth0, 一怒之下, 把所有的1000M 网卡驱动都选为模块. 总算成功. 以后有空仔细看看. 再就是声卡驱动也要注意. 可参考我blog里另一篇文章: 配置2.6内核选项注解

也可以到Linux Kernel Configuration Archive看一看, 虽然它里面的内容与图形化配置工具中的help大同小异.


编译内核


配置完成后,就要进行编译了。编译2.6的内核很简单, Makefile自动检测依赖性,产生编译文件(bzImage),你也不用另外编译modules!. 只需运行:
$ make
 
使用make编译内核的技巧    

1, 可以略去编译信息(但仍能看到warning, error)
$sudo make > /dev/null
$sudo make -j2 > /dev/null

2, 加速编译过程.
(1) 可以使用 $ make -j<n> . 其中n = 2 * cpu的个数. 对于一般的单CPU系统, 通常用 $ make -j2 . 为编译过程分配2个人物, 这样在进行磁盘I/O时候, CPU就不会空闲了. 一般这个选项可以将速度提高10%左右.
 
(2) 还可以使用
ccache 来提高编译速度.  Debian/Ubuntu系统中默认没有安装, 首先安装它: $ sudo apt-get install ccache . 然后更改内核根目录的Makefile, 将CC和HOSTCC变量定义前添加ccache:
CC               = $(CROSS_COMPILE)gcc
HOSTCC      = gcc
更改为:
CC               = ccache $(CROSS_COMPILE)gcc
HOSTCC      = ccache gcc


编译生成的文件介绍

vmlinux   :  未经压缩的原始linux内核镜像.
/arch/<arch>/boot/zImage(bzImage):  使用zlib压缩后的内核镜像.
注意, 不同的体系结构对压缩后内核镜像的默认命名不同, 比如arm的是zImage, 而i386的是bzImage. (z表示zlib, bz表示"big zlib", 而非bzip2!)


安装内核

编译完成后, 在 arch/i386/boot目录中会有 bzImage 映象文件.

安装内核步骤如下:
(1)在/boot目录下新建mynewkernel目录,并将bzImage拷贝到/boot/mynewkernel目录下:
$ sudo cp arch/i386/boot/bzImage /boot/mynewkernel

(2)更改/boot/mynewkernel中bzImage的名字
$ sudo mv bzImage vmlinuz-2.6.17.13

(3)备份、修改grub配置文件
$sudo cp /boot/grub/menu.lst menu.lst.origin

修改menu.list,加入以下内容(从既有的menu.list中相关的内容拷贝):

title        zp, make defconfig, 2.6.17.13
root        (hd0,2)
kernel        /boot/mynewkernel/vmlinuz-2.6.17.13 root=/dev/sda3 ro quiet splash
savedefault
boot


(4)安装模块:

$sudo make modules_install

reboot, 在grub启动菜单中选择新内核启动...


参考资料

(1) Linux-kernel-tree/README
(2) kernel-build-howto
(3) 关于ccache, 可参考IBM developworks上的 这篇介绍.

 另:《鸟哥的私房菜》

 http://linux.vbird.org/linux_basic/0540kernel.php

 

 

 

 

 

 

 

 

 

 

grub.conf 配置参考:

 

Article  2:http://www.linuxsir.org/main/?q=node/129#4.1

 

摘自《系统引导管理器GRUB,为初学者指南》

作者:北南南北
来自:LinuxSir.Org

 

 

 

1、menu.lst的写法之一;

首先我们看一下我的Fedora 4.0 中的/boot/grub/menu.lst 的内容;

 

default=0
timeout=5
#splashimage=(hd0,6)/boot/grub/splash.xpm.gz
hiddenmenu
title Fedora Core (2.6.11-1.1369_FC4)
        root (hd0,6)
        kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/
        initrd /boot/initrd-2.6.11-1.1369_FC4.img
title WinXp
        rootnoverify (hd0,0)
        chainloader +1

 

注解:

default=0


default=0 是默认启动哪个系统,从0开始;每个操作系统的启动的定义都从title开始的,第一个title 在GRUB的启动菜单上显示为0,第二个启动为1,以此类推;

timeout=5


注:表示在开机后,GRUB画面出现几秒后开始以默认启动;如果在启动时,移动上下键,则解除这一规则;
#splashimage=(hd0,6)/boot/grub/splash.xpm.gz 注:GRUB的背景画面,这个是可选项;我不喜欢GRUB的背景画面,所以加#号注掉,也可以删除;

hiddenmenu

 

注解:隐藏GRUB的启动菜单,这项也是可选的,也可以用#号注掉;

一般的情况下对Linux操作系统的启动,一般要包括四行;title 行;root行;kernel 行;initrd 行;


1)在menu.lst中 ,通过 root (hd[0-n],y)来指定/boot 所在的分区;

title XXXXX 注:title 后面加一个空格,title 是小写的,后面可以自己定义;比如FC4,自己定义一个名字就行;
root (hd[0-n],y) ,在本例中,我们看到的是root (hd0,6) ,root (hd[0-n],y)表示的是/boot所在的分区;有时我们安装Linux的时候,大多是不设置/boot的,这时/boot和/所在的同一个分区; 这个root (hd[0-n],y)很重要,因为/boot目录中虽然有grub目录,最为重要的是还有kernel 和initrd文件,这是Linux能启动起来最为重要东西;

有的弟兄会问,root (hd[0-n],y)是怎么来的?

请参考:《在Linux系统中存储设备的两种表示方法》

2)在menu.lst中,kernel 命令行的写法;

kernel 一行,是通指定内核及Linux的/分区所在位置;

比如例子中是;

kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/


在这里以kernel 起始,指定Linux的内核的文件所处的绝对路径;因为内核是处在/boot目录中的, 如果/boot是独立的一个分区,则需要把boot省略;如果/boot是独立的分区,这行要写成:

kernel /vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/ 

 

因为/boot所处的分区已经在title 下一行root (hd[0-n],y)中指定了,所以就无需要再指明内核处在哪个分区了;另外Linux系统的硬盘分区的挂载配置文件在/etc/fstab ,原理是通过 mount /dev/hd[a-z]X /boot 来进行的;您可以对照着来理解;

ro 表示只读; root=LABEL=/ 来表示Linux的根所处的分区。LABEL=/ 这是硬盘分区格式化为相应文件系统后所加的标签;如果您不了解什么是标签,也可以直接以/dev/hd[a-z]X 或者/dev/sd[a-z]X来表示;就看您的Linux是根分区是在哪个分区了。比如我的是在/dev/hda7 , 那这里就可以写成root=/dev/hda7;

如果查看系统运行所挂载的分区,请用 df -lh 来查看,就能明白是不是/boot是独立的分区,或者查看/etc/fstab也能知道;

 

[root@localhost ~]# df -lh
Filesystem            容量  已用 可用 已用% 挂载点
/dev/hda7              11G  9.2G  1.2G  90% /
/dev/shm              236M     0  236M   0% /dev/shm

 

在这个例子中,我们可以发现 /boot并没有出现只有/dev/hda7,这表示/boot并不是独立的一个分区;所有的东西都包含在/中;于是我们在/boot中查看内核版本;

[root@localhost ~]# ls /boot/vmlinuz*
/boot/vmlinuz-2.6.11-1.1369_FC4   注:看到内核vmlinuz所处的目录;

 

于是我们就可以这样kernel 这行了;

 

kernel /boot/vmlinuz-2.6.11-1.1369_FC4  ro root=/dev/hda7



3)initrd 命令行的写法;

 

如果是/boot独立一个分区,initrd 一行要把/boot中省略;如果/boot不是处于一个分区,而是和Linux的/分区处于同一分区,不应该省略;

比如我们在2)中用的例子;现在拿到这里,我们应该首先查看 /boot中的initrd的文件名到底是什么;

 

[root@localhost ~]# ls /boot/initrd*
/boot/initrd-2.6.11-1.1369_FC4.img

 

如果是通过df -lh 得知或查看/etc/fstab 也行, 得知/boot是独立的分区;这时initrd 应该写成;

 

initrd  /initrd-2.6.11-1.1369_FC4.img

 

如果是 /boot不是独处一个分区,而是在/同一处一个分区, 则要写成;

 

initrd  /boot/initrd-2.6.11-1.1369_FC4.img

 


4)menu.lst第一种写法的总结和实践;

在这里,我们只说重要的,不重要的就一带而过了;

1]用fdisk -l ;df -lh ;more /etc/fstab来确认分区情况;

我们过fdisk -l ;df -lh ; more /etc/fstab 来确认/boot所在的分区,及Linux的根分区所在位置;

比如我们确认/boot和Linux的/分区同处一个分区;

 

[root@localhost ~]# df -lh
Filesystem            容量  已用 可用 已用% 挂载点
/dev/hda7              11G  9.2G  1.2G  90% /
/dev/shm              236M     0  236M   0% /dev/shm

 

然后我们/etc/fstab 中,查看/分所在的分区或分区标签是什么;

[root@localhost ~]# more /etc/fstab
# This file is edited by fstab-sync - see 'man fstab-sync' for details
LABEL=/                 /                       ext3    defaults        1 1
/dev/devpts             /dev/pts                devpts  gid=5,mode=620  0 0
/dev/shm                /dev/shm                tmpfs   defaults        0 0
/dev/proc               /proc                   proc    defaults        0 0
/dev/sys                /sys                    sysfs   defaults        0 0
LABEL=SWAP-hda1         swap                    swap    defaults        0 0
/dev/hdc                /media/cdrecorder       auto    pamconsole,exec,noauto,managed 0 0

 

经过上面的df -lh 和more /etc/fstab 的对照中得知,/boot并是独处一个分区,而是和/在同一个分区;这个Linux系统安装在/dev/hda7上,文件系统(此分区)的标签为LABEL=/ ,/boot也是处于/dev/hda7 ,/dev/hda7也可以说是 root (hd0,6);

2]查看内核vmlinuz的和initrd文件名的全称;

 

[root@localhost ~]# ls -lh /boot/vmlinuz*
-rw-r--r--  1 root root 1.6M 2005-06-03  /boot/vmlinuz-2.6.11-1.1369_FC4
[root@localhost ~]# ls -lh /boot/initrd*
-rw-r--r--  1 root root 1.1M 11月 26 22:30 /boot/initrd-2.6.11-1.1369_FC4.img

 


3]开始写menu.lst ;

我们根据上面所提到的,可以写成如下的样子;

 

default=0 
timeout=5
title FC4
        root (hd0,6)
        kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/
        initrd /boot/initrd-2.6.11-1.1369_FC4.img

 

也可以写成;

 

default=0 
timeout=5
title FC4
        root (hd0,6)
        kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=/dev/hda7
        initrd /boot/initrd-2.6.11-1.1369_FC4.img

 

注解:上面两个不同之处在于一指定Linux的根/所在的分区时,一个是用了文件系统的标签,另一个没有用标签;

2、menu.lst的写法之二,精简型;

本写法主要是把指定/boot所位于的所分区直接写入kernel 指令行;这样就省略了通过root (hd[0-n],y)来指定/boot所位于的分区;


1)第一种情况:/boot和Linux的/根分区在同一个分区;

有前面的那么多的讲解,menu.lst写法之二就好理解多了;也得分两种情况,咱们先把/boot并不是独处一个分区,而是和Linux的根分区处于同一个分区;我们以 4)menu.lst第一种方法的写法总结 的实例为例子;

 

default=0 
timeout=5
title FC4x
        kernel (hd0,6)/boot/vmlinuz-2.6.11-1.1369_FC4 ro root=/dev/hda7
        initrd (hd0,6)/boot/initrd-2.6.11-1.1369_FC4.img

 

注解:

title FC4x 注:自己为这个Linux 起个简单的名,以title开头,然后一个空格,后面就自己发挥吧,FC4或FC4x都行;

kernel 空格 (hd0,6)/boot/vmlinuz-2.6.11-1.1369_FC4 空格 ro 空格 root=/dev/hda7

kernel 这行这样理解 kernel (boot所在的分区)/boot/内核文件件全称 ro root=Linux根所位于的分区或标签

initrd 空格 (hd0,6)/boot/initrd-2.6.11-1.1369_FC4.img
initrd 这行可以这样理解 initrd (/boot所在的分区)/boot/内核文件名全称


2)第二种情况:/boot独立一个分区,和Linux的根分区不是同一个分区;

比如我们查看到df -lh 得到的是

[root@localhost ~]# df -lh
Filesystem            容量  已用 可用 已用% 挂载点
/dev/hda6              200M  120M  80M  60% /boot
/dev/hda7              11G  9.2G  1.2G  90% /

 

我们再进一行查看/etc/fstab 得知;

LABEL=/                 /                       ext3    defaults        1 1
LABEL=/boot             /boot                   ext3    defaults        1 2

 

所以我们应该写成如下的;

 

title FC4x
        kernel (hd0,5)/vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/
        initrd (hd0,5)/initrd-2.6.11-1.1369_FC4.img

 

因为Linux的根分区是/dev/hda7,通过/etc/fstab和df -h的内容得知标签为 LABEL=/的分区就是/dev/hda7 ,所以有;

 

title FC4x
        kernel (hd0,5)/vmlinuz-2.6.11-1.1369_FC4 ro root=/dev/hda7
        initrd (hd0,5)/initrd-2.6.11-1.1369_FC4.img

 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值