基于Linux与Busybox的Reboot命令流程分析

1、电源管理入门之关机重启详解 也就是说,这个接口被执行后,只有一个CPU在运行,用于完成后续的reboot动作。mhu-->transport-->scmi-->scmi_system_power-->power_domain-->ppu/system_power-->i2c/cru,最后SCP固件通过控制PMIC/CRU的硬件寄存器实现对系统的关机重启设置。调用blocking_notifier_call_chain接口,向关心reboot事件的进程,发送SYS_RESTART、 SYS_HALT或者SYS_POWER_OFF事件。 阅读详情
***************************************************************************************************************************
作者:EasyWave                                                                                 时间:2013.01.26

类别:Linux 内核系统源码分析                                                             声明:转载,请保留链接

注意:如有错误,欢迎指正。这些是我学习的日志文章......

***************************************************************************************************************************

一:Busyobx层的分析

        这段时间,在忙到一个项目时,需要在busybox中用到reboot命令,开始在busybox中的shell中输入reboot命令,始终如下的信息,然后就停止在那里了,无法重启...为了彻底的弄明白这个问题,我在网络上找了很久,终于有个人写的一个reboot流程分析,我就借花献佛.在这里重新分析下busybox是如何运行这个命令,同时又是如何调用到Linux内核中的mach_reset中的arch_reset,当针对不同的ARM芯片时,作为Linux内核开发和驱动开发的朋友,对于这个流程还是一定要了解的。要不,出现问题,又如何找出问题呢。忘记了reboot的打印信息了,如下:

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. The system is going down NOW !!  
  2. Sending SIGTERM to all processes.  
  3. Sending SIGKILL to all processes.  
  4. Please stand by while rebooting the system.  
  5. Restarting system.  
  6. .  


通过分析busybox1.20.0的代码可以看出在init.c中有这样一行的代码,如下:

  1. int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;  
  2. int init_main(int argc UNUSED_PARAM, char **argv)  
  3. {  
  4.     static const int magic[] = {  
  5.         RB_HALT_SYSTEM,  
  6.         RB_POWER_OFF,  
  7.         RB_AUTOBOOT  
  8.     };  
  9.     static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };  
  10.     ......  
  11.     /* struct sysinfo is linux-specific */  
  12. #ifdef __linux__  
  13.     /* Make sure there is enough memory to do something useful. */  
  14.     if (ENABLE_SWAPONOFF) { //是否配置了swapoff命令  
  15.         struct sysinfo info;  
  16.   
  17.         if (sysinfo(&info) == 0  
  18.          && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024  
  19.         ) {  
  20.             message(L_CONSOLE, "Low memory, forcing swapon");  
  21.             /* swapon -a requires /proc typically */  
  22.             new_init_action(SYSINIT, "mount -t proc proc /proc", "");  
  23.             /* Try to turn on swap */  
  24.             new_init_action(SYSINIT, "swapon -a", "");  
  25.             run_actions(SYSINIT);   /* wait and removing */  
  26.         }  
  27.     }  
  28. #endif  
  29.     ......  
  30. /* Make the command line just say "init"  - thats all, nothing else */  
  31.     strncpy(argv[0], "init", strlen(argv[0]));  
  32.     /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */  
  33.     while (*++argv)  
  34.         memset(*argv, 0, strlen(*argv));  
  35.   
  36.     /* Set up signal handlers */  
  37.     /* Set up signal handlers */  
  38.     if (!DEBUG_INIT) {  
  39.         struct sigaction sa;  
  40.   
  41.         bb_signals(0  
  42.             + (1 << SIGUSR1) /* halt */  
  43.             + (1 << SIGTERM) /* reboot */  
  44.             + (1 << SIGUSR2) /* poweroff */  
  45.             , halt_reboot_pwoff);//看到这个halt_reboot_pwoff  
  46.         signal(SIGQUIT, restart_handler); /* re-exec another init */ //看到这个restart_handler函数,这是我们需要分析的  
  47.   
  48.         /* Stop handler must allow only SIGCONT inside itself */  
  49.         memset(&sa, 0, sizeof(sa));  
  50.         sigfillset(&sa.sa_mask);  
  51.         sigdelset(&sa.sa_mask, SIGCONT);  
  52.         sa.sa_handler = stop_handler;  
  53.         /* NB: sa_flags doesn't have SA_RESTART. 
  54.          * It must be able to interrupt wait(). 
  55.          */  
  56.         sigaction_set(SIGTSTP, &sa); /* pause */  
  57.         /* Does not work as intended, at least in 2.6.20. 
  58.          * SIGSTOP is simply ignored by init: 
  59.          */  
  60.         sigaction_set(SIGSTOP, &sa); /* pause */  
  61.   
  62.         /* SIGINT (Ctrl-Alt-Del) must interrupt wait(), 
  63.          * setting handler without SA_RESTART flag. 
  64.          */  
  65.         bb_signals_recursive_norestart((1 << SIGINT), record_signo);  
  66.     }  
  67.     ......  
  68. }  

