积累的知识点

运行库和运行环境

任何一门语言,包括其运行库和运行环境,都不可能创造出操作系统不支持的功能,Go语言也是这样,不管它的特性描述看起来多么炫丽,那必然都是其他语言也可以做到的,只不过Go提供了更方便更清晰的语义和支持,提高了开发的效率。

go运行库是go运行环境的充分条件

并行和并发

并发是指程序的逻辑结构。非并发的程序就是一根竹竿捅到底,只有一个逻辑控制流,也就是顺序执行的(Sequential)程序,在任何时刻,程序只会处在这个逻辑控制流的某个位置。而如果某个程序有多个独立的逻辑控制流,也就是可以同时处理(deal)多件事情,我们就说这个程序是并发的。这里的“同时”,并不一定要是真正在时钟的某一时刻(那是运行状态而不是逻辑结构),而是指:如果把这些逻辑控制流画成时序流程图,它们在时间线上是可以重叠的。

并行是指程序的运行状态。如果一个程序在某一时刻被多个CPU流水线同时进行处理,那么我们就说这个程序是以并行的形式在运行。(严格意义上讲,我们不能说某程序是“并行”的,因为“并行”不是描述程序本身,而是描述程序的运行状态,但这篇小文里就不那么咬文嚼字,以下说到“并行”的时候,就是指代“以并行的形式运行”)显然,并行一定是需要硬件支持的。

  • 并发是并行的必要条件,如果一个程序本身就不是并发的,也就是只有一个逻辑控制流,那么我们不可能让其被并行处理。

  • 并发不是并行的充分条件,一个并发的程序,如果只被一个CPU流水线进行处理(通过分时),那么它就不是并行的。

  • 并发只是更符合现实问题本质的表达方式,并发的最初目的是简化代码逻辑,而不是使程序运行的更快。

互斥锁/自旋锁/读写锁/条件锁

互斥锁:进程/线程操作加锁资源时,发现加锁资源被占用,则线程挂起,等待加锁资源状态改变由操作系统唤醒,抢占资源。互斥量是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行。

自旋锁:非阻塞锁,发现加锁资源被占用后会一直尝试获取加锁资源控制权(忙等待)。也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁。(自旋锁一般只在内核层存在,因为操作系统是不能被阻塞的,操作系统阻塞了就没有人能唤醒了,用户层想用自旋锁,需要自己实现)

互斥锁和自旋锁两种锁适用于不同场景

  • 如果是多核处理器,如果预计线程等待锁的时间很短,短到比线程两次上下文切换时间要少的情况下,使用自旋锁是划算的。

  • 如果是多核处理器,如果预计线程等待锁的时间较长,至少比两次线程上下文切换的时间要长,建议使用互斥量。

  • 如果是单核处理器,一般建议不要使用自旋锁。因为,在同一时间只有一个线程是处在运行状态,那如果运行线程发现无法获取锁,只能等待解锁,但因为自身不挂起,所以那个获取到锁的线程没有办法进入运行状态,只能等到运行线程把操作系统分给它的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高。

  • 如果加锁的代码经常被调用,但竞争情况很少发生时,应该优先考虑使用自旋锁,自旋锁的开销比较小,互斥量的开销较大。

读写锁:简单归为读(共享)-写(独占)锁。相对互斥量只有加锁和不加锁两种状态,读写锁有三种状态:读模式下的加锁,写模式下的加锁,不加锁。

读写锁的使用规则

  • 只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁;

  • 只有读写锁处于不加锁状态时,才能进行写模式下的加锁;

排队自旋锁的设计原理

uint64的变量 slock

  • 0-15 owner
  • 16-31 next
  • 32-63 reserve

初始化时,slock 置为0, owner = 0, next = 0。

当有线程想要执行自旋锁的时候,next原子++,并返回给线程一个票据id。当线程释放自旋锁的时候,owner原子++。(只有owner == 自己票据id 表示此时锁对自己可用,否则忙等待)

与传统自旋锁对比

解决了传统自旋锁的不公平性(因为当临界资源的锁被释放时,具体next锁占用的线程是谁,没人知道,不可预估,完全由操作系统决定),而排队自旋锁一定程度上保证了线程对锁资源的顺序。

阻塞/非阻塞/同步/异步

  • 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

  • 同步和异步关注的是消息通信机制(synchronous communication/ asynchronous communication)。

阻塞:执行某个操作会导致当前进程阻塞状态。

非阻塞:执行某个操作不会导致当前进程阻塞状态。

同步:调用者会等待调用返回,并获取到调用结果。

异步:调用者会立即返回,获取不到结果。

异步设计能避免阻塞。

进程状态图:

进程状态图.png

创建:新创建的进程会先创建一个PCB(进程控制块),然后等待内核给他分配资源(进程处于创建状态),当分配资源准备好了,此时进程状态由创建->就绪。

就绪:进程资源准备就绪,处于就绪队列,等待内核调度分配cpu控制权。

执行:进程被内核调度,获得cpu控制权,执行程序逻辑。

阻塞:进程发现某些事情(i/o操作),无法申请资源等等,无法继续执行下去,系统则调用阻塞原语,使进程陷入阻塞状态。等待内核调用唤醒原语再次唤醒。

终止:进程结束,内核回收PCB以及清理工作。

进程切换

