python多进程 保活_Linux后台开发应该具备技能(持续更新中)

防止丢失,仅以备份,原文有格式。

Linux后台开发应该具备技能(持续更新中)

一、linux和os:

1、linux下io,cpu,memory相关指令需要熟练使用(free ps netstat tcpdump iostat lsof)

memory:top, free, sar, proc/meminfo, proc/pid/status, pmap

cpu:top, sar,

io:iostat, df,du

network:netstat,tcpdump,ping,telnet,traceroute,lsof

system:ulimit

2、ipcs,ipcrm相关指令。

理解:ipcs提供关于一些进程间通信方式的信息,包括共享内存,消息队列,信号。ipcrm移除一个消息对象。

3、awk sed需用掌握

理解:sed,非交互式的流编辑器 awk示例:netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a,S[a]}'

4、buffer和cache的区别

理解:cache,缓存,快取。高速缓存,加快读取速度,比如 CPU->cache->内存。buffer,缓冲区,整合写。

5、scp,wget,curl,rsync等文件传输指令

6、find,cat,tail,grep,xargs等组合文本查找过滤指令

7、gdb调试相关的经验,熟练使用gdb(包括调试coredump,调试死锁,调试cpu占满等等)

8、虚拟内存映射,mmap相关(共享内存的使用实现原理、然后共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少)

8、虚拟内存如何映射到物理内存,物理内存不够怎么处理。缺页换页原理。

理解:CPU在获得虚拟地址之后,需要通过MMU将虚拟地址翻译为物理地址。而在翻译的过程中还需要借助页表,所谓页表就是一个存放在物理内存中的数据结构,它记录了虚拟页与物理页的映射关系。

9、page cache。

10、mmap内存映射。

11、c++进程内存空间分布

12、ELF是什么?其大小与程序中全局变量的是否初始化有什么关系(注意.bss段)

理解:从可执行文件a.out的角度来讲,如果一个数据未被初始化那就不需要为其分配空间,所以.data和.bss一个重要的区别就是.bss并不占用可执行文件的大小,它只是记载需要多少空间来存储这些未初始化数据,而不分配实际的空间

13、使用过哪些进程间通讯机制,并详细说明

理解:进程间通讯,管道(pipe),有名管道(named pipe),消息队列(message queue),共享内存(shared memory),信号量(semophore),信号(singal),套接字(socket)。

线程间同步:互斥量(mutex),条件变量(cond),读写锁(rwlock),信号量(semophore),自旋锁(spinlock)。

14、makefile编写 (跟我一起写Makefile-陈皓)

13、如何定位内存泄露?

14、动态链接和静态链接的区别

15、32位系统一个进程最多多少堆内存

16、系统如何将一个信号通知到进程?

17、写一个c程序辨别系统是64位 or 32位

18、写一个c程序辨别系统是大端or小端字节序

19、信号:列出常见的信号,信号怎么处理?

20、i++是否原子操作?并解释为什么?

15、什么是死锁?如何避免死锁,怎么定位死锁

22、列举说明linux系统的各类异步机制

23、exit() _exit()的区别?

24、如何实现守护进程?

25、linux的内存管理机制是什么?

26、linux的任务调度机制是什么?

16、mv/cp指令的底层实现的区别。

理解:linux文件系统是有一个inode即i节点的机制。

mv 的主要功能就是检查初始文件和目标文件是否存在及是否有访问权限,之后执行 rename 系统调用,因而,当目标文件存在时,mv 的行为由 rename() 系统调用决定,即类似于删除文件后再重建一个同名文件。

目标文件存在,在执行cp 命令之后,文件的 inode 号并没有改变,并且可以看出,cp 使用了 open 及O_TRUNC 参数打开了目标文件。因而当目标文件已经存在时,cp 命令实际是清空了目标文件内容,之后把新的内容写入目标文件。

17、git/svn相关指令及原理?

18、fork子进程之后是怎么实现COW的。

理解:fork()之后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

19、多线程和多进程的区别。