单独拿出halt_reboot_pwoff和restart_handler这个函数来看看

  1. /* The SIGUSR[12]/SIGTERM handler */  
  2. static void halt_reboot_pwoff(int sig) NORETURN;  
  3. static void halt_reboot_pwoff(int sig)  
  4. {  
  5.     const char *m;  
  6.     unsigned rb;  
  7.   
  8.     /* We may call run() and it unmasks signals, 
  9.      * including the one masked inside this signal handler. 
  10.      * Testcase which would start multiple reboot scripts: 
  11.      *  while true; do reboot; done 
  12.      * Preventing it: 
  13.      */  
  14.     reset_sighandlers_and_unblock_sigs();  
  15.   
  16.     run_shutdown_and_kill_processes();  
  17.   
  18.     m = "halt";  
  19.     rb = RB_HALT_SYSTEM;  
  20.     if (sig == SIGTERM) {  
  21.         m = "reboot";  
  22.         rb = RB_AUTOBOOT;  
  23.     } else if (sig == SIGUSR2) {  
  24.         m = "poweroff";  
  25.         rb = RB_POWER_OFF;  
  26.     }  
  27.     message(L_CONSOLE, "Requesting system %s", m);  
  28.     pause_and_low_level_reboot(rb);  
  29.     /* not reached */  
  30. }  


restart_handler函数如下:

  1. /* Handler for QUIT - exec "restart" action, 
  2.  * else (no such action defined) do nothing */  
  3. static void restart_handler(int sig UNUSED_PARAM)  
  4. {  
  5.     struct init_action *a;  
  6.   
  7.     for (a = init_action_list; a; a = a->next) {  
  8.         if (!(a->action_type & RESTART))  
  9.             continue;  
  10.   
  11.         /* Starting from here, we won't return. 
  12.          * Thus don't need to worry about preserving errno 
  13.          * and such. 
  14.          */  
  15.   
  16.         reset_sighandlers_and_unblock_sigs();  
  17.   
  18.         run_shutdown_and_kill_processes();  
  19.   
  20. #ifdef RB_ENABLE_CAD  
  21.         /* Allow Ctrl-Alt-Del to reboot the system. 
  22.          * This is how kernel sets it up for init, we follow suit. 
  23.          */  
  24.         reboot(RB_ENABLE_CAD); /* misnomer */  
  25. #endif  
  26.   
  27.         if (open_stdio_to_tty(a->terminal)) {  
  28.             dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);  
  29.             /* Theoretically should be safe. 
  30.              * But in practice, kernel bugs may leave 
  31.              * unkillable processes, and wait() may block forever. 
  32.              * Oh well. Hoping "new" init won't be too surprised 
  33.              * by having children it didn't create. 
  34.              */  
  35.             //while (wait(NULL) > 0)  
  36.             //  continue;  
  37.             init_exec(a->command);  
  38.         }  
  39.         /* Open or exec failed */  
  40.         pause_and_low_level_reboot(RB_HALT_SYSTEM);  
  41.         /* not reached */  
  42.     }  
  43. }  


