精华内容
下载资源
问答
  • 缓冲区
    万次阅读
    2021-12-09 22:11:39


    前言

    缓冲区的功能是用一块内存空间来暂时存放命令数据,以免出现因为数据和命令的处理速度慢于发送速度而导致的数据丢失和性能问题。但因为缓冲区的内存空间有限,如果往里面写入数据的速度持续地大于从里面读取数据的速度,会导致缓冲区需要越来越多的内存来暂存数据。缓冲区占用的内存超出了设定的上限阈值时,会出现缓冲区溢出。

    如果发生了溢出,会丢数据了。随着累积的数据越来越多,缓冲区占用内存空间越来越大,一旦耗尽了 Redis 实 例所在机器的可用内存,就会导致 Redis 实例崩溃。

    缓冲区是用来避免请求或数据丢失,只有用对了,才能真正起到“避免”的作用。

    Redis 是典型的 client-server 架构,所有的操作命令都需要通过客户端发送给服务器端。缓冲区在 Redis 中主要应用场景:

    1. 是在客户端和服务器端之间进行通信时,用来暂存客户端发送的命令数据,或者是服务器端返回给客户端的数据结果。
    2. 是在主从节点间进行数据同步时,用来暂存主 节点接收的写命令和数据。

    一、客户端输入和输出缓冲区

    为了避免客户端和服务器端的请求发送和处理速度不匹配,服务器端给每个连接的客户端都设置了一个输入缓冲区和输出缓冲区,称之为客户端输入缓冲区和输出缓冲区。

    输入缓冲区会先把客户端发送过来的命令暂存起来,Redis 主线程再从输入缓冲区中读取命令进行处理。当 Redis 主线程处理完数据后,会把结果写入到输出缓冲区,再通过输出缓冲区返回给客户端,如下图:
    在这里插入图片描述

    二、输入缓冲区溢出的应对方法

    输入缓冲区就是用来暂存客户端发送的请求命令,导致溢出的情况主要是下面两种:

    1. 写入了 bigkey,比如一下子写入了多个百万级别的集合类型数据;
    2. 服务器端处理请求的速度过慢,例如,Redis 主线程出现了间歇性阻塞,无法及时处理正常发送的请求,导致客户端发送的请求在缓冲区越积越多。

    要查看和服务器端相连的每个客户端对输入缓冲区的使用情况,使用 CLIENT LIST 命令:

    CLIENT LIST 
    id=5 addr=127.0.0.1:50487 fd=9 name= age=4 idle=0 flags=N db=0 sub=0 psub=0 mu 
    

    CLIENT 命令返回的信息虽然很多,但只需要重点关注两类信息就可以:

    1. 与服务器端连接的客户端的信息。案例展示的是一个客户端的输入缓冲区情况,有多个客户端,输出结果中的 addr 会显示不同客户端的 IP 和端口号。
    2. 与输入缓冲区相关的三个参数:
      • cmd,表示客户端最新执行的命令。这个例子中执行的是 CLIENT 命令。
      • qbuf,表示输入缓冲区已经使用的大小。这个例子中的 CLIENT 命令已使用了 26 字节大小的缓冲区。
      • qbuf-free,表示输入缓冲区尚未使用的大小。这个例子中的 CLIENT 命令还可以使用 32742 字节的缓冲区。qbuf 和 qbuf-free 的总和就是,Redis 服务器端当前为已连接 的这个客户端分配的缓冲区总大小。这个例子中总共分配了 26 + 32742 = 32768 字 节,也就是 32KB 的缓冲区。

    CLIENT LIST 命令可以通过输出结果来判断客户端输入缓冲区的内存占用情况。如果 qbuf 很大,而同时 qbuf-free 很小,就要引起注意了,因为这时候输入缓冲区已经占用了很多内存,而且没有什么空闲空间了。此时客户端再写入大量命令的话, 会引起客户端输入缓冲区溢出,Redis 的处理办法就是把客户端连接关闭,结果就是业 务程序无法进行数据存取了。

    通常情况下 Redis 服务器端不止服务一个客户端,当多个客户端连接占用的内存总量, 超过了 Redis 的 maxmemory 配置项时(例如 4GB),会触发 Redis 进行数据淘汰。 一旦数据被淘汰出 Redis,再要访问这部分数据,就需要去后端数据库读取,降低了业务应用的访问性能。更糟糕的是使用多个客户端,导致 Redis 内存占用过大,也会导致内存溢出(out-of-memory)问题,进而会引起 Redis 崩溃,给业务应用造成严重影响。

    Redis 的客户端输入缓冲区大小的上限阈值,在代码中就设定为了 1GB。Redis 服务器端允许为每个客户端最多暂存 1GB 的命令和数据。1GB 的大小,对于一般的生产环 境已经是比较合适的了。一方面,这个大小对于处理绝大部分客户端的请求已经够用了; 另一方面,如果再大的话 Redis 就有可能因为客户端占用了过多的内存资源而崩溃。

    所以,Redis 并没有提供参数调节客户端输入缓冲区的大小。如果要避免输入缓冲区溢出,就只能从数据命令的发送和处理速度入手,也就是前面提到的避免客户端写入 bigkey,以及避免 Redis 主线程阻塞。

    三、输出缓冲区溢出的应对方法

    Redis 的输出缓冲区暂存的是 Redis 主线程要返回给客户端的数据。主线程返回给客户端的数据,既有简单且大小固定的 OK 响应(例如,执行 SET 命令)或报错信息,也有大小不固定的、包含具体数据的执行结果(例如,执行 HGET 命令)。

    Redis 为每个客户端设置的输出缓冲区也包括两部分:

    1. 一个大小为 16KB 的固定缓冲空间,用来暂存 OK 响应和出错信息;
    2. 一个可以动态增加的缓冲空间,用来暂存大小可变的响应结果。

    发生输出缓冲区溢出的三种情况:

    • 服务器端返回 bigkey 的大量结果;
    • 执行了 MONITOR 命令;
    • 缓冲区大小设置得不合理。

    bigkey 原本就会占用大量的内存空间,所以服务器端返回的结果包含 bigkey,必然会影响输出缓冲区。

    MONITOR 命令是用来监测 Redis 执行的。执行这个命令之后,会持续输出监测到的各个命令操作,如下所示:

    MONITOR 
    OK 
    1600617456.437129 [0 127.0.0.1:50487] "COMMAND" 
    1600617477.289667 [0 127.0.0.1:50487] "info" "memory" 
    

    MONITOR 的输出结果会持续占用输出缓冲区,并越占越多,最后的结果就是发生溢出。建议 MONITOR 命令主要用在调试环境中,不要在线上生产环境中持续使用 MONITOR。如果在线上环境中偶尔使用 MONITOR 检查 Redis 的命令执行情况是没问题的。

    输出缓冲区大小设置的问题和输入缓冲区不同,通过 client- output-buffer-limit 配置项来设置缓冲区的大小。设置的内容包括两方面:

    1. 设置缓冲区大小的上限阈值;
    2. 设置输出缓冲区持续写入数据的数量上限阈值,和持续写入数据的时间的上限阈值。

    在具体使用 client-output-buffer-limit 来设置缓冲区大小的时候,需要先区分下客户端的类型。

    两类客户端和 Redis 服务器端交互:

    1. 常规和 Redis 服务器端进行读写命令交互的普通客户端;
    2. 订阅了 Redis 频道的订阅客户端。
    3. 在 Redis 主从集群中,主节点上也有一类客户端(从节点客户端)用来和从节点进行数据同步。

    给普通客户端设置缓冲区大小时,在 Redis 配置文件中进行这样的设置:

    client-output-buffer-limit normal 0 0 0
    

    normal 表示当前设置的是普通客户端,第 1 个 0 设置的是缓冲区大小限制,第 2 个 0 和第 3 个 0 分别表示缓冲区持续写入量限制和持续写入时间限制。

    普通客户端每发送完一个请求,会等到请求结果返回后,再发送下一个请求,这种发送方式称为阻塞式发送。如果不是读取体量特别大的 bigkey, 服务器端的输出缓冲区一般不会被阻塞的。 所以通常把普通客户端的缓冲区大小限制,以及持续写入量限制、持续写入时间限 制都设置为 0,也就是不做限制。

    订阅客户端一旦订阅的 Redis 频道有消息了,服务器端都会通过输出缓冲区把消息发给客户端。所以订阅客户端和服务器间的消息发送方式,不属于阻塞式发送。如果频道消息较多的话,也会占用较多的输出缓冲区空间。 因此给订阅客户端设置缓冲区大小限制、缓冲区持续写入量限制,以及持续写入时间限制,在 Redis 配置文件中设置:

    client-output-buffer-limit pubsub 8mb 2mb 60
    

    pubsub 参数表示当前是对订阅客户端进行设置;8mb 表示输出缓冲区的大小上限为 8MB,一旦实际占用的缓冲区大小要超过 8MB,服务器端就会直接关闭客户端的连接;2mb 和 60 表示连续 60 秒内对输出缓冲区的写入量超过 2MB 的话,服务器端也会关闭客户端连接。

    总结下如何应对输出缓冲区溢出:

    1. 避免 bigkey 操作返回大量数据结果;
    2. 避免在线上环境中持续使用 MONITOR 命令。
    3. 使用 client-output-buffer-limit 设置合理的缓冲区大小上限,或是缓冲区连续写入时 间和写入量上限。

    四、主从集群中的缓冲区

    主从集群间的数据复制包括全量复制和增量复制两种。全量复制是同步所有数据,增量复制只会把主从库网络断连期间主库收到的命令同步给从库。无论在哪种形式的复制中,为了保证主从节点的数据一致,都会用到缓冲区。但是这两种复制场景下的缓冲区,在溢出影响和大小设置方面并不一样。

    复制缓冲区的溢出问题

    全量复制过程主节点在向从节点传输 RDB 文件的同时,会继续接收客户端发送的写命令请求。这些写命令就会先保存在复制缓冲区中,等 RDB 文件传输完成后,再发送给从节点去执行。主节点上会为每个从节点都维护一个复制缓冲区,来保证主从节点间的数据同步。
    在这里插入图片描述全量复制从节点接收和加载 RDB 较慢,同时主节点接收到了大量的写命令,写命令在复制缓冲区中就会越积越多,最终导致溢出。

    主节点上的复制缓冲区,本质上也是一个用于和从节点连接的客户端(称之为从节点客户端),使用的输出缓冲区。复制缓冲区一旦发生溢出,主节点也会直接关闭和从节点进行复制操作的连接,导致全量复制失败。避免复制缓冲区发生溢出:

    1. 控制主节点保存的数据量大小。把主节点的数据量控制在 2~4GB,让全量同步执行得更快些,避免复制缓冲区累积过多命令。
    2. 使用 client-output-buffer-limit 配置项,来设置合理的复制缓冲区大小。设置的依据是主节点的数据量大小、主节点的写负载压力和主节点本身的内存大小。
      在主节点执行如下命令:
    config set client-output-buffer-limit slave 512mb 128mb 60
    

    slave 参数表明该配置项是针对复制缓冲区的。512mb 代表将缓冲区大小的上限设置为 512MB;128mb 和 60 代表的设置是,如果连续 60 秒内的写入量超过 128MB 的话,也会触发缓冲区溢出。

    假设一条写命令数据是 1KB,复制缓冲区可以累积 512K 条(512MB/1KB = 512K)写命令。同时主节点在全量复制期间,可以承受的写命令速率上限是 2000 条 /s(128MB/1KB/60 约等于 2000)。

    得到了一种方法:在实际应用中设置复制缓冲区的大小时,根据写命令数据的大小和应用的实际负载情况(也就是写命令速率),来粗略估计缓冲区中会累积的写命令数据量;然后再和所设置的复制缓冲区大小进行比较,判断设置的缓冲区大小是否足够支撑累积的写命令数据量。

    复制缓冲区还会遇到一个问题:主节点上复制缓冲区的内存开销,会是每个从节点客户端输出缓冲区占用内存的总和。如果集群中的从节点数非常多的话,主节点的内存开销就会非常大。所以还必须得控制和主节点连接的从节点个数,不要使用大规模的主从集群。

    总结:为了避免复制缓冲区累积过多命令造成溢出,引发全量复制失败,控制主节点保存的数据量大小,并设置合理的复制缓冲区大小。 同时需要控制从节点的数量,来避免主节点中复制缓冲区占用过多内存的问题。

    复制积压缓冲区的溢出问题

    增量复制使用的缓冲区称为复制积压缓冲区。 主节点在把接收到的写命令同步给从节点时,同时会把这些写命令写入复制积压缓冲区。 一旦从节点发生网络闪断,再次和主节点恢复连接后,从节点就会从复制积压缓冲区中, 读取断连期间主节点接收到的写命令,进而进行增量同步,如下图:
    在这里插入图片描述
    复制积压缓冲区的英文名字 repl_backlog_buffer。从缓冲区溢出的角度再来回顾下两个重点:复制积压缓冲区溢出的影响,以及如何应对复制积压缓冲区的溢出问题。

    1. 复制积压缓冲区是一个大小有限的环形缓冲区。当主节点把复制积压缓冲区写满后,会覆盖缓冲区中的旧命令数据。如果从节点还没有同步这些旧命令数据,就会造成主从节点间重新开始执行全量复制。
    2. 为了应对复制积压缓冲区的溢出问题,调整复制积压缓冲区的大小,设置 repl_backlog_size 这个参数的值。

    总结

    使用缓冲区以后,当命令数据的接收方处理速度跟不上发送方的发送速度时,缓冲区可以避免命令数据的丢失。

    按照缓冲区的用途,例如是用于客户端通信还是用于主从节点复制,把缓冲区分成了客户端的输入和输出缓冲区,以及主从集群中主节点上的复制缓冲区和复制积压缓冲区。在排查问题的时候,可以快速找到方向:从客户端和服务器端的通信过程以及主从节点的复制过程中分析原因。

    从缓冲区溢出对 Redis 的影响的角度,把这四个缓冲区分成两类做个总结:

    1. 缓冲区溢出导致网络连接关闭:普通客户端、订阅客户端,以及从节点客户端,它们使用的缓冲区,本质上都是 Redis 客户端和服务器端之间,或是主从节点之间为了传输命令数据而维护的。这些缓冲区一旦发生溢出,处理机制都是直接把客户端和服务器端的连接,或是主从节点间的连接关闭。网络连接关闭造成的直接影响,就是业务程序无法读写 Redis,或者是主从节点全量同步失败,需要重新执行。
    2. 缓冲区溢出导致命令数据丢失:主节点上的复制积压缓冲区属于环形缓冲区,一旦发生溢出,新写入的命令数据就会覆盖旧的命令数据,导致旧命令数据的丢失,进而导致主从节点重新进行全量复制。

    缓冲区溢出的三个原因:

    1. 命令数据发送过快过大;
    2. 命令数据处理较慢;
    3. 缓冲区空间过小。

    缓冲区溢出的三个应对策略:

    1. 针对命令数据发送过快过大的问题,对于普通客户端来说可以避免 bigkey,而对于复制缓冲区来说,就是避免过大的 RDB 文件。
    2. 针对命令数据处理较慢的问题,解决方案就是减少 Redis 主线程上的阻塞操作,例如使用异步的删除操作。
    3. 针对缓冲区空间过小的问题,解决方案就是使用 client-output-buffer-limit 配置项设置合理的输出缓冲区、复制缓冲区和复制积压缓冲区大小。输入缓冲区的大小默认是固定的,无法通过配置来修改它,除非直接去修改 Redis 源码。
    更多相关内容
  • 多图详解缓冲区溢出问题

    万次阅读 多人点赞 2020-11-18 23:43:21
    缓冲区溢出一个常见的后果是:黑客利用函数调用过程中程序的返回地址,将存放这块地址的指针精准指向计算机中存放攻击代码的位置,造成程序异常中止。为了防止发生严重的后果,计算机会采用栈随机化,利用金丝雀值...

    蠕虫病毒是一种常见的利用Unix系统中的缺点来进行攻击的病毒。缓冲区溢出一个常见的后果是:黑客利用函数调用过程中程序的返回地址,将存放这块地址的指针精准指向计算机中存放攻击代码的位置,造成程序异常中止。为了防止发生严重的后果,计算机会采用栈随机化,利用金丝雀值检查破坏栈,限制代码可执行区域等方法来尽量避免被攻击。虽然,现代计算机已经可以“智能”查错了,但是我们还是要养成良好的编程习惯,尽量避免写出有漏洞的代码,以节省宝贵的时间!

    1. 蠕虫病毒简介

      蠕虫是一种可以自我复制的代码,并且通过网络传播,通常无需人为干预就能传播。蠕虫病毒入侵并完全控制一台计算机之后,就会把这台机器作为宿主,进而扫描并感染其他计算机。当这些新的被蠕虫入侵的计算机被控制之后,蠕虫会以这些计算机为宿主继续扫描并感染其他计算机,这种行为会一直延续下去。蠕虫使用这种递归的方法进行传播,按照指数增长的规律分布自己,进而及时控制越来越多的计算机。

    2. 缓冲区溢出

      缓冲区溢出是指计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上。理想的情况是:程序会检查数据长度,而且并不允许输入超过缓冲区长度的字符。但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所使用的缓冲区,又被称为“堆栈”,在各个操作进程之间,指令会被临时储存在“堆栈”当中,“堆栈”也会出现缓冲区溢出

    3. 缓冲区溢出举例

    void echo()
    {
      char buf[4];   /*buf故意设置很小*/
      gets(buf);
      puts(buf);
    }
    void call_echo()
    {
      echo();
    }
    

      反汇编如下:

    /*echo*/
    000000000040069c <echo>: 
    40069c:48 83 ec 18         sub $0x18,%rsp  /*0X18 == 24,分配了24字节内存。计算机会多分配一些给缓冲区*/
    4006a0:48 89 e7            mov %rsp,%rdi   
    4006a3:e8 a5 ff ff ff      callq 40064d <gets>
    4006a8::48 89 e7           mov %rsp,%rdi
    4006ab:e8 50  fe ff ff     callq callq 400500 <puts@plt>
    4006b0:48 83 c4 18         add $0x18,%rsp 
    4006b4:c3                  retq 
    
    /*call_echo*/
    4006b5:48 83  ec 08             sub $0x8,%rsp 
    4006b9:b8 00 00 00 00           mov $0x0,%eax
    4006be:e8 d9 ff ff ff           callq 40069c <echo>
    4006c3:48 83 c4 08              add $0x8,%rsp 
    4006c7:c3                       retq
    

      在这个例子中,我们故意把buf设置的很小。运行该程序,我们在命令行中输入012345678901234567890123,程序立马就会报错:Segmentation fault。

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/qq_16933601/article/details/109638145

      要想明白为什么会报错,我们需要通过分析反汇编来了解其在内存是如何分布的。具体如下图所示:

      如下图所示,此时计算机为buf分配了24字节空间,其中20字节还未使用。

    image-20201111215122537

      此时,准备调用echo函数,将其返回地址压栈。

    image-20201111214702010

      当我们输入“0123456789012345678
    9012"时,缓冲区已经溢出,但是并没有破坏程序的运行状态

    image-20201111214811039

      当我们输入:“012345678901234567
    890123"。缓冲区溢出,返回地址被破坏,程序返回 0x0400600。

    image-20201111214914863

      这样程序就跳转到了计算机中其他内存的位置,很大可能这块内存已经被使用。跳转修改了原来的值,所以程序就会中止运行。

      黑客可以利用这个漏洞,将程序精准跳转到其存放木马的位置(如nop sled技术),然后就会执行木马程序,对我们的计算机造成破坏。

    4. 缓冲区溢出的危害

      缓冲区溢出可以执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。第一个缓冲区溢出攻击–Morris蠕虫,发生在二十年前,它曾造成了全世界6000多台网络服务器瘫痪。

      在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出。因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址。带来的危害有两种,一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。

    5. 内存在计算机中的排布方式

      内存在计算机中的排布方式如下,从上到下依次为共享库,栈,堆,数据段,代码段。各个段的作用简介如下:
    image-20201111151446190

      共享库:共享库以.so结尾.(so==share object)在程序的链接时候并不像静态库那样在拷贝使用函数的代码,而只是作些标记。然后在程序开始启动运行的时候,动态地加载所需模块。所以,应用程序在运行的时候仍然需要共享库的支持。共享库链接出来的文件比静态库要小得多。

      :栈又称堆栈,是用户存放程序临时创建的变量,也就是我们函数{}中定义的变量,但不包括static声明的变量,static意味着在数据段中存放变量

      除此之外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中,由于栈的先进后出特点,所以栈特别方便用来保存、恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存,交换临时数据的内存区。在X86-64 Linux系统中,栈的大小一般为8M(用ulitmit - a命令可以查看)。

      :堆是用来存放进程中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态分配到堆上,当利用free等函数释放内存时,被释放的内存从堆中被剔除。

      堆存放new出来的对象,栈里面所有对象都是在堆里面有指向的。假如栈里指向堆的指针被删除,堆里的对象也要释放(C++需要手动释放)。当然现在面向对象程序都有’垃圾回收机制’,会定期的把堆里没用的对象清除出去。

      数据段:数据段通常用来存放程序中已初始化的全局变量和已初始化为非0的静态变量的一块内存区域,属于静态内存分配。直观理解就是C语言程序中的全局变量(注意:全局变量才算是程序的数据,局部变量不算程序的数据,只能算是函数的数据

      代码段:代码段通常用来存放程序执行代码的一块区域。这部分区域的大小在程序运行前就已经确定了,通常这块内存区域属于只读,有些架构也允许可写,在代码段中也有可能包含以下只读的常数变量,例如字符串常量等。

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/qq_16933601/article/details/109638145

      下面举个例子来看下代码中各个部分在计算机中是如何排布的。

    #include <stdio.h>
    #include <stdlib.h>
    
    char big_array[1L<<24];     /*16 MB*/
    char huge_array[1L<<31];    /*2 GB*/
    
    int global = 0;
    
    int useless() {return 0;}
    
    int main()
    {
      void *phuge1,*psmall2,*phuge3,*psmall4;
      int local = 0;
      phuge1 = malloc(1L<<28);    /*256 MB*/
      psmall2 = malloc(1L<<8);    /*256 B*/
      phuge3 = malloc(1L<<32);    /*4 GB*/
      psmall4 = malloc(1L<<8);    /*256 B*/
    }
    
    

      上述代码中,程序中的各个变量在内存的排布方式如下图所示。根据颜色可以一一对应起来。由于了local变量存放在栈区,四个指针变量使用了malloc分配了空间,
    所以存放在堆上,两个数组big_
    array,huge_array存放在数据段,main,useless函数的其他部分存放在代码段中。

    image-20201111153803357

    6. 计算机中越界访问的后果

      下面再看一个例子,看下越界访问内存会有什么结果。

    typedef struct 
    {
      int a[2];
      double d;
    }struct_t;
    
    double fun(int i)
    {
      volatile struct_t s;
      s.d = 3.14;
      s.a[i] = 1073741824;  /*可能越界*/
      return s.d;
    }
    
    int main()
    {
      printf("fun(0):%lf\n",fun(0));
      printf("fun(1):%lf\n",fun(1));
      printf("fun(2):%lf\n",fun(2));
      printf("fun(3):%lf\n",fun(3));
      printf("fun(6):%lf\n",fun(6));
      return 0; 
    }
    

      打印结果如下所示:

    fun(0):3.14
    fun(1):3.14
    fun(2):3.1399998664856
    fun(3):2.00000061035156
    fun(6):Segmentation fault
    

      在上面的程序中,我们定义了一个结构体,其中 a 数组中包含两个整数值,还有 d 一个双精度浮点数。在函数fun中,fun函数根据传入的参数i来初始化a数组。显然,i的值只能为0和1。在fun函数中,同时还设置了d的值为3.14。当我们给fun函数传入0和1时可以打印出正确的结果3.14。但是当我们传入2,3,6时,奇怪的现象发生了。为什么fun(2)和fun(3)的值会接近3.14,而fun(6)会报错呢?

      要搞清楚这个问题,我们要明白结构体在内存中是如何存储的,具体如下图所示。

    结构体在内存中的存储方式

      GCC默认不检查数组越界(除非加编译选项)。这也是C语言的bug之一,越界会修改某些内存的值,得出我们意想不到的结果。即使有些数据相隔万里,也可能受到影响。当一个系统这几天运行正常时,过几天可能就会崩溃。(如果这个系统是运行在我们的心脏起搏器,又或者是航天飞行器上,那么这无疑将会造成巨大的损失!)

      如上图所示,对于最下面的两个元素,每个块代表 4 字节。a数组占用8个字节,d变量占用8字节,d排布在a数组的上方。所以我们会看到,如果我引用 a[0] 或者 a[1],会按照正常修改该数组的值。但是当我调用 fun(2) 或者 fun(3)时,实际上修改的是这个浮点数 d 所对应的内存位置。这就是为什么我们打印出来的fun(2)和fun(3)的值如此接近3.14。

      当输入 6 时,就修改了对应的这块内存的值。原来这块内存可能存储了其他用于维持程序运行的内容,而且是已经分配的内存。所以,我们程序就会报出Segmentation fault的错误。

    7. 避免缓冲区溢出的三种方法

      为了在系统中插入攻击代码,攻击者既要插入代码,也要插入指向这段代码的指针。这个指针也是攻击字符串的一部分。产生这个指针需要知道这个字符串放置的栈地址。在过去,程序的栈地址非常容易预测。对于所有运行同样程序和操作系统版本的系统来说,在不同的机器之间,栈的位置是相当固定的。因此,如果攻击者可以确定一个常见的Web服务器所使用的栈空间,就可以设计一个在许多机器上都能实施的攻击。

    7.1 栈随机化

      栈随机化的思想使得栈的位置在程序每次运行时都有变化。因此,即使许多机器都运行同样的代码,它们的栈地址都是不同的。实现的方式是:程序开始时,在栈上分配一段0 ~ n字节之间的随机大小的空间,例如,使用分配函数alloca在栈上分配指定字节数量的空间。程序不使用这段空间,但是它会导致程序每次执行时后续的栈位置发生了变化。分配的范围n必须足够大,才能获得足够多的栈地址变化,但是又要足够小,不至于浪费程序太多的空间。

    int main()
    {
    	long local;
    	printf("local at %p\n",&local);
    	return 0;
    }
    

      这段代码只是简单地打印出main函数中局部变量的地址。在32位 Linux上运行这段代码10000次,这个地址的变化范围为0xff7fc59c到0xffffd09c,范围大小大约是 2 23 {2^{23}} 223。在64位 Linux机器上运行,这个地址的变化范围为0x7fff0001b698到0x7ffffffaa4a8,范围大小大约是 2 32 {2^{32}} 232

      其实,一个好的黑客专家,可以使用暴力破坏栈的随机化。对于32位的机器,我们枚举 2 15 = 32768 {2^{15}} = 32768 215=32768个地址就能猜出来栈的地址。对于64位的机器,我们需要枚举 2 24 = 16777216 {2^{24}} = 16777216 224=16777216次。如此看来,栈的随机化降低了病毒或者蠕虫的传播速度,但是也不能提供完全的安全保障。

    7.2 检测栈是否被破坏

      计算机的第二道防线是能够检测到何时栈已经被破坏。我们在echo函数示例中看到,当访问缓冲区越界时,会破坏程序的运行状态。在C语言中,没有可靠的方法来防止对数组的越界写。但是,我们能够在发生了越界写的时候,在造成任何有害结果之前,尝试检测到它。

      GCC在产生的代码中加人了一种栈保护者机制,来检测缓冲区越界。其思想是在栈帧中任何局部缓冲区与栈状态之间存储一个特殊的金丝雀( canary)值,如下图所示:

    image-20201112085448688

      这个金丝雀值,也称为哨兵值,是在程序每次运行时随机产生的,因此,攻击者很难猜出这个哨兵值。在恢复寄存器状态和从函数返回之前,程序检查这个金丝雀值是否被该函数的某个操作或者该函数调用的某个函数的某个操作改变了。如果是的,那么程序异常中止。

    image-20201112085829359

    英国矿井饲养金丝雀的历史大约起始1911年。当时,矿井工作条件差,矿工在下井时时常冒着中毒的生命危险。后来,约翰·斯科特·霍尔丹(John Scott Haldane)在经过对一氧化碳一番研究之后,开始推荐在煤矿中使用金丝雀检测一氧化碳和其他有毒气体。金丝雀的特点是极易受有毒气体的侵害,因为它们平常飞行高度很高,需要吸入大量空气吸入足够氧气。因此,相比于老鼠或其他容易携带的动物,金丝雀会吸入更多的空气以及空气中可能含有的有毒物质。这样,一旦金丝雀出了事,矿工就会迅速意识到矿井中的有毒气体浓度过高,他们已经陷入危险之中,从而及时撤离。

      GCC会试着确定一个函数是否容易遭受栈溢出攻击,并且自动插入这种溢出检测。实际上,对于前面的栈溢出展示,我们可以使用命令行选项“-fno- stack- protector”来阻止GCC产生这种代码。当用这个选项来编译echo函数时(允许使用栈保护),得到下面的汇编代码

    /*void echo */
    subq $24,%rsp Allocate 24 bytes on stack
    movq  %fs:40,%rax  Retrieve canary 
    movq %rax,8(%rsp) Store on stack
    xorl %eax, %eax Zero out register    //从内存中读出一个值
    movq %rsp, %rdi  Compute buf as %rsp 
    call gets Call gets 
    movq ‰rsp,%rdi Compute buf as %rsp
    call puts Call puts 
    movq 8(%rsp),%rax Retrieve canary 
    xorq %fs:40,%rax Compare to stored value   函数将存储在栈位置处的值与金丝雀值做比较
    je .L9  If =, goto ok 
    call __stack_chk_fail Stack corrupted  
    .L9
    addq $24,%rsp Deallocate stack space 
    ret
    

      这个版本的函数从内存中读出一个值(第4行),再把它存放在栈中相对于%rsp偏移量为8的地方。指令参数各fs:40指明金丝雀值是用段寻址从内存中读入的。段寻址机制可以追溯到80286的寻址,而在现代系统上运行的程序中已经很少见到了。将金丝雀值存放在一个特殊的段中,标记为只读这样攻击者就不能覆盖存储金丝雀值。在恢复寄存器状态和返回前,函数将存储在栈位置处的值与金丝雀值做比较(通过第10行的xorq指令)。如果两个数相同,xorq指令就会得到0,函数会按照正常的方式完成。非零的值表明栈上的金丝雀值被修改过,那么代码就会调用一个错误处理例程。

      栈保护很好地防止了缓冲区溢出攻击破坏存储在程序栈上的状态。一般只会带来很小的性能损失。

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/qq_16933601/article/details/109638145

    7.3 限制可执行代码区域

      最后一招是消除攻击者向系统中插入可执行代码的能力。一种方法是限制哪些内存区域能够存放可执行代码。在典型的程序中,只有保存编译器产生的代码的那部分内存才需要是可执行的。其他部分可以被限制为只允许读和写。

      许多系统都有三种访问形式:读(从内存读数据)、写(存储数据到内存)和执行(将内存的内容看作机器级代码)。以前,x86体系结构将读和执行访问控制合并成一个1位的标志,这样任何被标记为可读的页也都是可执行的。栈必须是既可读又可写的,因而栈上的字节也都是可执行的。已经实现的很多机制,能够限制一些页是可读但是不可执行的,然而这些机制通常会带来严重的性能损失。

    8. 总结

      计算机提供了多种方式来弥补我们犯错可能产生的严重后果,但是最关键的还是我们尽量减少犯错。

      例如,对于gets,strcpy等函数我们应替换为 fgets,strncpy等。在数组中,我们可以将数组的索引声明为size_t类型,从根本上防止它传递负数。此外,还可以在访问数组前来加上num小于ARRAY_MAX 语句来检查数组的上界。总之,要养成良好的编程习惯,这样可以节省很多宝贵的时间。同时最后也推荐两本相关书籍如下所示。

    代码大全(第二版)
    高质量程序设计指南

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/qq_16933601/article/details/109638145

    展开全文
  • GIS应用技巧之缓冲区分析

    千次阅读 2022-03-22 20:39:13
    一、缓冲区介绍 缓冲区是地理空间目标的一种影响范围或服务范围,是对选中的一组或一类地图要素(点、线或面)按设定的距离条件,围绕其要素而形成一定缓冲区多边形实体,从而实现数据在二维空间得以扩展的信息分析...

    一、缓冲区介绍

    缓冲区是地理空间目标的一种影响范围或服务范围,是对选中的一组或一类地图要素(点、线或面)按设定的距离条件,围绕其要素而形成一定缓冲区多边形实体,从而实现数据在二维空间得以扩展的信息分析方法。缓冲区分析在农业、城市规划、生态保护等诸多领域都有应用。例如,在环境治理时,常在污染的河流周围划出一定宽度的范围表示受到污染的区域;又如交通基础设施建设时,可根据道路扩宽宽度对道路创建缓冲区,然后将缓冲区图层与建筑图层叠加,通过叠加分析查找落入缓冲区而需要被拆除的建筑等等。缓冲区主要分为以下几种形式:

    (1)点缓冲区

    点的缓冲区是以点对象为圆心,以给定的缓冲距离为半径生成的圆形区
    域。当缓冲距离足够大时,两个或多个点对象的缓冲区可能有重叠。选择合并
    缓冲区时,重叠部分将被合并,最终得到的缓冲区是一个复杂面对象。

    (2)线缓冲区

    线的缓冲区是沿线对象的法线方向,分别向线对象的两侧平移一定的距离
    而得到两条线,并与在线端点处形成的光滑曲线(或平头)接合形成的封闭区
    域。同样,当缓冲距离足够大时,两个或多个线对象的缓冲区可能有重叠。合并缓冲区的效果与点的合并缓冲区相同。

    当线数据的缓冲类型设置为平头缓冲时,线对象两侧的缓冲宽度可以不一
    致,从而生成左右不等缓冲区;也可以只在线对象的一侧创建单边缓冲区,下图所示:

    (3)面缓冲区

    面的缓冲区生成方式与线的缓冲区类似,区别是面的缓冲区仅在面边界的
    一侧延展或收缩。当缓冲半径为正值时,缓冲区向面对象边界的外侧扩展;为
    负值时,向边界内收缩。同样,当缓冲距离足够大时,两个或多个线对象的缓冲区可能有重叠。也可以选择合并缓冲区,其效果与点的合并缓冲区相同。

    (4)多重缓冲区

    多重缓冲区是指在几何对象的周围,根据给定的若干缓冲区半径,建立相
    应数据量的缓冲区。对于线对象,还可以建立单边多重缓冲区,如下图所示:

    二、案例分析

    这里以全国各省省会城市的坐标为点要素进行缓冲区分析各省会城市的半小时圈影响范围,以主城为核心,在交通半小时可通达的范围内,形成一个具有明显聚集效应、具备竞争优势的地区,比如现在最热门的就是京津半小时经济圈,从北京到天津最快半小时可到达。通常对于公路来说,一小时圈的距离一般为50Km左右,这里以 50Km 进行缓冲区分析,并看看在半小时圈范围内有区域。

    (1)向 Arcmap 中添加要建立缓冲区的图层,中心城市点图层,全国的县域矢量图层,考虑到以km为单位距离,故需对数据框进行投影坐标系转换,由于图层是县域层面,这里需将数据框投影为Gauss—Kruger坐标系。按照下图进行操作。

    (2)调出建立缓冲区工具,有两种方法可以调出缓冲区工具,一种是在【菜单地理】处理中选择缓冲区。另外一种方法是在 工具箱中选择【分析工具】→【邻域分析】→【缓冲区】。

    (3)打开建立缓冲区对话框,选择输入【中心城市】图层要素和缓冲距离,字段一项可以根据需要进行设置,例如我们可以不根据距离来进行缓冲,可能实际中中心城市影响范围汇通中心城市发展实力有关,发展实力越强的城市可能缓冲范围越大,这时可以选取融合字段,选择代表城市规模的字段进行缓冲。由于这里探讨的缓冲距离,故将距离设置为 50Km,此外,针对点要素融合类型有三种:NONE:无论如何重叠,均保持每个要素的独立缓冲区。这是默认设置;ALL:将所有缓冲区融合为单个要素,从而移除所有重叠;LIST:融合共享所列字段(传递自输入要素)属性值的所有缓冲区。实际操作中,要素类型不同,其融合类型也会有差异。

    (4)点击确定生成缓冲区,结果如下​:

    展开全文
  • 缓冲区

    千次阅读 2016-04-15 15:30:51
    缓冲区像前篇文章讨论的那样被写满和释放,对于每个非布尔原始数据类型都有一个缓冲区类,尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节,非字节缓冲区可以再后台执行从字节或到字节的转换,...

    什么是缓冲区

    一个缓冲区对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里数据可被存储并在之后用于检索。缓冲区像前篇文章讨论的那样被写满和释放,对于每个非布尔原始数据类型都有一个缓冲区类,尽管缓冲区作用于它们存储的原始数据类型,但缓冲区十分倾向于处理字节,非字节缓冲区可以再后台执行从字节或到字节的转换,这取决于缓冲区是如何创建的。

    缓冲区的工作与通道紧密联系。通道是I/O传输发生时通过的入口,而缓冲区是这些数据传输的来源或目标。对于离开缓冲区的传输,待传递出去的数据被置于一个缓冲区,被传送到通道;待传回的缓冲区的传输,一个通道将数据放置在所提供的缓冲区中。这种在协同对象之间进行的缓冲区数据传递时高效数据处理的关键。

    Buffer类的家谱

    下图是Buffer的类层次图。在顶部是通用Buffer类,Buffer定义所有缓冲区类型共有的操作,无论是它们所包含的数据类型还是可能具有的特定行为:

    缓冲区基础

    概念上,缓冲区是包在一个对象内的基本数据元素数组。Buffer类相比一个简单数组的优点是它将关于数据的数据内容和信息包含在一个单一的对象中,Buffer类以及它专有的子类定义了一个用于处理数据缓冲区的API。下面来看一下Buffer类所具有的属性和方法:

    1、属性

    所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息,它们是:

    属      性作      用
    capacity容量,指缓冲区能够容纳的数据元素的最大数量,这一容量在缓冲区创建时被设定,并且永远不能被改变
    limit上界,指缓冲区的第一个不能被读或写的元素,或者说是,缓冲区中现存元素的计数
    position位置,指下一个要被读或写的元素的索引,位置会自动由相应的get()和put()函数更新
    mark标记,指一个备忘位置,调用mark()来设定mark=position,调用reset()来设定postion=mark,标记未设定前是未定义的

    这四个属性总是遵循以下的关系:0 <= mark <= position <= limit <= capacity

    2、方法

    下面看一下如何使用一个缓冲区,Buffer中提供了以下的一些方法:

    方      法作      用
    Object array()返回此缓冲区的底层实现数组
    int arrayOffset()返回此缓冲区的底层实现数组中第一个缓冲区还俗的偏移量
    int capacity()返回此缓冲区的容量
    Buffer clear()清除此缓冲区
    Buffer flip()反转此缓冲区
    boolean hasArray()告知此缓冲区是否具有可访问的底层实现数组
    boolean hasRemaining()告知在当前位置和限制之间是否有元素
    boolean isDirect()告知此缓冲区是否为直接缓冲区
    boolean isReadOnly()告知此缓冲区是否为只读缓存
    int limit()返回此缓冲区的上界
    Buffer limit(int newLimit)设置此缓冲区的上界
    Buffer mark()在此缓冲区的位置设置标记
    int position()返回此缓冲区的位置
    Buffer position(int newPosition)设置此缓冲区的位置
    int remaining()返回当前位置与上界之间的元素数
    Buffer reset()将此缓冲区的位置重置为以前标记的位置
    Buffer rewind()重绕此缓冲区

    关于这个API有一点值得注意的,像clear()这类函数,通常应当返回的是void而不是Buffer引用。这些函数将引用返回到它们在(this)上被引用的对象,这是一个允许级联调用的类设计方法。级联调用允许这种类型的代码:

    buffer.mark();
    buffer.position(5);
    buffer.reset();

    被简写成:

    buffer.mark().position(5).reset();
    

    缓冲区代码实例

    对缓冲区的使用,先看一段代码,然后解释一下:

    复制代码
     1 public class TestMain
     2 {
     3     /**
     4      * 待显示的字符串
     5      */
     6     private static String[] strs = 
     7     {
     8         "A random string value",
     9         "The product of an infinite number of monkeys",
    10         "Hey hey we're the monkees",
    11         "Opening act for the Monkees:Jimi Hendrix",
    12         "Scuse me while I kiss this fly",
    13         "Help Me! Help Me!"
    14     };
    15     
    16     /**
    17      * 标识strs的下标索引
    18      */
    19     private static int index = 0;
    20     
    21     /**
    22      * 向Buffer内放置数据
    23      */
    24     private static boolean fillBuffer(CharBuffer buffer)
    25     {
    26         if (index >= strs.length)
    27             return false;
    28         
    29         String str = strs[index++];
    30         for (int i = 0; i < str.length(); i++)
    31         {
    32             buffer.put(str.charAt(i));
    33         }
    34         
    35         return true;
    36     }
    37     
    38     /**
    39      * 从Buffer内把数据拿出来
    40      */
    41     private static void drainBuffer(CharBuffer buffer)
    42     {
    43         while (buffer.hasRemaining())
    44         {
    45             System.out.print(buffer.get());
    46         }
    47         System.out.println("");
    48     }
    49     
    50     public static void main(String[] args)
    51     {
    52         CharBuffer cb = CharBuffer.allocate(100);
    53         while (fillBuffer(cb))
    54         {
    55             cb.flip();
    56             drainBuffer(cb);
    57             cb.clear();
    58         }
    59     }
    60 }
    复制代码

    逐一解释一下:

    1、第52行,CharBuffer是一个抽象类,它不能被实例化,因此利用allocate方法来实例化,相当于是一个工厂方法。实例化出来的是HeapCharBuffer,默认大小是100。根据上面的Buffer的类家族图谱,可以看到每个Buffer的子类都是使用allocate方法来实例化具体的子类的,且实例化出来的都是Heap*Buffer

    2、第24行~第36行,每次取String数组中的一个,利用put方法放置一个数据进入CharBuffer中

    3、第55行,调用flip方法,这是非常重要的。在缓冲区被写满后,必须将其清空,但是如果现在在通道上直接执行get()方法,那么它将从我们刚刚插入的有用数据之外取出未定义数据;如果此时将位置重新设置为0,就会从正确的位置开始获取数据,但是如何知道何时到达我们所插入数据末端呢?这就是上界属性被引入的目的----上界属性指明了缓冲区有效内容的末端。因此,在读取数据的时候我们需要做两件事情:

    (1)将上界属性limit设置为当前位置    (2)将位置position设置为0

    这两步操作,JDK API给开发者提供了一个filp()方法来完成,flip()方法将一个能够继续添加数据元素的填充状态的缓冲区翻转成一个准备读出元素的释放状态,因此每次准备读出元素前,都必须调用一次filp()方法

    4、第41行~第48行,每次先判断一下是否已经达到缓冲区的上界,若存在则调用get()方法获取到此元素,get()方法会自动移动下标position

    5、第57行,对Buffer的操作完成之后,调用clear()方法将所有属性回归原位,但是clear()方法并不会改变缓冲区中的任何数据

    缓冲区比较

    缓冲区的比较即equals方法,缓冲区的比较并不像我们想像得这么简单,两个缓冲区里面的元素一样就是相等,两个缓冲区相等必须满足以下三个条件:

    1、两个对象类型相同,包含不同数据类型的buffer永远不会像等,而且buffer绝不会等于非buffer对象

    2、两个对象都剩余相同数量的元素,Buffer的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同。但每个缓冲区中剩余元素的数目(从position到limit)必须相同

    3、在每个缓冲区中应被get()函数返回的剩余数据元素序列必须一致

    如果不满足上面三个条件,则返回false。下面两幅图演示了两个缓冲区相等和不相等的场景,首先是两个属性不同的缓冲区也可以相等:

    然后是两个属性相同但是被等为不相等的缓冲区:

    批量移动数据

    缓冲区的设计目的就是为了能够高效地传输数据。一次移动一个数据元素,其实并不高效,如在下面的程序清单中所看到的那样,Buffer API提供了向缓冲区内外批量移动数据元素的函数:

    复制代码
    public abstract class CharBuffer
        extends Buffer
        implements Comparable<CharBuffer>, Appendable, CharSequence, Readable
    {
        ...
        public CharBuffer get(char[] dst){...}
        public CharBuffer get(char[] dst, int offset, int length){...}
        public final CharBuffer put(char[] src){...}
        public CharBuffer put(char[] src, int offset, int length){...}
        public CharBuffer put(CharBuffer src){...}
        public final CharBuffer put(String src){...}
        public CharBuffer put(String src, int start, int end){...}
        ...      
    }
    复制代码

    其实这种批量移动的合成效果和前文的循环在底层实现上是一样的,但是这些方法可能高效得多,因为这种缓冲区实现能够利用本地代码或其他的优化来移动数据。

    字节缓冲区

    字节缓冲区和其他缓冲区类型最明显的不同在于,它们可能成为通道所执行I/O的源头或目标,如果对NIO有了解的朋友们一定知道,通道只接收ByteBuffer作为参数。

    如我们所知道的,操作系统在内存区域进行I/O操作,这些内存区域,就操作系统方面而言,是相连的字节序列。于是,毫无疑问,只有字节缓冲区有资格参与I/O操作。也请回想一下操作系统会直接存取进程----在本例中是JVM进程的内存空间,以传输数据。这也意味着I/O操作的目标内存区域必须是连续的字节序列,在JVM中,字节数组可能不会在内存中连续存储,或者无用存储单元收集可能随时对其进行移动。在Java中,数组是对象,而数据存储在对象中的方式在不同的JVM实现中各有不同。

    出于这一原因,引入了直接缓冲区的概念。直接缓冲区被用于与通道和固有I/O线程交互,它们通过使用固有代码来告知操作系统直接释放或填充内存区域,对用于通道直接或原始存取的内存区域中的字节元素的存储尽了最大的努力。

    直接字节缓冲区通常是I/O操作最好的选择。在设计方面,它们支持JVM可用的最高效I/O机制,非直接字节缓冲区可以被传递给通道,但是这样可能导致性能损耗,通常非直接缓冲不可能成为一个本地I/O操作的目标,如果开发者向一个通道中传递一个非直接ByteBuffer对象用于写入,通道可能会在每次调用中隐含地进行下面的操作:

    1、创建一个临时的直接ByteBuffer对象

    2、将非直接缓冲区的内容复制到临时缓冲中

    3、使用临时缓冲区执行低层次I/O操作

    4、临时缓冲区对象离开作用于,并最终成为被回收的无用数据

    这可能导致缓冲区在每个I/O上复制并产生大量对象,而这种事都是我们极力避免的。

    直接缓冲区是I/O的最佳选择,但可能比创建非直接缓冲区要花费更高的成本。直接缓冲区使用的内存是通过调用本地操作系统的代码分配的,绕过了标准JVM堆栈。建立和销毁直接缓冲区会明显比具有堆栈的缓冲区更极爱破费,这取决于主操作系统以及JVM实现。直接缓冲区的内存区域不受无用存储单元收集支配,因为它们位于标准JVM堆栈之外。

    直接ByteBuffer是通过调用具有所需容量的ByteBuffer.allocateDirect()函数产生的:

    复制代码
    public abstract class ByteBuffer
        extends Buffer
        implements Comparable<ByteBuffer>
    {
        ...
        public static ByteBuffer allocateDirect(int capacity)
        {
            return new DirectByteBuffer(capacity);
        }
        ...
    }
    复制代码
    展开全文
  • Author:阿冬哥Created:2013-4-17Blog:http://blog.csdn.net/c359719435/Copyright 2013阿冬哥http://blog.csdn.net/c359719435/使用以及转载请注明出处1 设置...SNDBUF、SO_RCVBUF这连个默认缓冲区的值,再用ge...
  • 从抽象的角度看,也就是说,一个用户进程在内存中有自己的一个进程缓冲区,系统底层也有自己的一个内核缓冲区,read系统调用,就是将进程缓冲区中的数据复制到内核缓冲区中,而write系统调用则是将内核缓冲区中的...
  • 什么是缓冲区(buffer),什么是缓存(cache)

    万次阅读 多人点赞 2020-11-17 22:31:03
    比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故...
  • 上一章有简单讲过系统在每个socket创建的时候底层会配套给socket分配一个发送和接收缓冲区,发送数据时send返回后数据在发送缓冲区中由系统在合适时间真正的发送出去,接收数据时,数据先从网卡到接收缓冲区,再由...
  • C语言常见漏洞-缓冲区溢出

    千次阅读 2021-07-21 11:27:21
    缓冲区溢出1.原理2.攻击方式2.1 利用shellcode2.2 跳到其它函数2.3 其它情况3.防护3.1 canary(栈保护)3.2 NX3.3 RELRO3.4 PIE 缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖...
  • 在C和C ++中创建循环缓冲区

    千次阅读 2021-03-12 10:02:52
    由于嵌入式系统的资源限制,在大多数项目中都可以找到循环缓冲区数据结构。 循环缓冲区(也称为环形缓冲区)是固定大小的缓冲区,其工作方式就像内存是连续的且本质上是循环的。随着内存的生成和消耗,不需要重新...
  • 内存缓冲区

    千次阅读 2022-04-20 08:48:27
    但是有了缓冲区,就可以将数据先放入缓冲区中,程序可以继续向下执行而无需等待,等到缓冲区充满了便将数据全部放入硬盘中,而缓冲区又可以开始存入新一轮的数据。 2. 减少操作硬件的次数 ......
  • JavaScript WebGL 帧缓冲区对象

    千次阅读 2022-02-01 18:44:38
    引子 在看 How I built a wind...在默认情况下,WebGL 最终绘图结果存储在颜色缓冲区,帧缓冲区对象可以用来代替颜色缓冲区,如下图所示,绘制在帧缓冲区中的对象并不会直接显示在 Canvas 上,因此这种技术也被称为离屏
  • 一文搞懂用户缓冲区与内核缓冲区

    千次阅读 2020-09-21 09:58:46
    但实际上有一部分人把用户进程缓存区和系统空间缓存区的概念混淆了,包括这两种缓冲区的用法和所要解决的问题,还有其它类似的概念。本文就来区分一下不同的缓冲区概念(主要针对类unix平台)。  用户进程和操作...
  • ringbuff | 通用FIFO环形缓冲区实现库

    千次阅读 2020-06-06 10:58:39
    丢包测试 经过3.2节的测试,不丢包的最小缓冲区大小是140个字节,接下里我们将缓冲区大小修改为100个字节,测试一下是否产生丢包: //开辟一块内存用于缓冲区 #define USART1_BUFFDATA_SIZE 100 //会发生丢包 //#...
  • 队列1-环形缓冲区

    千次阅读 2019-01-27 16:58:34
    本篇为队列的第一篇文章,介绍基于数组结构的一个环形缓冲区队列。我觉得没有必要再从数组来写起,毕竟对于数组本身来说,我觉得是没有太多可说的,但是基于数组的数据结构就有的说了。 什么是环形缓冲区 环形缓冲...
  • ArcGIS 缓冲区与叠加分析

    千次阅读 2022-01-27 20:56:16
    找到分析工具下的proximity工具,然后选择buffer做缓冲区分析。 其中参数设置里将distance作为缓冲距离,其他参数设置如图所示。 打开原始数据的属性表可知,总共有8个垃圾处理站。 如图所示,为输出的缓冲区结果...
  • 环形缓冲区(ringbuffer)

    千次阅读 多人点赞 2021-03-29 20:55:38
    环形缓冲区是嵌入式系统中十分重要的一种数据结构,比如在串口处理中,串口中断接收数据直接往环形缓冲区丢数据,而应用可以从环形缓冲区取数据进行处理,这样数据在读取和写入的时候都可以在这个缓冲区里循环进行,...
  • STM32串口开发之环形缓冲区

    千次阅读 热门讨论 2021-07-07 00:09:24
    //创建一个ringBuff的缓冲区 当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。 环形缓冲区的初始化 /** * @brief RingBuff_Init ...
  • 缓冲区和双缓冲区

    千次阅读 2020-07-24 15:26:55
    缓冲区 在单缓冲情况下,每当用户进程发出一I/O请求时,OS便在主存中为之分配一缓冲区。在块设备输入时,假定从磁盘把一块数据输入到缓冲区的时间为T,OS将该缓冲区中的数据传送到用户区的时间为M,而CPU对这一块...
  • 一、缓冲区管理 (一)什么是缓冲区?有什么作用? 缓冲区是一个存储区域,可以由专门的硬件寄存器组成,也可利用内存作为缓冲区。 使用硬件作为缓冲区的成本较高,容量也较小,一般仅用在对速度要求非常高的场合...
  • C++入门——缓冲区溢出

    千次阅读 2021-07-13 11:51:20
    缓冲区溢出 概念 缓冲区是程序运行时机器内存中的一个连续块,它保存了...人为的缓冲区溢出一般是由于攻击者写一个超过缓冲区长度的字符串植入到缓冲区,然后再向一个有限空间的缓冲区中植入超长字符串,这是可能会出
  • AAudio 音频流内部缓冲区 与 音频数据读写缓冲区 概念 II . AAudio 音频流内部缓冲区 缓冲区帧容量 BufferCapacityInFrames 与 缓冲区帧大小 BufferSizeInFrames 区分 III . AAudio 音频流内部缓冲区 缓冲区帧容量 ...
  • C语言中如何清空输入输出缓冲区

    千次阅读 2021-05-20 09:32:44
    C语言中如何清空输入输出缓冲区答案:5信息版本:手机版解决时间 2019-10-06 09:40已解决2019-10-05 13:54C语言中如何清空输入输出缓冲区最佳答案2019-10-05 15:11最简单的是用fflush()函数。此函数包含在stdio.h...
  • 缓冲区溢出(buffer overflow),是针对程序设计缺陷,向程序输入缓冲区写入使之溢出的内容(通常是超过缓冲区能保存的最大数据量的数据),从而破坏程序运行、趁著中断之际并获取程序乃至系统的控制权。 项目十七:...
  • 一、缓冲区概念: 缓冲区到底是个啥,看ARCGIS的说明就行: 缓冲区: https://desktop.arcgis.com/zh-cn/arcmap/latest/tools/analysis-toolbox/buffer.htm 缓冲区(分析)的工作原理: ...arcgis的...
  • STM32进阶之串口环形缓冲区实现

    千次阅读 多人点赞 2019-10-15 22:34:52
    文章目录队列的概念队列的特点队列的常见两种形式普通队列环形队列从队列到串口缓冲区的实现定义一个结构体:初始化写入环形缓冲区的代码实现:读取缓冲区的数据的代码实现:测试效果补充喜欢就关注我吧!...
  • 缓冲区溢出详解

    千次阅读 2019-05-13 11:44:42
    缓冲区溢出(Buffer Overflow)是计算机安全领域内既经典而又古老的话题。随着计算机系统安全性的加强,传统的缓冲区溢出攻击方式可能变得不再奏效,相应的介绍缓冲区溢出原理的资料也变得“大众化”起来。其中看雪...
  • 文章目录Pre流为什么要缓冲区缓冲区总结 Pre 流和缓冲区都是用来描述数据的。 计算机中,数据往往会被抽象成流,然后传输。比如读取一个文件,数据会被抽象成文件流;播放一个视频,视频被抽象成视频流。处理...
  • github地址 一、 实验目标: 理解程序函数调用中参数传递机制; 掌握缓冲区溢出攻击方法; 进一步熟练掌握GDB调试工具和objdump反汇编工具。...程序运行中有3个关卡,每个关卡需要用户输入正确的缓冲区内容,.
  • 1 设置socket tcp缓冲区大小的疑惑疑惑1:通过setsockopt设置SO_SNDBUF、SO_RCVBUF这连个默认缓冲区的值,再用getsockopt获取设置的值,发现返回值是设置值的两倍。为什么?通过网上查找,看到linux的内核代码/usr/...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 919,155
精华内容 367,662
关键字:

缓冲区