理解:a、多线程共享数据,同步复杂,多进程数据不共享,需要IPC。

b、(多线程)占用内存少,切换简单,cpu利用率高。

c、创建销毁,切换简单,速度快。

b、多线程适用于多核分布式,多进程适用于多核、多机分布式,扩展简单。

线程共享:代码段,公有数据,打开的文件描述符;不共享:线程ID,寄存器的值,线程的栈,错误返回码。

二、c++:

1、string相关,能手写一个相对完整的string(拷贝构造,赋值构造)。string的COW,SSO优缺点(gcc已经用SSO代替COW)。

2、多态和虚函数,多重继承。

理解:编译期多态,函数重载和函数模板;运行期多态,虚函数。多重继承,多个虚指针。

3、sizeof一个类求大小(注意成员变量,函数,虚函数,继承等等对大小的影响)

4、指针和引用的区别

5、多重类构造和析构的顺序

6、stl各容器的实现原理

7、extern c的作用。

理解:被extern "C"修饰的变量和函数是按照C语言方式编译和连接的,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。

8、volatile关键字的意义

理解:正常的变量,从内存中读取之后,CPU的寄存器中就有了这个值,后面就可能会直接复用而不再次读取内存。

volatile代表这个变量是易变的,需要每次都从内存读取。volatile并不能解决多线程同步问题。

9、vector的扩容原理,扩容后的数据拷贝机制?

理解:对于POD类型的数据,直接调用系统函数memcpy,整体拷贝;对于非POD类型,调用其拷贝构造函数,一个一个拷贝。

10、RTTI实现原理,dynamic_cast是怎么保证类型安全转换的?

理解:虚指针指向的虚函数表的-1项保存了type_info*,里面有类的继承体系信息。

11、string与int的各种转换函数(包括c++11),string相关的接口熟练使用

12、模板特化、偏特化、类型萃取

13、static的用法。

static在c语言中有两种用法,修饰静态局部变量,这种变量的生存期长于该函数;修饰全局变量或者函数,代表作用域为文件可见;在c++语言中有两种用法,静态数据成员,静态成员函数。

14、const的用法。

14、const char*/char const */char * const 的区别。

理解:

把一个声明从右向左读( * 读成 pointer to )

const在*后面,表示指针不变:

char * const cp; //cp is a const pointer to char

const在*前,表示值不变:

const char * p; //p is a pointer to const char;

char const * p; //同上

所以:const char *与char const * 效果一样,都是不允许修改指针指向的地址空间的值,即把值作为常量,而char * const则是不允许修改指针自身,不能再指向其他地方,把指针自己当作常量使用。需要注意的是,使用char * const 定一个常量指针的时候一定记得赋初始值,否则再其他地方就没法赋值了。

15、手写一个c++单例模式。

16、C++构造函数初始化列表与构造函数中的赋值的区别

理解:以下三种情况下需要使用初始化成员列表:

情况一、类成员为没有默认构造函数的类类型 因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化

情况二、const修饰的类成员或引用成员数据;

情况三、子类初始化父类的私有成员; 如果类存在继承关系,派生类必须在其初始化列表中调用基类的构造函数

内置数据类型/POD类型,在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的

用户定义类型,结果上相同,但是性能上存在很大的差别。因为类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用拷贝构造函数,在进入函数体之后,进行的是之前的默认构造函数+函数体内的赋值操作。

17、多重继承的内存布局

理解:第一个父类的虚指针,第二个父类的虚指针,第一个父类的数据成员,第二个父类的数据成员,自己的数据成员,其中,自己的虚函数在第一个父类的虚函数表的后面。

三、续:c++11

1、shard_ptr/weak_ptr等智能指针相关,enable_shard_from_this的作用。

理解:enable_shared_from_this是用来让被shared_ptr管理的对象T* p1安全的生成额外的shared_ptr实例p2,且p2与p1共享所有权,可以安全的把对象传给回调而不用担心对象的释放。

2、std::function/std::bind的使用

3、lambda表达式