通过分析,我们看到他们都会有调用这两个函数:reset_sighandlers_and_unblock_sigs();以及 run_shutdown_and_kill_processes();,我们重点关注如下这个函数:

  1. static void run_shutdown_and_kill_processes(void)  
  2. {  
  3.     /* Run everything to be run at "shutdown".  This is done _prior_ 
  4.      * to killing everything, in case people wish to use scripts to 
  5.      * shut things down gracefully... */  
  6.     run_actions(SHUTDOWN);  
  7.   
  8.     message(L_CONSOLE | L_LOG, "The system is going down NOW!");  
  9.   
  10.     /* Send signals to every process _except_ pid 1 */  
  11.     kill(-1, SIGTERM);  
  12.     message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");  
  13.     sync();  
  14.     sleep(1);  
  15.   
  16.     kill(-1, SIGKILL);  
  17.     message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");  
  18.     sync();  
  19.     /*sleep(1); - callers take care about making a pause */  
  20. }  


嘿嘿,终于看到了上面的打印信息:The system is going down NOW !! 以及Sending SIGTERM to all processes. 同时在上面的halt_reboot_pwoff和restart_handler中都会调用这样一个函数,如下:

  1. static void pause_and_low_level_reboot(unsigned magic) NORETURN;  
  2. static void pause_and_low_level_reboot(unsigned magic)  
  3. {  
  4.     pid_t pid;  
  5.   
  6.     /* Allow time for last message to reach serial console, etc */  
  7.     sleep(1);  
  8.   
  9.     /* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) 
  10.      * in linux/kernel/sys.c, which can cause the machine to panic when 
  11.      * the init process exits... */  
  12.     pid = vfork();  
  13.     if (pid == 0) { /* child */  
  14.         reboot(magic);  
  15.         _exit(EXIT_SUCCESS);  
  16.     }  
  17.     while (1)  
  18.         sleep(1);  
  19. }  


看到了吗?有一个reboot(magic)函数,对于vfork函数,请参考fork函数。这里不多说了.... 我们现在来看看reboot.h文件,如下:

  1. /* 
  2.  * Definitions related to the reboot() system call, 
  3.  * shared between init.c and halt.c. 
  4.  */  
  5.   
  6. #include <sys/reboot.h>  
  7.   
  8. #ifndef RB_HALT_SYSTEM  
  9. # if defined(__linux__)  
  10. #  define RB_HALT_SYSTEM  0xcdef0123  
  11. #  define RB_ENABLE_CAD   0x89abcdef  
  12. #  define RB_DISABLE_CAD  0  
  13. #  define RB_POWER_OFF    0x4321fedc  
  14. #  define RB_AUTOBOOT     0x01234567  
  15. # elif defined(RB_HALT)  
  16. #  define RB_HALT_SYSTEM  RB_HALT  
  17. # endif  
  18. #endif  
  19.   
  20. /* Stop system and switch power off if possible.  */  
  21. #ifndef RB_POWER_OFF  
  22. # if defined(RB_POWERDOWN)  
  23. #  define RB_POWER_OFF  RB_POWERDOWN  
  24. # elif defined(__linux__)  
  25. #  define RB_POWER_OFF  0x4321fedc  
  26. # else  
  27. #  warning "poweroff unsupported, using halt as fallback"  
  28. #  define RB_POWER_OFF  RB_HALT_SYSTEM  
  29. # endif  
  30. #endif  

而在linux的内核中的定义如下:


busybox和linux内核中的REBOOT的定义值是一样的。看到了没有了。这个很重要的哦,否则busybox是无法调用linux内核的reboot函数。

 

二:Linux内核层的分析


