Linux进程概念_副进程

最全的Linux教程,Linux从入门到精通

======================

  1. linux从入门到精通(第2版)

  2. Linux系统移植

  3. Linux驱动开发入门与实战

  4. LINUX 系统移植 第2版

  5. Linux开源网络全栈详解 从DPDK到OpenFlow

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。

需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在这里插入图片描述

比如等待键盘输入的状态:
在这里插入图片描述

**挂起状态:**当内存不足的时候,OS通过置换进程的代码和数据到磁盘进程的状态就叫做挂起。
将长时间不执行的进程代码和数据(此时进程只有pcb)换出到磁盘。(磁盘swap分区)

如果频繁挂起状态 磁盘内部空间满了怎么办?

首先,当内存不足的时候,OS会将进程的代码和数据切换到磁盘中的swap分区,但是肯定不是很频繁的,因为频繁的换入换出会极大影响操作系统的运行效率。
当磁盘swap分区满了,就说明者操作系统面临着极大的内存压力,就只能宕机了。

运行状态—R

一个进程处于运行状态,并不意味着进程一定处于运行当中,运行状态表明一个进程要么在运行中,要么在运行状态等待里。也就是说,可以存在多个R状态的进程.
注意:所有处于运行状态,即可被调度的进程,都被放到运行队列中,当操作系统需要切换进程运行时,就直接在运行队列中选取进程运行。

可中断睡眠—S

一个进程处于可中断睡眠状态,意味着该进程正在等待某件事情的完成,,处于可中断睡眠的进程随时可以被唤醒,也可以杀掉。
例如:
我们正在不断循环输出一句代码如图:
在这里插入图片描述
可中断睡眠状态是可以被杀死的!
如图:
正在处于可中断睡眠状态的进程被 kill - 9 PID(目标进程PID)指令杀死。
在这里插入图片描述

运行结果和查看运行状态打如下:
我们明明是在运行程序,应该处于运行态,但是我们利用ps命令查的状态却是可中断睡眠S状态,为什么会+早就这样的结果?
在这里插入图片描述
因为在冯诺依曼体系中显示器属于外设,PCB在运行队列的时间远远小于在阻塞队列的时间(相比较而言),并且外设的效率较慢,所以我们所查的状态为S状态(浅度睡眠状态)。

深度睡眠状态-D

一个进程处于深度睡眠状态,表示该进程不会被杀掉,即便是当操作系统压力过大也不行,也不可被唤醒,只有当该进程得到外设设备的反馈才可以被杀死.

例如: 当一进程要求对磁盘进行写入操作,在磁盘的进行写入期间,该进程就在等待外设设备—磁盘的反馈,类似于阻塞状态,此时进程状态为深度状态D,在得到磁盘的反馈之前是不会被操作系统杀死的,以免数据丢失。(除非拔电源!)

暂停状态-T

kill -l 可以展现所有kill指示命令。
在这里插入图片描述
在Linux中,我们可以采用kill -9 目标进程PID 来使进程暂停。
在这里插入图片描述
kill -18 进程PID 让暂停的进程重新执行。
在这里插入图片描述

暂停状态和休眠状态的区别

休眠:阻塞状态,就是等待某中资源就绪。
暂停:只是单纯地把代码停住了。

例如:我们在使用gdb时在第7行处打了断点,实际gdb进程给当前进程在对应地断点上发送了kill -19命令使我的进程暂停,但是当前进程还是处于运行状态,他并没有和休眠状态一样等待某种资源,只是把这个进程停住了。

gdb调试时打上断点.
在这里插入图片描述
此时该进程打处于打暂停状态,t。在这里插入图片描述

僵尸状态- Z

一个进程已经退出,但是还是不被OS释放,而是处于一个被检测的状态。(一般由父进程或者操作系统调查的)
当一个程序在运行时把子进程退出,但是父进程没有读取到子进程到子进程退出时的返回代码时就会产生僵尸进程。

例如: fork函数创建父子子进程后打印5次后会推出,而父进程则会不断循环打印信息,5次之后,子进程推出了,父进程还在运行,但是父进程并没有读取子进程的退出信息,此时子进程就陷入僵尸状态。