4、实现一个shard_ptr智能指针,理解侵入式和非侵入式的区别。

5、移动构造函数,右值引用,移动语义、std::move

理解:移动构造函数使用右值引用这个定义来实现移动语义,从而减少内存分配和释放操作。std::move作用是把左值变为右值,并没有移动操作。移动语义提高性能的示例请看:https://github.com/majianfei/practice/blob/master/move/move.cpp

6、c++11内存模型

四、数据结构或者算法:(请刷leetcode)

1、各类排序算法:手写快排(如何避免最糟糕的状态)。

理解:快排平均复杂度n*log(n),最坏n*n,不稳定,递归和非递归实现(非递归利用stack)。

2、堆排序。

3、topK问题。

理解:冒泡时间复杂度n*k,快排时间复杂度n*log(n),堆排时间复杂度n*log(K)

2、字符串转整数(atoi)。

3、大数相乘。

4、二分查找相关

5、bitmap算法相关(处理海量数据)

6、hash相关(例如为什么一般hashtable的桶数会取一个素数?如何有效避免hash结果值的碰撞)

7、二叉树相关

8、两个栈实现一个队列?

9、多叉树深度非递归遍历?

10、B树和B+树的区别(涉及mysql索引)

11、100w数据实时排行榜。(1、桶排序,2、跳跃表)

五、网络编程:(后台开发优先度最高的技能)

1、tcp与udp的区别。

2、udp调用connect有什么作用。

理解:如果一个udp socket,只作为client 向同一个server地址通讯,那么应该调用connect。udp socket connect的作用是,提前告知内核对端的地址,从而可以在随后用send和recv而不是sendto和recvfrom,从而少写一个参数。

少写一个参数几乎没有什么意义。有意义的是,你调用connect之后,一个非server地址发给你的udp包会让系统直接返回icmp并且不会导致recvfrom返回。如果你没调用connect,那么你只能用recvfrom收包,那么你收到包之后首先得判断fromaddr,这很麻烦,用了connect之后就可以省掉这个步骤了。

3、可靠UDP编程。(eg::KCP)

理解:由于TCP的拥塞控制,流量控制,超时重传等机制,导致在网络环境较差的情况下,延迟高。因此在UDP上实现NODELAY,快速重传并关闭拥塞控制算法能降低延迟。

4、TCP和UDP绑定同一端口。

理解:五元组。

4、tcp三次握手和四次挥手的时序图,状态变迁图。

5、TIME_WAIT,如何避免TIME_WAIT状态占用资源。

理解:主动断开的一端会进入TIME_WAIT状态,TIME_WAIT有两个作用,a、可靠地实现TCP全双工连接的终止;b、允许老的重复分节在网络中消逝。blog:TIME_WAIT状态的作用 - only_eVonne - 博客园

修改/etc/sysctl.conf -> net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_timestamps = 1

6、CLOSE_WAIT状态的原因与解决办法。

理解:一般是程序本身的问题,代码层面收到对端的close,需要发送close关闭连接;也有可能是程序来不及响应,导致大量CLOSE_WAIT堆积;BACKLOG太大,导致来不及消费,导致多余的请求还在队列就被对方关闭了。

6、tcp头多少字节?哪些字段?

7、拥塞控制和流量控制的区别。

理解:拥塞控制,作用于网络,防止过多数据注入到网络中,避免网络负载过大。方法:a、慢启动,拥塞避免;b、快重传,快恢复。流量控制,滑动窗口协议,保证分组无差错、有序接受。发送接收双方同步自己的窗口大小,控制发送的流量,消除接收方缓存溢出的可能。

6、慢启动算法。

理解:慢启动为发送方的TCP增加一个窗口:拥塞窗口(cwnd),初始化为一个报文段,指数增长。然后有一个慢开始门限(ssthresh),当cwnd

8、Nagle算法和延迟ACK。

8、connect非阻塞编程如何实现。

理解:非阻塞connect不等待连接成功直接返回-1,且errno=EINPROGRESS,需要用IO多路复用(select)去判断。判断规则:

