首页 > 免root版 > 用gg修改器必须root吗_gg修改器免root
用gg修改器必须root吗_gg修改器免root

用gg修改器必须root吗_gg修改器免root

版本:V5.47   大小:16.44MB
语言:中文   系统:Android/ 
相关专题:破解版 汉化版 手机版
免积分无病毒不花钱

软件简介

大家好,今天小编为大家分享关于用gg修改器必须root吗_gg修改器免root的内容,赶快来一起来看看吧。

学习云原生不仅要学习云原生的理论知识,动手操作,懂原理,预期还是找到更好的工作,go语言的原理面试的时候基本必问,go的线程调度和多线程的模型,无论觉得他难很生僻也好,一定要学的,可能一时难以吸收,很多知识就是不断的重复,加深印象,尽量吸收,随着时间的推移,你会发现某一天突然就顿悟了。

线程加锁(一)

  • ① 理解线程安全

做任何语言的开发,都要去考虑线程安全,可能不少人没做过多线程开发,对于线程安全的理解不太深入。现在大部分的CPU机器,都是多核的,多核的特性,一个计算节点有多个CPU,如果一个应用启动了多线程,一部分线程第一个CPU运行,一部分线程被第二个CPU运行,多个CPU跑多个线程,如果多个线程同时访问多个内存地址,假设没有任何优化的机制,CPU是要读内存的,但是CPU的运行速度是非常快的,至少是快过内存,如果所有的数据都访问内存效率就降低了,目前的CPU的架构体系里面,都有缓存机制,L1、L2、L3这些缓存会缓存一些数据,这些数据就是会被放到缓存上,这个线程在访问这些数据时候,第一次会从内存加载,后面读写都是通过本地缓存来进行的,这样就会出现,可见性。举个例子:内存里面放了一个k-v,两个线程同时读写,在最开始的时间两边读到的是一样的,在某个时间线程1的value修改了,在另一个线程value没有发生改变,依然是原来的值,这就是线程不安全了,通过一种方式保证线程的安全,希望在多线程的编程模式下面,我的程序不会出错。解决方案就是加锁。保护你对变量的访问,当有多个线程在访问一个数据的时候,做过数据库的开发,需要进行行锁的,在我释放锁之前,别人是不能进行修改的。另一个线程要修改的时候,需要拿到锁,才可以进行修改。

  • ② 锁
  1. Go语言不仅仅提供基于 CSP的通讯模型 ,也支持基于共享内存的多线程数据访问。
  2. Sync 包提供了锁的基本原语。
  3. sync.Mutex互斥锁:Lock()加锁, Unlock(解锁。
  4. sync.RWMutex读写分离锁:不限制并发读,只限制并发写和并发读写) 。
  5. sync.WaitGroup:等待一组groutie返回来。
  6. sync.Once:保证某段代码只执行- -次。
  7. sync.Cond:让一组goroutine在满足特定条件时被唤醒。

线程调度(二)

  • ① 介绍
  1. 进程:资源分配的基本单位
  2. 线程:调度的基本单位
  3. 无论是线程还是进程,在linux中都以task_ struct 描述,从内核角度看,与进程无本质区别
  4. Glibc 中的pthread库提供NPTL ( Native POSIX Threading Library )支持

  • ② Linux进程的内存使用

早期的操作系统访问内存,直接就访问对应的物理地址了,如果多个进程的时候它们之间互相抢内存的地址,一个电脑就4G的物理地址很多进程都在抢,具体给那个呢?就开始有了虚拟地址,一个机器有4G的内存,这些物理地址就是固定的了,每起一个应用进程的时候,就会虚拟出来一部分的内存空间出来,这些内存空间的地址就是虚拟的地址,虚拟地址包含下面图例的内容。追求最高的性能,就是一个物理内存对应一个虚拟内存,如果是这种方式,记录很多很多页的数据,就发展成多级的页面,比较常见的就是4级页表,PGD: page global directory;PUD: page upper directory;PMD: page middle directory;PT: page table。真正的物理内存的地址就是通过这4级索引找到page table。

  • ③ CPU对内存的访问
  1. CPU.上有个Memory Management Unit ( MMU )单元。
  2. CPU把虚拟地址给MMU , MMU去物理内存中查询页表,得到实际的物理地址。
  3. CPU维护一份缓存Translation Lookaside Buffer ( TLB ) ,缓存虚拟地址和物理地址的映射关
    系。

  • ④ 进程切换开销