```cpp
在这里插入代码片
```#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	printf("I am running...\n");
	pid_t id = fork();
	if(id == 0){ //child
		int count = 5;
		while(count){
			printf("I am child...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);
			sleep(1);
			count--;
		}
		printf("child quit...\n");
		exit(1);
	}
	else if(id > 0){ //father
		while(1){
			printf("I am father...PID:%d, PPID:%d\n", getpid(), getppid());
			sleep(1);
		}
	}
	else{ //fork error
	}
	return 0;
} 


再通过脚本指令检测该进程的变化:

while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;sleep 1;done

五次打印之后,子进程退出,父进程还在运行,子进程的状态就变味僵尸状态Z。
在这里插入图片描述

僵尸进程的危害

1:僵尸进程的必须一直维持下去,因为僵尸基础进程要告诉父进程的退出信息,如果父进程一直没有读取到僵尸进程的退出信息,那么子就将会一直维持在僵尸状态。

2:僵尸进程维护推出状态本身就是要用数据维护,也属于进程基本信息,及保存在PCB中,Z状态不退出,则操作系统需要PCB一直维护。

3:一个父进程创造了很多进程,但是一直处于僵尸状态而不能回收,此时就会造成内存资源的浪费,因为PCB也是一种数据结构,也要占用内存。

4:僵尸进程申请的资源无法进行回收,僵尸进程越多,实际可用的资源就越少,这就会造成内存泄漏等问题。

孤儿进程

在Linux进程中大多数是父子关系,如果子进程先退出,父进程没有接受子进程退出的指令,那么子进程就为僵尸进程。如果父进程先退出,子进程没有父进程进行内存资源的回收,那么子进程就为孤儿进程。

如图以下代码,子进程一直处于死循环状态,父进程在打印五次代码后就先退出。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	printf("I am running...\n");
	pid_t id = fork();
	if(id == 0){ //child
		int count = 5;
		while(1){
			printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid(), count);
			sleep(1);
		}
	}
	else if(id > 0){ //father
		int count = 5;
		while(count){
			printf("I am father...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);
			sleep(1);
			count--;
		}
		printf("father quit...\n");
		exit(0);
	}
	else{ //fork error
	}
	return 0;
} 

经历五秒之后,子进程的PPID为1,说明当父进程退出后子进程已经被操作系统领养了。
在这里插入图片描述

进程优先级

什么是优先级?
优先级实际上就是获取某种资源的先后顺序,而进程优先级就是指cpu资源分配的先后顺序。

优先级存在的原因?
CPU的资源是有限的,当有多个进程时,cpu需要进程优先级来确定进程获取CPU资源的先后顺序来提高操作系统的整体效能。

查看系统进程

p -l 查看系统进程
在这里插入图片描述

UID:代表执行者的身份
PID: 代表这个进程的代号
PPID:代表在这个进程是由哪个进程衍生呢而来,即父进程的代号
PRI: 代表这个进程可被执行的优先级,其值越小越早被执行。
NI: 代表这个进程的nice值。

PRI与NI

NI代表的时nice值,表示其可被执行的优先级的修正数值。

PRI越小越快被执行,有了nice值后,PRI = PRI + nice;

NI的取值范围打为 -20到19。

通过top命令来更改进程的nice值

top指令进入页面,查看CPU被调用情况。
在这里插入图片描述
进入top页面后按r后输入你想改变进程的PID
例如 :我要改变优先级的目标进程为ps进程
在这里插入图片描述
输入你要改变的nice值:
比如当前输入的nice的值为1;
在这里插入图片描述
查看被调用的目标进程PID,发现PRI值已经发生改变,即为默认值80 + N;
当前ps进程的PRI值正好为80+1;
在这里插入图片描述

通过renic命令修改进程的nice值

输入renice 修改的nice值 目标进程PID

例如:我要修改的目标进程打为txt进程,修改的nice为-1
在这里插入图片描述
之后利用命令指令ps -al查看进程,发现txt进程的nice值为-1,该进程PRI进程值就变为79;
在这里插入图片描述
**注意:**当我们要修改的nice值为负数时,及提高该进程的优先级需要在renice指令前加上sudo命令。

四个重要概念】

竞争性:系统进程数目众多,而cpu资源只有少量,甚至只有1个,所以进程之间是有竞争属性的。为了高效完成任务,更合理地竞争相关资源,便有了优先级。

独立性:多进程运行,需要独享各中资源,多进程运行期间互不干扰。

并行多个进程多个CPU下分别,同时进行进行,称之为并行。
在这里插入图片描述

并发多个进程一个cpu下采用进程切换的方式,在一段时间内,让多个进程都得以推进,称之为并发。

