进程:CPU分配资源的最小单位,进程拥有代码和打开的文件资源、数据资源、独立的内存空间。 进程的切换内容:页全局目录、内核栈、硬件上下文。线程是后面的第二个和第三个。协程只有硬件上下文。 线程:从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程,线程拥有自己的栈空间。 线程拥有5种状态:初始化、可运行、运行中、阻塞、销毁。 对操作系统来说,线程是最下的执行单元,进程是最小的资源管理单元。进程和线程都是操作系统所管理的。 协程不是被操作系统内核所管理,而完全是由程序员所控制的(在用户态执行)。这样带来的好处就是性能得到很大的提升,不会像线程那样需要上下文切换来耗费资源。 协程的好处: 缺点:无法利用多核资源、进行阻塞操作会阻塞掉整个程序。 跨平台、跨体系架构、无需线程上下文切换。无需原子操作锁定及同步开销。方便切换控制流、高并发+高拓展+低成本。
扣的别人博客上的图。当应用程序访问某块数据时,操作系统首先会检查,是不是最近访问过此文件,文件内容是否缓存在内核缓存区。如果是,OS直接根据read系统调用提供的buf地址,将内核缓存区的内容拷贝到buf所指定的用户空间中去。如果不是,操作系统则首先将磁盘上的数据拷贝到内核缓存区,这一步主要依靠DMA传输,然后再把内核缓冲区上的内容拷贝到用户缓存区中。wirte函数再把用户缓冲区的内容拷贝到网络堆栈相关的内核缓冲区中,最后socket再把内核缓冲区的内容发送到网卡上。
DMA用来处理与硬件的通讯,CPU仍然需要处理两次数据拷贝,与此同时,在用户态与内核态也发生了多次的上下文切换,加重了CPU的负担。
让数据传输不需要经过user space 使用mmap来代替read调用,可以减少一次读数据时候的拷贝,在文件很大的时候,是能够提高效率的。 当你的程序map了一个文件,但是当这个文件被另一个进程截断时,write系统调用会因为访问非法地址而被sigbus终止。 解决方式:为SIGBUS信号建立信号处理程序;使用文件租借锁,在mmap文件之前加锁,并且在操作完文件后解锁。使用sendfile 只能将文件传递到套接字上去,反之则不行。不仅减少了拷贝次数还减少了上下文切换,数据传送只发生在内核空间使用splice 该系统调用在两个文件描述符之间移动数据,而不需要在内核空间和用户空间来回拷贝,splice调用利用了Linux提出的管道缓冲区机制, 所以至少一个描述符要为管道。A:客户端 B:服务端 考虑一种正常的情况,A发送了一个请求连接,但因为连接请求报文丢失而未收到确认。于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕就释放了连接。A总共传了两个请求连接,第一个丢失,第二个到达了B。现在假定出现一种情况A发出了一个请求报文并没有丢失,而是在某些网络节点长时间滞留了,以至于延误到连接释放以后的某个时间才能够到达B。本来这是一个已经失效的连接。但B收到连接之后,误以为A又发起了一次新的连接。于是向A发出确认的报文段,同意建立连接。假定不采用三次握手,那么B只要发送了确认,新的连接就建立了。由于现在A并没有发出连接请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的连接已经建立了,并一直等待A发来数据。B的许多资源就这样浪费掉了。
补充问题:为什么A在time-wait必须等待2MSL?
为了保证A发送的最后一个ACK报文段能够到达B 因为A发送的ACK报文有可能会丢失,B就会重传FIN+ACK,A在2MSL(最长报文段寿命) 中收到之后,再重传一次确认,然后B进入到close状态。如果没有2MSl 的话,B就无法按照正常的步骤进入CLOSE状态。防止“已失效的连接请求报文”出现在本连接中。模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。模板是创建泛型类或者函数的蓝图或者公式。 库容器,比如迭代器和算法,都是泛型编程的例子。您可以使用模板来定义函数和类。 类模板,type是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。
类模板,可以定义相同的操作,拥有不同数据类型的成员属性。函数模板。