直接开销

  1. 切换页表全局目录( PGD )
  2. 切换内核态堆栈
  3. 切换硬件上下文 (进程恢复前,必须装入寄存器的数据统称为硬件上下文)
  4. 刷新TLB
  5. 系统调度器的代码执行

间接开销:CPU 缓存失效导致的进程需要到内存直接访问的IO操作变多

  • ⑤ 线程切换
  1. 线程本质上只是- -批共享资源的进程,线程切换本质上依然需要内核进行进程切换。
  2. 一组线程因为共享内存资源,因此一个进程的所有线程共享虚拟地址空间,线程切换相比进程切换,主要节省了虚拟地址空间的切换。
  • ⑥ 用户线程

无需内核帮助,应用程序在用户空间创建的可执行单元,创建销毁完全在用户态完成。

  • ⑥ Goroutine

Go语言基于GMP模型实现用户态线程

  1. Goroutine :表示goroutine ,每个goroutine都有自己的栈空间,定时器,初始化的栈空间在2k左右,空间会随着需求增长。
  2. Machine :抽象化代表内核线程 ,记录内核线程栈信息, 当goroutine调度到线程时,使用该goroutine自己的栈信息。
  3. Process :代表调度器,负责调度goroutine ,维护一个本地goroutine队列, M从P.上获得goroutine并执行,同时还负责部分内存的管理。

  • ⑦ MPG的对应关系

  • ⑧ GMP 模型细节

  • ⑨ P的状态
    _Pidle:处理器没有运行用户代码或者调度器,被空闲队列或者改变其状态的结构持有,运行队列为空
    _Prunning:被线程M持有,并且正在执行用户代码或者调度器
    _Psyscall:没有执行用户代码,当前线程陷入系统调用
    _Pgcstop:被线程M持有,当前处理器由于垃圾回收被停止
    _Pdead:当前处理器已经不被使用

  • ⑩ G的状态

_Gidle:刚刚被分配并且还没有被初始化,值为0,为创建goroutine后 的默认值
_Grunnable:没有 执行代码,没有栈的所有权,存储在运行队列中,可能在某个P的本地队列或全局队列中(如上图)。
_Grunning:正在执行代码的goroutine,拥有栈的所有权
_Gsyscall:正在执行系统调用,拥有栈的所有权,与P脱离,但是与某个M绑定,会在调用结束后被分配到运行队列
_Gwaiting:被阻塞的goroutine,阻塞在某个channel的发送或者接收队列
_Gdead:当 前goroutine未被使用,没有执行代码,可能有分配的栈,分布在空闲列表gFree,可能是一个刚刚初始化的goroutine,也可能是执行了goexit退出的goroutine
_Gcopystar:栈正在被拷贝,没有执行代码,不在运行队列上,执行权在
_Gscan :GC 正在扫描栈空间,没有执行代码,可以与其他状态同时存在

  • ⑪ G的状态转换图

  • ⑫ G所在的位置
  1. 进程都有一个全局的G队列
  2. 每个P拥有自己的本地执行队列
  3. 有不在运行队列中的G
    3.1. 处于channel阻塞态的G被放在sudog
    3.2. 脱离P绑定在M上的G,如系统调用
    3.3. 为了复用,执行结束进入P的gFree列表中的G
  • ⑬ Goroutine 创建过程