时间片:给每个进程一个固定的时间享受cpu资源,当该进程时间到了,便会切换到其他进程。

抢占:当更高优先级进程到来之时抢占正在被cpu运行的优先级较低的进程。

出让:进程让出cpu资源

例如:
当cpu执行进程1到了超出时间片时,cpu会切换运行队列中的下一个进程继续运行,在这段时间内一个cpu得以让多个进程都得以得到推进,这样称之为并发。
在这里插入图片描述
切换:
**上下文数据:**寄存器中的临时数据。
我们知道当进程A暂时被切换的时候,需要进程A顺便带走自己的上下文数据!就是为了下次切换到A进程的时候能够按照之前的逻辑继续往后运行。
在这里插入图片描述

环境变量

基本概念

环境变量一般指在操作系统中用来指定操作系统运行环境的一些参数。

例如,我们编写的C/C++代码,在各个目标文件进行链接的时候,从来不知道我们所连接的东京库在哪里,但是照样可以连接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找

常见环境变量

PATH: 指定命令搜索路径

HOME: 指定用户的主工作目录(及用户登录到Linux系统中的默认所处目录)

SHELL: 当前Shell,它的值通常是/bin/bash

环境变量相关命令

echo $NAME NAME为带查看环境变量名
env 查看系统中的环境变量
set 查看系统中的所有变量
unset 清楚环境变量

测试PATH

例如:当要运行我们所写的程序时是要带上路径的,
在这里插入图片描述

但是:我们在Linux下输入命令行不用带上路径是因为这些命令的路径在环境变量所维护的搜索路径中,当我们使用ls命令时系统就会通过环境变量从左到右依次在各个路劲当中查找。
在这里插入图片描述
我们可以将我们的可执行程序的路径加入到PATH环境变量中之后,我们就可以不带路径直接写程序名就可以了。

export PATH=$PATH:运行程序目录
在这里插入图片描述

总结:系统运行一个命令必须要获取它的路径,系统命令的路径在环境变量中的搜索路径中,而我们的运行程序的路径不在PATH环境变量搜索路径中,要运行必须带上路径。

测试HOME:
任何一个用户在运行系统登录时都有自己的主工作目录(家目录),环境变量当中即保存着该用户的主工作目录
对于普通用户:
在这里插入图片描述
对于超级用户:
在这里插入图片描述

环境变量的组织方式

在系统中,环境变量的组织方式如下:
在这里插入图片描述
每个程序都会收到一张环境变量表,环境表是一个字符指针数组,每个指针指向一个以\0为结尾的环境字符串,最后一个字符指针为空。

通过代码获取环境变量

当我们调用main函数时实际给main函数传递了三个参数。

一:main函数的第三个参数接受的实际就是环境变量表,调用main函数时编译器通过第三个参数来获取系统的坏境变量
1:例如:我们通过命令行来输出第三个参数获取的环境变量。
在这里插入图片描述
运行结果如下:
成功打印出各个环境变量的值。
在这里插入图片描述
2:除了使用main函数的第三参数获取环境变量之外,我们还可以通过第三方全局变量environ获取。(本质上通过main()函数的第三个参数获取方式相同)
在这里插入图片描述
运行结果如下:
在这里插入图片描述
注意: libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern进行声明。

二:main函数的前两个参数中,第二个参数是一个指向指针数组的地址,第一个参数是代表该指针数组个数的整数,代表数组的大小。
例如:
让我们通过代码来看看该指针数组的值:
在这里插入图片描述
运行结果如下:
可以看到该argv指向的指针数组存储着命令行参数的地址,而argv整数代表指针数组的个数。
在这里插入图片描述
三:命令行参数的意义
让我们看看测试代码:
在这里插入图片描述
运行结果如下:
我们发现该程序能根据不同的选项输出不同的结果
在这里插入图片描述
**总结:**命令行参数的意义就是让同一个程序,通过多个选项的方式来实现的不同的子功能,这些命令行参数是在当我们进行命令行调用的时候由当前bash(父进程)先拿到后由子进程继承的。

通过系统调用来获取环境变量

getenv函数可以根据所给的环境变量名,在系统所给的环境变量表中搜索,并返回一个指向相应指针的字符串地址。

例如:我们使用getenv函数获取环境变量PATH的值。
在这里插入图片描述
运行结果如下:
使用getenv函数成获取环境变量PATH的值:
在这里插入图片描述

