协程与线程辨析
协程与线程的区别:
协程只在用户态进行切换,协程上下文切换开销比较小(仅寄存器和栈),协程栈大小灵活且通常来说比较小,一个线程有多个协程,同一时间内一个线程仅执行一个协程,协程在C++通过主动让出来调度
线程切换需要进入内核态,调度由操作系统控制,如果同CPU切换了属于不同进程的线程会导致上下文切换开销大(如PCB/VMA/CR3等),线程栈固定且通常大很多,线程是CPU调度的最小单元,CPU同一个时间内只执行一个线程(创建线程数的上限和虚拟内存空间有关!!!)
协程的栈比线程的栈小
好处:在有限内存内能够创建更多协程,支持更多的并发
坏处:运行在协程上的程序,函数调用不能太深,不然会爆栈
如何解决爆栈问题
有栈协程可以手动设置栈的大小,或者部分语言可以设置动态扩容的栈
无栈协程通过将协程内语句抽象为有限状态机,将各个状态离散地存在堆上来避免爆栈,不过这就比较费时间
协程通常用在I/O密集型的任务,比如DART一个设计就是,scan操作会同时对多个slot发出获取该slot指向的远端数据节点的RDMA read请求,当一个 ...
场景:测远端RDMA读SSD的带宽,使用InfiniBand网卡
RDMA prepareRDMA在CPU-DRAM层面的连接是比较常规的,但是对于新手来说容易忽略一些事情,而且这种代码比较繁琐,也没什么所谓的逻辑性,通常是哪里细节之类的没处理好导致寄了
在RDMA连接之前,需要先TCP连接,交换彼此的GID/LID/QP等一众元数据,而且是需要双方都互相交换的
交换完元数据之后,两侧的RDMA程序都需要将自己的状态转为RTS(Ready to Send),顺序一般是INIT->RTR->RTS
1234567891011121314read(tcp_fd, &meta, sizeof(meta)); // meta is metadata.write(tcp_fd, &client_meta, sizeof(client_meta));...memset(&attr, 0, sizeof(attr)); attr.qp_state = IBV_QPS_RTS; attr.sq_psn = 0; attr.timeout ...
计算机系统
未读2-23 con本节介绍了一些让web服务器能够并发的方法
1,创建进程这里我们看代码,在父进程accept到请求后,子进程关闭监听fd,父进程关闭连接fd,这样子可以避免内存泄漏
1234567891011121314151617181920int main(int argc, char **argv){ int listenfd, connfd; struct sockaddr_in clientaddr; socklen_t clientlen = sizeof(clientaddr); Signal(SIGCHLD, sigchld_handler); listenfd = Open_listenfd(argv[1]); while (1) { connfd = Accept(listenfd, (SA *) &clientaddr, &clientlen); if (Fork() == 0) { Close(listenfd); /* Child closes its l ...
计算机系统
未读不得不吐槽你软的计算机网络教育真的十分零散,建议从计算机系引入计网,并且删除《无人系统设计》捏
2-20 net这只是入个门
局域网有一个hub/switch,被称为交换机,可以将与其相连的机器进行局部通信;通信的时候是需要标识的,这里在内部是使用Mac来标识地址的
局域网之间可以通过bridge来进行通信
上面的统称为局域网,LAN,因为他们的范围有限;不同的局域网之间可以通过路由器来链接,路由器之间的连接为WAN广域网
如果要从某个主机将数据传给另一个主机,那么网络上是有一些机制的,比如子网掩码可以获取到目标主机和发送主机是否在同一个局域网这个信息(IP地址和子网掩码255.255.255.0取与);网关是局域网和外部网络通信的接口,连接网络的节点:IP地址?IPv4是32位4字节的地址(其实是按照整型数来存储的),不能作为主机的唯一标识,一般是分层的(类似于你可能在校内网被分配了一个地址,但是校内网在整个世界对外的地址是另一个地址),通常用点十进制方式来表示,以下是一些常用的IP地址:
127.0.0.1 localhost主机的回送地址loopback addres ...
前排声明:本系列笔记为ICS2课堂笔记,由于本人在大二下才开始电子化笔记,因此只能上传ICS2的笔记,故本节有缺失
鲁棒IO主要是为了实现IO读取的高效性和鲁棒性,高效性主要是通过缓存buffer来实现的,buffer会读取文件里面的一段内容,然后根据实际的读取需要来进行读取,如下图
实际上的I/O是基于底层IO来实现的,无论是standard还是rio,比如之前常说的文件流stream其实也是一个类buffer的东西,会根据一些条件,比如\n fflush(stdout)来对缓冲区的东西进行真正的写入
RIO的代码还是有意思的,可以看看:
short count实际读取的数据小于要求的数据:
实际的数据量并没有要求的那么多
确实没读好
计算机系统
未读2-17 mallocmalloc函数概览,要求多少空间就开多少能用的空间,不过事实上是有一定的多余空间的——必须开的是地址8对齐的空间,因为type *p=(type *)malloc(sizeof(type)),在实际使用的时候,为了保险起见,能够让空间能够存放uint64_t类型的数据,所以强制8对齐地址,这会导致一定的空间浪费
拓展堆区空间的函数sbrk(int),如果开空间成功,返回原来的栈顶(堆区空间的可伸长的末尾),参数可以为负,表示收缩空间
优化堆区空间内存的性能:
throughput是指存取数据的速度,比如1秒能够存/取多少数据
utilization是指空间利用率
碎片:内部碎片与外部碎片内部碎片是指给数据开空间的时候,为了让数据满足地址8对齐,或者为了加上内存数据块隐形链表指针,导致的已使用但是并非用于存储数据的空间;外部碎片是指,在内部总空间足够,但是没有一块连续的能够存储预开空间大小的空间,就会产生外部碎片
堆区内存块的结构、只读数据区域、数据区域(可读写);乃至于局部变量和全局变量也是在不同的区域(局部变量更有可能直接在寄存器里面)
链接一下看看!这里数据和代码(指令)区隔得比较远,可以看到%rip需要再加上较多的偏移量才可以到达数据区;这里其实相当于将两个文件的指令整合在一起,将两个文件的数据整合在一起,数据和指令倒还是分开的
可执行可链接模式文件 ...
计算机系统
未读2-11 ramSRAM在访问速度、传送数据量、信息存储持久度、以及信息抗干扰(主要体现为干扰后能恢复原来的状态)优于DRAM,但是太贵了内存控制器会将行列信息分布传送到DRAM内,这样子对于addr的引线就只需要两条(0-3就是00-11,如果是一维数组直接输入下标,则需要0-15即0000-1111,引线的条数就是bit数);一个超单元其实涵盖了8个bit;这里就是先传一个row,DRAM将对应行的信息传到行缓冲区,然后传col,DRAM将缓冲区中列的内容传给data引线(共8条)这里一个内存模块(memory module)有8个DRAM芯片,每个超单元存一个字节,一次可以取8字节
总线(bus) 是携带地址、数据、控制信号的并行管道的集合,通常总线被许多设备共用;读事务从主存传送数据到CPU,写事务从CPU传送数据到主存考虑一个movq A, %rax,分为三个步骤:1,CPU的总线接口将需要的地址传给主存2,主存将对应地址的内容传给总线接口3,总线接口数据传给寄存器如果是movq %rax, A,则是先总线接口去找主存对应地址,然后将寄存器内容写入总线接口,然后数据从总线接口 ...
计算机系统
未读2-8 mio对于一个程序来说,访问内存、调用函数都是不小的开销;编译器有提高代码执行效率的编译方法,同时也有一些地方在明面上能够做优化但是编译器出于自己的考虑并没有选择做优化
优化的情况:
一些简单的get()函数直接转化成从寄存器中取值,相当于汇编代码中去掉了调用函数的步骤
编译器没法优化的情况:
一直调用一个返回值的函数,尽管很多时候其返回值固定,但是出于对这个值改变了的担忧,他还是会选择跑去函数里面取值(比如你在遍历一个数组的时候一直调用.length()方法
传入的函数参数是指针,不清楚这些指针会不会指向同一处(这个是真的有歧义!);以及函数内部有对全局变量的更改,调用单次和多次的效果不一样课本的例子中提到了关于函数调用和访问内存两种情况的性能损耗,不过如果函数调用中本身就含有访问内存的操作,那么这里主要的时间损耗其实还是内存访问
2-9 opt(modern processors)现代处理器的结构:
cache 缓存:instruction cache存放有最近需要用到的指令,方便于ICU去获取需要执行的操作;data cache存放有最近需要用到的内存数据,方便 ...
2-1 risc
程序员可见状态是指Y86-64的指令可以查看或者修改的部分
寄存器比X86-64少了一个%r15,因为需要有一个无寄存器的编码$F$
PC程序计数器
条件码只有SF ZF OF(Negative Zero Overflow)
状态码
内存(小端法)
12种指令(V-value D-address)每种指令比特数长度固定,由第一个byte确定其长度、指令类型以及功能,其中前4个bit确定类型,后4个bit确定功能运算指令包括addp subp andp xorp等等,为上面表格中的OPq所涵盖举例:addq %rax, %rsi为60 06,60表示addq,06 代表 %rax,%rsi
OPq的分支,注意只允许double register
movq的各个情况举例
条件move(double register)跳转指令也是由第一个byte的后4bit确定跳转条件Y86-64的栈跟X86差不多,往低地址增加,栈顶 %rsp 指向栈中的最低地址;然后是关于栈的操作:注意如果是pushq %rsp,先保存原来的rsp再改变rsp;popq %rsp是将栈顶的值保存到rs ...