1.获取或者创建新的Goroutine结构体
1.1 从处理器的gFree列表中查找空闲的Goroutine
1.2 如果不存在空闲的Goroutine ,会通过runtime.malg创建一个栈大小足够的新结构体
2. 将函数传入的参数移到Goroutine的栈上
3. 更新Goroutine调度相关的属性,更新状态为Grunnable
4. 返回的Goroutine会存储到全局变量allgs中

  • ⑭ 将Goroutine 放到运行队列上
  1. Goroutine设置到处理器的runnext作为下一个处理器执行的任务
  2. 当处理器的本地运行队列已经没有剩余空间时(256) ,就会把本地队列中的一部分Goroutine和待加入的Goroutine通过runtime.runqputslow添加到调度器持有的全局运行队列上
  • ⑮调度器行为
  1. 为了保证公平,当全局运行队列中有待执行的Goroutine时,通过schedtick保证有一定几率(1/61)会从全局的运行队列中查找对应的Goroutine
  2. 从处理器本地的运行队列中查找待执行的Goroutine
  3. 如果前两种方法都没有找到Goroutine ,会通过runtime.findrunnable进行阻塞地查找Goroutine
    3.1 从本地运行队列、 全局运行队列中查找
    3.2 从网络轮询器中查找是否有 Goroutine等待运行
    3.3 通过runtime.runqsteal尝试从其他随机的处理器中窃取一半待运行的Goroutine

内存管理(三)

  • ① 关于内存管理的争论
  1. java/golang:内存管理太重要了!手动管理麻烦且容易出错,所以我们应该交给机器去管理!【不相信人,给人的精力腾出来做业务功能,让机器管理内存】
  2. c/c++:内存管理太重要了!所以如果交给机器管理,我不能放心!【语言本身,相信开发人员的素质,相信开发理解内存管理的机制,容易分散精力】
  • ② 堆管理

  • ③ 堆内存管理
  1. 初始化连续内存块作为堆
  2. 有内存申请的时候,Allocator从堆内存的未分配区域分割小内存块
  3. 用链表将已分配内存连接起来
  4. 需要信息描述每个内存块的元数据:大小,是否使用,下一个内存块的地址等
  5. 内存回收就是扫描堆内存,将不再被使用的内存设置为unused

  • ④ 堆内存管理的挑战
  1. 内存分配需要系统调用,在频繁内存分配的时候,系统性能较低。
  2. 多线程共享相同的内存空间,同时申请内存时,需要加锁,否则会产生同-块内存被多个线程访问的情况。
  3. 内存碎片的问题,经过不断的内存分配和回收,内存碎片会比较严重,内存的使用效率降低。
  • ⑤ ThreadCacheMalloc概览

  • ⑥ TCMalloc
  1. page:内存页,-块8K大小的内存空间。Go与操作系统之间的内存申请和释放,都是以
  2. page为单位的
  3. span:内存块,一个或多个连续的page组成一个 span
  4. sizeclass :空间规格,每个span都带有一个 sizeclass ,标记着该 span中的page应该如何使用
  5. object :对象,用来存储一个变量数据内存空间 ,-个span在初始化时,会被切割成一堆等大的object ; 假设object的大小是16B,span大小是8K , 那么就会把span中的page就会被初始化8K/ 16B = 512个object。所谓内存分配,就是分配一个object出去

对象大小定义
1.小对象大小: 0~256KB
2.中对象大小 : 256KB~1MB
3.大对象大小: >1MB

小对象的分配流程:ThreadCache -> CentralCache -> HeapPage ,大部分时候, ThreadCache缓存都是足够的,不需要去访问CentralCache和HeapPage,无系统调用配合无锁分配,分配效率是非常高的

中对象分配流程:直接在 PageHeap中选择适当的大小即可, 128 Page的Span所保存的最大内存就是1MB

大对象分配流程:从large span set选择合适数量的页面组成span ,用来存储数据

  • ⑦ Go语言内存分配

两个大小一样的span class对应一个size class ,一个存指针,一个存直接引用,内存直接引用的span无需内存回收

  • mcache :小对象的内存分配直接走sizeclass从1到66,每个class两个spanSpan大小是8KB,按span class大小切分
  • mcentralSpan内的所有内存块都被占用时,没有剩余空间继续分配对象, mcache会向mcentral申请1个
    span,mcache拿到span后继续分配对象当mcentral向mcache提供span时,如果没有符合条件的span,mcentral会向mheap申请span
  • mheap当mheap没有足够的内存时,mheap会向OS申请内存,Mheap把Span组织成了树结构,而不是链表然后把Span分配到heapArena进行管理,它包含地址映射和span是否包含指针等位图
    为了更高效的分配、回收和再利用内存
  • ⑧ 内存回收