用select对socket的读写进行监听

那么监听结果有四种可能

a. 可写(当连接成功后,sockfd就会处于可写状态,此时表示连接成功)

b. 可读可写(在出错后,sockfd会处于可读可写状态,但有一种特殊情况见第三条)

c. 可读可写(我们可以想象,在我们connect执行完到select开始监听的这段时间内,

如果连接已经成功,并且服务端发送了数据,那么此时sockfd就是可读可写的,

因此我们需要对这种情况特殊判断)

说白了,在可读可写时,我们需要甄别此时是否已经连接成功,我们采用这种方案:

再次执行connect,然后查看error是否等于EISCONN(表示已经连接到该套接字)。

d. 错误

9、如果select返回可读,结果只读到0字节是什么情况。

理解:表示对端关闭。socket read返回0表示对端关闭,需要自己close(fd),不然会产生CLOST_WAIT状态。

10、keepalive如何理解(tcp的,http的,应用层心跳heart-beat)

理解:tcp keepalive只能判断链接存在,不能判断应用是否可用(死锁,死循环等),且是系统参数,不能单独修改。

http KeepAlive复用tcp链接,避免每次建立链接的开销。

11、列举你所知道的tcp套接字选项,并说明其作用。(SO_LINGER、SO_KEEPALIVE、SO_SENDBUF、SO_RECVBUF)

12、socket什么情况下可读/可写。(注意:默认的可写的套接字发送缓冲区中所需的可用空间是2048)

14、backlog的作用。

理解:backlog用于socket listen(fd, backlog),在linux系统内核中维护了两个队列:syn队列和accept队列, syn队列的长度由/proc/sys/net/ipv4/tcp_max_syn_backlog确定,accept队列的长度由backlog和/proc/sys/net/core/somaxconn中的较小值确定。blog:浅谈tcp socket的backlog参数

16、http1.0,http1.1,http2.0,https的区别?https的加密原理/请求过程?

理解:HTTP1.1:keep-alive,断点续传,HOST域。HTTP2.0:IO多路复用,数据压缩,服务器推送。

17、CGI和FastCGI的区别。

理解:CGI,通用网关接口,是一种通讯协议,让脚本语言具备和HttpServer交互的能力。每次都需要启动一个进程,解析配置文件,初始化执行环境等操作。

FastCGI是CGI的一种改良,使用线程池/进程池来处理一连串的请求,它会由master初始化环境变量和配置文件,每次有新的请求直接传给slave进程/线程去处理请求。

18、http怎么实现的断点续传。

20、EWOULDBLOCK EAGAIN EINPROGRESS等errno的含义

理解:EWOULDBLOCK用于非阻塞模式,不需要重新读或者写

EINTER指操作被中断唤醒,需要重新读/写

EAGAIN 在非阻塞读写的时候,read的时候receive buffer为空,或者write的时候send buffer已满,系统调用返回-1且errno等于EAGAIN,不用特殊处理。

21、epoll的内核实现;epoll和select的区别;epoll的LT和ET模式的区别;epoll的ET模式怎么编程判断读完(EAGAIN);手写epoll echo;

22、tcp连接异常情况(网线插拔,路由器重启,服务器宕机重启等)

23、什么情况下产生RST

理解:a、建立连接的SYN到达某端口,但是该端口上没有正在 监听的服务。b、TCP收到了一个根本不存在的连接上的分节。c、请求超时。 使用setsockopt的SO_RCVTIMEO选项设置recv的超时时间。接收数据超时时,会发送RST包。

24、半打开,半关闭,半连接

25、线程间同步/进程间通讯

理解:进程间通讯:管道,有名管道,信号,信号量,套接字,消息队列,共享内存。

线程间同步:互斥量,条件变量,读写锁,自旋锁...

26、多进程监听同一socket、惊群

理解:在linux2.6版本以后,linux内核已经解决了accept()函数的“惊群”现象,但是epoll的惊群还存在。