所有环境变量通常是具有全局属性的

我们知道在命令行中运行命令时它的父进程都可以叫作当前bash。
例如:
我们运行一个进程查看该进程的PID和PPID
在这里插入图片描述
再打通过ps axj | head -1 && ps axj | grep PPID(父进程)查看:
可知当前bash的PID就是我们运行程序的父进程。
在这里插入图片描述
任何一条命令在命令行启动的时候,所有环境变量都是继承当前bash(父进程)
例如:
我们查找了一个不存在的环境变量class
在这里插入图片描述
运行结果:
该环境变量是不存在的。
在这里插入图片描述
我们向当前bash(父进程)中导出环境变量class后再运行时发现该进程有了环境变量。
在这里插入图片描述
总结: 所有父进程的环境变量都会被子进程继承,当命令行打运行的时候,会创建一个子进程,再次运行的时候该子进程就会继承class这个环境变量。所以每个子进程都会继承当前bash(父进程)的环境变量,即环境变量具有全局属性,所有子进程都会继承。

Linux下的全局变量和局部变量

在命令行中一般可以定义两种变量,一种为局部变量,一种为全局变量(环境变量)。
例如:我们在当前bash(父进程)临时定义了一个hello的局部变量,可以利用set | grep hello 命令查询。
在这里插入图片描述
但是我们运行程序创建子进程的过程中却无法从当前bash(父进程)继承。
在这里插入图片描述

在这里插入图片描述
当我们利用export命令在当前bash导出环境变量hello时,运行程序发现该进程可以继承当前bash的环境变量。
在这里插入图片描述
**总结:**对于在命令行中(当前bash)定义的局部变量不能被子进程继承,对于export定义的全局变量可以被子进程继承。

程序地址空间

以下为虚拟内存空间分布图:
在这里插入图片描述

在Linux操作系统中我们通过打印各种区域的变量地址来进行验证。
在这里插入图片描述
运行结果如下:我们发现各种区域变量的地址与分布图是相同的。
在这里插入图片描述
我们来看看一段奇怪的代码:
我们使用fork函数创建一个子进程,父进程执行else 语句休眠3秒,子进程先执行代码else if(id == 0),修改变量 g_val 为100后,父进程再进行继续执行以下代码。
在这里插入图片描述
运行结果如下:
我们发现父子进程的输出地址是一样的,但是变量内容却不一样.
在这里插入图片描述
总结:
1:变量内容不一样,所以父子进程绝对输出的变量绝对不是同一个变量,但是两个变量的输出地址确实一样的,说明这两个输出地址绝对不是物理地址。
2:在Linux地址下,这种地址叫做虚拟地址。我们在C/C++所看到的地址,全部都是虚拟地址。

进程地址空间

其实对于我们之前所说“程序地址空间”是不准确的,准确应该来说是“进程地址空间”。

历史上计算机的程序空间

原来操作系统是直接访问物理内存,将可执行程序加载导物理程序,操作系统在调度时会从众多进程中通过数据结构的方式打选择一个进程让CPU执行。
但是如果进程一的地址为野指针(地址是任意的)的话,打cpu有可能会去访问进程三的物理内存,进而有可能将进程三的内存修改或者读取,这样做法特别不安全,还会违反了进程的独立性。
在这里插入图片描述

现代计算机的程序空间

为了保持进程的独立性,现代计算机提出了以下方式。
当我们的进程被cpu访问时,cpu根据虚拟地址,然后通过映射找到特定的物理内存进行访问。当虚拟地址为非法地址时(如野指针), cpu就无法通过虚拟地址空间映射为物理内存,所以就保护了物理内存。
在这里插入图片描述

地址空间是什么?

地址空间是一种内核数据结构,它里面包含着各个区域的划分,在Linux当中进程地址空间具体由结构体 struct mm_struct 实现。在结构题mm_struct中,记录这各个特定的区域的划分,分别为start和and。在程序的执行过程中,进栈出栈时也会发生各个区域范围变化,本质上就是对stat和end标记值 ± 特定的范围即可。
在这里插入图片描述
进程地空间和页表(用户级)每个进程都私有一份,只要保证每一个进程的页表映射的是物理内存的不同区域,就能做到进程之间不会相互干扰,从而保证进程的独立性。