Linux内核是如何衔接busybox的reboot函数的呢,如下代码:

  1. /* 
  2.  * Reboot system call: for obvious reasons only root may call it, 
  3.  * and even root needs to set up some magic numbers in the registers 
  4.  * so that some mistake won't make this reboot the whole machine. 
  5.  * You can also set the meaning of the ctrl-alt-del-key here. 
  6.  * 
  7.  * reboot doesn't sync: do that yourself before calling this. 
  8.  */  
  9. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  
  10.         void __user *, arg)  
  11. {  
  12.     char buffer[256];  
  13.     int ret = 0;  
  14.   
  15.     /* We only trust the superuser with rebooting the system. */  
  16.     if (!capable(CAP_SYS_BOOT))  
  17.         return -EPERM;  
  18.   
  19.     /* For safety, we require "magic" arguments. */  
  20.     if (magic1 != LINUX_REBOOT_MAGIC1 ||  
  21.         (magic2 != LINUX_REBOOT_MAGIC2 &&  
  22.                     magic2 != LINUX_REBOOT_MAGIC2A &&  
  23.             magic2 != LINUX_REBOOT_MAGIC2B &&  
  24.                     magic2 != LINUX_REBOOT_MAGIC2C))  
  25.         return -EINVAL;  
  26.   
  27.     /* Instead of trying to make the power_off code look like 
  28.      * halt when pm_power_off is not set do it the easy way. 
  29.      */  
  30.     if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  31.         cmd = LINUX_REBOOT_CMD_HALT;  
  32.   
  33.     lock_kernel();  
  34.     switch (cmd) {  
  35.     case LINUX_REBOOT_CMD_RESTART:  
  36.         kernel_restart(NULL); //这个就是重新启动Linx的命令  
  37.         break;  
  38.   
  39.     case LINUX_REBOOT_CMD_CAD_ON:  
  40.         C_A_D = 1;  
  41.         break;  
  42.   
  43.     case LINUX_REBOOT_CMD_CAD_OFF:  
  44.         C_A_D = 0;  
  45.         break;  
  46.   
  47.     case LINUX_REBOOT_CMD_HALT:  
  48.         kernel_halt();  
  49.         unlock_kernel();  
  50.         do_exit(0);  
  51.         panic("cannot halt");  
  52.   
  53.     case LINUX_REBOOT_CMD_POWER_OFF:  
  54.         kernel_power_off();  
  55.         unlock_kernel();  
  56.         do_exit(0);  
  57.         break;  
  58.   
  59.     case LINUX_REBOOT_CMD_RESTART2:  
  60.         if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {  
  61.             unlock_kernel();  
  62.             return -EFAULT;  
  63.         }  
  64.         buffer[sizeof(buffer) - 1] = '\0';  
  65.   
  66.         kernel_restart(buffer);  
  67.         break;  
  68.   
  69. #ifdef CONFIG_KEXEC  
  70.     case LINUX_REBOOT_CMD_KEXEC:  
  71.         ret = kernel_kexec();  
  72.         break;  
  73. #endif  
  74.   
  75. #ifdef CONFIG_HIBERNATION  
  76.     case LINUX_REBOOT_CMD_SW_SUSPEND:  
  77.         ret = hibernate();  
  78.         break;  
  79. #endif  
  80.   
  81.     default:  
  82.         ret = -EINVAL;  
  83.         break;  
  84.     }  
  85.     unlock_kernel();  
  86.     return ret;  
  87. }  


继续跟踪kernel_restart()函数,如下:

最终会调用一个machine_restart(cmd)函数,这个是跟具体的芯片有很大的关系的,我们进一步的分析如下:

看到了吗,最终是调用arch_reset来复位整个系统的。同时我们也看到了S3C2440的reset的函数如下:

在arm_pm_restart = s3c24xx_pm_restart()函数,最终也是调用arm_machine_restart(mod, cmd)来实现的。而在arm_machine_restart()函数中,最终也是调用arch_reset()函数来实现,而这个函数是在哪里呢。在S3C2440没有看到arch_reset函数的实现,因此从S3C2410中找到了如下的代码,请继续看下面的代码:

       终于看到了arch_reset函数,最终是采用S3C2410或者S3C2440的WatchDog来实现reboot的命令的。大家可以想想,busybox的poweroff命令,是如何实现通过Linux系统关闭整个系统的电源呢,其实很简单,只需要实现下面的函数中的pm_power_off的回调函数即可。

       我们可以通过一个GPIO来控制整个系统的电源,而通过上面的pm_power_off的回调函数来实现,只需要在pm_power_off函数对GPIO进行操作就可以了。你看不是很简单吗?