解决方案:nginx的互斥锁/SO_REUSEPORT

Linux内核的3.9版本带来了SO_REUSEPORT特性,该特性支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,允许多个套接字bind()以及listen()同一个TCP或UDP端口,并且在内核层面实现负载均衡。

27、SO_RESUEADDR、SO_REUSEPORT的区别。

理解:SO_REUSEADDR作用 a、只要地址不是完全相同,就可以多个socket可以绑定到同一ip上,比如 0.0.0.0和192.168.0.100;b、绑定TIME_WAIT的地址。

SO_REUSEPORT可以将多个socket绑定到同一ip和端口(内核负责负载均衡)。

28、多进程编程中,怎么传递socket描述符(sockfd)。

理解:父进程fork子进程,然后父进程使用socketpair创建流管道,然后父进程可以使用sendmsg传递fd,子进程可以使用recvmsg接收fd。参考nginx。

六、数据库(mysql相关):

1、mysql优化方案

2、mysql的储存引擎。(InnoDB,MyISAM)

理解:InnoDB,支持事务和行锁定,支持外键,索引和数据放在一起。支持行级锁。

MyISAM,查询和插入更快。锁级别为表锁,表锁优点是开销小,加锁快;缺点是锁粒度大,发生锁冲动概率较高,容纳并发能力低,这个引擎适合查询为主的业务。

3、mysql的索引原理。(B+树)

理解:MyISAM->非聚簇索引,数据表和索引表是分开储存的,主索引和辅助索引叶子节点都指向对应数据的物理地址。InnoDB->聚簇索引,主索引指向的是数据本身,辅助索引指向的是主索引。B+树,磁盘以页为单位,B+树可以在每一页上放更多的索引,降低层数,减少IO次数。

4、mysql的几种隔离级别。

5、高并发访问mysql时怎么保证数据不为脏数据(超卖问题)?(1.事务+for update,排他锁,2.消息队列)。

6、悲观锁、乐观锁、CAS、MVCC已经在数据库领域的应用。

理解:相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的重读与否进行检测。乐观锁的实现,CAS/MVCC。悲观锁具有独占和排他特性,使用select ... for update,进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行。

5、mysql explain和慢查询优化

七、redis相关

1、redis zset的底层实现?(跳跃表),为什么不用平衡树?

理解:zset底层实现简单来说是跳跃表,复杂来说是ziplist或者skiplist,不用平衡数据原因:a、平衡树不适合范围查找;b、平衡树的插入和删除引发子树调整,逻辑复杂,skiplist只需要修改相邻节点的指针,简单快速;c、平衡树每个节点包含两个指针,skiplist平均不到2个指针,比平衡树内存上面更有优势(主要取决于每一层的概率p的值,默认1/4,则平均指针数量为1/1-p=1.33,如果为1/2则平均2个指针)。跳跃表在redis中只适用于于sorted set和集群节点的内部结构。

2、redis持久化机制,bgsave的实现原理。(子进程执行save的时候主进程继续修改数据怎么办,fork时COW)

3、redis销毁机制

理解:Redis中同时使用了惰性过期和定期过期两种过期策略。

4、memcached线程模型,内存模型,扩容原理。(包括redis的各种模型,rehash规则)

理解:memcached使用Slab Allocator的内存分配机制,按照预先规定的大小,和增长因子,分割成各种尺寸的块。...

5、redis做消息队列,redis实现优先级队列

6、为什么mysql有自己的缓存还需要redis,redis缓存一致性。

理解:mysql缓存mysql语句和查询结果,消耗内存,不支持分布式。

缓存一致性需要后删或者双删,如果需要强一致性,则需要使用binlog同步到redis,阿里开源canal可以做到。

7、redis锁。

理解:redis能用的的加锁命令分表是INCR、SETNX、SET。

8、redis+mysql怎么保证缓存一致性。