进程切换的过程如下:

  • 保存处理机上下文,包括程序计数器和其他寄存器。(处理机信息保存)

  • 更新PCB信息。

  • 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。

  • 选择另一个进程执行,并更新其PCB。

  • 更新内存管理的数据结构。

  • 恢复处理机上下文。

注意,进程切换与处理机模式切换是不同的,模式切换时,处理机逻辑上可能还在同一进程中运行。如果进程因中断或异常进入到核心态运行,执行完后又回到用户态刚被中断的程序运行,则操作系统只需恢复进程进入内核时所保存的CPU现场,无需改变当前进程的环境信息。但若要切换进程,当前运行进程改变了,则当前进程的环境信息也需要改变。

进程间通信

  • 共享内存:由于进程间用户空间是相互独立的,必须通过特殊系统调用实现两个进程用户空间共享。而线程内是天然共享用户空间。(linux下使用shmget实现)

  • 消息队列:

  • 管道:

  • 信号:

  • 信号量:

  • 套接字:

线程间通信

  • 操作全局变量

线程与进程:

在没有线程的进程:进程是拥有资源和独立调度的基本单位

线程:调度基本单位

进程:资源的基本单位

多线程之间共享进程的用户空间(线程只拥有自己的独立栈空间),进程是系统资源的基本单位。

线程属性

在多线程操作系统中,把线程作为独立运行(或调度)的基本单位,此时的进程,已不再是一个基本的可执行实体。但进程仍具有与执行相关的状态,所谓进程处于“执行”状态,实际上是指该进程中某线程正在执行。

线程是一个轻型实体,它不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。

不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统为它们创建成不同的线程。

同一进程中的各个线程共享该进程所拥有的资源。

线程是处理机的独立调度单位,多个线程是可以并发执行的。在单CPU的计算机系统中,各线程可交替地占用CPU;在多CPU的计算机系统中,各线程可同时占用不同的CPU,若各个CPU同时为一个进程内的各线程服务则可缩短进程的处理时间。

—个线程被创建后便开始了它的生命周期,直至终止,线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。

tcp滑动窗口协议

接收窗口大小:
发送窗口大小:
ack包:
当前发送seq:

停等协议:滑动窗口接收方与发送方一致1。(发1个,停下来等一下,接收ack)

后退n协议:滑动窗口接收方1,发送方n。(发送方一次发送n个,接收方一次接受1个,当发现第x个丢包了,则需要重发x-n个包)

重传协议:后退协议问题上,丢哪个包,重传哪个包。

tcp建立连接

tcp.png
  1. 第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;

  2. 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;

  3. 第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

    完成了三次握手,客户端和服务器端就可以开始传送数据。以上就是TCP三次握手的总体介绍。

那四次分手呢?

当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。

  1. 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

  2. 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;

  3. 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;

  4. 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

为什么需要2MSL等待时间:

MSL定义的规则如下:在TIME_WAIT状态下,客户端发出ACK以后需要等待2个MSL的时间。假如ACK在传输中丢失,服务端会重新发送FIN,客户端收到以后会重新发ACK。假如在2MSL时间中客户端都没有收到服务端重发的FIN,那么认为服务端已经收到了客户端发送的ACK。此时客户端可以安全断开连接。

客户端发送的ACK segment存活期1MSL,服务端重发FIN segment存活期1MSL,加一起2MSL。2MSL是一个临界值,利用尽量大的等待时间来确保TCP连接断开的可靠性。

假设握手需要两次:c->s 和 s->c建立连接

有一种情况:
c->s 发起连接,服务端收到之后过了很长时间才响应(c端的连接已经失效),c端因为连接已经失效,不理会,然后s端以为建立起连接,等待c端传输数据流, 会造成连接资源的浪费。

  • FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。(主动方)

  • FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你(ACK信息),稍后再关闭连接。(主动方)

  • CLOSE_WAIT:这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。(被动方)

  • LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。(被动方)

  • TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FINWAIT1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。(主动方)

  • CLOSED: 表示连接中断。

非抢占式(Nonpreemptive)

让进程运行直到结束或阻塞的调度方式 容易实现 适合专用系统,不适合通用系统

抢占式(Preemptive)

允许将逻辑上可继续运行的在运行过程暂停的调度方式 可防止单一进程长时间独占CPU 系统开销大(降低途径:硬件实现进程切换,或扩充主存以贮存大部分程序

go 语言中的抢占式调度(总结):

GOMAXPROCS == 1时 tight loop 情况下的抢占式调度

引用一句话:Currently goroutines are only preemptible at function call points. 现在goroutine抢占式调度只在函数调用点插入调度埋点。 (而且现在go语言只能算得上半抢占半协作式调度, 因为所谓的在函数调用点插入调用埋点不准确,准确的说法是,在morestack->newstack(连续栈的扩展)这个过程才会插入调度埋点,实现抢占式调度)

DDOS解决办法

  • 限制同时打开的SYN半连接数目,缩短SYN半连接的time out 时间,限制SYN/ICMP流量

  • 用户应在路由器上配置SYN/ICMP的最大流量来限制SYN/ICMP封包所能占有的最高频宽,这样,当出现大量的超过所限定的SYN/ICMP流量时,说明不是正常的网络访问,而是有黑客入侵。早期通过限制SYN/ICMP流量是最好的防范DOS的方法,虽然目前该方法对于Ddos效果不太明显了, 不过仍然能够起到一定的作用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容