转自:http://blog.youkuaiyun.com/wavemcu/article/details/8544333

[mtk7621] reboot -f处理流程 reboot -f调用的是下面的处理函数:命令行为:machine_restart函数用的是下面的文件: 阅读详情

相关推荐

高通linux-reboot 分析

reboot 分析 1       版本 平台:高通8009 busybox:1.24 linux kernel:3.18.48   2       流程 可以通过reboot、halt、poweroff等命令,对linux操作系统进行控制,具体的操作流程如下:   3       分析 在arm 板上,可知: # ls /sbin/reboot -la lrwxrwx...

智能硬件研发,欢迎交流 2447

busybox之reboot命令流程分析

busybox启动的时候,会注册reboot的处理信号。

szembed的博客 1517

NDVI空间分布变化Slope趋势分析与F检验(自用)

1.NDVI数据预处理2.一元线性回归分析3.Slope趋势分析与F检验

什么都学亿点点的博客 9017

Kernel函数解析之kernel_restart

主要功能为回调注册到reboot_notifier_list链表中的回调函数,因为有部分模块,需要在重启系统前做一些模块相关的工作,举个简单的例子,比如系统要重启时edma正在搬移数据,那么回调后可以把EDMA停下来,以防止系统挂死。回调所有注册syscore shutdown回调的回调函数,通常注册syscore的回调有3个:suspend\resume\shutdown,其中suspend和resume在低功耗流程中调用,shutdown则在此处调用。回调,注册的模块可能会保存一些自己关心的数据。...

szembed的博客 2177

中国1990-2021连续30年土地利用CLCD数据分享

CLCD(China Land Cover Dataset)数据集是由武汉大学黄昕老师基于Google Earth Engine上的335,709景Landsat数据制作的中国年度土地覆盖数据集。为了提高CLCD的时空一致性,还提出了一种包含时空滤波和逻辑推理的后处理方法。该数据集最大的优势在于每年30米的土地利用分类结果,且连续覆盖了30年时间。相比其他产品如GLC_FCS30、Global30、AGLC2000_2015、FROM-GLC10、ESA10、ESRI10等,CLCD在时间分辨率上更高。

qq_48539301的博客 1万+

图文实录|大规模预训练模型:探讨与展望

第二期「澜舟分享会」我们邀请到来自复旦大学计算机学院邱锡鹏教授分享了“大规模预训练模型:探讨与展望”,本此演讲主要围绕大规模预训练语言模型展开讨论,并探讨大模型成功的原因、适用场景、未来发展方向以及身处大模型时代我们的机遇。

langboat_mengzi的博客 1204

linux reboot 流程,基于Linux与Busybox的Reboot命令流程分析

一:Busyobx层的分析这段时间,在忙到一个项目时,需要在busybox中用到reboot命令,开始在busybox中的shell中输入reboot命令,始终如下的信息,然后就停止在那里了,无法重启...为了彻底的弄明白这个问题,我在网络上找了很久,终于有个人写的一个reboot流程分析,我就借花献佛.在这里重新分析下busybox是如何运行这个命令,同时又是如何调用到Linux内核中的mach...

weixin_29002209的博客 909

Calibre电子书阅读器个性化样式定制指南

本文详细介绍了如何通过Calibre电子书阅读器的用户样式表功能进行深度个性化样式定制。从修改全局字体、行距、背景配色(包括深色/浅色模式)到美化代码块、引用等特殊元素,本指南提供了完整的CSS代码示例与实战技巧,帮助用户打造专属的舒适阅读环境,显著提升电子书阅读体验。

vv45678的博客 920

基于linux与busybox的reboot命令流程分析

http://www.xuebuyuan.com/736763.html 基于Linux与Busybox的Reboot命令流程分析 *************************************************************************************************************************** 作者:Easy...

weixin_34038293的博客 194

102、栅极驱动芯片选型与设计