blog:缓存更新的套路 | | 酷 壳 - CoolShell 中的先update再delete的方案,此问题唯一可能导致脏数据的情况就是缓存失效且select并更新缓存,且在此期间另一个请求update并删除了缓存,且在第一个请求的时间线之间完成,由于一般read比write会快很多,write会加锁等操作,所以出现的概率很低。

blog:Redis和mysql数据怎么保持数据一致的? - 掘金 中的第二种方案,使用mysql binglog的增量订阅+消息队列更新到redis的方案。

9、redis为什么这么快。(100000+的QPS(每秒内查询次数))

理解:内存数据库、专门优化的数据结构、单线程无锁无上下文切换消耗、IO多路复用非阻塞IO、...

10、redis主从同步原理

理解:从服务器向主服务器发送SYNC命令,主服务器执行BGSAVE,并用缓存区记录接下来执行的写命令,向从服务器发送RDB文件,然后向从服务器发送接下来的写命令。(第一次连接)。断线重连后会根据双方的偏移量部分同步。《redis设计与实现》

七、其他。

1、一致性哈希算法

3、协程的原理。

理解:协程可以理解为用户态的线程,所有的操作由用户态完成,创建和切换的消耗低,非抢占式。

4、实现一个定长内存池。

5、Lock-Free、无锁队列。

理解:无锁队列依赖CAS的原子操作和Retry-Loop实现,并不是真正的无锁,只是锁的粒度小,和互斥锁的区别在于这是非阻塞锁,而互斥锁是阻塞锁。blog:无锁队列的实现 | | 酷 壳 - CoolShell

6、互斥锁和自旋锁,读写锁。

理解:互斥锁是阻塞锁,会有上下文切换;自旋锁是非阻塞锁,没有切换的开销,适合不长期占有锁被持有的时间较短,而且进程并不希望在重新调度上花费太多的成本;读写锁有三种状态:读加锁状态、写加锁状态和不加锁状态。

7、AOI(九宫格和十字链表,同步玩家移动和同步NPC事件)

8、帧同步、状态同步

理解:帧同步的技术难点:浮点计算,随机数,网路延迟,延迟补偿,预测,快照,回退。

9、如何设计应用层保活机制

10、mq实现原理

11、断线重连机制怎么设计

12、定时器怎么设计

13、应用层buffer设计

理解:参考muduo的Buffer类或者libevent的evbuffer。

14、游戏协议加密算法

八、python

1、python的内存垃圾回收机制

理解:python使用以引用计数为主,分代回收和标记清除为辅的垃圾回收机制。

2、python的线程模型(GIL是CPython专有的,PyPy没有)

3、python的闭包机制

理解:在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

装饰器是闭包的一种应用,但是传递的是函数。

4、字符串编码

5、单例实现

6、可变对象、不可变对象

7、装饰器

8、元类

9、python协程

10、new、init、call

11、python热更新原理

12、生成器、迭代器

理解:可迭代对象,具有__iter__方法,返回一个迭代器,迭代器对象,具有__next__方法,通过for循环本质就是不断调用next()函数实现的。生成器,特殊的迭代器,通过生成器表达式或者yield实现。返回的是一个生成器对象,通过next获得下一个值。原理请看blog里面的文章

13、JIT、字节码和机器码

14、python解释执行过程

15、python虚拟机在执行一个func的流程。

16、python中map和reduce的用法。

九、lua

十、项目相关。

1、简历上面自己参与的每一个功能都能深入回答。

2、画出服务器的框架图?

3、服务器的各种并发/数据同步问题怎么解决?

4、服务器任意连接异常问题怎么解决?

十一、源代码

1、muduo

2、libevent/libev

3、memcached

4、skynet

5、kcp

6、rpc相关(pb,phxrpc...)

7、redis

附:选看:asio,

十二、附录

1、后台开发所需知识点

2、linux相关指令

3、共享内存相关知识

4、高质量面试题

5、gdb调试

6、redis相关

7、如何阅读redis源码

---------------------

作者:majianfei1023

来源:CSDN

版权声明:本文为博主原创文章,转载请附上博文链接!

防止丢失,仅以备份。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值