引用计数(Python,PHP,Swift)

  1. 对每一个对象维护一 个引用计数,当引用该对象的对象被销毁的时候,引用计数减1,当引用计数为0的时候,回收该对象
  2. 优点:对象可以很快的被回收,不会出现内存耗尽或达到某个阀值时才回收
  3. 缺点:不能很好的处理循环引用,而且实时维护引用计数,有也一定的代价

标记-清除( Golang )

  1. 从根变量开始遍历所有引用的对象 ,引用的对象标记为”被引用” ,没有被标记的进行回收
  2. 优点:解决引用计数的缺点
  3. 缺点:需要STW ( stop the word ) , 即要暂停程序运行

分代收集( Java )
按照生命周期进行划分不同的代空间,生命周期长的放入老年代,短的放入新生代,新生代的回收频率高于老年代的频率

  • ⑨ mspan
  1. allocBits :记录了每块内存分配的情况
  2. gcmarkBits:记录了每块内存的引用情况,标记阶段对每块内存进行标记,有对象引用的内存标记为1 ,没有的标记为0

这两个位图的数据结构是完全一致的 ,标记结束则进行内存回收,回收的时候,将allocBits指向gcmarkBits ,标记过的则存在,未进行标记的则进行回收。

  • ⑩ GC 工作流程

Golang GC的大部分处理是和用户代码并行的

Mark :

  1. Mark Prepare:初始化GC任务,包括开启写屏障(write barrier)和辅助GC(mutator assist) ,统计root对象的任务数量等。这个过程需要STW
  2. GC Drains:扫描所有root对象,包括全局指针和goroutine(G)栈上的指针(扫描对应G栈时需停止该G) ,将其加入标记队列(灰色队列),并循环处理灰色队列的对象,直到灰色队列为空。该过程后台并行执行

Mark Termination:完成标记工作,重新扫描(re-scan)全局指针和栈。因为Mark和用户程序是并行的,所以在Mark过程中可能会有新的对象分配和指针赋值,这个时候就需要通过写屏障( write barrier)记录下来,re-scan再检查一下,这个过程也是会STW的

Sweep:按照标记结果回收所有的白色对象,该过程后台并行执行

Sweep Termination:对未清扫的span进行清扫,只有上-轮的GC的清扫工作完成才可以开始新一轮的 GC

  • ⑪ 三色标记【白色、灰色、黑色】
  1. GC开始时,认为所有object都是白色,即垃圾
  2. 从root区开始遍历,被触达的object 置成灰色
  3. 遍历所有灰色object ,将他们内部的引用变量置成灰色,自身置成黑色
  4. 循环第3步,直到没有灰色object了,只剩下了黑白两种,白色的都是垃圾
  5. 对于黑色object ,如果在标记期间发生了写操作,写屏障会在真正赋值前将新对象标记为灰色
  6. 标记过程中, mallocgc新分配的object ,会先被标记成黑色再返回

包引用与依赖管理(四)

  • ① Go 语言依赖管理的演进

回顾GOPATH

  1. 通过环境变量设置系统级的 Go语言类库目录
  2. GOPATH的问题?
    不同项目可能依赖不同版本、代码被clone以后需要设置GOPATH才能编译

vendor

  1. 自1.6版本,支持vendor目录,在每个Go语言项目中,创建-个名叫vendor的目录,并将依赖拷贝至该目录.
  2. Go语言项目会自动将vendor目录作为自身的项目依赖路径

好处

  1. 每个项目的vendor目录是独立的,可以灵活的选择版本
  2. Vendor目录与源代码-起check in到github ,其他人checkout以后可直接编译
  3. 无需在编译期间下载依赖包 ,所有依赖都已经与源代码保存在一起
  • ② vendor管理工具