文章摘要 本文分享了栅极驱动芯片选型与设计的实战经验。通过一次因驱动芯片选型不当导致的炸管事故,作者总结了栅极驱动芯片选型的五个关键参数:峰值驱动电流、驱动电压范围、传播延迟匹配、欠压锁定和隔离等级。文章详细分析了自举电路设计中的常见陷阱,以及栅极电阻对开关特性的影响,并强调了PCB布局布线的重要性。最后,作者提供了七条实用建议,包括避免通用型驱动芯片、关注CMTI参数、使用插件电阻等。核心观点是:栅极驱动设计需要综合考虑参数匹配、电路布局和实际调试,示波器实测波形是验证设计的唯一标准。

2401_88858637的博客 44

Windows 下 Claude Code Desktop 安装与使用全记录:从零到一的保姆级图文教程

本文主要介绍了Windows 下 Claude Code 安装全记录:从零到一的保姆级图文教程,希望能对学习使用 Claude Code 的同学们有所帮助。 文章目录 1. 背景 2. 安装Claude Code Desktop 3. 配置第三方API 4. 日常使用

weixin_43178406的博客 8万+

BusyBox 版本1.10-2

BusyBox 版本1.10-2 用户在console下输入reboot命令, busybox会调用到halt_main int halt_main(int argc ATTRIBUTE_UNUSED, char **argv) { ..... /* Perform action. */ if (ENABLE_INIT && !(flags & 4)) { if (ENABLE

sunxianchuan123的专栏 796

超简单五步实现VOS3000客户端呼入配置

1.查看SIP代理  在注册管理中查看自己线路注册的SIP代理IP地址,记录下来。 2.创建普通账户  创建一个呼入使用的普通账号,选择“计费费率”,根据自己情况选择费率,这里做测试就配置了免费的费率,如果费率不是免费,需要给这个普通账户充值。 3.新建对接网关 新建一个对接网关,点击该网关的“补充设置”,选择“网关类型”为静态IP,然后在IP地址栏填上刚才在注册管理里面记录的SIP代...

非著名运维的博客 1万+

BusyBox reboot 流程分析

用户在console下输入reboot命令, busybox会调用到halt_main   int halt_main(int argc ATTRIBUTE_UNUSED, char **argv) { ..... /* Perform action. */  if (ENABLE_INIT && !(flags & 4)) {   if (ENABLE_FEATURE_INITR

嵌入式与人工智能天地 2361

Unity小游戏接入抖音敏感词检测

摘要: 本文介绍了Unity小游戏接入抖音敏感词检测的方法。抖音官方提供了两种API:SensitiveWordCheck用于检测敏感词,ReplaceSensitiveWords用于替换敏感词。此外,若使用键盘输入事件,可通过tt.onKeyboardComplete监听回调,平台会自动屏蔽敏感词(替换为*)。接入官方字库可有效避免审核驳回,适用于小游戏中的用户输入功能(如起名系统)。附官方文档链接及参数说明。

类人_猿的博客 4594

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Linux reboot全过程

嵌入式Linux 下面的reboot命令看似简单,但出问题时定位起来发现别有洞天。 下面就按在shell下执行reboot命令之后程序的执行过程进行解析。 Busybox:1.23.2 ——制作跟文件系统,/sbin/reboot程序的由来 Libc:2.6.1 ——标准C库 Linux kernel:2.6.35 ——内核版本

风之伤 4万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

linux reboot流程,从命令行到内核全解析

针对对象 这篇文章我尽量写的细一些,主要针对于不太懂得嵌入式命令行到系统层过程的新手看,如果觉得有用可以关注一下,不定期写一些新手需要看的文章。 正文 引言 我们在做嵌入式设备的时候,经常会输入一些命令,例如:cat test.txt来查看test.txt文本;又比如输入reboot, 来重启系统。从我们输入reboot到系统重启的整个过程都是如何操作的呢?如果你还不懂,那么可以继续往下面看。 由...

qq_30027083的博客 1万+

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

上一篇: QT显示图片
下一篇: 关于left / right / stereo / mono
simonjay2007
博客等级 码龄17年 104粉丝 112原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值