onelong's blog www.ways2u.com

onelong's blog

站名: onelong's blog

网址: http://www.ways2u.com

目录: 电脑网络 > 手机通信

PR:

Alexa: 1,461,876

备案号:

服务器IP: 198.252.106.223   加拿大

描述: 分享我的乐事,分享知识,与你一起进步......

查询: [ 网站综合查询 | PR查询 | Alexa排名 | 同IP网站 ]

详细介绍

onelong's blog
分享我的乐事,分享知识,与你一起进步......
首页
点滴
知识库
相册
工具
登录
Linux线程和go协程
filed in 工作
post by onelong / 2016-9-22 21:59 Thursday (edit)
记得我以前在简书上写过一个篇文章,多线程基础 今晚我想继续聊一聊这个坑,先聊一下最近的惨状吧,中秋前感冒了一次,吃了点感冒了好了,中秋后再来一个半死的感冒,全身疼痛,感觉是被人毒打了一顿,经过几天的悉心治疗,略见好转,想把今天所想的东西写下来。病号突然想去了解一下golang,想了想学习新的东西主要是了解它的特性,就是特有的功能,而不是简单复习一次语言的语法。golang里面最大的不同就是协程,golang里面没有线程这种东西的,可能是谷歌故意放弃了吧,后来细心想了一想,有协程就够了,已经不需要线程了。看了知乎的文章后,http://www.zhihu.com/question/20862617?sort=created 我更加坚信有协程就够了,不需要线程,协程是更小的执行粒子,作为一个java出身,自以为了解操作系统的人,肯定会去对比协程并发和当前多线程并发,于是就引出了一堆问题了。因为操作系统概念里面是没有协程这个东西的,所以语言上面的协程肯定在操作系统上得不到对应的映射。所以第一次猜想协程是依附在操作系统线程上执行的,因为线程是操作系统最小的执行单位。golang在语言级支持了协程,对开发者很友好。协程和线程的对应关系可以是一对一,多对一的,总而言之,尽可能的减少线程上下文(cpu context)的切换,极大提高cpu的利用率。在网络的开发中,我们会遇到很多io阻塞,系统调用阻塞等,这些操作可能会导致中断,切换线程上下文,这些都是不利于我们更有效的使用cpu的。golang很优雅的封装了一套协程api(事件驱动+channel),使得原本需要复杂代码实现东西,变得极其简单高效。事件驱动+channel是golang的灵魂,如果没有这两样东西,golang就什么都不是了,因为用户控制协程切换的途径就是channel等待。golang的协程并不能完全避免线程上下文的切换,在sys call的时候,还是要切换线程的,和操作系统的做法区别不大,到这里我对协程就聊到这里算了,想了解各种细节的人可以看看知乎的文章,尤其是那些图片,这是很有帮助的。下面我来说说另外的坑。
工作5-6年了,时间算长了,看到一篇文章说golang的协程不是基于用户线程实现的,是库实现的,我就在想难道没用pthread?然后引发一堆狗血。其实我们会接触一堆概念:内核线程,内核进程,轻量级进程,普通进程(用户进程),用户线程等。 确实我很多年前就看过一些相关的文章,那时候看过之后自以为了解了,而今天发现确实想得太少了。对内核而已,是没有进程的,所以没有内核进程,只有内核线程。在Linux的书籍上都是这样说的,“linux线程是轻量级进程”,它是一个特殊的进程,没有共享内存而已。内核线程和轻量级进程是一对一的,也就是说对Linux而言,管理线程和管理进程是一样的。下面继续引用一段描述帮我们理清关系吧:
“我们知道,Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和 fork (),最终都用不同的参数调用do_fork()核内API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的,因 此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文 件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。当使用fork系统 调用时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所 有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的"进程"拥有共享的运行环境,只有栈是独立的,由 __clone()传入。
Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外 pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为 线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。”
从上面可以知道, 轻量级进程是用户态调用sys call创建的,而子线程是用master线程创建的,都是需要系统调用的,开销不小。线程池技术是为了减少频繁的创建销毁线程,提高cpu利用率而生的。所以golang协程执行在一个线程池上,至于这个线程池是不是基于pthread lib这个就不好说了,或者Google的牛人直接sys call 实现了,不使用用户线程,不管怎样,协程是新概念,但不是新技术,是另一种比较科学并发模型。最后留下一点点东西吧,https://github.com/cloudwu/coroutine c语言版本的协程,因为没有在语言级别上支持,比较难用,需要消息队列来配合使用,但是原理是相似的。
引用(0)
浏览(138)
自定义网络协议
filed in 工作
post by onelong / 2016-8-31 20:48 Wednesday (edit)
2012年的时候,来到深圳做局域网无线控制,最开始参考别人协议做的控制,别人用的是http,http是无状态的,一个请求完成会断开,下一个控制要重新连接,做实时控制比较麻烦。所以我们我用的socket长连接,在最初的时候,因为没人有经验,直接tcp,只管发送,不管发送是否成功,默认它是成功的,假如丢包了,tcp本身就有重发功能,也就是这样,在断网或切换网络的时候,客户端并不能马上检测出来是否断开了。于是加了心跳和超时,心跳也是单向发包的,需要服务器端确认,如果超时了就认为是断开了,则重连。经过一系列的修改,客户端通讯变得稳定了,但是还是有问题,控制数据包有时候也会丢掉,导致控制无反应。于是还是加了响应包,解决了问题。后来数据包多了,发现有些丢包是因为粘包了,后来继续定了一个规则进行把粘的数据包分开。最初的分包规则很简单,和http差不多用\r\n分开,也就是每个数据固定用\r\n结束。我最初接触到网络封装协议就是这么粗暴的,我知道很多人大学的时候都搞过了,我们当时的老大却没有给我任何建议,就这样弄成了一个产品,后来这个产品装机量也到达30w,还得了很多客户的好评。
每次说到网络协议,我们总觉得这个东西特别高深的样子,因为概念有点抽象,一说到自定义协议,很多人就吓住了。其实协议有简单的也有复杂的,主要是看需求 ,上面我们定义的协议

猜你喜欢