一:例如
我们曾经谈过的代码,如当子进程修改全局变量g_val之后,父进程再继续执行,此时输出值是不同的,输出地址确实相同的。这是为什么呢?
在这里插入图片描述
父子进程被创建,子进程就会继承父进程的相关属性,其中包括了地址空间和页表。当子进程尝试修改去全局变量g_val时,为了保持进程的独立性,操作系统会将原来的内存空间重新拷贝一份,然后将子进程的的页表进行修改,映射到操作系统新开辟的物理空间中。完成更改之后,父子进程的地址空间虽然相同,但是通过页表映射到的物理内存是不同的区域,所以我们看到的值是不同的,但是虚拟地址没有改变,即输出的地址也就是相同的。

在这里插入图片描述
例如二:
我们曾经用过fork函数,使用fork函数之后会有两个返回值,且这两个返回值的内容是不同的。这是为什么呢?

return 的本质:就是将id进行写入,写入之后父子进程都会执行相同的if else语句进行判断,其中父子进程也是拿着各自的ID。父子进程return 写入ID时发生了写时拷贝,所以父子进程其实存在于不同的物理内存,有着属于自己变量的物理空间。 只不过在用户层上,用同一个地址(虚拟地址)标识了。

程序编译连接形成一个可执行程序时,程序内部有地址么?

1:地址空间不仅是操作系统内部要遵守的,其实编译也要遵守。即编译器编译代码的时候,就已经给我们形成各个区域,代码区,字符常量区,初始化数区,未初始化数据区等,并且采用和Linux内核中一样的编址方式,给每一个变量,每一行代码都进行了编址,所以程序在编译的时候,每一个字段早已有了一个虚拟地址。
2:每次调用函数时,每一个程序都会保留着调用函数的地址(虚拟地址),当程序加载到物理内存中(数据和代码的拷贝),操作系统在物理内存中会给每一个函数定义一个物理内存地址。
总结:在编译完毕时,将各种范围代码程序的start和end会对应填充到进程地址空间的各种代码区。可执行程序每一个变量,每一个函数都有地址(虚拟地址),加载时将每一个变量的虚拟地址填充到页表的左侧,将程序加载到物理内存中操作系统所分配的物理地址加载到页表的右侧建立映射关系。所以cpu能够根据变量名(本质也是虚拟地址),找到特定区域的虚拟地址,根据映射关系找到对应的物理内存进行访问。
在这里插入图片描述

为什么要存在进程地址空间?

为了做好运维面试路上的助攻手,特整理了上百道 【运维技术栈面试题集锦】 ,让你面试不慌心不跳,高薪offer怀里抱!

这次整理的面试题,小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。

本份面试集锦涵盖了

  • 174 道运维工程师面试题
  • 128道k8s面试题
  • 108道shell脚本面试题
  • 200道Linux面试题
  • 51道docker面试题
  • 35道Jenkis面试题
  • 78道MongoDB面试题
  • 17道ansible面试题
  • 60道dubbo面试题
  • 53道kafka面试
  • 18道mysql面试题
  • 40道nginx面试题
  • 77道redis面试题
  • 28道zookeeper

总计 1000+ 道面试题, 内容 又全含金量又高

  • 174道运维工程师面试题

1、什么是运维?

2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

3、现在给你三百台服务器,你怎么对他们进行管理?

4、简述raid0 raid1raid5二种工作模式的工作原理及特点

5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

7、Tomcat和Resin有什么区别,工作中你怎么选择?

8、什么是中间件?什么是jdk?

9、讲述一下Tomcat8005、8009、8080三个端口的含义?

10、什么叫CDN?

11、什么叫网站灰度发布?

12、简述DNS进行域名解析的过程?

13、RabbitMQ是什么东西?

14、讲一下Keepalived的工作原理?

15、讲述一下LVS三种模式的工作过程?

16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

17、如何重置mysql root密码?

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?

3、现在给你三百台服务器,你怎么对他们进行管理?

4、简述raid0 raid1raid5二种工作模式的工作原理及特点

5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?

6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?

7、Tomcat和Resin有什么区别,工作中你怎么选择?

8、什么是中间件?什么是jdk?

9、讲述一下Tomcat8005、8009、8080三个端口的含义?

10、什么叫CDN?

11、什么叫网站灰度发布?

12、简述DNS进行域名解析的过程?

13、RabbitMQ是什么东西?

14、讲一下Keepalived的工作原理?

15、讲述一下LVS三种模式的工作过程?

16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?

17、如何重置mysql root密码?

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值