通过声明式配置,实现vendor管理的自动化

  1. 在早期,Go语言无自带依赖管理工具,社区方案鱼龙混杂此较出名的包括:Godeps, Glide
  2. Go 语言随后发布了自带的依赖管理工具Gopkg
  3. 很快用新的工具gqmod替换掉了gopkg
    ● 切换mod开启模式: export GO111MODULE=on/off/auto
    ● Go mod相比之前的工具更灵活易用,以基本统一了 Go语言依赖管理
  4. 目的
    ● 版本管理
    ● 防篡改
  • ③ Go mod 使用
  1. 创建项目
  2. 初始化Go模块
    ● go mod init
  3. 下载依赖包
    ● go mod download (下载的依赖包在$GOPATH/pkg.如果没有设置GOPATH。则下载在项目根目最/pkg )
    ” ● 在源代码中使用某个依赖包 .如/emicklei/go-resthul
  4. 添加缺少的依赖并为依赖包瘦身
    ” ● go mod tidy
  5. 把Go依赖模块添加到vendor目录
    ● go mod vendor
  6. 配置细节会被保存在项目根目录的go.mod中,可在require或者replacement中指定版本

Go mod 样例

动手编写一个HTTP Server(五)

  • ① 理解网络协议层

  • ② 理解socket

socket被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。Linux中的一-切都是文件,为了表示和区分已经打开的文件,UNIX/Linux会给每个文件分配一个ID,这个ID就是一个整数,被称为文件描述符网络连接也是一个文件,它也有文件描述符服务器端先初始化Socket,然后与端C口绑定(bind),对端C门进行监听(listen),调用accept阻塞,等待客户端连接在这时如果有个客户端初始化一个Socket ,然后连接服务器(connect) ,如果连接成功,这时客户端与服务器端的连接就建立了服务端的Accept接收到请求以后,会生成连接FD,借助这个FD我们就可以使用普通的文件操作函数来传输数据了,例如:

  • 用read()读取从远程计算机传来的数据
  • 用write()向远程计算机写弓入数据
  • ③ 阻塞IO模型

  • ④ 非阻塞 IO 模型

  • ⑤ IO 多路复用

  • ⑥ 异步IO

  • ⑦ Go语言高性能 httpserver的实现细节

Go语言将协程与fd资源绑定

  1. 一个socket fd与一个协程绑定
  2. 当socket fd未就绪时,将对应协程设置为Gwaiting状态,将CPU时间片让给其他协程
  3. Go语言runtime调度器进行调度唤醒协程时,检查fd是否就绪,如果就绪则将协程置为Grunnable并加入执行队列
  4. 协程被调度后处理fd数据

调试(六)

  • ① debug

gdb

  1. Gccgo原生支持gdb,因此可以用gdb调试Go语言代码,但dIv对Go语言debug的支持比gdb更好
  2. Gdb对Go语言的栈管理,多线程支持等方面做的不够好,调试代码时可能有错乱现象

dlv:Go语言的专用debugger。

  • ② 更多debug方法

添加加日志

  1. 在关键代码分支中加入日志
  2. 基于fmt包将日志输出到标准输出stdout: fmt.Println()
  3. fmt无日志重定向,无日志分级

即与日志框架将日志输出到对应的appender

  1. 比如可利用glog进行日志输出
  2. 可配置appender,将标准输出转至文件
  3. ]支持多级日志输出,可修改配置调整日志等级
  4. 自带时间戳和代码行,方便调试
  • ③ 性能分析(Performance Profiling)

CPU Profiling:在代码中添加CPUProfile代码,runtime/pprof包提供支持

  1. CPU profile:程序的CPU使用情况,每100毫秒采集一次CPU使用情况
  2. Memory Profile:程序的内存使用情况
  3. Block Profiling:非运行态的goroutine细节,分析和查找死锁
  4. Goroutine Profiling:所有goroutines的细节状态,有哪些goroutine,它们的调用关系是怎样的

PS:开发很多人是应用开发,有些高级语言导致开发人员对于底层了解的非常少,很多高级语言已经封装了底层的调用,很多时候不需要解除的,建议去理解下为什么语言要有这个功能,要具体是解决了什么问题,例如这些线程问题,在linux里面是怎么解决的,从一个点去解决的,分享学习方法的时候解决的,本质上要对linux操作系统了解,也不用急着死磕,积累到一定程度可能就顿悟了。

以上就是关于用gg修改器必须root吗_gg修改器免root的全部内容,希望对大家有帮助。

教程推荐

热门下载

大家都在搜

网站地图 Copyright © 2022 gg修改器 版权所有