当前位置:首页 > 内存
  • e络盟新增美光科技世界级内存和存储解决方案,进一步扩充半导体产品阵营

    e络盟新增美光科技世界级内存和存储解决方案,进一步扩充半导体产品阵营

    中国上海,2021年4月7日 – 安富利旗下全球电子元器件产品与解决方案分销商e络盟宣布与全球内存和存储解决方案领先品牌美光科技签署全球分销协议。通过这项协议,e络盟进一步拓展了其半导体产品阵容和供应范围,从而能够让客户更方便地使用世界级存储器件来进行人工智能和5G等技术的突破性应用开发。 美光系列产品广泛适用于各类市场,如消费电子产品、移动通信、汽车、工业设计和数据中心,以及个人计算、网络和服务器应用。用户现可通过e络盟选购美光系列创新产品解决方案,均有现货库存,其中包括: · SLC NAND闪存(包括MT29F4G08ABAFAH4-IT:F)是业内每兆比特成本最低的串行闪存解决方案,可降低智能互动玩具、图书和游戏、网络打印机、计算机外设以及向后兼容WiMAX机顶盒等嵌入式应用的整体BOM成本。与MLC和TLC NAND等其他技术相比,美光SLC NAND闪存具备高达10万次的P/E周期数和更快的数据存取能力。该系列提供存储密度从1GB到256GB的三种不同配置选择。 · 车规级LPDDR4系列DRAM芯片旨在提供新一代汽车应用所需的超高速、高可靠性和耐高温性。MT53E256M32D2DS-046 AIT:B是一款容量为4Gb的移动低功耗DDR4 SDRAM设备,可通过内部配置其高速CMOS来满足极端环境应用要求。 · 串行NOR闪存(包括MT25QU128ABA1EW9-0SIT)融合了一系列优异特性,包括高密度快速数据存取、安全数据存储、灵活架构及长期产品支持。该解决方案在功能设计和成本之间实现了完美平衡。 · 基于NAND闪存的2100AI PCIe NVMe SSD产品可为极端环境应用提供更高的性能和可靠性,能够为各种工业应用提供理想的小尺寸、大容量存储解决方案。2100AI系列SSD(包括MTFDHBK128TDP-1AT12AIYY)采用带PCIe Gen3 接口的单芯片控制器,可将多达四条PCIe通道连接到美光3D TLC NAND闪存。该系列SSD专为在读写过程中高效使用 PCIe接口而设计,同时还能提供高带宽低延迟性能。SSD技术加快了启动和应用加载速度,同时也降低了功耗。 Farnell及e络盟全球半导体和单板机业务部总监Lee Turner表示:“美光是全球最知名的半导体品牌之一,也是内存与存储解决方案设计、开发和生产领域的真正创新者。在过去的 12 个月里,我们持续加大投入扩充半导体产品阵容,以确保满足客户的多样化需求。美光的加入让我们备感荣幸。与美光的合作将让我们的客户能够更方便地购买到适合其方案开发的高性能内存组件。” 作为全球第四大半导体制造商,美光拥有丰富的高性能内存和存储技术产品,包括DRAM、NAND、3D XPoint™内存及NOR。美光在过去40多年来一直处于技术领先地位,其创新技术为全球核心技术进步发挥了重要作用。 e络盟拥有全面的半导体产品组合,产品丰富,能够为设计工程师提供有力支持。同时,客户还可以通过e络盟官网免费访问在线资源、数据表、应用说明、视频和网络研讨会,并可获取每周5天、每天8小时的技术支持。 客户现可通过Farnell(欧洲、中东和非洲地区)、Newark(北美地区) 和e络盟(亚太地区)购买美光系列产品。

    时间:2021-04-07 关键词: e络盟 内存 半导体

  • Java后端线上问题排查常用命令收藏

    本文来源:http://r6d.cn/b97q7 内存瓶颈 free free是查看内存使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存。 free -h -s 3表示每隔三秒输出一次内存情况,命令如下 [1014154@cc69dd4c5-4tdb5 ~]$ free               total        used        free      shared  buff/cache   available Mem:      119623656    43052220    45611364     4313760    30960072    70574408 Swap:             0           0           0 [1014154@cc69dd4c5-4tdb5 ~]$ free -h -s 3               total        used        free      shared  buff/cache   available Mem:           114G         41G         43G        4.1G         29G         67G Swap:            0B          0B          0B               total        used        free      shared  buff/cache   available Mem:           114G         41G         43G        4.1G         29G         67G Swap:            0B          0B          0B Mem:是内存的使用情况。 Swap:是交换空间的使用情况。 total:系统总的可用物理内存和交换空间大小。 used:已经被使用的物理内存和交换空间。 free:还有多少物理内存和交换空间可用使用, 是真正尚未被使用的物理内存数量。 shared:被共享使用的物理内存大小。 buff/cache:被 buffer(缓冲区) 和 cache(缓存) 使用的物理内存大小。 available:还可以被应用程序使用的物理内存大小, 它是从应用程序的角度看到的可用内存数量,available ≈ free + buffer + cache。 交换空间(swap space) swap space 是磁盘上的一块区域,当系统物理内存吃紧时,Linux 会将内存中不常访问的数据保存到 swap 上,这样系统就有更多的物理内存为各个进程服务,而当系统需要访问 swap 上存储的内容时,再将 swap 上的数据加载到内存中,这就是常说的换出和换入。交换空间可以在一定程度上缓解内存不足的情况,但是它需要读写磁盘数据,所以性能不是很高。 vmstat(推荐) vmstat(VirtualMeomoryStatistics,虚拟内存统计)是Linux中监控内存的常用工具,可对操作系统的虚拟内存、进程、CPU等的整体情况进行监视,推荐使用。 vmstat 5 3表示每隔5秒统计一次,一共统计三次。 [1014154@cc69dd4c5-4tdb5 ~]$ vmstat 5 3 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st  8  0      0 45453212 374768 30763728    0    0    14    99    1    1 11 10 78  0  1 10  0      0 45489232 374768 30763360    0    0     2  1275 95118 97908 13 11 75  0  1  6  0      0 45452908 374768 30765148    0    0     0  3996 89924 92073 12 10 78  0  1 procs r:表示运行和等待CPU时间片的进程数(就是说多少个进程真的分配到CPU),这个值如果长期大于系统CPU个数,说明CPU不足,需要增加CPU。b:表示在等待资源的进程数,比如正在等待I/O或者内存交换等。 memory swpd:表示切换到内存交换区的内存大小,即虚拟内存已使用的大小(单位KB),如果大于0,表示你的机器物理内存不足了,如果不是程序内存泄露的原因,那么你该升级内存了或者把耗内存的任务迁移到其他机器。free:表示当前空闲的物理内存。buff:表示缓冲大小,一般对块设备的读写才需要缓冲Cache:表示缓存大小,一般作为文件系统进行缓冲,频繁访问的文件都会被缓存,如果cache值非常大说明缓存文件比较多,如果此时io中的bi比较小,说明文件系统效率比较好。 swap si:表示数据由磁盘读入内存;通俗的讲就是每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。so:表示由内存写入磁盘,也就是由内存交换区进入内存的数据大小。 !! 注意:一般情况下si、so的值都为0,如果si、so的值长期不为0,则说明系统内存不足,需要增加系统内存 io bi:表示由块设备读入数据的总量,即读磁盘,单位kb/sbo:表示写到块设备数据的总量,即写磁盘,单位kb/s !! 注意:如果bi+bo的值过大,且wa值较大,则表示系统磁盘IO瓶颈。 system in:表示某一时间间隔内观测到的每秒设备终端数。cs:表示每秒产生的上下文切换次数,这个值要越小越好,太大了,要考虑调低线程或者进程的数目。例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。 !! 注意:这两个值越大,则由内核消耗的CPU就越多。 CPU us:表示用户进程消耗的CPU时间百分比,us值越高,说明用户进程消耗CPU时间越多,如果长期大于50%,则需要考虑优化程序或者算法。sy:表示系统内核进程消耗的CPU时间百分比,一般来说us+sy应该小于80%,如果大于80%,说明可能存在CPU瓶颈。id:表示CPU处在空间状态的时间百分比。wa:表示IP等待所占用的CPU时间百分比,wa值越高,说明I/O等待越严重,根据经验wa的参考值为20%,如果超过20%,说明I/O等待严重,引起I/O等待的原因可能是磁盘大量随机读写造成的,也可能是磁盘或者监控器的贷款瓶颈(主要是块操作)造成的。 sar sar和free类似sar -r 3每隔三秒输出一次内存信息: [root@localhost ~]# sar -r 3 Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain)    2020年04月28日  _x86_64_        (2 CPU) 15时40分10秒 kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty 15时40分13秒    106800   1314960     92.49      2144    573248   4110864    116.82    563664    498888        36 15时40分16秒    106816   1314944     92.49      2144    573248   4110864    116.82    563668    498888        36 15时40分19秒    106816   1314944     92.49      2144    573248   4110864    116.82    563668    498888        36 CPU瓶颈 查看机器cpu核数 CPU总核数 = 物理CPU个数 * 每颗物理CPU的核数  总逻辑CPU数 = 物理CPU个数 * 每颗物理CPU的核数 * 超线程数 查看CPU信息(型号) [1014154@cc69dd4c5-4tdb5 ~]$ cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c      32  Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz 查看物理CPU个数 [1014154@cc69dd4c5-4tdb5 ~]$ cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l 16 查看每个物理CPU中core的个数(即核数) [1014154@cc69dd4c5-4tdb5 ~]$ cat /proc/cpuinfo| grep "cpu cores"| uniq cpu cores       : 2 查看逻辑CPU的个数 [1014154@cc69dd4c5-4tdb5 ~]$ cat /proc/cpuinfo| grep "processor"| wc -l 32 top 在Linux内核的操作系统中,进程是根据虚拟运行时间(由进程优先级、nice值加上实际占用的CPU时间进行动态计算得出)进行动态调度的。在执行进程时,需要从用户态转换到内核态,用户空间不能直接操作内核空间的函数。通常要利用系统调用来完成进程调度,而用户空间到内核空间的转换通常是通过软中断来完成的。例如要进行磁盘操作,用户态需要通过系统调用内核的磁盘操作指令,所以CPU消耗的时间被切分成用户态CPU消耗、系统(内核) CPU 消耗,以及磁盘操作 CPU 消耗。执行进程时,需要经过一系列的操作,进程首先在用户态执行,在执行过程中会进行进程优先级的调整(nice),通过系统调用到内核,再通过内核调用,硬中断、软中断,让硬件执行任务。执行完成之后,再从内核态返回给系统调用,最后系统调用将结果返回给用户态的进程。 top可以查看CPU总体消耗,包括分项消耗,如User,System,Idle,nice等。Shift + H显示java线程;Shift + M按照内存使用排序;Shift + P按照CPU使用时间(使用率)排序;Shift + T按照CPU累积使用时间排序;多核CPU,进入top视图1,可以看到各各CPU的负载情况。 top - 15:24:11 up 8 days,  7:52,  1 user,  load average: 5.73, 6.85, 7.33 Tasks:  17 total,   1 running,  16 sleeping,   0 stopped,   0 zombie %Cpu(s): 13.9 us,  9.2 sy,  0.0 ni, 76.1 id,  0.1 wa,  0.0 hi,  0.1 si,  0.7 st KiB Mem : 11962365+total, 50086832 free, 38312808 used, 31224016 buff/cache KiB Swap:        0 total,        0 free,        0 used. 75402760 avail Mem    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND    300 ymmapp    20   0 17.242g 1.234g  14732 S   2.3  1.1   9:40.38 java      1 root      20   0   15376   1988   1392 S   0.0  0.0   0:00.06 sh     11 root      20   0  120660  11416   1132 S   0.0  0.0   0:04.94 python     54 root      20   0   85328   2240   1652 S   0.0  0.0   0:00.00 su     55 ymmapp    20   0   17432   1808   1232 S   0.0  0.0   0:00.00 bash     56 ymmapp    20   0   17556   2156   1460 S   0.0  0.0   0:00.03 control.sh     57 ymmapp    20   0   11880    740    576 S   0.0  0.0   0:00.00 tee    115 ymmapp    20   0   17556   2112   1464 S   0.0  0.0   0:00.02 control_new_war    133 root      20   0  106032   4240   3160 S   0.0  0.0   0:00.03 sshd    134 ymmapp    20   0   17080   6872   3180 S   0.0  0.0   0:01.82 ops-updater    147 ymmapp    20   0   17956   2636   1544 S   0.0  0.0   0:00.07 control.sh   6538 ymmapp    20   0  115656  10532   3408 S   0.0  0.0   0:00.46 beidou-agent   6785 ymmapp    20   0 2572996  22512   2788 S   0.0  0.0   0:03.44 gatherinfo4dock  29241 root      20   0  142148   5712   4340 S   0.0  0.0   0:00.04 sshd  29243 1014154   20   0  142148   2296    924 S   0.0  0.0   0:00.00 sshd  29244 1014154   20   0   15208   2020   1640 S   0.0  0.0   0:00.00 bash  32641 1014154   20   0   57364   2020   1480 R   0.0  0.0   0:00.00 top 第一行:15:24:11 up 8 days, 7:52, 1 user, load average: 5.73, 6.85, 7.33:15:24:11 系统时间,up 8 days 运行时间,1 user 当前登录用户数,load average 负载均衡情况,分别表示1分钟,5分钟,15分钟负载情况。 第二行:Tasks: 17 total, 1 running, 16 sleeping, 0 stopped, 0 zombie:总进程数17,运行数1,休眠 16,停止0,僵尸进程0。 第三行:%Cpu(s): 13.9 us, 9.2 sy, 0.0 ni, 76.1 id, 0.1 wa, 0.0 hi, 0.1 si, 0.7 st:用户空间CPU占比13.9%,内核空间CPU占比9.2%,改变过优先级的进程CPU占比0%,空闲CPU占比76.1,IO等待占用CPU占比0.1%,硬中断占用CPU占比0%,软中断占用CPU占比0.1%,当前VM中的cpu 时钟被虚拟化偷走的比例0.7%。 第四和第五行表示内存和swap区域的使用情况。 第七行表示: PID: 进程id USER:进程所有者 PR:进程优先级 NI:nice值。负值表示高优先级,正值表示低优先级 VIRT:虚拟内存,进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES RES:常驻内存,进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA SHR:共享内存,共享内存大小,单位kb S:进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程 %CPU:上次更新到现在的CPU时间占用百分比 %MEM:进程使用的物理内存百分比 TIME+:进程使用的CPU时间总计,单位1/100秒 COMMAND:进程名称(命令名/命令行) 计算在cpu load里面的uninterruptedsleep的任务数量 top -b -n 1 | awk '{if (NR<=7)print;else if($8=="D"){print;count++}}END{print "Total status D:"count}' [root@localhost ~]# top -b -n 1 | awk '{if (NR<=7)print;else if($8=="D"){print;count++}}END{print "Total status D:"count}' top - 15:35:05 up 1 day, 26 min,  3 users,  load average: 0.00, 0.01, 0.05 Tasks: 225 total,   1 running, 224 sleeping,   0 stopped,   0 zombie %Cpu(s):  2.5 us, 10.0 sy,  0.0 ni, 87.5 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st KiB Mem :  1421760 total,   104516 free,   777344 used,   539900 buff/cache KiB Swap:  2097148 total,  2071152 free,    25996 used.   456028 avail Mem    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND Total status D: sar 通过sar -u 3可以查看CUP总体消耗占比: [root@localhost ~]# sar -u 3 Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain)    2020年05月01日  _x86_64_        (2 CPU) 15时18分03秒     CPU     %user     %nice   %system   %iowait    %steal     %idle 15时18分06秒     all      0.00      0.00      0.17      0.00      0.00     99.83 15时18分09秒     all      0.00      0.00      0.17      0.00      0.00     99.83 15时18分12秒     all      0.17      0.00      0.17      0.00      0.00     99.66 15时18分15秒     all      0.00      0.00      0.00      0.00      0.00    100.00 15时18分18秒     all      0.00      0.00      0.00      0.00      0.00    100.00 %user:用户空间的CPU使用。 %nice:改变过优先级的进程的CPU使用率。 %system:内核空间的CPU使用率。 %iowait:CPU等待IO的百分比 。 %steal:虚拟机的虚拟机CPU使用的CPU。 %idle:空闲的CPU。 在以上的显示当中,主要看%iowait和%idle: 若%iowait的值过高,表示硬盘存在I/O瓶颈; 若%idle的值高但系统响应慢时,有可能是 CPU 等待分配内存,此时应加大内存容量; 若%idle的值持续低于 10,则系统的 CPU 处理能力相对较低,表明系统中最需要解决的资源是 CPU; 定位线上最耗CPU的线程 准备工作 启动一个程序。arthas-demo是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。 curl -O https://alibaba.github.io/arthas/arthas-demo.jar java -jar arthas-demo.jar [root@localhost ~]# curl -O https://alibaba.github.io/arthas/arthas-demo.jar % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                  Dload  Upload   Total   Spent    Left  Speed 100  3743  100  3743    0     0   3022      0  0:00:01  0:00:01 --:--:--  3023 [root@localhost ~]# java -jar arthas-demo.jar 1813=7*7*37 illegalArgumentCount:  1, number is: -180005, need >= 2 illegalArgumentCount:  2, number is: -111175, need >= 2 18505=5*3701 166691=7*23813 105787=11*59*163 60148=2*2*11*1367 196983=3*3*43*509 illegalArgumentCount:  3, number is: -173479, need >= 2 illegalArgumentCount:  4, number is: -112840, need >= 2 39502=2*19751 .... 通过top命令找到最耗时的进程 [root@localhost ~]# top top - 11:11:05 up 20:02,  3 users,  load average: 0.09, 0.07, 0.05 Tasks: 225 total,   1 running, 224 sleeping,   0 stopped,   0 zombie %Cpu(s):  0.0 us,  0.7 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st KiB Mem :  1421760 total,   135868 free,   758508 used,   527384 buff/cache KiB Swap:  2097148 total,  2070640 free,    26508 used.   475852 avail Mem Change delay from 3.0 to    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND  98344 root      20   0 2422552  23508  12108 S   0.7  1.7   0:00.32 java      1 root      20   0  194100   6244   3184 S   0.0  0.4   0:20.41 systemd      2 root      20   0       0      0      0 S   0.0  0.0   0:00.12 kthreadd      4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H      6 root      20   0       0      0      0 S   0.0  0.0   0:20.25 ksoftirqd/0 找到进程号是98344。 找到进程中最耗CUP的线程 使用ps -Lp #pid cu命令,查看某个进程中的线程CPU消耗排序: [root@localhost ~]# ps -Lp 98344 cu USER        PID    LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND root      98344  98344  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:00 java root      98344  98345  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:04 java root      98344  98346  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:01 VM Thread root      98344  98347  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:00 Reference Handl root      98344  98348  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:00 Finalizer root      98344  98349  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:00 Signal Dispatch root      98344  98350  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:05 C2 CompilerThre root      98344  98351  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:00 C1 CompilerThre root      98344  98352  0.0   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:00 Service Thread root      98344  98353  0.1   10  4.1 2422552 59060 pts/0   Sl+  11:09   0:19 VM Periodic Tas 看TIME列可以看出那个线程耗费CUP多,根据LWP列可以看到线程的ID号,但是需要转换成16进制才可以查询线程堆栈信息。 获取线程id的十六进制码 使用printf '%x\n' 98345命令做进制转换: [root@localhost ~]# printf '%x\n' 98345 18029 查看线程堆栈信息 使用jstack获取堆栈信息jstack 98344 | grep -A 10 18029: [root@localhost ~]# jstack 98344 | grep -A 10 18029 "main" #1 prio=5 os_prio=0 tid=0x00007fb88404b800 nid=0x18029 waiting on condition [0x00007fb88caab000]    java.lang.Thread.State: TIMED_WAITING (sleeping)         at java.lang.Thread.sleep(Native Method)         at java.lang.Thread.sleep(Thread.java:340)         at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)         at demo.MathGame.main(MathGame.java:17) "VM Thread" os_prio=0 tid=0x00007fb8840f2800 nid=0x1802a runnable "VM Periodic Task Thread" os_prio=0 tid=0x00007fb884154000 nid=0x18031 waiting on condition 通过命令我们可以看到这个线程的对应的耗时代码是在demo.MathGame.main(MathGame.java:17) grep -C 5 foo file 显示file文件里匹配foo字串那行以及上下5行 grep -B 5 foo file 显示foo及前5行 grep -A 5 foo file 显示foo及后5行 网络瓶颈 定位丢包,错包情况 watch more /proc/net/dev用于定位丢包,错包情况,以便看网络瓶颈,重点关注drop(包被丢弃)和网络包传送的总量,不要超过网络上限: [root@localhost ~]# watch -n 2 more /proc/net/dev Every 2.0s: more /proc/net/dev                                                                                                                                                   Fri May  1 17:16:55 2020 Inter-|   Receive                                                |  Transmit  face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed     lo:   10025     130    0    0    0     0          0         0    10025     130    0    0    0     0       0          0  ens33: 759098071  569661    0    0    0     0          0         0 19335572  225551    0    0    0     0       0          0 最左边的表示接口的名字,Receive表示收包,Transmit表示发送包; bytes:表示收发的字节数; packets:表示收发正确的包量; errs:表示收发错误的包量; drop:表示收发丢弃的包量; 查看路由经过的地址 traceroute ip可以查看路由经过的地址,常用来统计网络在各个路由区段的耗时,如: [root@localhost ~]# traceroute 14.215.177.38 traceroute to 14.215.177.38 (14.215.177.38), 30 hops max, 60 byte packets  1  CD-HZTK5H2.mshome.net (192.168.137.1)  0.126 ms * *  2  * * *  3  10.250.112.3 (10.250.112.3)  12.587 ms  12.408 ms  12.317 ms  4  172.16.227.230 (172.16.227.230)  2.152 ms  2.040 ms  1.956 ms  5  172.16.227.202 (172.16.227.202)  11.884 ms  11.746 ms  12.692 ms  6  172.16.227.65 (172.16.227.65)  2.665 ms  3.143 ms  2.923 ms  7  171.223.206.217 (171.223.206.217)  2.834 ms  2.752 ms  2.654 ms  8  182.150.18.205 (182.150.18.205)  5.145 ms  5.815 ms  5.542 ms  9  110.188.6.33 (110.188.6.33)  3.514 ms 171.208.199.185 (171.208.199.185)  3.431 ms 171.208.199.181 (171.208.199.181)  10.768 ms 10  202.97.29.17 (202.97.29.17)  29.574 ms 202.97.30.146 (202.97.30.146)  32.619 ms * 11  113.96.5.126 (113.96.5.126)  36.062 ms 113.96.5.70 (113.96.5.70)  35.940 ms 113.96.4.42 (113.96.4.42)  45.859 ms 12  90.96.135.219.broad.fs.gd.dynamic.163data.com.cn (219.135.96.90)  35.680 ms  35.468 ms  35.304 ms 13  14.215.32.102 (14.215.32.102)  35.135 ms 14.215.32.110 (14.215.32.110)  35.613 ms 14.29.117.242 (14.29.117.242)  54.712 ms 14  * 14.215.32.134 (14.215.32.134)  49.518 ms 14.215.32.122 (14.215.32.122)  47.652 ms 15  * * * ... 查看网络错误 netstat -i可以查看网络错误: [root@localhost ~]# netstat -i Kernel Interface table Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg ens33            1500   570291      0      0 0        225897      0      0      0 BMRU lo              65536      130      0      0 0           130      0      0      0 LRU Iface: 网络接口名称; MTU: 最大传输单元,它限制了数据帧的最大长度,不同的网络类型都有一个上限值,如:以太网的MTU是1500; RX-OK:接收时,正确的数据包数。 RX-ERR:接收时,产生错误的数据包数。 RX-DRP:接收时,丢弃的数据包数。 RX-OVR:接收时,由于过速(在数据传输中,由于接收设备不能接收按照发送速率传送来的数据而使数据丢失)而丢失的数据包数。 TX-OK:发送时,正确的数据包数。 TX-ERR:发送时,产生错误的数据包数。 TX-DRP:发送时,丢弃的数据包数。 TX-OVR:发送时,由于过速而丢失的数据包数。 Flg:标志,B 已经设置了一个广播地址。L 该接口是一个回送设备。M 接收所有数据包(混乱模式)。N 避免跟踪。O 在该接口上,禁用ARP。P 这是一个点到点链接。R 接口正在运行。U 接口处于“活动”状态。 包的重传率 cat /proc/net/snmp用来查看和分析240秒内网络包量,流量,错包,丢包。通过RetransSegs和OutSegs来计算重传率tcpetr=RetransSegs/OutSegs。 [root@localhost ~]# cat /proc/net/snmp Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates Ip: 1 64 241708 0 0 0 0 0 238724 225517 15 0 0 0 0 0 0 0 0 Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps Icmp: 149 0 0 50 99 0 0 0 0 0 0 0 0 0 147 0 147 0 0 0 0 0 0 0 0 0 0 IcmpMsg: InType3 InType11 OutType3 IcmpMsg: 50 99 147 Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors Tcp: 1 200 120000 -1 376 6 0 0 4 236711 223186 292 0 4 0 Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors Udp: 1405 438 0 1896 0 0 0 UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors UdpLite: 0 0 0 0 0 0 0 重传率=292/223186≈0.13% 平均每秒新增TCP连接数:通过/proc/net/snmp文件得到最近240秒内PassiveOpens的增量,除以240得到每秒的平均增量; 机器的TCP连接数 :通过/proc/net/snmp文件的CurrEstab得到TCP连接数; 平均每秒的UDP接收数据报:通过/proc/net/snmp文件得到最近240秒内InDatagrams的增量,除以240得到平均每秒的UDP接收数据报; 平均每秒的UDP发送数据报:通过/proc/net/snmp文件得到最近240秒内OutDatagrams的增量,除以240得到平均每秒的UDP发送数据报; 磁盘瓶颈 查磁盘空间 查看磁盘剩余空间 查看磁盘剩余空间使用df -hl命令: [root@localhost ~]# df -hl 文件系统                       容量  已用  可用 已用% 挂载点 devtmpfs                       678M     0  678M    0% /dev tmpfs                          695M     0  695M    0% /dev/shm tmpfs                          695M   28M  667M    4% /run tmpfs                          695M     0  695M    0% /sys/fs/cgroup /dev/mapper/centos_aubin-root   27G  5.6G   22G   21% / /dev/sda1                     1014M  211M  804M   21% /boot 查看磁盘已使用空间 du -sh命令是查看磁盘已使用空间的情况,这里的“已使用的磁盘空间”意思是指定的文件下的整个文件层次结构所使用的空间,在没给定参数的情况下,du报告当前目录所使用的磁盘空间。其实就是显示文件或目录所占用的磁盘空间的情况: [root@localhost ~]# du -sh 64K -h:输出文件系统分区使用的情况,例如:10KB,10MB,10GB等。 -s:显示文件或整个目录的大小,默认单位是KB。 !! du的详细信息可以通过man du查看。 查看磁盘读写情况 查看磁盘总体读写情况 通iostat查看磁盘总体的读写情况: [root@localhost ~]# iostat Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain)    2020年05月02日  _x86_64_        (2 CPU) avg-cpu:  %user   %nice %system %iowait  %steal   %idle            0.17    0.00    0.20    0.46    0.00   99.17 Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn sda               1.56        30.45        39.61    4659620    6060644 scd0              0.00         0.02         0.00       3102          0 dm-0              1.96        30.01        38.42    4591998    5878155 dm-1              0.09         0.09         0.30      13840      45328 tps:该设备每秒的传输次数。 kB_read/s:每秒从设备(drive expressed)读取的数据量; kB_wrtn/s:每秒向设备(drive expressed)写入的数据量; kB_read:读取的总数据量; kB_wrtn:写入的总数量数据量; 查看磁盘详细读写情况 通过iostat -x 1 3可以看到磁盘详细读写情况,没隔一秒输出一次一共输出3次,当看到I/O等待时间所占CPU时间的比重很高的时候,首先要检查的就是机器是否正在大量使用交换空间,同时关注iowait占比cpu的消耗是否很大,如果大说明磁盘存在大的瓶颈,同时关注await,表示磁盘的响应时间以便小于5ms: [root@localhost ~]# iostat -x 1 3 Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain)    2020年05月02日  _x86_64_        (2 CPU) avg-cpu:  %user   %nice %system %iowait  %steal   %idle            0.17    0.00    0.20    0.46    0.00   99.16 Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util sda               0.01     0.49    0.63    0.95    30.59    39.78    89.58     0.34  214.23   49.16  323.48   8.55   1.34 scd0              0.00     0.00    0.00    0.00     0.02     0.00    98.48     0.00    1.21    1.21    0.00   0.95   0.00 dm-0              0.00     0.00    0.62    1.35    30.15    38.59    69.70     0.91  460.67   49.12  648.54   6.66   1.31 dm-1              0.00     0.00    0.02    0.07     0.09     0.30     8.52     0.04  442.74   95.43  521.17   6.91   0.06 avg-cpu表示总体cpu使用情况统计信息,对于多核cpu,这里为所有cpu的平均值: %user:CPU处在用户模式下的时间百分比。 %nice:CPU处在带NICE值的用户模式下的时间百分比。 %system:CPU处在系统模式下的时间百分比。 %iowait:CPU等待输入输出完成时间的百分比, 如果%iowait的值过高,表示硬盘存在I/O瓶颈。 %steal:管理程序维护另一个虚拟处理器时,虚拟CPU的无意识等待时间百分比。 %idle:CPU空闲时间百分比, 如果%idle值高,表示CPU较空闲;如果%idle值高但系统响应慢时,可能是CPU等待分配内存,应加大内存容量;如果%idle值持续低于10,表明CPU处理能力相对较低,系统中最需要解决的资源是CPU。。 Device表示设备信息: rrqm/s:每秒对该设备的读请求被合并次数,文件系统会对读取同块(block)的请求进行合并 wrqm/s:每秒对该设备的写请求被合并次数 r/s:每秒完成的读次数 w/s:每秒完成的写次数 rkB/s:每秒读数据量(kB为单位) wkB/s:每秒写数据量(kB为单位) avgrq-sz:平均每次IO操作的数据量(扇区数为单位) avgqu-sz:平均等待处理的IO请求队列长度 await:平均每次IO请求等待时间(包括等待时间和处理时间,毫秒为单位) svctm:平均每次IO请求的处理时间(毫秒为单位) %util:一秒中有百分之多少的时间用于 I/O如果%util接近100%,说明产生的I/O请求太多,I/O系统已经满负荷。idle小于70% IO压力就较大了,一般读取速度有较多的wait。 !! iostat -xmd 1 3:新增m选项可以在输出是使用M为单位。 查看最耗IO的进程 一般先通过iostat查看是否存在io瓶颈,再使用iotop命令来定位那个进程最耗费IO: [root@localhost ~]# iotop Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s    TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND 123931 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.02 % [kworker/1:30]  94208 be/4 xiaolyuh    0.00 B/s    0.00 B/s  0.00 %  0.00 % nautilus-desktop --force [gmain]      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --system --deserialize 62      2 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kthreadd]  94211 be/4 xiaolyuh    0.00 B/s    0.00 B/s  0.00 %  0.00 % gvfsd-trash --spawner :1.4 /org/gtk/gvfs/exec_spaw/0      4 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kworker/0:0H]      6 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [ksoftirqd/0]      7 rt/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [migration/0]      8 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [rcu_bh]      9 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [rcu_sched]     10 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [lru-add-drain] ... 通过iotop -p pid可以查看单个进程的IO情况: [root@localhost ~]# iotop -p 124146 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s    TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND 124146 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % java -jar arthas-demo.jar 应用瓶颈 查看某个进程的PID 如查看java的进程的pid,ps -ef | grep java: [root@localhost ~]# ps -ef | grep java root     124146   1984  0 09:13 pts/0    00:00:06 java -jar arthas-demo.jar root     125210  98378  0 10:07 pts/1    00:00:00 grep --color=auto java 查看特定进程的数量 如查看java进程的数量,ps -ef | grep java| wc -l: [root@localhost ~]# ps -ef | grep java| wc -l 2 查看线程是否存在死锁 查看线程是否存在死锁,jstack -l pid: [root@localhost ~]# jstack -l 124146 2020-05-02 10:13:38 Full thread dump OpenJDK 64-Bit Server VM (25.252-b09 mixed mode): "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f27f013c000 nid=0x1e4f9 waiting on condition [0x0000000000000000]    java.lang.Thread.State: RUNNABLE    Locked ownable synchronizers:         - None "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f27f012d000 nid=0x1e4f8 waiting on condition [0x0000000000000000]    java.lang.Thread.State: RUNNABLE    Locked ownable synchronizers:         - None "main" #1 prio=5 os_prio=0 tid=0x00007f27f004b800 nid=0x1e4f3 waiting on condition [0x00007f27f7274000]    java.lang.Thread.State: TIMED_WAITING (sleeping)         at java.lang.Thread.sleep(Native Method)         at java.lang.Thread.sleep(Thread.java:340)         at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)         at demo.MathGame.main(MathGame.java:17)    Locked ownable synchronizers:         - None ... 查看某个进程的线程数 ps -efL | grep [PID] | wc -l,如: [root@localhost ~]# ps -efL | grep 124146 | wc -l 12 查看具体有哪些线程用ps -Lp [pid] cu: [root@localhost ~]# ps -Lp 124146 cu USER        PID    LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND root     124146 124146  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 java root     124146 124147  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:01 java root     124146 124148  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 VM Thread root     124146 124149  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 Reference Handl root     124146 124150  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 Finalizer root     124146 124151  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 Signal Dispatch root     124146 124152  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 C2 CompilerThre root     124146 124153  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 C1 CompilerThre root     124146 124154  0.0   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:00 Service Thread root     124146 124155  0.1   11  2.5 2489116 35724 pts/0   Sl+  09:13   0:05 VM Periodic Tas root     124146 125362  0.0   11  2.5 2489116 35724 pts/0   Sl+  10:13   0:00 Attach Listener 统计所有的log文件中,包含Error字符的行 find / -type f -name "*.log" | xargs grep "ERROR",这个在排查问题过程中比较有用: [root@localhost ~]# find / -type f -name "*.log" | xargs grep "ERROR" /var/log/tuned/tuned.log:2020-03-13 18:05:59,145 ERROR    tuned.utils.commands: Writing to file '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor' error: '[Errno 19] No such device' /var/log/tuned/tuned.log:2020-03-13 18:05:59,145 ERROR    tuned.utils.commands: Writing to file '/sys/devices/system/cpu/cpu1/cpufreq/scaling_governor' error: '[Errno 19] No such device' /var/log/tuned/tuned.log:2020-04-28 14:55:34,857 ERROR    tuned.utils.commands: Writing to file '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor' error: '[Errno 19] No such device' /var/log/tuned/tuned.log:2020-04-28 14:55:34,859 ERROR    tuned.utils.commands: Writing to file '/sys/devices/system/cpu/cpu1/cpufreq/scaling_governor' error: '[Errno 19] No such device' /var/log/tuned/tuned.log:2020-04-28 15:23:19,037 ERROR    tuned.utils.commands: Writing to file '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor' error: '[Errno 19] No such device' ... 应用启动时指定JVM参数 java -jar -Xms128m -Xmx1024m -Xss512k -XX:PermSize=128m -XX:MaxPermSize=64m -XX:NewSize=64m -XX:MaxNewSize=256m arthas-demo.jar,如: [root@localhost ~]# java -jar -Xms128m -Xmx1024m -Xss512k -XX:PermSize=128m -XX:MaxPermSize=64m -XX:NewSize=64m -XX:MaxNewSize=256m  arthas-demo.jar OpenJDK 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0 OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=64m; support was removed in 8.0 157518=2*3*3*3*2917 illegalArgumentCount:  1, number is: -187733, need >= 2 illegalArgumentCount:  2, number is: -102156, need >= 2 173379=3*57793 总结 在使用linux命令时,如果想看帮助可以使用--help或者man查看帮助信息: [root@localhost ~]# grep --help 用法: grep [选项]... PATTERN [FILE]... 在每个 FILE 或是标准输入中查找 PATTERN。 默认的 PATTERN 是一个基本正则表达式(缩写为 BRE)。 例如: grep -i 'hello world' menu.h main.c ... [root@localhost ~]# man grep GREP(1)                                                        General Commands Manual                                                        GREP(1) NAME        grep, egrep, fgrep - 打印匹配给定模式的行 总览 SYNOPSIS        grep [options] PATTERN [FILE...]        grep [options] [-e PATTERN | -f FILE] [FILE...] 描述 DESCRIPTION        Grep    搜索以    FILE    命名的文件输入   (或者是标准输入,如果没有指定文件名,或者给出的文件名是   -   的话),寻找含有与给定的模式   PATTERN ... 类别 监控命令 描述 备注 内存瓶颈 free 查看内存使用 vmstat 3(间隔时间) 100(监控次数) 查看swap in/out详细定位是否存在性能瓶颈 推荐使用 sar -r 3 和free命令类似,查看内存的使用情况,但是不包含swap的情况 cpu瓶颈 top -H 按照cpu消耗高低进行排序 ps -Lp 进程号 cu 查看某个进程的cpu消耗排序 cat /proc/cpuinfo |grep 'processor'|wc -l 查看cpu核数 top 查看cpu总体消耗,包括分项消耗如user,system,idle,nice等消耗 top 然后shift+h:显示java线程,然后shift+M:按照内存使用进行排序;shift+P:按照cpu时间排序;shift+T:按照cpu累计使用时间排序多核cpu,按“1”进入top视图 专项性能排查,多核CPU主要看CUP各个内核的负载情况 sar -u 3(间隔时间) 查看cpu总体消耗占比 sar -q 查看cpu load top -b -n 1 | awk '{if (NR<=7)print;else if($8=="D"){print;count++}}END{print "Total status D:"count}' 计算在cpu load里面的uninterruptedsleep的任务数量 uninterruptedsleep的任务会被计入cpu load,如磁盘堵塞 网络瓶颈 cat /var/log/messages 查看内核日志,查看是否丢包 watch more /proc/net/dev 用于定位丢包,错包情况,以便看网络瓶颈 重点关注drop(包被丢弃)和网络包传送的总量,不要超过网络上限 sar -n SOCK 查看网络流量 netstat -na|grep ESTABLISHED|wc -l 查看tcp连接成功状态的数量 此命令特别消耗cpu,不适合进行长时间监控数据收集 netstat -na|awk'{print $6}'|sort |uniq -c |sort -nr 看tcp各个状态数量 netstat -i 查看网络错误 ss state ESTABLISHED| wc -l 更高效地统计tcp连接状态为ESTABLISHED的数量 cat /proc/net/snmp 查看和分析240秒内网络包量,流量,错包,丢包 用于计算重传率tcpetr=RetransSegs/OutSegs ping $ip 测试网络性能 traceroute $ip 查看路由经过的地址 常用于定位网络在各个路由区段的耗时 dig $域名 查看域名解析地址 dmesg 查看系统内核日志 磁盘瓶颈 iostat -x -k -d 1 详细列出磁盘的读写情况 当看到I/O等待时间所占CPU时间的比重很高的时候,首先要检查的就是机器是否正在大量使用交换空间,同时关注iowait占比cpu的消耗是否很大,如果大说明磁盘存在大的瓶颈,同时关注await,表示磁盘的响应时间以便小于5ms iostat -x 查看系统各个磁盘的读写性能 重点关注await和iowait的cpu占比 iotop 查看哪个进程在大量读取IO 一般先通过iostat查看是否存在io瓶颈,再定位哪个进程在大量读取IO df -hl 查看磁盘剩余空间 du -sh 查看磁盘使用了多少空间 应用瓶颈 ps -ef grep java 查看某个进程的id号 ps -ef | grep httpd| wc -l 查看特定进程的数量 cat ***.log | grep ***Exception| wc -l 统计日志文件中包含特定异常数量 jstack -l pid 用于查看线程是否存在死锁 awk'{print $8}' 2017-05-22-access_log|egrep '301|302'| wc -l 统计log中301、302状态码的行数,$8表示第八列是状态码,可以根据实际情况更改 常用于应用故障定位 grep 'wholesaleProductDetailNew' cookie_log | awk '{if($10=="200")}'print}' awk 'print $12' | more 打印包含特定数据的12列数据 grep "2017:05:22" cookielog | awk '($12>0.3){print 8}' | sort > 目录地址 对apache或者nginx访问log进行响应时间排序,$12表示cookie log中的12列表示响应时间 用于排查是否是由于是某些访问超长造成整体的RT变长 grep -v 'HTTP/1.1" 200' 取出非200响应码的URL pgm -A -f $应用集群名称 "grep "'301' log文件地址 | wc -l" 查看整个集群的log中301状态码的数量 ps -efL | grep [PID] | wc -l 查看某个进程创建的线程数 find / -type f -name "*.log" | xargs grep "ERROR" 统计所有的log文件中,包含Error字符的行 这个在排查问题过程中比较有用 jstat -gc [pid] 查看gc情况 jstat -gcnew [pid] 查看young区的内存使用情况,包括MTT(最大交互次数就被交换到old区),TT是目前已经交换的次数 jstat -gcold 查看old区的内存使用情况 jmap -J-d64 -dump:format=b,file=dump.bin PID dump出内存快照 -J-d64防止jmap导致虚拟机crash(jdk6有bug) -XX:+HeapDumpOnOutOfMemeryError 在java启动时加入,当出现内存溢出时,存储内存快照 jmap -histo [pid] 按照对象内存大小排序 注意会导致full gc gcore [pid] 导出完成的内存快照 通常和jmap -permstat /opt/**/java gcore.bin一起使用,将core dump转换成heap dump -XX:HeapDumpPath=/home/logs -Xloggc:/home/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps 在Java启动参数中加入,打印gc日志 -server -Xms4000m -Xmx4000m -Xmn1500m -Xss256k -XX:PermSize=340m -XX:MaxPermSize=340m -XX:+UseConcMarkSweepGC 调整JVM堆大小 xss是栈大小 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    时间:2021-04-07 关键词: Java Java后端 内存

  • 解读世界上最先进的1α DRAM工艺——专访美光DRAM制程集成副总裁Thy Tran

    解读世界上最先进的1α DRAM工艺——专访美光DRAM制程集成副总裁Thy Tran

    对于半导体器件而言, 制程工艺的缩放将带来效能提升和成本下降的多重利好,所以对于工艺制程向更小节点追求是整个行业的目标。但随着工艺节点的逐步缩减,小到一定的尺寸后,挑战并不来自于几何约束,而进入到了更微观的领域——因为电荷的尺寸本身并不会改变,所以工艺制程到10nm以下后面临的电荷积累的问题尤为突出。除此外,生产设备本身的计量水平的挑战也变得尤为突出。而对于DRAM器件而言,缩放的难度比起CPU等更为困难,因此在今天之前仅从数字上看,DRAM的工艺制程也对应着略落后于CPU的制程。但最近美光于业界内率先实现了DRAM工艺制程的突破,将DRAM的工艺跃进到了第四代——1α。对此笔者专门与美光DRAM制程集成副总裁Thy Tran女士进行了采访,Thy Tran针对这一最新的DRAM工艺进行了详细的解读。 *美光DRAM制程集成副总裁Thy Tran* Micron 1α 的产品进入到用户的消费市场后,给终端用户的最直观的体验提升是什么?Thy Tran:美光的创新带来了业界功耗最低的移动DRAM,与上一代1z美光移动DRAM相比,实现了15%的节能。这使得5G移动用户可以在智能手机上进行更多任务操作,而不会牺牲续航。这一点很重要,因为智能手机的关键在于便携性,尽管用户希望手机能更快地执行更多的任务,但也不愿意牺牲续航或外形尺寸。例如,有些手机现在可以同时用两个摄像头拍摄视频。这对于像视频博主这样的人来说很有用,他们可以只使用一台设备同时摄录周围的环境和自己。然而,同时录制多个视频意味着要处理的数据量增加一倍,功耗也会随之增加一倍。如果为此续航会降低一半,或者手机要做得更大以容纳更大的电池,用户就不会觉得这一功能有什么用。在这种情况下,功耗降低15%为移动用户创造了对消费者来说更友好的体验。 1α还为PC市场提供了更节能的DDR4和LPDDR4解决方案,对于当前在家工作和在家学习的环境,笔记本电脑需要更长的续航时间,这为其带来了移动性优势。我们的汽车客户也在使用我们的移动低功耗DRAM,例如LPDDR4和LPDDR5,因此他们也能受益于这种节能特性。 低能耗对电动汽车和自动驾驶汽车尤其有利。随着ADAS和AI等数据密集型汽车技术的兴起,现代联网汽车目前运行的代码超过1亿行,每秒需要进行数百万亿次的运算,与数据中心的计算性能水平不相上下。这些汽车,或者称之为车轮上的数据中心,需要管理高性能计算,但不能让司机不断地为电动汽车充电或者加油以满足高耗电应用需求——因此,1α DRAM的能效也将有助于降低能耗,帮助自动驾驶汽车以更低的排放实现绿色交通的承诺。汽车所需的密集计算和处理带来的另一独特挑战是,所有的能量都会产生物理热量。在数据中心,我们可以通过风扇和水冷却等方式来管理热量,但在汽车中,热量很难释放出来。从用户体验的角度来看,司机们不愿意听到车内有嘈杂的风扇声,而且,对于成本敏感的汽车,水冷却并不实用。通过降低能耗,我们的1α低功耗DRAM还将减少自动驾驶汽车和智能汽车中多余的热量,实现对驾驶员来说更友好和更环保的体验。 普通工艺制程我们通常用40nm、22nm、7nm等数字直接来表示,在内存中使用1x、1y和1z等。请给我们分享下这种制程的节点的表达,与实际的“nm”有何关系?为何在DRAM上要采用不一样的工艺节点表达方式? Thy Tran:存储行业在节点与节点间往往遵循类似的规律。例如,在本世纪初我们处在180nm节点。大约十年前,我们来到22nm节点。正如您所知,几年前,我们在内存行业不再使用确切的数字,而是开始使用1x、1y和1z之类的术语。其原因很复杂,但很大程度上是因为确切的数字与性能没有很好的相关性。电路结构是三维的,使用线性的衡量方式并不适合。因此,每一个新字母都代表一个新的制程,表示性能有了很大的提高。特别是对于DRAM,节点的名称通常对应于最小特征尺寸,即内存单元阵列激活区的“半间距”的尺寸。对于1α,您可以将其视为10nm级别的第四代制程,其半间距在10nm到19nm之间。从1x纳米到1y、1z和1α,这一尺寸变得越来越小。我们是从1x开始的,但随着节点的不断缩小,要不断命名下一节点,就达到了罗马字母表的末尾。所以我们改用希腊字母alpha、beta、gamma等等。 EUV目前无法应用于DRAM生产的原因是什么?何时EUV可以满足DRAM生产需求?关于EUV,我们专有的创新多重曝光(multi-patterning)制程能够满足目前的性能和成本要求。通过我们的制程解决方案和先进的控制能力,我们可以满足技术节点的要求。 此外,EUV未必是制程发展的关键促成因素,而且目前EUV设备的性能也不如先进的浸润式光刻技术。虽然EUV技术还在改进,但其成本和性能仍然落后于当前的多重曝光和先进的浸润式光刻技术。其中一个原因是,EUV波长太短,光线不能透过玻璃,因此在进行EUV光刻时,传统的光学透镜不起作用。我们正在不断评估EUV,相信在未来三年内,EUV会取得必要的进展,在成本和性能上能够与先进的间距倍增和浸润式技术相竞争。当该技术符合我们的要求时,我们会在适当的时候引入。目前,美光拥有先进的光刻能力和间距倍增方法,可满足曝光要求,并拥有前沿的技术,以确保良好的层间堆叠。 美光的1α是如何突破物理极限的?之后的beta,gamma...将如何继续实现制程的缩进?一些物理限制和挑战包括:实现足够大的单元存储节点电容、阵列杂散(电阻和电容)以及曝光(即确定晶圆上的电路图案)等传统挑战。我们使用的制程和设备解决方案大大缩小了电路中的图案和特征尺寸,同时仍然满足电气要求,从而使我们能够不断向前迈进。 光刻能力决定了我们如何确定曝光流程。我们使用193nm浸润式光刻机和配备最新计算光刻技术的先进光刻掩膜板,从而实现了40nm以下制程。 为进一步发展,我们使用了四重曝光,这是一系列非光刻步骤,将一个大的特征尺寸分成两个,然后再分成四个特征尺寸,每个特征尺寸是原始特征尺寸的四分之一。早在2007年,美光就率先采用双重曝光开发了闪存产品。采用这一制程,我们可以精确地曝光出需要的细微特征尺寸,但是离一个完整的裸片还有很长的路要走,更不用说大批量生产了。我们只是刚刚勾画出一层的特征尺寸,而每个芯片有几十层。非常自豪的是,我们能够精确地控制层间的叠加。准确无误地做到这一点是让整个过程顺利进行的关键。然后我们必须把电路图案转变成功能电路器件,比如控制读写数据的晶体管以及可以存储代表1和0的电荷的高而薄的电容。这个过程意味着必须精确地控制材料构成以及这些材料的机械和电性能,并且每次都完全相同。我们充分发挥圆晶厂、实验室和合作伙伴的先进和创新优势,使这一切成为可能,并克服了DRAM扩展(或缩小)带来的物理挑战。我们对这个节点还采取了不同的方法,使风险承受能力更强。我们不是被动地等待数据以证明新技术可行,而是先行承担了更多的风险,然后开始确定缓解和降低风险的方法。这种基于工程知识和创新能力来博弈新方法的模式,使我们能够更积极地实现1α目标,同时为将来的节点应用这些新方法奠定了可扩展的基础。 展望未来,我们希望在后续节点(如beta和gamma)中继续这一创新,同样把重点放在制程改进上,并借鉴之前节点的经验教训。我们甚至利用从NAND团队那里学到的制程经验,他们最近推出了世界上第一款176层3D NAND,取得了业界领先的成就。此外,值得注意的是,我们的1α里程碑是通过技术开发、设计、产品和测试工程、制造和质量等各方面的协作来实现的——这是我们第一次进行如此全面的多学科协作,我们1α节点的领先优势充分证明了其可行性。我们希望通过同样的整体合作,在未来的节点上继续突破,使美光始终站在DRAM行业创新的最前沿。 *Quad patterning process flow (图片来源: Lam Research)*1α工艺的制造过程中是否有引入新类型的设备?我们的创新和创举无处不在:新材料,包括更好的导体、更好的绝缘体;用于沉积的新设备,修改或者有选择地去除、蚀刻这些材料。美光的领导团队非常愿意投资提升我们的节点领导优势,并提供了资源和新设备,全方位增强我们的制程能力。我们还将我们称之为晶圆厂的制造工厂发展成人工智能驱动的高度自动化工厂,不可不谓之奇迹。美光在世界各地拥有数以万计的科学家和工程师,致力于开发大家每天使用的内存、存储和加速器技术。我们设计电路、光掩膜技术、制程技术和封装技术,涉及从硅片到系统的各个领域。此外,美光拥有世界上最先进的智能工厂,世界经济论坛将我们新加坡和台湾地区工厂加入其Global Lighthouse Network(全球灯塔工厂网络),该网络包括了在应用第四次工业革命技术方面发挥了领导作用的很多领先制造商。美光是否有布局在DRAM的替代产品上?如果有的话,哪种产品和技术会是一种更有可能的更好的选择?对于应用,内存和存储技术有一个典型的性能与容量三角关系。三角形的顶部是DRAM,对于要求最苛刻的易失性应用,DRAM在数据延迟和耐久性方面是最好的。三角形的底部是闪存技术(TLC、QLC),它们是块存储应用的最佳选择。随着大量资本投资于创新设计,我们认为DRAM和NAND未来十年仍然会占据这种架构的顶部和底部。 美光不断探索新兴的内存技术,但我们的研究(如下所示)表明,DRAM仍然最适合低延迟易失性应用。MRAM,例如STTRAM,具有易于与逻辑半导体制程集成的优点,然而,STTRAM的数据延迟和能耗稍高于DRAM,耐久性也差一些,并且在密度方面还存在设计实现难点。因此,业界是否采用Logic+STTRAM还有待观察。RRAM是一种有趣的低延迟块存储技术,但目前还难以确定其面密度的经济性是否能带来广泛的市场部署。总的来说,新内存技术的研究和创新是非常激动人心的,但要赶超DRAM和NAND尚需时日。

    时间:2021-04-02 关键词: DRAM 美光 内存

  • 你不好奇内存耗尽后Redis会发生什么?

    作者:双子孤狼 原文地址:https://www.cnblogs.com/lonely-wolf/p/14403264.html 作为一台服务器来说,内存并不是无限的,所以总会存在内存耗尽的情况,那么当 Redis 服务器的内存耗尽后,如果继续执行请求命令,Redis 会如何处理呢? 设置有效期 使用Redis 服务时,很多情况下某些键值对只会在特定的时间内有效,为了防止这种类型的数据一直占有内存,我们可以给键值对设置有效期。Redis 中可以通过 4 个独立的命令来给一个键设置过期时间: expire key ttl:将  key 值的过期时间设置为  ttl  秒。 pexpire key ttl:将  key 值的过期时间设置为  ttl  毫秒。 expireat key timestamp:将  key 值的过期时间设置为指定的  timestamp  秒数。 pexpireat key timestamp:将  key 值的过期时间设置为指定的  timestamp  毫秒数。 PS:不管使用哪一个命令,最终 Redis 底层都是使用 pexpireat 命令来实现的。另外,set 等命令也可以设置 key 的同时加上过期时间,这样可以保证设值和设过期时间的原子性。 设置了有效期后,可以通过 ttl 和 pttl 两个命令来查询剩余过期时间(如果未设置过期时间则下面两个命令返回 -1,如果设置了一个非法的过期时间,则都返回 -2): ttl key 返回  key 剩余过期秒数。 pttl key 返回  key 剩余过期的毫秒数。 过期策略 如果将一个过期的键删除,我们一般都会有三种策略: 定时删除 :为每个键设置一个定时器,一旦过期时间到了,则将键删除。这种策略对内存很友好,但是对  CPU 不友好,因为每个定时器都会占用一定的  CPU 资源。 惰性删除 :不管键有没有过期都不主动删除,等到每次去获取键时再判断是否过期,如果过期就删除该键,否则返回键对应的值。这种策略对内存不够友好,可能会浪费很多内存。 定期扫描 :系统每隔一段时间就定期扫描一次,发现过期的键就进行删除。这种策略相对来说是上面两种策略的折中方案,需要注意的是这个定期的频率要结合实际情况掌控好,使用这种方案有一个缺陷就是可能会出现已经过期的键也被返回。 在 Redis 当中,其选择的是策略 2 和策略 3 的综合使用。不过 Redis 的定期扫描只会扫描设置了过期时间的键,因为设置了过期时间的键 Redis 会单独存储,所以不会出现扫描所有键的情况: typedef struct redisDb {    dict *dict; //所有的键值对    dict *expires; //设置了过期时间的键值对   dict *blocking_keys; //被阻塞的key,如客户端执行BLPOP等阻塞指令时   dict *watched_keys; //WATCHED keys   int id; //Database ID   //... 省略了其他属性} redisDb; 8 种淘汰策略 假如 Redis 当中所有的键都没有过期,而且此时内存满了,那么客户端继续执行 set 等命令时 Redis 会怎么处理呢?Redis 当中提供了不同的淘汰策略来处理这种场景。 首先 Redis 提供了一个参数 maxmemory 来配置 Redis 最大使用内存: maxmemory  或者也可以通过命令 config set maxmemory 1GB 来动态修改。 如果没有设置该参数,那么在 32 位的操作系统中 Redis 最多使用 3GB 内存,而在 64 位的操作系统中则不作限制。 Redis 中提供了 8 种淘汰策略,可以通过参数 maxmemory-policy 进行配置: 淘汰策略 说明 volatile-lru 根据 LRU 算法删除设置了过期时间的键,直到腾出可用空间。如果没有可删除的键对象,且内存还是不够用时,则报错 allkeys-lru 根据 LRU 算法删除所有的键,直到腾出可用空间。如果没有可删除的键对象,且内存还是不够用时,则报错 volatile-lfu 根据 LFU 算法删除设置了过期时间的键,直到腾出可用空间。如果没有可删除的键对象,且内存还是不够用时,则报错 allkeys-lfu 根据 LFU 算法删除所有的键,直到腾出可用空间。如果没有可删除的键对象,且内存还是不够用时,则报错 volatile-random 随机删除设置了过期时间的键,直到腾出可用空间。如果没有可删除的键对象,且内存还是不够用时,则报错 allkeys-random 随机删除所有键,直到腾出可用空间。如果没有可删除的键对象,且内存还是不够用时,则报错 volatile-ttl 根据键值对象的 ttl 属性, 删除最近将要过期数据。如果没有,则直接报错 noeviction 默认策略,不作任何处理,直接报错 PS:淘汰策略也可以直接使用命令 config set maxmemory-policy  来进行动态配置。 LRU 算法 LRU 全称为:Least Recently Used。即:最近最长时间未被使用。这个主要针对的是使用时间。 Redis 改进后的 LRU 算法 在 Redis 当中,并没有采用传统的 LRU 算法,因为传统的 LRU 算法存在 2 个问题: 需要额外的空间进行存储。 可能存在某些  key 值使用很频繁,但是最近没被使用,从而被  LRU 算法删除。 为了避免以上 2 个问题,Redis 当中对传统的 LRU 算法进行了改造,通过抽样的方式进行删除。 配置文件中提供了一个属性 maxmemory_samples 5,默认值就是 5,表示随机抽取 5 个 key 值,然后对这 5 个 key 值按照 LRU 算法进行删除,所以很明显,key 值越大,删除的准确度越高。 对抽样 LRU 算法和传统的 LRU 算法,Redis 官网当中有一个对比图: 浅灰色带是被删除的对象。 灰色带是未被删除的对象。 绿色是添加的对象。 左上角第一幅图代表的是传统 LRU 算法,可以看到,当抽样数达到 10 个(右上角),已经和传统的 LRU 算法非常接近了。 Redis 如何管理热度数据 前面我们讲述字符串对象时,提到了 redisObject 对象中存在一个 lru 属性: typedef struct redisObject {    unsigned type:4;//对象类型(4位=0.5字节)    unsigned encoding:4;//编码(4位=0.5字节)    unsigned lru:LRU_BITS;//记录对象最后一次被应用程序访问的时间(24位=3字节)    int refcount;//引用计数。等于0时表示可以被垃圾回收(32位=4字节)    void *ptr;//指向底层实际的数据存储结构,如:SDS等(8字节)} robj; lru 属性是创建对象的时候写入,对象被访问到时也会进行更新。正常人的思路就是最后决定要不要删除某一个键肯定是用当前时间戳减去 lru,差值最大的就优先被删除。但是 Redis 里面并不是这么做的,Redis 中维护了一个全局属性 lru_clock,这个属性是通过一个全局函数 serverCron 每隔 100 毫秒执行一次来更新的,记录的是当前 unix 时间戳。 最后决定删除的数据是通过 lru_clock 减去对象的 lru 属性而得出的。那么为什么 Redis 要这么做呢?直接取全局时间不是更准确吗? 这是因为这么做可以避免每次更新对象的 lru 属性的时候可以直接取全局属性,而不需要去调用系统函数来获取系统时间,从而提升效率(Redis 当中有很多这种细节考虑来提升性能,可以说是对性能尽可能的优化到极致)。 不过这里还有一个问题,我们看到,redisObject 对象中的 lru 属性只有 24 位,24 位只能存储 194 天的时间戳大小,一旦超过 194 天之后就会重新从 0 开始计算,所以这时候就可能会出现 redisObject 对象中的 lru 属性大于全局的 lru_clock 属性的情况。 正因为如此,所以计算的时候也需要分为 2 种情况: 当全局  lruclock >  lru,则使用  lruclock -  lru 得到空闲时间。 当全局  lruclock   now 时,默认为过了一个周期( 16 位,最大  65535),则取差值  65535-ldt+now:当  lru 

    时间:2021-03-11 关键词: 服务器 Redis 内存

  • 全球内存价格暴涨,国产芯片能否实现弯道超车?

    全球内存价格暴涨,国产芯片能否实现弯道超车?

    长期以来,中国在芯片市场缺少话语权和定价权,这导致国内一直被海外国际大厂垄断。虽然我国已经在尽力追赶,但技术上仍然与国际大厂有不小的差距。 国内厂商由于仍处于起步阶段,存储器的研发能否成功,未来几年将是关键期;研发成功后,良率能否提升到较高水平,成本控制是否能够达到预期,知识产权能否做到有效保护等,仍然有一定的不确定性;从研发成功至量产并形成销售,仍然需要长达几年时间。 存储器是半导体行业的重要分支,在经历了2015和2016年的持续走低后,2017年,全球存储器市场迎来了爆发,增长率达到60%,销售额超过1200亿美元,占全球半导体市场总值的30.1%。 20年下半年以来,全球汽车行业遭到芯片短缺的严重影响,不少一二线车企的部分工厂都已经因为“无芯可用”而被迫停工停产。 令人意想不到的是,汽车行业的缺芯现象已经逐渐蔓延至智能手机行业以及游戏机行业。全球芯片短缺的状况在2021年或将持续存在。 从去年底开始,DRAM内存市场价格下滑趋势已经止住了,在停电、火灾等意外因素影响下开始涨价,2021年全球内存涨价已经是定局。截止2月3日,8GB DDR4内存颗粒的报价来到3.93美元。要知道,在2020年8月,相关产品的报价仅为2.54美元。这短短六个月的时间,价格涨幅就达到54.7%。威刚DDR4-3200 16GB单条,去年11月是399元,截稿前已经涨到了569元。实际上,按照华邦电子的说法,DRAM和闪存价格的上扬将有助于公司在3、4月份实现营收增长。 内存接口芯片集成于DRAM内存模组(存储介质)中,是服务器内存模组的核心逻辑器件,其主要作用是提升内存数据访问的速度和稳定性,以匹配CPU日益提高的运行速度和性能。内存接口芯片具有较高的技术门槛,认证过程严格,尤以CPU厂商认证最为关键,按功能划分,内存接口芯片主要分为:寄存缓冲器(RCD)、数据缓冲器(DB)、内存缓冲器(MB/AMB)三类。 DDR是21世纪初主流内存规范,内存接口芯片技术经历了DDR2、DDR3、DDR4世代,目前DDR4内存技术处于成熟期,随着内存接口芯片的不断升级,内存技术逐渐由DDR4向DDR5升级迭代,相比于前一代内存接口芯片,DDR5可以支持更低电压工作环境及更高的运行速率。 其实,全球半导体目前正经历着大缺货,雪上加霜的是,美国得州遭遇重大自然灾害,日本福岛发生7.3级地震,导致部分半导体厂商暂时停产,比如三星的S2晶圆厂。 由于市场供不应求、供需状况得到控制,再加上智能手机、服务器市场的迅速增长,2020年的内存行业还是十分赚钱的内存行业的寡头现象也是越来越明显,前五大厂商拿走了81%毛利润,前十大厂商则占了总收入的92%。但这对于国产芯片而言,却是一个不可多得的机遇。 在全球内存版图中,三星、SK海力士及美光是三大巨头,合计占据全球95%以上的产能,其他厂商的份额非常小,后来的竞争者面临着强大的压力。 相比三星等厂商进军第三代、第四代10nm级内存工艺(1Znm之后),国产内存的19nm工艺还是第一代的,好在合肥长鑫之前已经表态会攻克17nm工艺,达到第二代10nm级工艺的水平,同时还会推出DDR5、LPDDR5等新标准内存。 以内存芯片为例。时至今日,三星等头部厂商已经在进军第三代、第四代(10nm级别)内存工艺。反观国产内存芯片的工艺依然停留在19nm,而且还是第一代。 对国产厂商来说,2021年内存涨价是个极好的机遇,但面临的挑战也不少,主要来自于技术及产能上的。近年来,中国芯片产业的崛起之势全球有目共睹。由于美国芯片出口的规则改变,全球芯片出口格局也在发生重大变化。为此,中国也在加快步伐,试图尽快实现芯片国产化,其中最为典型的就是中科院,该院上周已经宣布,要将光刻机等关键设备列入科研清单。最后也希望属于我们自己的光刻机早日可以研发出来,制造出属于我们自己的核心芯片,不再受外国力量的制裁和牵制。

    时间:2021-02-26 关键词: 国产芯片 内存

  • 美光发布面向汽车安全应用的低功耗内存

    美光发布面向汽车安全应用的低功耗内存

    2021 年 2 月 25 日,中国上海 — 内存与存储解决方案领先供应商Micron Technology Inc.(美光科技股份有限公司)今日宣布,已开始出样业内首款车用低功耗 DDR5 DRAM (LPDDR5) 内存。该款内存经过硬件评估,满足最高级别的汽车安全完整性等级 (ASIL) 标准,即 ASIL D。此外,美光还有一系列符合国际标准化组织 (ISO) 26262 标准、面向汽车安全的内存和存储新品。 该款 LPDDR5 DRAM 已通过功能安全评估,可用于高级驾驶辅助系统 (ADAS) 技术,包括自适应巡航控制系统、自动紧急刹车系统、车道偏离警告系统以及盲区侦测系统。它同时具备高性能、低功耗和低延迟特性,为满足日益增长的下一代汽车系统带宽需求提供了性能保障和发展空间。 美光嵌入式产品事业部企业副总裁兼总经理 Kris Baxter 表示:“自动驾驶能让我们的道路更加安全,但自动驾驶技术需要倚赖功能强大且可靠的内存,才能在极端环境下实时做出决策。为了满足不断增长的市场需求,我们优化了车用 LPDDR5,为未来的智能安全汽车提供优异的性能、质量和可靠性。” 随着电子元件成为保障行车安全的必要零部件,汽车制造商必须遵循严格的功能安全标准,确保安全机制能够在汽车发生故障时减轻风险。美光深谙功能安全与日俱增的重要意义,在公司内设立专项部门,根据汽车安全系统设计的内存要求与客户进行紧密协作。为了帮助客户履行复杂的合规义务,该部门在美光内部主导了 LPDDR5 上市,随附安全应用说明,并发布了行业首份由供应商提供的 DRAM 硬件评估报告。美光的硬件评估报告还由汽车安全领域的知名专业公司 exida 进行了独立评估和验证。通过在公司内部进行此项严格评估,美光旨在帮助汽车领域的客户简化系统设计,加快其产品上市。 exida 首席运营官兼首席安全专家 Alexander Griessing 表示:“功能安全是先进汽车系统开发过程中至关重要的一环,但到目前为止,内存在某种程度上受到了忽视,只被当作一种即买即用的商品。美光推出了领先业界的车用 LPDDR5,精准符合 ISO 26262 标准,为内存行业树立了新的标杆。提升对功能安全的关注将惠及各方,包括汽车制造商和需要先进、安全车辆的消费者。” 美光低功耗内存推动汽车创新和绿色出行 随着 ADAS 和自动驾驶技术应用的日益普及,数据采集和高效处理能力正逐渐成为汽车创新的关键所在。Gartner预测汽车内存的市场规模将在 2020 年 24 亿美元的基础上翻番,成长至 2024 年的 63 亿美元。伴随数据密集型汽车技术的崛起,如今配备 ADAS 的车辆运行超过 1 亿行代码,每秒需进行高达数百万亿次的数据处理,可匹敌数据中心。LPDDR5 能将数据访问速度提高 50%,将能效提升 20% 以上,从而有效应对这些需求。这使智能汽车能够近乎实时地处理和决策来自多个传感器和数据源的数据,例如雷达、激光雷达、高分辨率成像、5G 网络和光学图像识别等。 LPDDR5 的高能效可助力实现汽车的高性能计算,同时大幅降低电动汽车和传统汽车的能耗,从而实现更加环保的交通出行,减少尾气排放。美光的车用 LPDDR5 还加强了稳固性,可应用于极端温度范围,符合多种汽车可靠性标准,包括美国汽车电子协会的 AEC-Q100 规范和国际汽车工作组的 IATF 16949 标准。 通过功能安全评估的独特 DRAM,加快智能安全汽车上市 美光 LPDDR5 附带详细的功能安全说明文件,可在系统配置期间帮助客户开展全面的安全分析。美光提供硬件评估报告,严格依照 ISO 26262 标准,对广泛的功能安全分析进行了核验。为了满足更高的安全要求,LPDDR5 包含稳健的安全机制,可在运行期间检测和控制内存错误,系统集成商还可选择部署更多机制,从而进一步降低风险。 凭借高质量内存和存储解决方案,美光服务汽车市场 30 年,广受领先汽车制造商的青睐,累计行驶里程达万亿英里 [5] 。 美光深厚的专业知识源自与汽车领域客户的紧密协作,共同将内存设计集成到系统架构中,为 ADAS、车载信息娱乐系统、数字化座舱和机器学习提供支持。美光是汽车行业排名首位的内存供应商。

    时间:2021-02-25 关键词: 低功耗 美光 内存

  • 美光发布1α DRAM 制程技术,引领DRAM技术革新

    美光发布1α DRAM 制程技术,引领DRAM技术革新

    内存和存储领域仍将保持高速增长势头 据Sumit分享,目前已经有多个成长动能因素交织在一起:汽车业需求逐步反弹,5G手机数量今年将实现翻倍增长,疫情带来的线上业务增多同样也发挥巨大推动作用。旺盛的内存和存储市场需求对于制造商的生产能力也提出了更高的要求,而这一部分的压力主要是集中在DRAM这边。据Sumit先生观察,在当前第一季度已经可以看到某些DRAM产品的价格有所上涨,并且随着全球经济复苏这种供给紧俏的现象还会持续下去。而在NAND端目前虽然市场供应充足,随着价格浮动市场会自行进行调节,在年底有望达到产能稳定。 在此次媒体会上,美光发布了其最新的1α DRAM 制程技术,这代表着业界DRAM技术的最高水平。对于美光上一代1z DRAM 制程,1α 技术将内存密度提升了 40%。采用该制程技术的LPDDR5产品将在实现更高性能同时达到更好的功耗表现,为一共行业提供15%节能的DRAM平台,帮助5G手机实现更长续航表现。1α 工艺制程提供了从8Gb~16Gb的密度,DDR4和LPDDR4产品也可以使用该新工艺来生产。所以针对有着存量市场的客户而言,可以采用更低成本实现系统性能提升,例如数据中心等。 从美光科技最新公布的财报信息来看,在截至去年12月3日的2021财年第一财季,美光科技营收较上一财年同期增12.23%。而随着2021年全球经济复苏、5G手机普及以及中国双循环和新基建的开启,美光在2021年的有着强劲的增长机会。在2021年作为NAND和DRAM技术先驱者的美光又将给我们带来哪些突破性的技术革新,让我们拭目以待。

    时间:2021-02-02 关键词: DRAM 美光 内存

  • 弄懂这些指针基础知识,再遇C指针咱就不慌了

    一、前言 二、变量与指针的本质 三、指针的几个相关概念 四、指向不同数据类型的指针 五、总结 一、前言 如果问C语言中最重要、威力最大的概念是什么,答案必将是指针!威力大,意味着使用方便、高效,同时也意味着语法复杂、容易出错。指针用的好,可以极大的提高代码执行效率、节约系统资源;如果用的不好,程序中将会充满陷阱、漏洞。 这篇文章,我们就来聊聊指针。从最底层的内存存储空间开始,一直到应用层的各种指针使用技巧,循序渐进、抽丝剥茧,以最直白的语言进行讲解,让你一次看过瘾。 说明:为了方便讲解和理解,文中配图的内存空间的地址是随便写的,在实际计算机中是要遵循地址对齐方式的。 二、变量与指针的本质 1. 内存地址 我们编写一个程序源文件之后,编译得到的二进制可执行文件存放在电脑的硬盘上,此时它是一个静态的文件,一般称之为程序。 当这个程序被启动的时候,操作系统将会做下面几件事情: 把程序的内容(代码段、数据段)从硬盘复制到内存中; 创建一个数据结构PCB(进程控制块),来描述这个程序的各种信息(例如:使用的资源,打开的文件描述符...); 在代码段中定位到入口函数的地址,让CPU从这个地址开始执行。 当程序开始被执行时,就变成一个动态的状态,一般称之为进程。 内存分为:物理内存和虚拟内存。操作系统对物理内存进行管理、包装,我们开发者面对的是操作系统提供的虚拟内存。 这2个概念不妨碍文章的理解,因此就统一称之为内存。 在我们的程序中,通过一个变量名来定义变量、使用变量。变量本身是一个确确实实存在的东西,变量名是一个抽象的概念,用来代表这个变量。就比如:我是一个实实在在的人,是客观存在与这个地球上的,道哥是我给自己起的一个名字,这个名字是任意取得,只要自己觉得好听就行,如果我愿意还可以起名叫:鸟哥、龙哥等等。 那么,我们定义一个变量之后,这个变量放在哪里呢?那就是内存的数据区。内存是一个很大的存储区域,被操作系统划分为一个一个的小空间,操作系统通过地址来管理内存。 内存中的最小存储单位是字节(8个bit),一个内存的完整空间就是由这一个一个的字节连续组成的。在上图中,每一个小格子代表一个字节,但是好像大家在书籍中没有这么来画内存模型的,更常见的是下面这样的画法: 也就是把连续的4个字节的空间画在一起,这样就便于表述和理解,特别是深入到代码对齐相关知识时更容易理解。(我认为根本原因应该是:大家都这么画,已经看顺眼了~~) 2. 32位与64位系统 我们平时所说的计算机是32位、64位,指的是计算机的CPU中寄存器的最大存储长度,如果寄存器中最大存储32bit的数据,就称之为32位系统。 在计算机中,数据一般都是在硬盘、内存和寄存器之间进行来回存取。CPU通过3种总线把各组成部分联系在一起:地址总线、数据总线和控制总线。地址总线的宽度决定了CPU的寻址能力,也就是CPU能达到的最大地址范围。 刚才说了,内存是通过地址来管理的,那么CPU想从内存中的某个地址空间上存取一个数据,那么CPU就需要在地址总线上输出这个存储单元的地址。假如地址总线的宽度是8位,能表示的最大地址空间就是256个字节,能找到内存中最大的存储单元是255这个格子(从0开始)。即使内存条的实际空间是2G字节,CPU也没法使用后面的内存地址空间。如果地址总线的宽度是32位,那么能表示的最大地址就是2的32次方,也就是4G字节的空间。 【注意】:这里只是描述地址总线的概念,实际的计算机中地址计算方式要复杂的多,比如:虚拟内存中采用分段、分页、偏移量来定位实际的物理内存,在分页中还有大页、小页之分,感兴趣的同学可以自己查一下相关资料。 3. 变量 我们在C程序中使用变量来“代表”一个数据,使用函数名来“代表”一个函数,变量名和函数名是程序员使用的助记符。变量和函数最终是要放到内存中才能被CPU使用的,而内存中所有的信息(代码和数据)都是以二进制的形式来存储的,计算机根据就不会从格式上来区分哪些是代码、哪些是数据。CPU在访问内存的时候需要的是地址,而不是变量名、函数名。 问题来了:在程序代码中使用变量名来指代变量,而变量在内存中是根据地址来存放的,这二者之间如何映射(关联)起来的? 答案是:编译器!编译器在编译文本格式的C程序文件时,会根据目标运行平台(就是编译出的二进制程序运行在哪里?是x86平台的电脑?还是ARM平台的开发板?)来安排程序中的各种地址,例如:加载到内存中的地址、代码段的入口地址等等,同时编译器也会把程序中的所有变量名,转成该变量在内存中的存储地址。 变量有2个重要属性:变量的类型和变量的值。 示例:代码中定义了一个变量 int a = 20; 类型是int型,值是20。这个变量在内存中的存储模型为: 我们在代码中使用变量名a,在程序执行的时候就表示使用0x11223344地址所对应的那个存储单元中的数据。因此,可以理解为变量名a就等价于这个地址0x11223344。换句话说,如果我们可以提前知道编译器把变量a安排在地址0x11223344这个单元格中,我们就可以在程序中直接用这个地址值来操作这个变量。 在上图中,变量a的值为20,在内存中占据了4个格子的空间,也就是4个字节。为什么是4个字节呢?在C标准中并没有规定每种数据类型的变量一定要占用几个字节,这是与具体的机器、编译器有关。 比如:32位的编译器中: char: 1个字节; short int: 2个字节; int: 4个字节; long: 4个字节。 比如:64位的编译器中: char: 1个字节; short int: 2个字节; int: 4个字节; long: 8个字节。 为了方便描述,下面都以32位为例,也就是int型变量在内存中占据4个字节。 另外,0x11223344,0x11223345,0x11223346,0x11223347这连续的、从低地址到高地址的4个字节用来存储变量a的数值20。在图示中,使用十六进制来表示,十进制数值20转成16进制就是:0x00000014,所以从开始地址依次存放0x00、0x00、0x00、0x14这4个字节(存储顺序涉及到大小端的问题,不影响文本理解)。 根据这个图示,如果在程序中想知道变量a存储在内存中的什么位置,可以使用取地址操作符&,如下: printf("&a = 0x%x \n", &a); 这句话将会打印出:&a = 0x11223344。 考虑一下,在32位系统中:指针变量占用几个字节? 4. 指针变量 指针变量可以分2个层次来理解: 指针变量首先是一个变量,所以它拥有变量的所有属性:类型和值。它的类型就是指针,它的值是其他变量的地址。 既然是一个变量,那么在内存中就需要为这个变量分配一个存储空间。在这个存储空间中,存放着其他变量的地址。 指针变量所指向的数据类型,这是在定义指针变量的时候就确定的。例如:int *p; 意味着指针指向的是一个int型的数据。 首先回答一下刚才那个问题,在32位系统中,一个指针变量在内存中占据4个字节的空间。因为CPU对内存空间寻址时,使用的是32位地址空间(4个字节),也就是用4个字节就能存储一个内存单元的地址。而指针变量中的值存储的就是地址,所以需要4个字节的空间来存储一个指针变量的值。 示例: int a = 20; int *pa; pa = &a; printf("value = %d \n", *pa); 在内存中的存储模型如下: 对于指针变量pa来说,首先它是一个变量,因此在内存中需要有一个空间来存储这个变量,这个空间的地址就是0x11223348; 其次,这个内存空间中存储的内容是变量a的地址,而a的地址为0x11223344,所以指针变量pa的地址空间中,就存储了0x11223344这个值。 这里对两个操作符&和*进行说明: &:取地址操作符,用来获取一个变量的地址。上面代码中&a就是用来获取变量a在内存中的存储地址,也就是0x11223344。 *:这个操作符用在2个场景中:定义一个指针的时候,获取一个指针所指向的变量值的时候。 int pa; 这个语句中的表示定义的变量pa是一个指针,前面的int表示pa这个指针指向的是一个int类型的变量。不过此时我们没有给pa进行赋值,也就是说此刻pa对应的存储单元中的4个字节里的值是没有初始化的,可能是0x00000000,也可能是其他任意的数字,不确定; printf语句中的*表示获取pa指向的那个int类型变量的值,学名叫解引用,我们只要记住是获取指向的变量的值就可以了。 5. 操作指针变量 对指针变量的操作包括3个方面: 操作指针变量自身的值; 获取指针变量所指向的数据; 以什么样数据类型来使用/解释指针变量所指向的内容。 5.1 指针变量自身的值 int a = 20;这个语句是定义变量a,在随后的代码中,只要写下a就表示要操作变量a中存储的值,操作有两种:读和写。 printf("a = %d \n", a);这个语句就是要读取变量a中的值,当然是20; a = 100;这个语句就是要把一个数值100写入到变量a中。 同样的道理,int *pa;语句是用来定义指针变量pa,在随后的代码中,只要写下pa就表示要操作变量pa中的值: printf("pa = %d \n", pa);这个语句就是要读取指针变量pa中的值,当然是0x11223344; pa = &a;这个语句就是要把新的值写入到指针变量pa中。再次强调一下,指针变量中存储的是地址,如果我们可以提前知道变量a的地址是 0x11223344,那么我们也可以这样来赋值:pa = 0x11223344; 思考一下,如果执行这个语句printf("&pa =0x%x \n", &pa);,打印结果会是什么? 上面已经说过,操作符&是用来取地址的,那么&pa就表示获取指针变量pa的地址,上面的内存模型中显示指针变量pa是存储在0x11223348这个地址中的,因此打印结果就是:&pa = 0x11223348。 5.2 获取指针变量所指向的数据 指针变量所指向的数据类型是在定义的时候就明确的,也就是说指针pa指向的数据类型就是int型,因此在执行printf("value = %d \n", *pa);语句时,首先知道pa是一个指针,其中存储了一个地址(0x11223344),然后通过操作符*来获取这个地址(0x11223344)对应的那个存储空间中的值;又因为在定义pa时,已经指定了它指向的值是一个int型,所以我们就知道了地址0x11223344中存储的就是一个int类型的数据。 5.3 以什么样的数据类型来使用/解释指针变量所指向的内容 如下代码: int a = 30000; int *pa = &a; printf("value = %d \n", *pa); 根据以上的描述,我们知道printf的打印结果会是value = 30000,十进制的30000转成十六进制是0x00007530,内存模型如下: 现在我们做这样一个测试: char *pc = 0x11223344; printf("value = %d \n", *pc); 指针变量pc在定义的时候指明:它指向的数据类型是char型,pc变量中存储的地址是0x11223344。当使用*pc获取指向的数据时,将会按照char型格式来读取0x11223344地址处的数据,因此将会打印value = 0(在计算机中,ASCII码是用等价的数字来存储的)。 这个例子中说明了一个重要的概念:在内存中一切都是数字,如何来操作(解释)一个内存地址中的数据,完全是由我们的代码来告诉编译器的。刚才这个例子中,虽然0x11223344这个地址开始的4个字节的空间中,存储的是整型变量a的值,但是我们让pc指针按照char型数据来使用/解释这个地址处的内容,这是完全合法的。 以上内容,就是指针最根本的心法了。把这个心法整明白了,剩下的就是多见识、多练习的问题了。 三、指针的几个相关概念 1. const属性 const标识符用来表示一个对象的不可变的性质,例如定义: const int b = 20; 在后面的代码中就不能改变变量b的值了,b中的值永远是20。同样的,如果用const来修饰一个指针变量: int a = 20; int b = 20; int * const p = &a; 内存模型如下: 这里的const用来修饰指针变量p,根据const的性质可以得出结论:p在定义为变量a的地址之后,就固定了,不能再被改变了,也就是说指针变量pa中就只能存储变量a的地址0x11223344。如果在后面的代码中写p = &b;,编译时就会报错,因为p是不可改变的,不能再被设置为变量b的地址。 但是,指针变量p所指向的那个变量a的值是可以改变的,即:*p = 21;这个语句是合法的,因为指针p的值没有改变(仍然是变量c的地址0x11223344),改变的是变量c中存储的值。 与下面的代码区分一下: int a = 20; int b = 20; const int *p = &a; p = &b; 这里的const没有放在p的旁边,而是放在了类型int的旁边,这就说明const符号不是用来修饰p的,而是用来修饰p所指向的那个变量的。所以,如果我们写p = &b;把变量b的地址赋值给指针p,就是合法的,因为p的值可以被改变。 但是这个语句*p = 21就是非法了,因为定义语句中的const就限制了通过指针p获取的数据,不能被改变,只能被用来读取。这个性质常常被用在函数参数上,例如下面的代码,用来计算一块数据的CRC校验,这个函数只需要读取原始数据,不需要(也不可以)改变原始数据,因此就需要在形参指针上使用const修饰符: short int getDataCRC(const char *pData, int len) { short int crc = 0x0000; // 计算CRC return crc; } 2. void型指针 关键字void并不是一个真正的数据类型,它体现的是一种抽象,指明不是任何一种类型,一般有2种使用场景: 函数的返回值和形参; 定义指针时不明确规定所指数据的类型,也就意味着可以指向任意类型。 指针变量也是一种变量,变量之间可以相互赋值,那么指针变量之间也可以相互赋值,例如: int a = 20; int b = a; int *p1 = &a; int *p2 = p1; 变量a赋值给变量b,指针p1赋值给指针p2,注意到它们的类型必须是相同的:a和b都是int型,p1和p2都是指向int型,所以可以相互赋值。那么如果数据类型不同呢?必须进行强制类型转换。例如: int a = 20; int *p1 = &a; char *p2 = (char *)p1; 内存模型如下: p1指针指向的是int型数据,现在想把它的值(0x11223344)赋值给p2,但是由于在定义p2指针时规定它指向的数据类型是char型,因此需要把指针p1进行强制类型转换,也就是把地址0x11223344处的数据按照char型数据来看待,然后才可以赋值给p2指针。 如果我们使用void *p2来定义p2指针,那么在赋值时就不需要进行强制类型转换了,例如: int a = 20; int *p1 = &a; void *p2 = p1; 指针p2是void*型,意味着可以把任意类型的指针赋值给p2,但是不能反过来操作,也就是不能把void*型指针直接赋值给其他确定类型的指针,而必须要强制转换成被赋值指针所指向的数据类型,如下代码,必须把p2指针强制转换成int*型之后,再赋值给p3指针: int a = 20; int *p1 = &a; void *p2 = p1; int *p3 = (int *)p2; 我们来看一个系统函数: void* memcpy(void* dest, const void* src, size_t len); 第一个参数类型是void*,这正体现了系统对内存操作的真正意义:它并不关心用户传来的指针具体指向什么数据类型,只是把数据挨个存储到这个地址对应的空间中。 第二个参数同样如此,此外还添加了const修饰符,这样就说明了memcpy函数只会从src指针处读取数据,而不会修改数据。 3. 空指针和野指针 一个指针必须指向一个有意义的地址之后,才可以对指针进行操作。如果指针中存储的地址值是一个随机值,或者是一个已经失效的值,此时操作指针就非常危险了,一般把这样的指针称作野指针,C代码中很多指针相关的bug就来源于此。 3.1 空指针:不指向任何东西的指针 在定义一个指针变量之后,如果没有赋值,那么这个指针变量中存储的就是一个随机值,有可能指向内存中的任何一个地址空间,此时万万不可以对这个指针进行写操作,因为它有可能指向内存中的代码段区域、也可能指向内存中操作系统所在的区域。 一般会将一个指针变量赋值为NULL来表示一个空指针,而C语言中,NULL实质是 ((void*)0) , 在C++中,NULL实质是0。在标准库头文件stdlib.h中,有如下定义: #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif 3.2 野指针:地址已经失效的指针 我们都知道,函数中的局部变量存储在栈区,通过malloc申请的内存空间位于堆区,如下代码: int *p = (int *)malloc(4); *p = 20; 内存模型为: 在堆区申请了4个字节的空间,然后强制类型转换为int*型之后,赋值给指针变量p,然后通过*p设置这个地址中的值为14,这是合法的。如果在释放了p指针指向的空间之后,再使用*p来操作这段地址,那就是非常危险了,因为这个地址空间可能已经被操作系统分配给其他代码使用,如果对这个地址里的数据强行操作,程序立刻崩溃的话,将会是我们最大的幸运! int *p = (int *)malloc(4); *p = 20; free(p); // 在free之后就不可以再操作p指针中的数据了。 p = NULL; // 最好加上这一句。 四、指向不同数据类型的指针 1. 数值型指针 通过上面的介绍,指向数值型变量的指针已经很明白了,需要注意的就是指针所指向的数据类型。 2. 字符串指针 字符串在内存中的表示有2种: 用一个数组来表示,例如:char name1[8] = "zhangsan"; 用一个char *指针来表示,例如:char *name2 = "zhangsan"; name1在内存中占据8个字节,其中存储了8个字符的ASCII码值;name2在内存中占据9个字节,因为除了存储8个字符的ASCII码值,在最后一个字符'n'的后面还额外存储了一个'\0',用来标识字符串结束。 对于字符串来说,使用指针来操作是非常方便的,例如:变量字符串name2: char *name2 = "zhangsan"; char *p = name2; while (*p != '\0') { printf("%c ", *p); p = p + 1; } 在while的判断条件中,检查p指针指向的字符是否为结束符'\0'。在循环体重,打印出当前指向的字符之后,对指针比那里进行自增操作,因为指针p所指向的数据类型是char,每个char在内存中占据一个字节,因此指针p在自增1之后,就指向下一个存储空间。 也可以把循环体中的2条语句写成1条语句: printf("%c ", *p++); 假如一个指针指向的数据类型为int型,那么执行p = p + 1;之后,指针p中存储的地址值将会增加4,因为一个int型数据在内存中占据4个字节的空间,如下所示: 思考一个问题:void*型指针能够递增吗?如下测试代码: int a[3] = {1, 2, 3}; void *p = a; printf("1: p = 0x%x \n", p); p = p + 1; printf("2: p = 0x%x \n", p); 打印结果如下: 1: p = 0x733748c0 2: p = 0x733748c1 说明void*型指针在自增时,是按照一个字节的跨度来计算的。 3. 指针数组与数组指针 这2个说法经常会混淆,至少我是如此,先看下这2条语句: int *p1[3]; // 指针数组 int (*p2)[3]; // 数组指针 3.1 指针数组 第1条语句中:中括号[]的优先级高,因此与p1先结合,表示一个数组,这个数组中有3个元素,这3个元素都是指针,它们指向的是int型数据。可以这样来理解:如果有这个定义char p[3],很容易理解这是一个有3个char型元素的数组,那么把char换成int*,意味着数组里的元素类型是int*型(指向int型数据的指针)。内存模型如下(注意:三个指针指向的地址并不一定是连续的): 如果向指针数组中的元素赋值,需要逐个把变量的地址赋值给指针元素: int a = 1, b = 2, c = 3; char *p1[3]; p1[0] = &a; p1[1] = &b; p1[2] = &c; 3.2 数组指针 第2条语句中:小括号让p2与*结合,表示p2是一个指针,这个指针指向了一个数组,数组中有3个元素,每一个元素的类型是int型。可以这样来理解:如果有这个定义int p[3],很容易理解这是一个有3个char型元素的数组,那么把数组名p换成是*p2,也就是p2是一个指针,指向了这个数组。内存模型如下(注意:指针指向的地址是一个数组,其中的3个元素是连续放在内存中的): 在前面我们说到取地址操作符&,用来获得一个变量的地址。凡事都有特殊情况,对于获取地址来说,下面几种情况不需要使用&操作符: 字符串字面量作为右值时,就代表这个字符串在内存中的首地址; 数组名就代表这个数组的地址,也等于这个数组的第一个元素的地址; 函数名就代表这个函数的地址。 因此,对于一下代码,三个printf语句的打印结果是相同的: int a[3] = {1, 2, 3}; int (*p2)[3] = a; printf("0x%x \n", a); printf("0x%x \n", &a); printf("0x%x \n", p2); 思考一下,如果对这里的p2指针执行p2 = p2 + 1;操作,p2中的值将会增加多少? 答案是12个字节。因为p2指向的是一个数组,这个数组中包含3个元素,每个元素占据4个字节,那么这个数组在内存中一共占据12个字节,因此p2在加1之后,就跳过12个字节。 4. 二维数组和指针 一维数组在内存中是连续分布的多个内存单元组成的,而二维数组在内存中也是连续分布的多个内存单元组成的,从内存角度来看,一维数组和二维数组没有本质差别。 和一维数组类似,二维数组的数组名表示二维数组的第一维数组中首元素的首地址,用代码来说明: int a[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // 二维数组 int (*p0)[3] = NULL; // p0是一个指针,指向一个数组 int (*p1)[3] = NULL; // p1是一个指针,指向一个数组 int (*p2)[3] = NULL; // p2是一个指针,指向一个数组 p0 = a[0]; p1 = a[1]; p2 = a[2]; printf("0: %d %d %d \n", *(*p0 + 0), *(*p0 + 1), *(*p0 + 2)); printf("1: %d %d %d \n", *(*p1 + 0), *(*p1 + 1), *(*p1 + 2)); printf("2: %d %d %d \n", *(*p2 + 0), *(*p2 + 1), *(*p2 + 2)); 打印结果是: 0: 1 2 3 1: 4 5 6 2: 7 8 9 我们拿第一个printf语句来分析:p0是一个指针,指向一个数组,数组中包含3个元素,每个元素在内存中占据4个字节。现在我们想获取这个数组中的数据,如果直接对p0执行加1操作,那么p0将会跨过12个字节(就等于p1中的值了),因此需要使用解引用操作符*,把p0转为指向int型的指针,然后再执行加1操作,就可以得到数组中的int型数据了。 5. 结构体指针 C语言中的基本数据类型是预定义的,结构体是用户定义的,在指针的使用上可以进行类比,唯一有区别的就是在结构体指针中,需要使用->箭头操作符来获取结构体中的成员变量,例如: typedef struct { int age; char name[8]; } Student; Student s; s.age = 20; strcpy(s.name, "lisi"); Student *p = &s; printf("age = %d, name = %s \n", p->age, p->name); 看起来似乎没有什么技术含量,如果是结构体数组呢?例如: Student s[3]; Student *p = &s; printf("size of Student = %d \n", sizeof(Student)); printf("1: 0x%x, 0x%x \n", s, p); p++; printf("2: 0x%x \n", p); 打印结果是: size of Student = 12 1: 0x4c02ac00, 0x4c02ac00 2: 0x4c02ac0c 在执行p++操作后,p需要跨过的空间是一个结构体变量在内存中占据的大小(12个字节),所以此时p就指向了数组中第2个元素的首地址,内存模型如下: 6. 函数指针 每一个函数在经过编译之后,都变成一个包含多条指令的集合,在程序被加载到内存之后,这个指令集合被放在代码区,我们在程序中使用函数名就代表了这个指令集合的开始地址。 函数指针,本质上仍然是一个指针,只不过这个指针变量中存储的是一个函数的地址。函数最重要特性是什么?可以被调用!因此,当定义了一个函数指针并把一个函数地址赋值给这个指针时,就可以通过这个函数指针来调用函数。 如下示例代码: int add(int x,int y) { return x+y; } int main() { int a = 1, b = 2; int (*p)(int, int); p = add; printf("%d + %d = %d\n", a, b, p(a, b)); } 前文已经说过,函数的名字就代表函数的地址,所以函数名add就代表了这个加法函数在内存中的地址。int (*p)(int, int);这条语句就是用来定义一个函数指针,它指向一个函数,这个函数必须符合下面这2点(学名叫:函数签名): 有2个int型的参数; 有一个int型的返回值。 代码中的add函数正好满足这个要求,因此,可以把add赋值给函数指针p,此时p就指向了内存中这个函数存储的地址,后面就可以用函数指针p来调用这个函数了。 在示例代码中,函数指针p是直接定义的,那如果想定义2个函数指针,难道需要像下面这样定义吗? int (*p)(int, int); int (*p2)(int, int); 这里的参数比较简单,如果函数很复杂,这样的定义方式岂不是要烦死?可以用typedef关键字来定义一个函数指针类型: typedef int (*pFunc)(int, int); 然后用这样的方式pFunc p1, p2;来定义多个函数指针就方便多了。注意:只能把与函数指针类型具有相同签名的函数赋值给p1和p2,也就是参数的个数、类型要相同,返回值也要相同。 注意:这里有几个小细节稍微了解一下: 在赋值函数指针时,使用p = &a;也是可以的; 使用函数指针调用时,使用(*p)(a, b);也是可以的。 这里没有什么特殊的原理需要讲解,最终都是编译器帮我们处理了这里的细节,直接记住即可。 函数指针整明白之后,再和数组结合在一起:函数指针数组。示例代码如下: int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int divide(int a, int b) { return a / b; } int main() { int a = 4, b = 2; int (*p[4])(int, int); p[0] = add; p[1] = sub; p[2] = mul; p[3] = divide; printf("%d + %d = %d \n", a, b, p[0](a, b)); printf("%d - %d = %d \n", a, b, p[1](a, b)); printf("%d * %d = %d \n", a, b, p[2](a, b)); printf("%d / %d = %d \n", a, b, p[3](a, b)); } 这条语句不太好理解:int (*p[4])(int, int);,先分析中间部分,标识符p与中括号[]结合(优先级高),所以p是一个数组,数组中有4个元素;然后剩下的内容表示一个函数指针,那么就说明数组中的元素类型是函数指针,也就是其他函数的地址,内存模型如下: 如果还是难以理解,那就回到指针的本质概念上:指针就是一个地址!这个地址中存储的内容是什么根本不重要,重要的是你告诉计算机这个内容是什么。如果你告诉它:这个地址里存放的内容是一个函数,那么计算机就去调用这个函数。那么你是如何告诉计算机的呢,就是在定义指针变量的时候,仅此而已! 五、总结 我已经把自己知道的所有指针相关的概念、语法、使用场景都作了讲解,就像一个小酒馆的掌柜,把自己的美酒佳肴都呈现给你,但愿你已经酒足饭饱! 如果以上的内容太多,一时无法消化,那么下面的这两句话就作为饭后甜点为您奉上,在以后的编程中,如果遇到指针相关的困惑,就想一想这两句话,也许能让你茅塞顿开。 指针就是地址,地址就是指针。 指针就是指向内存中的一块空间,至于如何来解释/操作这块空间,由这个指针的类型来决定。 另外还有一点嘱咐,那就是学习任何一门编程语言,一定要弄清楚内存模型,内存模型,内存模型! 祝您好运! 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    时间:2021-01-30 关键词: 指针 C语言 内存

  • 美光率先于业界推出1α DRAM制程技术

    2021年1月27日,中国上海——内存与存储解决方案领先供应商Micron Technology Inc.(美光科技股份有限公司)今日宣布批量出货基于1α(1-alpha)节点的DRAM产品。该制程是目前世界上最为先进的DRAM技术,在密度、功耗和性能等各方面均有重大突破。这是继最近首推全球最快显存和176层NAND产品后,美光实现的又一突破性里程碑,进一步加强了公司在业界的竞争力。 美光技术与产品执行副总裁Scott DeBoer先生表示:“1α节点印证了美光在DRAM领域的领先成就,同时也是我们对前沿设计和技术不懈追求的成果。对比我们上一代的1z DRAM制程,1α技术将内存密度提升了40%,为将来的产品和内存创新提供了坚实的基础。” 美光计划于今年将1α节点全面导入其DRAM产品线,从而更好地支持广泛的DRAM应用领域——为包括移动设备和智能车辆在内的各种应用提供更强的性能。 美光继续领跑多个内存应用市场 美光执行副总裁兼首席商务官Sumit Sadana先生表示:“我们全新的1α技术将为手机行业带来最低功耗的DRAM,同时也使美光于数据中心、客户端、消费领域、工业和汽车领域的DRAM客户受益匪浅。内存和存储预计是未来十年增长最快的半导体市场,美光凭借领先业界的DRAM和NAND技术,将在这个快速增长的市场中立于不败之地。” 美光的1α技术节点使内存解决方案更节能、更可靠,并为需要最佳低功耗DRAM产品的移动平台带来运行速度更快的LPDDR5。美光为移动行业提供最低功耗的DRAM平台,实现了15%的节能,使5G用户在不牺牲续航的同时能在手机上进行更多任务操作。 美光的1α先进内存节点提供8Gb至16Gb的密度,将助力美光现有的DDR4和LPDDR4系列产品延长生命周期,并能为美光在服务器、客户端、网络和嵌入式领域的客户提供更低功耗、更可靠的产品及更全面的产品支持,从而降低客户再次验证的成本。对于具备较长产品生命周期的汽车嵌入式解决方案、工业PC和边缘服务器等应用场景而言,1α制程同样保证了在整个系统生命周期内更具优势的总体拥有成本。 供应情况 美光位于台湾地区的工厂已开始批量生产1α节点DRAM,首先出货的是面向运算市场的DDR4内存以及英睿达(Crucial)消费级PC DRAM产品。美光同时也已开始向移动客户提供LPDDR4样片进行验证。公司将在2021自然年内推出基于该技术的更多新产品。

    时间:2021-01-27 关键词: DRAM 美光 内存

  • 一个产品级MCU内存管理方案

    前面已经将所有的硬件驱动实现,验证了硬件功能。但是每一个硬件都是单独测试的,而且并不完善。下一步,我们需要对各个驱动进行整合完善。在整合之前,需要做一些基础工作。其中之一就是实现内存管理。什么叫内存管理呢?为什么要做内存管理?前面我们已经大概了解了程序中的变量现在我们复习一下:局部变量、全局变量。 局部变量在进入函数时从栈空间分配,退出函数前释放。全局变量则在整个程序运行其中一直使用。在程序编译时就已经分配了RAM空间。 那还有没有第三种变量呢?可以说没有。但是如果从生存周期上看,是有的:一个变量,在多个函数内使用,但是又不是整个程序运行期间都使用。或:一个变量,在一段时间内使用,不是整个程序运行生命周期都要用,但是用这个变量的函数会退出,然后重复进入(用static定义的局部变量相当于全局变量) 如果不使用动态内存管理,这样的变量就只能定义为全局变量。如果将这些变量定义为指针,当要使用时,通过内存管理分配,使用完后就释放,这就叫做动态分配。举个实际的例子: 一个设备,有三种通信方式:串口,USB,网络,在通信过程每个通信方式需要1K RAM。经过分析,3种通信方式不会同时使用。那么,如果不使用动态内存,则需要3K变量。如果使用内存管理动态分配,则只需要1K内存就可以了。(这个只是举例,如果简单的系统,确定三种方式不同时使用,可以直接复用内存) 通信方式只是举例,其实一个系统中,并不是所有设备都一直使用,如果使用动态内存管理,RAM的峰值用量将会大大减少。 内存管理方案 不发明车轮,只优化轮胎。 内存管理是编程界的一个大话题,有很多经典的方案。很多人也在尝试写新的方案。内存分配模块我们使用K&R C examples作为基础,然后进行优化。K&R是谁?就是写《C程序设计语言》的两个家伙。如果你没有这本书,真遗憾。这本书的8.7章节,<实例--存储分配程序>,介绍了一种基本的存储分配方法。代码见alloc.c,整个代码只有120行,而且结构很美。 K&R 内存管理方案分析 下面我们结合代码分析这种内存分配方案。代码在wujique\Utilities\alloc文件夹。 内存分析 初始化 在malloc函数中,如果是第一次调用就会初始化内存链表。代码原来是通过获取堆地址,在堆上建立内存池。我们把他改为更直观的数组定义方式。内存建立后的内存视图如下: 内存分配的最小单元是: typedef struct ALLOC_HDR {  struct {    struct ALLOC_HDR *ptr;    unsigned int size;/*本块内存容量*/ } s;  unsigned int align;  unsigned int pad; } ALLOC_HDR; 这也就是内存管理结构体。在32位ARM系统上,这个结构体是16字节。 第一次分配 每次分配,就是在一块可以分配的空间尾部切割一块出来,切割的大小是16字节的倍数,而且会比需要的内存多一块头。这块头在内存释放时需要使用。这一块,也就是内存管理的开销。 分配释放后 经过多次分配释放后,内存可能如下图,绿色是两块不连续的空闲块,黄色是分配出去的块。分配出去的块,已经不在内存链表里面。 缺点 一般情况上面的代码已经能满足需求。但是,有以下缺陷: 缺点1:容易碎片化 分配使用首次适应法,也即是找到一块大于等于要分配内存的空闲块,立刻进行分配。这种方法的优点是速度较快,缺点是容易内存碎片化,分配时将很多大块内存切割成小内存了。经过多次分配后,很可能出现以下情况: 空闲内存总量还有10K,但是却被分散在10个块内,而且没有大容量的内存块,再申请2K内存就出现失败。如果对时间并不是那么敏感,我们可以使用最适合法,也即是遍历空闲链表,查找一个最合适的内存(大于要分配内存且容量最小的空闲块),减少大内存被切碎的概率。需要注意的是,最适合法,除了会增加分配时间,不会减少内存碎片数量,只是增加了空闲内存的集中度。假设经过多次分配后,空闲总量还是10K,也是分散在10个空闲块,但是在这10个空闲块中,会有5K的大块,再申请2K的时候,就可以申请到2K内存了。 缺点2:内存消耗 内存分配方案使用了一个结构体,每次分配的最小单位就是这个结构体的大小16字节。 typedef struct ALLOC_HDR {  struct {    struct ALLOC_HDR *ptr;    unsigned int size;/*本块内存容量*/ } s;  unsigned int align;  unsigned int pad; } ALLOC_HDR; 一次分配,最少就是2个结构体(一个结构体用于管理分配出去的内存,其余结构体做为申请内存),也就是32字节。如果代码有大量小内存申请,例如申请100次8个字节 需求内存:100X8=800字节实际消耗内存100X32 = 3200字节利用率只有800/3200 =25% 如果内存分配只有25%的使用率,对于小内存嵌入式设备来说,是致命的方案缺陷。 如何解决呢?我们可以参考LINUX内存分配方案SLAB。在LINUX中,有很多模块需要申请固定大小的内存(例如node结构体),为了加快分配速度,系统会使用malloc先从大内存池中申请一批node结构体大小的内存,作为一个slab内存池。当需要分配node结构体时,就直接从slab内存池申请。同理,可以将内存分配优化为:需要小内存时,从大块内存池分配一块大内存,例如512,使用新算法管理,用于小内存分配。当512消耗尽,再从大内存池申请第二块512字节大内存。当小内存释放时,判断小块内存池是否为空,如为空,将小块内存池释放回大内存池。那如何管理这个小内存池呢? 缺点3:没有管理已分配内存 内存分配没有将已分配内存管理起来。我们可以对已分配内存进行统一管理: 1 已分配内存在头部有原来的结构体,通过ptr指针,将所有已分配内存连接在已分配链表上。2 利用不使用的align跟pad成员,记录分配时间跟分配对象(记录哪个驱动申请的内存) 通过上面优化后,就可以统计已经分配了多少内存,还有多少空闲内存,哪个模块申请了最多内存等数据。 使用 1 将代码中的所有free改为为wjq_free,malloc改为wjq_malloc。 串口缓冲用了free跟malloc.fatfs的syscall.c 用了lwip的mem.h用了。 2 修改启动代码, 栈跟堆改小。不用库的malloc,堆可以完全不要。栈,还是要保留,但是不需要那么大,如果函数内用到比较大的局部变量,改为动态申请。 Stack_Size      EQU     0x00002000 AREA    STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem       SPACE   Stack_Size __initial_sp ;Heap Configuration ;Heap Size (in Bytes);Heap_Size       EQU     0x00000010        AREA    HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem        SPACE   Heap_Size __heap_limit 3 内存池开了80K,编译不过 linking... .\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching dev_touchscreen.o(.bss). .\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching mcu_uart.o(.bss). .\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching etharp.o(.bss). .\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching mcu_can.o(.bss). .\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching netconf.o(.bss). 先把内存池改小,编译通过之后,分析 map文件,用了较多全局变量的统统改小或者改为动态申请。分析map文件,还可以检查还有没有使用库里面的malloc。 Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name    124         32          0          4      40976       1658   alloc.o     16          0          0          0          0       2474   def.o     96         34       8640          4          0       1377   dev_dacsound.o    300         36          0          0          0       2751   dev_esp8266.o    204         38          0          1          0       1446   dev_key.o    436         98          0         10         16       3648   dev_touchkey.o    310         18          0         14       3000       3444   dev_touchscreen.o    932         18          0          4          0      15981   dhcp.o      0          0          0          0       3964       5933   dual_func_demo.o    280         14         12          0        200       5963   etharp.o      0          0          0          0          0      35864   ethernetif.o      0          0          0          0          0       3820   inet.o     98          0          0          0          0       2022   inet_chksum.o      0          0          0          0          0       4163   init.o    168          4          0         20          0       4763   ip.o      0          0          4          0          0       6463   ip_addr.o    386          4          0          0          0       4118   ip_frag.o    264         38          0          8         16     383399   main.o     84          8          0          0          0       1410   mcu_adc.o     60         32          0          1         68       1511   mcu_can.o     12          0          0          0          0        521   mcu_dac.o    128         14          0          0          0       2352   mcu_i2c.o     28          8          0          1          0        630   mcu_i2s.o    336         92          0          0          0       2689   mcu_rtc.o    430         86          0          1          0       4396   mcu_timer.o   1564         82          0          0        328       9072   mcu_uart.o    504         20          0         12          0       4510   mem.o     56         10          0          0       9463       3250   memp.o    120         14          0          0          0       1651   misc.o      0          0          0          0         56       1066   netconf.o    118          0          0          0          0       4267   netif.o    684          0          0          0          0       6971   pbuf.o     36          8        392          0       8192        824   startup_stm32f40_41xxx.o alloc.o 内存池dev_touchscreen.o 触摸屏缓冲dual_func_demo.o USB,应该能优化memp.o 什么鬼?又一个内存池?应该是要优化掉startup_stm32f40_41xxx.o 启动代码,是栈跟堆用的RAM. 由于编译器的优化,项目没用到的代码没有编译进来,上面的map数据并不完整。等后面我们做完全部测试程序,所有用到的代码都会参与连接,到时还需要优化一次。 总结 内存管理暂时到此,等后面所有功能都完成后,再进行一次优化。如果对内存分配时间有更高要求,可使用伙伴内存分配法。大家可以参考《都江堰操作系统与嵌入式系统设计》,这个文档里面的一些软件设计策略非常好。http://www.djyos.com/?page_id=50 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    时间:2021-01-23 关键词: MCU 内存

  • 内存随机也比顺序访问慢,带你深入理解内存IO过程

    平时大家都知道内存访问很快,今天来让我们来思考两个问题: 问题1: 内存访问一次延时到底是多少?你是否会进行大概的估算? 例如笔者的内存条的Speed显示是1066MHz,那是否可以推算出内存IO延时是1s/1066MHz=0.93ns? 这种算法大错特错。 问题2: 内存存在随机IO比顺序IO慢的问题吗?我们都知道磁盘的随机IO要比顺序IO慢的多(操作系统底层还专门实现了电梯调度算法来缓解这个问题),那么内存的随机IO会比顺序IO慢吗? 要想彻底弄明白以上两个问题,我想我们得从内存IO的物理过程中来寻找答案。 1先给你讲个图书管理员的故事 在开始介绍枯燥的内存工作原理之前。我想先给你讲一个故事,并带你去认识一个人,图书馆的管理员。 在我们的这个故事中,你是故事的主角。你有一所房子,房子里有一个仆人,他每天帮你处理各种各样的图书数据。但是北京房价太贵,所以你的这个房子很小,只能放的下64本书。你家的马路对面,就是北京图书馆(你家房子虽然小但是地段还不错),你所需要的所有的图书在那里都可以找到。图书馆有个管理员,他负责帮你把你想要的书找出来。 图1 图书管理员的故事 好接下来,故事开始进行! 场景1: 你发现你需要编号为0的书的计算结果,你的仆人穿过马路告诉了图书管理员,告诉他请帮我把第0-63本书取出来。图书管理员帮你在电脑前查得该书在二楼。于是他,花了点时间坐电梯到了二楼。等到了二楼,他又花了点时间帮你找了出来。然后你的仆人抱着64本书放到了客厅,拿起第0本书帮你处理了起来。 场景2: 你发现你需要编号为1的书的计算结果,告诉你的仆人。你的仆人直接从客厅拿出来就可以处理了,这次你等的时间最短。 场景3: 你发现需要编号为65的书,你又告诉你的仆人。你的仆人穿过马路又去找了图书管理员。图书管理员还在二楼呢,听说这次需要65-127,这次他不用再花时间找楼层了。只是花时间找书就可以了。你的仆人把65-127的书放到了客厅(以前的0-63就都扔了),并帮你开始处理起65号书来。 场景4: 你发现你需要编号为10000的书,你告诉了你的仆人。你的仆人穿过马路去图书馆,找到了管理员。这次管理员查得你需要的书是在10楼,他得花点时间坐电梯过去。去了之后,他又得花点时间帮你找出来。 这四个场景里,我觉得你一定发现了不同情形下耗时的差异。 场景1和场景4花费的时间最多。因为图书管理员需要花时间坐电梯找楼层,需要花时间在楼内找书。 场景3次之,因为图书管理员直接就在楼层内,只需要花时间在楼内找书既可 场景2最快,因为只需要仆人帮你从客厅拿过来就好,连马路都不需要过。 之所以编造这么一个例子,是因为内存的工作方式和它太像了。接下来我们进入内存的实际分析。 2内存物理结构 在《带你理解内存对齐最底层原理》中我们了解了内存颗粒的物理构造以及IO过程,今天我们再来复习一下。 内存是由chip构成。每个chip内部,是由8个bank组成的。其构造如下图: 图2 内存颗粒chip内部结构 而每一个bank是一个二维平面上的矩阵,前面文章中我们说到过。矩阵中每一个元素中都是保存了1个字节,也就是8个bit。 图3 bank内部物理结构 每当CPU向内存请求数据的时候,内存芯片总是8个bank并行一起工作。每个bank在定位到行地址后,把对应的行copy到row buffer。再根据列地址把对应的元素中的数据取出来,8个bank把数据拼接一下,一个64位宽的数据就可以返回给CPU了。 图4 一次内存IO的过程示意 根据上面几张图我们可以大致了解内存的IO过程,在这个过程中每一步操作之间都有一些延迟,让我们来继续了解这些延迟。 3内存IO延迟 在《从DDR发展到DDR4,内存核心频率指标其实基本上就没太大的进步》里我们提到内存的延迟很大程度是受核心频率制约的,你也应该记得我们提到了内存延迟一般是通过CL-tRCD-tRP-tRAS四个参数来标识的。我们今天来详细理解一下这四个参数的含义: CL(Column Address Latency):发送一个列地址到内存与数据开始响应之间的周期数 tRCD(Row Address to Column Address Delay):打开一行内存并访问其中的列所需的最小时钟周期数 tRP(Row Precharge Time):发出预充电命令与打开下一行之间所需的最小时钟周期数。 tRAS(Row Active Time):行活动命令与发出预充电命令之间所需的最小时钟周期数。也就是对下一次预充电时间进行限制。 要注意除了CL是固定周期数以外,其它的三个都是最小周期。另外上面的参数都是以时钟周期为单位的。因为现代的内存都是一个时钟周期上下沿分别各传输一次数据,所以用Speed/2就可以得出,例如笔者的机器的Speed是1066MHz,则时钟周期为533MHz。你自己的机器可以通过dmidecode命令查看: # dmidecode | grep -P -A16 "Memory Device" Memory Device ...... Speed: 1067 MHz ...... 和“图书管理员”类似,内存芯片也有类似的工作场景: 场景1: 你的进程需要内存地址0x0000为的一个字节的数据,CPU这时候向内存控制器发出请求,内存控制器进行行地址的预充电,需要等待tRP个时钟周期。再发出打开一行内存的命令,又需要等待tRCD个时钟周期。接着发送列地址,再等待CL个周期。最终将0x0000-0x0007的数据全部返回给了CPU。CPU把这些数据放入到了自己的cache里,并帮你开始对0x0000的数据进行运算。 场景2: 你的进程需要内存地址0x0003的一个字节数据,CPU发现发现它在自己的cache里存在,直接使用就好了。这个场景里其实根本就没有内存IO发生。 场景3: 你的进程需要内存地址0x0008的一个字节数据,CPU的cache并没有命中,于是向内存控制器请求。内存控制器发现行地址和上一次工作的行地址一致,这次只需要发送列地址后等待CL个周期,就可以拿到0x0008-0x0015的数据并返回给CPU了。 场景4: 你的进程需要内存地址0xf000的一个字节数据,同样CPU的cache并不命中,向内存控制器请求。内存控制器一看(内心有些许的郁闷),这次行w地址又变了,得,和场景1一样。继续等待tRP+tRCD+CL个周期后,才能够取到数据并返回。 实际的计算机的内存IO过程中还需要进行逻辑地址和物理地址的转换,这里忽略不表。 4结论其中场景1和场景4是随机IO的情况,场景2无内存IO发生,场景3是顺序IO,。通过上面的过程描述我们可以得到结论。内存也存在和磁盘一样,随机IO比顺序IO要慢的问题。如果行地址同上一次访问的不一致,则需要重新拷贝row buffer,延迟周期需要tRP+tRCD+CL。而如果是顺序IO的话(行地址不变),只需要CL个周期既可完成。 我们接着估算下内存的延时,笔者的机器上的内存参数Speed为1066MHz(通过dmidecode查得),该值除以2就是时钟周期的频率=1066/2=533Mhz。其延迟周期为7-7-7-24。 随机IO:这种状况下需要tRP+tRCD+CL个时钟周期,7+7+7=21个周期。但是还有个tRAS的限制,两次行地址预充电不得小于24。所以我们得按24来计算,24*(1s/533Mhz) = 45ns 顺序IO:这种状况下只需要CL个时钟周期 7*(1s/533Mhz)=13ns 5扩展,CPU的cache line虚拟内存概念因为对于内存来说,随机IO一次开销比顺序IO高好几倍。所以操作系统在工作的时候,会尽量让内存通过顺序IO的方式来进行。做法关键就是Cache Line。当CPU发现缓存不命中的时候,实际上从来不会向内存去请求1个字节,8个字节这种。而是一次性就要64字节,然后放到自己的Cache中存起来。 用上面的例子来看, 如果随机请求8字节:耗时是45ns 如果随机请求64字节:耗时是45+7*13 = 136ns 开销也没贵多少,因为只有第一个字节是随机IO,后面的7个字节都是顺序IO。数据是8倍,但是IO耗时只有3倍,而且取出来的数据后面大概率要用,所以计算机内部就这么搞了,通过这种方式帮你避免一些随机IO! 另外,内存也支持burst(突发传输)模式,在这种模式下可以只传入一次行列地址,就命令内存返回该内存开头的连续字节数据,比如64字节。这种模式下,只有第一次的8字节需要真正的行列访问延迟,后面的7个字节可以直接按内存的数据频率给吐出来。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    时间:2021-01-21 关键词: 计算机 IO 内存

  • 干货 | 常见的C编程段错误及对策

    时间:2021-01-19 关键词: 指针 C编程 内存

  • 如何查询OS、CPU、内存、硬盘信息

    编排 | strongerHuang 微信公众号 | 嵌入式专栏 本文描述 linux 查询操作系统信息,CPU物理个数,CPU核心数,逻辑CPU数,内存信息查询,硬盘信息查询。 前言 当我们接手了一台或者几台服务器的时候,首先我们有必要对服务器的基本配置有所认识,这样才可以对症下药,对以后的软件部署,系统运维会有事半功倍的效果。 关于服务器基本配置 查询服务器的基本配置一般查询操作系统,CPU,内存,硬盘,下面进行逐一讲解。 2.1 操作系统基本配置查询 查看操作系统版本 #cat /etc/redhat-release这个命令主要是查看红帽发行的操作系统的版本号 [root@node5 ~]# cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) #cat /etc/issue这个命令适用于大多数linux发行版 [root@node5 ~]# cat /etc/issue \S Kernel \r on an \m 查看操作系统内核版本 [root@node5 ~]# uname -r 3.10.0-693.el7.x86_64 查看操作系统详细信息 [root@node5 ~]# uname -a Linux node5 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux #从上面这段输出可以看出,该服务器主机名是node5,linux内核版本是3.10.0-693.el7.x86_64,CPU是x86架构 #该命令可以查看更多信息 [root@node5 ~]# more /etc/*release :::::::::::::: /etc/centos-release :::::::::::::: CentOS Linux release 7.4.1708 (Core) :::::::::::::: /etc/os-release :::::::::::::: NAME="CentOS Linux" VERSION="7 (Core)" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="7" PRETTY_NAME="CentOS Linux 7 (Core)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:7" HOME_URL="https://www.centos.org/" BUG_REPORT_URL="https://bugs.centos.org/" CENTOS_MANTISBT_PROJECT="CentOS-7" CENTOS_MANTISBT_PROJECT_VERSION="7" REDHAT_SUPPORT_PRODUCT="centos" REDHAT_SUPPORT_PRODUCT_VERSION="7" :::::::::::::: /etc/redhat-release :::::::::::::: CentOS Linux release 7.4.1708 (Core) :::::::::::::: /etc/system-release :::::::::::::: CentOS Linux release 7.4.1708 (Core) 2.2 CPU基本配置查询 名词解释 名词 含义 CPU物理个数 主板上实际插入的cpu数量 CPU核心数 单块CPU上面能处理数据的芯片组的数量,如双核、四核等 (cpu cores) 逻辑CPU数/线程数 一般情况下,逻辑cpu=物理CPU个数×每颗核数,如果不相等的话,则表示服务器的CPU支持超线程技术 查看 CPU 物理个数 [root@node5 ~]# grep 'physical id' /proc/cpuinfo | sort -u | wc -l 1 查看 CPU 核心数量 [root@node5 ~]# grep 'core id' /proc/cpuinfo | sort -u | wc -l 4 查看 CPU 线程数 #逻辑cpu数:一般情况下,逻辑cpu=物理CPU个数×每颗核数,如果不相等的话,则表示服务器的CPU支持超线程技术(HT:简单来说,它可使处理#器中的1 颗内核如2 颗内核那样在操作系统中发挥作用。这样一来,操作系统可使用的执行资源扩大了一倍,大幅提高了系统的整体性能,此时逻#辑cpu=物理CPU个数×每颗核数x2) [root@node5 ~]# cat /proc/cpuinfo| grep "processor"|wc -l 4 [root@node5 ~]# grep 'processor' /proc/cpuinfo | sort -u | wc -l 4 查看 CPU 型号 [root@node5 ~]# cat /proc/cpuinfo | grep name | sort | uniq model name : Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz [root@node5 ~]# dmidecode -s processor-version | uniq #使用uniq进行去重 Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz 查看 CPU 的详细信息 #CPU有几个核,就会输出几个重复的信息 [root@node5 ~]# cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz stepping : 10 microcode : 0x96 cpu MHz : 2000.921 cache size : 8192 KB physical id : 0 siblings : 4 core id : 0 cpu cores : 4 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch tpr_shadow vnmi ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid mpx rdseed adx smap clflushopt xsaveopt xsavec arat bogomips : 4002.00 clflush size : 64 cache_alignment : 64 address sizes : 43 bits physical, 48 bits virtual power management: 查看CPU的详细信息 [root@node5 ~]# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 1 Core(s) per socket: 4 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 142 Model name: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz Stepping: 10 CPU MHz: 2000.921 BogoMIPS: 4002.00 Virtualization: VT-x Hypervisor vendor: VMware Virtualization type: full L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 8192K NUMA node0 CPU(s): 0-3 Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch tpr_shadow vnmi ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid mpx rdseed adx smap clflushopt xsaveopt xsavec arat CPU配置总结 通过以上的查询,我们可以知道该服务器是1路4核的CPU ,CPU型号是Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz,该CPU没有超线程 2.3 内存基本配置查询 名词解释 名词 含义 Mem 内存的使用情况总览表 Swap 虚拟内存。即可以把数据存放在硬盘上的数据,当物理内存不足时,拿出部分硬盘空间当SWAP分区(虚拟成内存)使用,从而解决内存容量不足的情况。SWAP意思是交换,顾名思义,当某进程向OS请求内存发现不足时,OS会把内存中暂时不用的数据交换出去,放在SWAP分区中,这个过程称为SWAP OUT。当某进程又需要这些数据且OS发现还有空闲物理内存时,又会把SWAP分区中的数据交换回物理内存中,这个过程称为SWAP IN。当然,swap大小是有上限的,一旦swap使用完,操作系统会触发OOM-Killer机制,把消耗内存最多的进程kill掉以释放内存。 shared 共享内存,即和普通用户共享的物理内存值, 主要用于进程间通信 buffers 用于存放要输出到disk(块设备)的数据的 cached 存放从disk上读出的数据 total 总的物理内存,total=used+free used 使用掉的内存 free 空闲的内存 查询服务器内存 [root@node5 ~]# free -m total used free shared buff/cache available Mem: 3941 286 3446 19 208 3407 Swap: 2047 0 2047 #注释 #linux的内存管理机制的思想包括(不敢说就是)内存利用率最大化。内核会把剩余的内存申请为cached,而cached不属于free范畴。当系统运#行时间较久,会发现cached很大,对于有频繁文件读写操作的系统,这种现象会更加明显。直观的看,此时free的内存会非常小,但并不代表可##用的内存小,当一个程序需要申请较大的内存时,如果free的内存不够,内核会把部分cached的内存回收,回收的内存再分配给应用程序。所以#对于linux系统,可用于分配的内存不只是free的内存,还包括cached的内存(其实还包括buffers)。 #对于操作系统: #MemFree=total-used #MemUsed = MemTotal - MemFree #对于应用程序: #MemFree=buffers+cached+free 每隔3秒查询一下内存 [root@node5 ~]# free -s 3 total used free shared buff/cache available Mem: 4036316 361144 3458272 19536 216900 3419776 Swap: 2097148 0 2097148 total used free shared buff/cache available Mem: 4036316 361144 3458272 19536 216900 3419776 Swap: 2097148 0 2097148 total used free shared buff/cache available Mem: 4036316 361144 3458272 19536 216900 3419776 Swap: 2097148 0 2097148 2.4 硬盘基本配置查询 查询磁盘整体使用情况 [root@node5 ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/centos-root 17G 4.1G 13G 24% / devtmpfs 2.0G 0 2.0G 0% /dev tmpfs 2.0G 8.0K 2.0G 1% /dev/shm tmpfs 2.0G 8.7M 2.0G 1% /run tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup /dev/sda1 1014M 125M 890M 13% /boot tmpfs 395M 0 395M 0% /run/user/0 #命令拓展 #df -a 显示全部的文件系统的使用情况 #df -i显示inode信息 #df -k 已字节数显示区块占用情况 #df -T 显示文件系统的类型 查询某个目录磁盘占用情况 #命令拓展 #du -s 指定目录大小汇总 #du -h带计量单位 #du -a 含文件 #du --max-depth=1 子目录深度 #du -c 列出明细的同时,增加汇总值 [root@node5 ~]# du -sh /home/ 1.7G /home/ [root@node5 ~]# du -ach --max-depth=2 /home/ 4.0K /home/www/.bash_logout 4.0K /home/www/.bash_profile 4.0K /home/www/.bashrc 4.0K /home/www/web 16K /home/www 4.0K /home/nginx/.bash_logout 4.0K /home/nginx/.bash_profile 4.0K /home/nginx/.bashrc 12K /home/nginx 4.0K /home/esnode/.bash_logout 4.0K /home/esnode/.bash_profile 4.0K /home/esnode/.bashrc 4.0K /home/esnode/.oracle_jre_usage 4.3M /home/esnode/elasticsearch-analysis-ik-6.2.2.zip 80M /home/esnode/kibana-6.2.2-linux-x86_64.tar.gz 300M /home/esnode/x-pack-6.2.2.zip 28M /home/esnode/elasticsearch-6.2.2.tar.gz 4.0K /home/esnode/.bash_history 294M /home/esnode/elasticsearch-6.2.2 4.0K /home/esnode/.ssh 4.0K /home/esnode/x-pack生成的秘钥.txt 1014M /home/esnode/kibana-6.2.2-linux-x86_64 8.0K /home/esnode/.viminfo 1.7G /home/esnode 1.7G /home/ 1.7G total 查看目录结构 #tree命令默认没有安装,需要手动安装一下 [root@node5 ~]# yum -y install tree #-L指定目录深度 [root@node5 ~]# tree -L 2 /home/ /home/ ├── esnode │   ├── elasticsearch-6.2.2 │   ├── elasticsearch-6.2.2.tar.gz │   ├── elasticsearch-analysis-ik-6.2.2.zip │   ├── kibana-6.2.2-linux-x86_64 │   ├── kibana-6.2.2-linux-x86_64.tar.gz │   ├── x-pack-6.2.2.zip │   └── x-pack\347\224\237\346\210\220\347\232\204\347\247\230\351\222\245.txt ├── nginx └── www └── web 6 directories, 5 files 以树状的格式显示所有可用的块设备信息 [root@node5 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 20G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 19G 0 part ├─centos-root 253:0 0 17G 0 lvm / └─centos-swap 253:1 0 2G 0 lvm [SWAP] sdb 8:16 0 1G 0 disk └─sdb1 8:17 0 200M 0 part sr0 11:0 1 1024M 0 rom #注释 #NAME —— 设备的名称 #MAJ:MIN —— Linux 操作系统中的每个设备都以一个文件表示,对块(磁盘)设备来说,这里用主次设备编号来描述设备。 #RM —— 可移动设备。如果这是一个可移动设备将显示 1,否则显示 0。 #TYPE —— 设备的类型 #MOUNTPOINT —— 设备挂载的位置 #RO —— 对于只读文件系统,这里会显示 1,否则显示 0。 #SIZE —— 设备的容量 列出所有可用的设备、通用唯一识别码(UUID)、文件系统类型以及卷标 [root@node5 ~]# blkid /dev/sda1: UUID="6503b4ad-2975-4152-a824-feb7bea1b622" TYPE="xfs" /dev/sda2: UUID="nqZ4uJ-ksnN-KzYS-N42b-00m3-Ohc2-BJXunP" TYPE="LVM2_member" /dev/sdb1: UUID="94396e17-4821-4957-aa76-d41f33958ff5" TYPE="xfs" /dev/mapper/centos-root: UUID="c1d38b37-821d-48e7-8727-3937ccc657a4" TYPE="xfs" /dev/mapper/centos-swap: UUID="c2fcaf11-42d8-4e4c-bf9e-6464f0777198" TYPE="swap" 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    时间:2021-01-19 关键词: CPU 硬盘 内存

  • 联想拯救者R9000X 2021游戏本磁盘、内存速度测评

    联想拯救者R9000X 2021游戏本磁盘、内存速度测评

    在这篇文章中,小编将对联想拯救者R9000X 2021游戏本进行磁盘读写速度测评和内存读取速度磁测评。如果你对联想拯救者R9000X游戏本,抑或是对它的性能具有兴趣,不妨继续往下阅读哦。 一、联想拯救者R9000X游戏本基本介绍 联想拯救者R9000X游戏本搭载了 AMD 8 核 16 线程的 R7 4800H 处理器,配以 RTX 2060 显卡,内存为 16GB DDR4-3200。此外,联想拯救者R9000X游戏本的SSD 为 512GB,双 M.2 SSD 槽,还有 2.5 英寸硬盘位。与此同时,联想拯救者R9000X游戏本配备了NVIDIA GeForce RTX2060 Max-Q 独立显卡,1920个流处理器,6GB 192bit GDDR6显存,性能释放可达75W,并且支持实时光线追踪和DLSS 2.0,这样的配置可以为玩家带来堪比真实世界般的光线视觉感受,这使得联想拯救者R9000X游戏本足以应对任何3A大作和大型网游。 二、联想拯救者R9000X游戏本性能测评 通过上面对联想拯救者R9000X游戏本的简单介绍,想必大家对联想拯救者R9000X游戏本已经具备一定的了解。在这里,小编将对联想拯救者R9000X游戏本进行磁盘读写速度测评和内存读取速度测评。 (一)联想拯救者R9000X游戏本磁盘读写测试 AS SSD Benchmark是一款SSD固态硬盘传输速度测速工具。此软件可以测出固态硬盘持续读写等的性能,让您只要使用此软件来测试就可以评估这个固态硬盘的传输速度好与不好。 在AS SSD Benchmark中,联想拯救者R9000X游戏本的固态连续读取速度、连续写入速度、随机读取速度、随机写入速度分别为2620MB/s、2460MB/s、51.98MB/s和116.65MB/s。 CrystalDiskMark是一个测试电脑硬盘检测工具,简单易于操作的界面随时可以测试存储设备,测试存储设备大小和测试数字都可以选择,还可测试可读和可写的速度。在CrystalDiskMark中,联想拯救者R9000X游戏本的连续读取速度、连续写入速度、随机读取速度、随机写入速度分别为3569MB/s、2821MB/s、41.8MB/s和90.14MB/s。 (二)联想拯救者R9000X游戏本内存读取速度测试 拯救者R9000X搭载了两根8GB 3200MHz DDR4内存,DDR4内存是新一代的内存规格。DDR4相比DDR3最大的区别有三点:16bit预取机制(DDR3为8bit),同样内核频率下理论速度是DDR3的两倍;更可靠的传输规范,数据可靠性进一步提升;工作电压降为1.2V,更节能。 通过测试我们可以知道,联想拯救者R9000X游戏本的读取速度、写入速度、拷贝速度和延迟分别为37902MB/s、36739MB/s、32229MB/s和84.8ns。 以上所有内容便是小编此次为大家带来的有关联想拯救者R9000X游戏本的所有介绍,如果你想了解更多有关联想拯救者R9000X游戏本的内容,不妨在我们网站或者百度、google进行探索哦。

    时间:2021-01-06 关键词: 磁盘 R9000X 内存

  • 戴尔Precision 7750工作站磁盘、内存测评

    戴尔Precision 7750工作站磁盘、内存测评

    在这篇文章中,小编将对戴尔Precision 7750工作站进行磁盘速度测评和内存速度测评。如果你对戴尔Precision 7750工作站或者对戴尔Precision 7750工作站的性能具有兴趣,不妨继续往下阅读哦。 一、戴尔Precision 7750工作站基本介绍 戴尔Precision 7750工作站配备了17.3英寸的大屏幕,屏幕左右边框控制得不错,上下边框会宽一点,视野开阔。此外,戴尔Precision 7750工作站上边框配备了一个摄像头,拥有一个物理滑动装置,可以隐藏摄像头,在这样的设置下,我们的隐私能够很大程度上得到保护。 而在接口方面,戴尔Precision 7750工作站提供三个USB-A 3.0、两个USB-C 3.1、一个HDMI 2.0、一个miniDP 1.4。除此以外,戴尔Precision 7750工作站还为我们提供了一个3.5mm耳麦一体、一个SD卡槽、一个智能卡槽、一个microSIM卡槽。 二、戴尔Precision 7750工作站性能测评 通过上面对戴尔Precision 7750工作站的简单介绍,想必大家对戴尔Precision 7750工作站已经具有初步了解。在这里,小编将对戴尔Precision 7750工作站的磁盘、内存性能加以测评。 (一)戴尔Precision 7750工作站硬盘速度测试 固态驱动器,俗称固态硬盘,也即SSD。固态硬盘是用固态电子存储芯片阵列而制成的硬盘,因为台湾英语里把固体电容称之为Solid而得名。SSD由控制单元和存储单元(FLASH芯片、DRAM芯片)组成。固态硬盘在接口的规范和定义、功能及使用方法上与普通硬盘的完全相同,在产品外形和尺寸上也完全与普通硬盘一致。被广泛应用于军事、车载、工控、视频监控、网络监控、网络终端、电力、医疗、航空、导航设备等诸多领域。 戴尔Precision 7750工作站的机身内部共有4个M.2硬盘位,目前插有3条海力士PC601A NVMe固态。在AS SSD Benchmark中,戴尔Precision 7750工作站的磁盘持续读取速度、持续写入速度、随机读取速度和随机写入速度分别为2836MB/s、738MB/s、43.62MB/s和110.78MB/s。 在 CrystalDiskMark中,戴尔Precision 7750工作站磁盘的持续读取速度、持续写入速度、随机读取速度和随机写入速度分别为3200MB/s、2669MB/s、48.47MB/s和144.7MB/s。 (二)戴尔Precision 7750工作站内存速度测试 DDR4内存是新一代的内存规格,DDR4相比DDR3最大的区别有三点:16bit预取机制(DDR3为8bit),同样内核频率下理论速度是DDR3的两倍;更可靠的传输规范,数据可靠性进一步提升;工作电压降为1.2V,更节能。 戴尔Precision 7750工作站搭载了4根 32GB 2666MHz DDR4内存,读取速度、写入速度、拷贝速度和延迟分别为39192MB/s、 41017MB/s、39525MB/s和62.6ns。 经由小编的介绍,不知道你对戴尔Precision 7750工作站是否充满了兴趣?如果你想对戴尔Precision 7750工作站有更多的了解,不妨尝试度娘更多信息或者在我们的网站里进行搜索哦。

    时间:2021-01-04 关键词: 戴尔 工作站 内存

  • 首长,Redis性能优化十三条军规立好了,请过目~

    前言 Redis作为高性能的内存数据库,在大数据量的情况下也会遇到性能瓶颈,日常开发中只有时刻谨记优化铁则,才能使得Redis性能发挥到极致。 本文将会介绍十三条性能优化军规,开发过程中只要按照执行,性能必能质的飞跃。 1. 避免慢查询命令 慢查询命令指的是执行较慢的命令,Redis自身提供了许多的命令,并不是所有的命令都慢,这和命令的操作复杂度有关,因此必须知道Redis不同命令的复杂度。 如说,Value 类型为 String 时,GET/SET 操作主要就是操作 Redis 的哈希表索引。这个操作复杂度基本是固定的,即 O(1)。但是,当 Value 类型为 Set 时,SORT、SUNION/SMEMBERS 操作复杂度分别为 O(N+M*log(M)) 和 O(N)。其中,N 为 Set 中的元素个数,M 为 SORT 操作返回的元素个数。这个复杂度就增加了很多。Redis 官方文档中对每个命令的复杂度都有介绍,当你需要了解某个命令的复杂度时,可以直接查询。 当你发现 Redis 性能变慢时,可以通过 Redis 日志,或者是 latency monitor 工具,查询变慢的请求,根据请求对应的具体命令以及官方文档,确认下是否采用了复杂度高的慢查询命令。 如果确实存在大量的慢查询命令,建议如下两种方式: 用其他高效的命令代替:比如说,如果你需要返回一个 SET 中的所有成员时,不要使用 SMEMBERS 命令,而是要使用 SSCAN 多次迭代返回,避免一次返回大量数据,造成线程阻塞。 当你需要执行排序、交集、并集操作时,可以在客户端完成,而不要用 SORT、SUNION、SINTER 这些命令,以免拖慢 Redis 实例。 2. 生产环境禁用keys命令 keys这个命令是最容易忽略的慢查询命令,因为keys命令需要遍历存储的键值对,所以操作延时很高,在生产环境使用很可能导致Redis阻塞;因此不建议在生产环境中使用keys命令。 3. keys需要设置过期时间 Redis作为内存数据库,一切的数据都是在内存中,一旦内存占用过大则会大大影响性能,因此需要对有时间限制的数据需要设置过期时间,这样Redis能够定时的删除过期的数据。 4. 禁止批量的给keys设置相同的过期时间 默认情况下,Redis 每 100 毫秒会删除一些过期 key,具体的算法如下: 采样 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 个数的 key,并将其中过期的 key 全部删除; 如果超过 25% 的 key 过期了,则重复删除的过程,直到过期 key 的比例降至 `25%`` 以下。 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 是 Redis 的一个参数,默认是 20,那么,一秒内基本有 200 个过期 key 会被删除。这一策略对清除过期 key、释放内存空间很有帮助。如果每秒钟删除 200 个过期 key,并不会对 Redis 造成太大影响。 但是,如果触发了上面这个算法的第二条,Redis 就会一直删除以释放内存空间。注意,删除操作是阻塞的(Redis 4.0 后可以用异步线程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作了,就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。 频繁使用带有相同时间参数的 EXPIREAT 命令设置过期 key 将会触发算法第二条,这就会导致在一秒内存在大量的keys过期。 因此开发中一定要禁止批量的给keys设置过期时间。 5. 谨慎选择数据结构 Redis 常用的数据结构一共有五种:string、hash、list、set、zset(sorted set)。可以发现,大多数场景下使用 string 都可以去解决问题。但是,这并不一定是最优的选择。下面,简单说明下它们各自的适用场景: string:单个的缓存结果,不与其他的 KV 之间有联系 hash:一个 Object 包含有很多属性,且这些属性都需要单独存储。注意:这种情况不要使用 string,因为 string 会占据更多的内存 list:一个 Object 包含很多数据,且这些数据允许重复、要求有顺序性 set:一个 Object 包含很多数据,不要求数据有顺序,但是不允许重复 zset:一个 Object 包含很多数据,且这些数据自身还包含一个权重值,可以利用这个权重值来排序 另外Redis还提供了几种的扩展类型,如下: HyperLogLog:适合用于 基数统计,比如PV,UV的统计,存在 误差问题,不适合精确统计。 BitMap:适合 二值状态的统计,比如签到打卡,要么打卡了,要么未打卡。 6. 检查持久化策略 Redis4.0之后使用了如下三种持久化策略: AOF日志:一种采用文件追加的方式将命令记录在日志中的策略,针对同步和异步追加还提供了三个配置项,有兴趣的可以查看官方文档。 RDB快照:以快照的方式,将某一个时刻的内存数据,以二进制的方式写入磁盘。 AOF和 RDB混用:Redis4.0新增的方式,为了采用两种方式各自的优点,在RDB快照的时间段内使用的AOF日志记录这段时间的操作的命令,这样一旦发生宕机,将不会丢失两段快照中间的数据。 由于写入磁盘有IO性能瓶颈,因此不是将Redis作为数据库的话(可以从后端恢复),建议禁用持久化或者调整持久化策略。 7. 采用高速的固态硬盘作为日志写入设备 由于AOF日志的重写对磁盘的压力较大,很可能会阻塞,如果需要使用到持久化,建议使用高速的固态硬盘作为日志写入设备。 8. 使用物理机而非虚拟机 由于虚拟机增加了虚拟化软件层,与物理机相比,虚拟机本身就存在性能的开销,可以使用如下命令来分别测试下物理机和虚拟机的基线性能: ./redis-cli --intrinsic-latency 120 测试结果可以知道,使用物理机的基线性能明显比虚拟机的基线性能更好。 9. 增加机器内存或者使用Redis集群 物理机器的内存不足将会导致操作系统内存的Swap。 内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,所以,一旦触发 swap,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。 Redis 是内存数据库,内存使用量大,如果没有控制好内存的使用量,或者和其他内存需求大的应用一起运行了,就可能受到 swap 的影响,而导致性能变慢。 这一点对于 Redis 内存数据库而言,显得更为重要:正常情况下,Redis 的操作是直接通过访问内存就能完成,一旦 swap 被触发了,Redis 的请求操作需要等到磁盘数据读写完成才行。而且,和我刚才说的 AOF 日志文件读写使用 fsync 线程不同,swap 触发后影响的是 Redis 主 IO 线程,这会极大地增加 Redis 的响应时间。 因此增加机器的内存或者使用Redis集群能够有效的解决操作系统内存的Swap,提高性能。 10. 使用 Pipeline 批量操作数据 Pipeline (管道技术) 是客户端提供的一种批处理技术,用于一次处理多个 Redis 命令,从而提高整个交互的性能。 11. 客户端使用优化 在客户端的使用上我们除了要尽量使用 Pipeline 的技术外,还需要注意要尽量使用 Redis 连接池,而不是频繁创建销毁 Redis 连接,这样就可以减少网络传输次数和减少了非必要调用指令。 12. 使用分布式架构来增加读写速度 Redis 分布式架构有三个重要的手段: 主从同步 哨兵模式 Redis Cluster 集群 使用主从同步功能我们可以把写入放到主库上执行,把读功能转移到从服务上,因此就可以在单位时间内处理更多的请求,从而提升的 Redis 整体的运行速度。 而哨兵模式是对于主从功能的升级,但当主节点奔溃之后,无需人工干预就能自动恢复 Redis 的正常使用。 Redis Cluster 是 Redis 3.0 正式推出的,Redis 集群是通过将数据分散存储到多个节点上,来平衡各个节点的负载压力。 Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383整数槽内,计算公式:slot = CRC16(key) & 16383,每一个节点负责维护一部分槽以及槽所映射的键值数据。这样 Redis 就可以把读写压力从一台服务器,分散给多台服务器了,因此性能会有很大的提升。 在这三个功能中,我们只需要使用一个就行了,毫无疑问 Redis Cluster 应该是首选的实现方案,它可以把读写压力自动的分担给更多的服务器,并且拥有自动容灾的能力。 13. 避免内存碎片 频繁的新增修改会导致内存碎片的增多,因此需要时刻的清理内存碎片。 Redis提供了INFO memory可以查看内存的使用信息,如下: INFO memory# Memoryused_memory:1073741736used_memory_human:1024.00Mused_memory_rss:1997159792used_memory_rss_human:1.86G…mem_fragmentation_ratio:1.86 这里有一个 mem_fragmentation_ratio 的指标,它表示的就是 Redis 当前的内存碎片率。那么,这个碎片率是怎么计算的呢?其实,就是上面的命令中的两个指标 used_memory_rss 和 used_memory 相除的结果。 mem_fragmentation_ratio = used_memory_rss/ used_memory used_memory_rss 是操作系统实际分配给 Redis 的物理内存空间,里面就包含了碎片;而 used_memory 是 Redis 为了保存数据实际申请使用的空间。 那么,知道了这个指标,我们该如何使用呢?在这儿,我提供一些经验阈值: mem_fragmentation_ratio 大于 1 但小于 1.5。这种情况是合理的。这是因为,刚才我介绍的那些因素是难以避免的。毕竟,内因的内存分配器是一定要使用的,分配策略都是通用的,不会轻易修改;而外因由 Redis 负载决定,也无法限制。所以,存在内存碎片也是正常的。 mem_fragmentation_ratio 大于 1.5 。这表明内存碎片率已经超过了 50%。一般情况下,这个时候,我们就需要采取一些措施来降低内存碎片率了。 一旦内存碎片率过高了,此时就应该采用手段清理内存碎片了,具体如何清理,参考文章:Redis清理内存碎片 总结 本文着重介绍了13条性能优化军规,在开发过程中还是需要针对性的具体问题具体分析,希望作者这篇文章能够帮助到你。 特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下: 长按订阅更多精彩▼如有收获,点个在看,诚挚感谢 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    时间:2020-12-23 关键词: 嵌入式 内存

  • 全面解读操作系统中的内存管理,你懂几点?

    转自 | 程序喵大人 今天分享关于内存的几点内容: 什么是物理内存 使用物理内存有什么缺点? 什么是虚拟内存? 虚拟内存如何映射到物理内存 什么是分页内存管理? 什么是缺页中断? 页面置换算法都有哪些? 什么是分段内存管理? 01什么是物理内存? 我们常说的物理内存大小就是指内存条的大小,一般买电脑时都会看下内存条是多大容量的,话说如果内存条大小是100G,那这100G就都能够被使用吗?不一定的,更多的还是要看CPU地址总线的位数,如果地址总线只有20位,那么它的寻址空间就是1MB,即使可以安装100G的内存条也没有意义,也只能视物理内存大小为1MB。 02使用物理内存有什么缺点? 这种方式下每个程序都可以直接访问物理内存,有两种情况: 1.系统中只有一个进程在运行:如果用户程序可以操作物理地址空间的任意地址,它们就很容易在不经意间破坏了操作系统,使系统出现各种奇奇怪怪的问题; 2.系统有多个进程同时在运行:如图,理想情况下可以使进程A和进程B各占物理内存的一边,两者互不干扰,但这只是理想情况下,谁能确保程序没有bug呢,进程B在后台正常运行着,程序员在调试进程A时有可能就会误操作到进程B正在使用的物理内存,导致进程B运行出现异常,两个程序操作了同一地址空间,第一个程序在某一地址空间写入某个值,第二个程序在同一地址又写入了不同值,这就会导致程序运行出现问题,所以直接使用物理内存会使所有进程的安全性得不到保证。 如何解决上述问题? 可以考虑为存储器创造新的抽象概念:地址空间,地址空间为程序创造了一种抽象的内存,是进程可用于寻址内存的一套地址集合,同时每个进程都有一套自己的地址空间,一个进程的地址空间独立于其它进程的地址空间。 如何为程序创造独立的地址空间? 最简单的办法就是把每个进程的地址空间分别映射到物理内存的不同部分。这样就可以保证不同进程使用的是独立的地址空间。 实现 给每个进程提供一个基址A和界限B,进程内使用的空间为x,则对应的物理地址为A + x,同时需要保证A + x < B,如果访问的地址超过的界限,需要产生错误并中止访问。为了达到目的CPU配置了两个特殊硬件寄存器:基址寄存器和界限寄存器,当一个进程运行时,程序的起始物理地址和长度会分别装入到基址寄存器和界限寄存器里,进程访问内存,在每个内存地址送到内存之前,都会先加上基址寄存器的内容。 缺点:每次访问内存都需要进行加法和比较运算,比较运算很快,但是加法运算由于进位传递事件的问题,在没有使用特殊电路的情况下会显得很慢。 此外,每个进程运行都会占据一定的物理内存,如果物理内存足够大到可以容纳许多个进程同时运行还好,但现实中物理内存的大小是有限的,可能会出现内存不够用的情况,怎么办? 方法一:如果是因为程序太大,大到超过了内存的容量,可以采用手动覆盖技术,只把需要的指令和数据保存在内存中。 方法二:如果是因为程序太多,导致超过了内存的容量,可以采用自动交换技术,把暂时不需要执行的程序移动到外存中。 覆盖技术 把程序按照自身逻辑结构,划分成多个功能相互独立的程序模块,那些不会同时执行的模块可以共享到同一块内存区域,按时间顺序来运行: 将常用功能需要的代码和数据常驻在内存中; 将不常用的功能划分成功能相互独立的程序模块,平时放到外存中,在需要的时候将对应的模块加载到内存中; 那些没有调用关系的模块平时不需要装入到内存,它们可以共用一块内存区,需要时加载到内存,不需要时换出到外存中; 如图: 交换技术 多个程序同时运行,可以将暂时不能运行的程序送到外存,获得更多的空闲内存,操作系统将一个进程的整个地址空间内容换出到外存中,再将外存中某个进程的整个地址空间信息换入到内存中,换入换出内容的大小是整个程序的地址空间。 交换技术在实现上有很多困难: 需要确定什么时候发生交换:简单的办法是可以在内存空间不够用时换出一些程序; 交换区必须足够大:多个程序运行时,交换区(外存)必须足够大,大到可以存放所有程序所需要的地址空间信息; 程序如何换入:一个程序被换出后又重新换入,换入的内存位置可能不会和上一次程序所在的内存位置相同,这就需要动态地址映射机制。 覆盖技术和交换技术的比较 覆盖只能发生在那些相互之间没有调用关系的程序模块之间,因此程序员必须给出程序内的各个模块之间的逻辑覆盖结构。 交换技术是以在内存中的程序大小为单位来进行的,它不需要程序员给出各个模块之间的逻辑覆盖结构。 通俗来说:覆盖发生在程序的内部,交换发生在程序与程序之间。 但是这两种技术都有缺点: 覆盖技术:需要程序员自己把整个程序划分为若干个小的功能模块,并确定各个模块之间的覆盖关系,增加了程序员的负担,很少有程序员擅长这种技术; 交换技术:以进程作为交换的单位,需要把进程的整个地址空间都换进换出,增加了处理器的开销,还需要足够大的外存。 那有没有更好的解决上述问题的方法呢?答案是虚拟内存技术。 03什么是虚拟内存? 虚拟内存,那就是虚拟出来的内存,它的基本思想就是确保每个程序拥有自己的地址空间,地址空间被分成多个块,每一块都有连续的地址空间,同时物理空间也分成多个块,块大小和虚拟地址空间的块大小一致,操作系统会自动将虚拟地址空间映射到物理地址空间,程序所关注的只是虚拟内存,请求的也是虚拟内存,其实真正使用的是物理内存。 虚拟内存技术有覆盖技术的功能,但它不是把程序的所有内容都放在内存中,因而能够运行比当前的空闲内存空间还要大的程序。它比覆盖技术做的更好,整个过程由操作系统自动来完成,无需程序员的干涉; 虚拟内存技术有交换技术的功能,能够实现进程在内存和外存之间的交换,因而获得更多的空闲内存空间。它比交换技术做的更好,它只对进程的部分内容在内存和外存之间进行交换。 虚拟内存技术的具体实现: 虚拟内存技术一般是在页式管理(下面介绍)的基础上实现 在装入程序时,不必将其全部装入到内存,而只需将当前需要执行的部分页面装入到内存,就可让程序开始执行; 在程序执行过程中,如果需执行的指令或访问的数据尚未在内存(称为缺页)。则由处理器通知操作系统将相应的页面调入到内存,然后继续执行程序; 另一方面,操作系统将内存中暂时不使用的页面调出保存在外存上,从而腾出更多空闲空间存放将要装入的程序以及将要调入的页面。 虚拟内存技术的特点 : 大的用户空间:通过把物理内存与外存相结合,提供给用户的虚拟内存空间通常大于实际的物理内存,即实现了两者的分离。如32位的虚拟地址理论上可以访问4GB,而可能计算机上仅有256M的物理内存,但硬盘容量大于4GB; 部分交换:与交换技术相比较,虚拟存储的调入和调出是对部分虚拟地址空间进行的; 连续性:程序可以使用一系列相邻连续的虚拟地址来映射物理内存中不连续的大内存缓冲区; 安全性:不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。 04虚拟内存如何映射到物理内存? 如图,CPU里有一个内存管理单元(Memory Management Unit),简称MMU,虚拟内存不是直接送到内存总线,而是先给到MMU,由MMU来把虚拟地址映射到物理地址,程序只需要管理虚拟内存就好,映射的逻辑自然有其它模块自动处理。 操作系统如何表示的内存被占用还是空闲? 05分页内存管理 将虚拟地址空间分成若干个块,每个块都有固定的大小,物理地址空间也被划分成若干个块,每个块也都有固定的大小,物理地址空间的块和虚拟地址空间的块大小相等,虚拟地址空间这些块就被称为页面,物理地址空间这些块被称为帧。 关于分页这里有个问题,页面的大小是多少合适呢?页面太大容易产生空间浪费,程序假如只使用了1个字节却被分配了10M的页面,这岂不是极大的浪费,页面太小会导致页表(下面介绍)占用空间过大,所以页面需要折中选择合适的大小,目前大多数系统都使用4KB作为页的大小。 上面关于虚拟内存如何映射到物理内存程序喵只介绍了MMU,但是MMU是如何工作的还没有介绍,MMU通过页表这个工具将虚拟地址转换为物理地址。32位的虚拟地址分成两部分(虚拟页号和偏移量),MMU通过页表找到了虚拟页号对应的物理页号,物理页号+偏移量就是实际的物理地址。 具体如图: 图只表示了页表的大体功能,页表的结构其实还很复杂,下面会具体介绍。 页表的目的就是虚拟页面映射为物理内存的页框,页表可以理解为一个数学函数,函数的输入是虚拟页号,函数的输出是物理页号,通过这个函数可以把虚拟页面映射到物理页号,从而确定物理地址。不同机器的页表结构不同,通常页表的结构如下: 页框号:最主要的一项,页表最主要的目的就是找到物理页号; 有效位:1表示有效,表示该表项是有效的,如果为0,表示该表项对应的虚拟页面现在不在内存中,访问该页面会引起缺页中断,缺页中断后会去物理空间找到一个可用的页框填回到页表中; 保护位:表示一个页允许什么类型的访问,可读可写还是可执行; 修改位:该位反应了页面的状态,在操作系统重新分配页框时有用,在写入一页时由硬件自动设置该位,重新分配页框时,如果一个页面已经被修改过,则必须把它这个脏页写回磁盘,如果没有被修改过,表示该页是干净的,它在磁盘上的副本依然是有效的,直接丢弃该页面即可。 访问位:该位主要用于帮助操作系统在发生缺页中断时选择要被淘汰的页面,不再使用的页面显然比正在使用的页面更适合被淘汰,该位在页面置换算法中发挥重要作用。 高速缓存禁止位:该位用于禁止该页面被高速缓存。 如何加快地址映射速度? 每次访问内存都需要进行虚拟地址到物理地址的映射,每次映射都需要访问一次页表,所有的指令执行都必须通过内存,很多指令也需要访问内存中的操作数,因此每条指令执行基本都会进行多次页表查询,为了程序运行速度,指令必须要在很短的时间内执行完成,而页表查询映射不能成为指令执行的瓶颈,所以需要提高页表查询映射的速度。 如何才能提高速度呢?可以为页表提供一个缓存,通过缓存进行映射比通过页表映射速度更快,这个缓存是一个小型的硬件设备,叫快表(TLB),MMU每次进行虚拟地址转换时,首先去TLB中查找,找到了有效的物理页框则直接返回,如果没有找到则进行正常的页表访问,页表中找到后则更新TLB,从TLB中淘汰一个表项,然后用新找到的表项替代它,这样下次相同的页面过来时可以直接命中TLB找到对应的物理地址,速度更快,不需要继续去访问页表。 这里之所以认为TLB能提高速度主要依靠程序局部性原理,程序局部性原理是指程序在执行过程中的一个较短时间,所执行的指令地址和要访问的数据通常都局限在一块区域内,这里可分为时间局部性和空间局部性: 时间局部性:一条指令的一次执行和下次执行,一个数据的一次访问和下次访问都集中在一个较短时间内; 空间局部性:当前指令和邻近的几条指令,当前访问的数据和邻近的几个数据都集中在一个较小区域内。 通过TLB可以加快虚拟地址到物理地址的转换速度,还有个问题,现在都是64位操作系统啦,有很大的虚拟地址空间,虚拟地址空间大那对应的页表也会非常大,又加上多个进程多个页表,那计算机的大部分空间就都被拿去存放页表,有没有更好的办法解决页表大的问题呢?答案是多级页表。 tips:页表为什么大?32位环境下,虚拟地址空间有4GB,一个页大小是4KB,那么整个页表就需要100万页,而每个页表项需要4个字节,那整个页表就需要4MB的内存空间,又因为每个进程都有一个自己的页表,多个进程情况下,这简直就是灾难。 如图,以一个32位虚拟地址的二级页表为例,将32位虚拟地址划分为10位的PT1域,10位的PT2域,以及12位的offset域,当一个虚拟地址被送入MMU时,MMU首先提取PT1域并把其值作为访问第一级页表的索引,之后提取PT2域把把其值作为访问第二级页表的索引,之后再根据offset找到对应的页框号。 32位的虚拟地址空间下:每个页面4KB,且每条页表项占4B: 一级页表:进程需要1M个页表项(4GB / 4KB = 1M, 2^20个页表项),即页表(每个进程都有一个页表)占用4MB(1M * 4B = 4MB)的内存空间。 二级页表:一级页表映射4MB(2^22)、二级页表映射4KB,则需要1K个一级页表项(4GB / 4MB = 1K, 2^10个一级页表项)、每个一级页表项对应1K个二级页表项(4MB / 4KB = 1K),这样页表占用4.004MB(1K * 4B + 1K * 1K * 4B = 4.004MB)的内存空间。 二级页表占用空间看着貌似变大了,为什么还说多级页表省内存呢? 每个进程都有4GB的虚拟地址空间,而显然对于大多数程序来说,其使用到的空间远未达到4GB,何必去映射不可能用到的空间呢? 也就是说,一级页表覆盖了整个4GB虚拟地址空间,但如果某个一级页表的页表项没有被用到,也就不需要创建这个页表项对应的二级页表了,即可以在需要时才创建二级页表。做个简单的计算,假设只有20%的一级页表项被用到了,那么页表占用的内存空间就只有0.804MB(1K*4B+0.2*1K*1K*4B=0.804MB),对比单级页表的4M是不是一个巨大的节约? 那么为什么不分级的页表就做不到这样节约内存呢?我们从页表的性质来看,保存在主存中的页表承担的职责是将虚拟地址翻译成物理地址。假如虚拟地址在页表中找不到对应的页表项,计算机系统就不能工作了。所以页表一定要覆盖全部虚拟地址空间,不分级的页表就需要有1M个页表项来映射,而二级页表则最少只需要1K个页表项(此时一级页表覆盖到了全部虚拟地址空间,二级页表在需要时创建)。 二级页表其实可以不在内存中:其实这就像是把页表当成了页面。当需要用到某个页面时,将此页面从磁盘调入到内存;当内存中页面满了时,将内存中的页面调出到磁盘,这是利用到了程序运行的局部性原理。我们可以很自然发现,虚拟内存地址存在着局部性,那么负责映射虚拟内存地址的页表项当然也存在着局部性了!这样我们再来看二级页表,根据局部性原理,1024个第二级页表中,只会有很少的一部分在某一时刻正在使用,我们岂不是可以把二级页表都放在磁盘中,在需要时才调入到内存? 我们考虑极端情况,只有一级页表在内存中,二级页表仅有一个在内存中,其余全在磁盘中(虽然这样效率非常低),则此时页表占用了8KB(1K*4B+1*1K*4B=8KB),对比上一步的0.804MB,占用空间又缩小了好多倍!(这里参考的下面知乎链接中大佬的回答) 06 什么是缺页中断? 缺页中断就是要访问的页不在主存中,需要操作系统将页调入主存后再进行访问,此时会暂时停止指令的执行,产生一个页不存在的异常,对应的异常处理程序就会从选择一页调入到内存,调入内存后之前的异常指令就可以继续执行。 缺页中断的处理过程如下: 如果内存中有空闲的物理页面,则分配一物理页帧r,然后转第4步,否则转第2步; 选择某种页面置换算法,选择一个将被替换的物理页帧r,它所对应的逻辑页为q,如果该页在内存期间被修改过,则需把它写回到外存; 将q所对应的页表项进行修改,把驻留位置0; 将需要访问的页p装入到物理页面r中; 修改p所对应的页表项的内容,把驻留位置1,把物理页帧号置为x; 重新运行被中断的指令。 07页面置换算法都有哪些? 当缺页中断发生时,需要调入新的页面到内存中,而内存已满时,选择内存中哪个物理页面被置换是个学问,由此引入了多种页面置换算法,致力于尽可能减少页面的换入换出次数(缺页中断次数)。尽量把未来不再使用的或短期内较少使用的页面换出,通常在程序局部性原理指导下依据过去的统计数据来进行预测。 最优页面置换算法:当一个缺页中断发生时,对于保存在内存当中的每一个逻辑页面,计算在它的下一次访问之前,还需等待多长时间,从中选择等待时间最长的那个,作为被置换的页面。注意这只是一种理想情况,在实际系统中是无法实现的,因为操作系统不可能预测未来,不知道每一个页面要等待多长时间以后才会再次被访问。该算法可用作其它算法的性能评价的依据(在一个模拟器上运行某个程序,并记录每一次的页面访问情况,在第二遍运行时即可使用最优算法)。 先进先出算法:最先进入的页面最先被淘汰,这种算法很简单,就不过多介绍啦。 最近最久未使用算法:传说中的LUR算法,当发生缺页中断时,选择最近最久没有使用过的页面淘汰,该算法会给每个页面一个字段,用于记录自上次访问以来所经历的时间T,当需要淘汰一个页面时,选择已有页面中T值最大的页面进行淘汰。 第二次机会页面置换算法:先进先出算法的升级版,只是在先进先出算法的基础上做了一点点改动,因为先进先出算法可能会把经常使用的页面置换出去,该方法会给这些页面多一次机会,给页面设置一个修改位R,每次淘汰最老页面时,检查最老页面的R位,如果R位是0,那么代表这个页面又老又没有被二次使用过,直接淘汰,如果这个页面的R位是1,表示该页面被二次访问过,将R位置0,并且把该页面放到链表的尾端,像该页面是最新进来的一样,然后继续按这种方法淘汰最老的页面。 时钟页面置换算法:第二次机会页面算法的升级版,尽管二次机会页面算法是比较合理的算法,但它需要在链表中经常移动页面,效率比较低,时钟页面置换算法如图,该算法把所有的页面都保存在一个类似时钟的环形链表中,一个表针指向最老的页面,当发生缺页中断时,算法首先检查表针指向的页面,如果它的R位是0就淘汰该页面,并且把新的页面插入这个位置,然后表针移动到下一个位置,如果R位是1就将R位置0并把表针移动到下一个位置,重复这个过程直到找到一个R位是0的页面然后淘汰。 时钟页面置换算法 最不常用算法:当发生缺页中断时,选择访问次数最少的那个页面去淘汰。该算法可以给每个页面设置一个计数器,被访问时,该页面的访问计数器+1,在需要淘汰时,选择计数器值最小的那个页面。 这里有个问题:一个页面如果在开始的时候访问次数很多,但之后就再也不用了,那它可能永远都不会淘汰,但它又确实需要被淘汰,怎么办呢?可以定期把减少各个页面计数器的值,常见的方法是定期将页面计数器右移一位。 tips:最不常用算法(LFU)和最近最久未使用算法(LRU)的区别:LRU考察的是最久未访问,时间越短越好,而LFU考察的是访问的次数或频度,访问次数越多越好。 工作集页面置换算法 介绍该算法时首先介绍下什么是工作集。 工作集是指一个进程当前正在使用的页面的集合,可以用二元函数W(t, s)表示: t表示当前的执行时刻)s表示工作集窗口,表示一个固定的时间段 W(t, s)表示在当前时刻t之前的s时间段中所有访问页面所组成的集合 不同时间下的工作集会有所变化,如图: 进程开始执行后随着访问新页面逐步建立较稳定的工作集 当内存访问的局部性区域的位置大致稳定时(只访问那几个页面 没有大的改变时) 工作集大小也大致稳定 局部性区域的位置改变时(进程前一项事情做完 去做下一项事情时) 工作集快速扩张和快速收缩过渡到下一个稳定值 工作集置换算法主要就是换出不在工作集中的页面,示例如图: 第0次访问e:缺页,装入e 第1次访问d:缺页,装入d 第2次访问a:缺页,装入a 第3次访问c:缺页,装入c 第4次访问c:命中,时间窗口【1-4】,淘汰e 第5次访问d:命中,时间窗口【2-5】 第6次访问b:缺页,时间窗口【3-6】,淘汰a,装入b 第7次访问c:命中,时间窗口【4-7】 第8次访问e:缺页,时间窗口【5-8】,装入e 第9次访问c:命中,时间窗口【6-9】,淘汰d,装入c 第10次访问e:命中,时间窗口【7-10】,淘汰b 第11次访问a:缺页,时间窗口【8-11】,装入a 第12次访问d:缺页,时间窗口【9-12】,装入d 工作集时钟页面置换算法 在工作集页面置换算法中,当缺页中断发生后,需要扫描整个页表才能直到页面的状态,进而才能确定被淘汰的是哪个页面,因此比较耗时,所以引入了工作集时钟页面算法。与时钟算法改进了先进先出算法类似,工作集页面置换算法+时钟算法=工作集时钟页面置换算法。避免了每次缺页中断都需要扫描整个页表的开销。 0 8 什么是分段内存管理? 关于分段内存管理我们平时见的最多的应该就是Linux可执行程序的代码段数据段之类的啦,要了解分段最好的方式就是了解它的历史。分段起源于8086CPU,那时候程序访问内存还是直接给出相应单元的物理地址,为了方便多道程序并发执行,需要支持对各个程序进行重定位,如果不支持重定位,涉及到内存访问的地方都需要将地址写死,进而把某个程序加载到物理内存的固定区间。通过分段机制,程序中只需要使用段的相对地址,然后更改段的基址,就方便对程序进行重定位。而且8086CPU的地址线宽度是20位,可寻址范围可以达到1MB,但是它们的寄存器都是16位,直接使用1个16位寄存器不可能访存达到1MB,因此引入了段,引入了段寄存器,段寄存器左移4位+偏移量就可以生成20位的地址,从而达到1MB的寻址范围。 以如今的科技水平,其实已经不再需要这种段移位加偏移的方式来访存,分段更多的是一种历史包袱,没有多大实际作用,而且我们经常见到的可执行程序中代码段数据段这些更多是为了在逻辑上能够更清晰有序的构造程序的组织结构。Linux实际上没有使用分段而只使用了分页管理,这样会更加简单,现在的分段其实更多是为了使逻辑更加清晰。一个公司,为了方便管理都会划分为好多个部门,这其实和分段逻辑相似,没有什么物理意义但是逻辑更加清晰。 关于操作系统的内存知识点就介绍到这里,希望对大家有所帮助! 参考资料 https://www.zhihu.com/question/50796850 https://www.zhihu.com/question/63375062 https://yuerer.com/操作系统之-虚拟存储页面置换算法/ 《现代操作系统》 《B站清华操作系统教学视频》 《B站哈工大操作系统教学视频》 ------------ END ------------ 关注

    时间:2020-12-22 关键词: 嵌入式 内存

  • 让数据实现更快更安全的传输,Rambus峰会展示业界领先接口和安全IP解决方案

    让数据实现更快更安全的传输,Rambus峰会展示业界领先接口和安全IP解决方案

    科技发展一直在加速,随着人工智能、5G和自动驾驶等演进,数据的生产节点数和吞吐量都在激增,节点激增同样带来数据安全转换的需求增加。算力和数据传输能力都要一同演进才能保证终端应用满足用户需求,但现在如何让数据高效安全的传输已经成为了诸多应用升级的一个瓶颈。如何让数据实现安全快速的传输是行业共同关心的话题,接口和安全IP的领先厂商Rambus近期召开了峰会,向业界展示了其领先的接口和安全IP解决方案。 接口IP向更高速率演进 “从IP接口整体大趋势来看,速度上向高速接口的方向发展。现在我们发现整个业界新推出的中速接口IP越来越少,低速的基本上没有了,大家都把注意力放在高速接口IP上。“Rambus大中华区总经理 苏雷先生分享到。从2012年到 2019年,人工智能训练集增长了30万倍,平均每3.43个月翻一番。人工智能和数据中心发展正在加速推动接口IP市场的发展。例如数据中心的主内存大约从明年开始将会逐渐从DDR4过渡到DDR5,最大容量提升了4倍左右。数据容量的提升也对内存系统提出更高的性能要求。高速内存控制器不断出现,比如GDDR6、HBM、PCIe5等;各种支持小芯片互联的技术也不断的出现,比如用于并行传输的HBI,用于串行传输的SerDes等。   Rambus针对各种不同的传输协议都有相应的业界领先解决方案。例如在HBM方面,Rambus的HBM2E接口提供了最佳性能与最小面积的特点和高效率,可以达到业界最快4Gbps的速率,已经超越HBM2E DRAM 当前最高的3.6Gbps。而在GDDR6方面,Rambus的GDDR6接口每个引脚支持高达18 Gbps。这都是行业领先的水平。另外Rambus最新也推出了PCIe 5.0接口IP,该协议支持32Gbps的最高传输速率,非常适合AI、服务器、5G 基础设施等应用。苏雷表示,达到业界最顶峰水平可以帮客户的设计方案提供足够裕量空间,保证整个系统的稳定性。   能够达到行业顶尖的接口传输速率,得益于芯片工艺的演进,但更重要的是Rambus的团队在对信号完整性和电源完整性方面有着深刻理解和丰富的专业知识积累。苏雷先生表示,Rambus在过去的30年中积累了大量的这方面的经验, 包括从建模到设计一整套的方法学,这些都是Rambus可以在接口IP领域保持领先的重要支撑,同时也是Rambus的核心优势所在。另一个重要的优势在于,Rambus的优势在于并不仅仅是提供一些单品,而是可以提供基于接口IP的全套的解决方案。Rambus资深应用工程师曹汪洋先生分享到,这些方案都通过了完整的软件仿真和硬件验证。因此可以大大简化用户设计的复杂度,用户在设计时不需要考虑不同厂商IP之间的配合兼容的问题。 用硬件安全IP加速安全协议 在数据传输的安全方面,这首先是一个安全的问题,同时在保证安全的基础上也还是是一个数据转换速率的问题。物联网、边缘计算、V2X等等将会带来越来越多的节点,需要大量的数据转换;同时在数据中心和AI训练中,也需要大量的数据在接口中心读取和搬迁,所以数据安全性的问题变得愈发重要,而且如何快速地进行安全协议的转换和确认也是影响系统整体性能的重要参数之一。   据Rambus资深现场工程师 张岩先生分享,虽然目前在不同的网络层次的协议上都有对应的一些加密方案,但是如果单纯使用软件实现安全协议就没法实现高速率。Rambus的方案,就是用硬件的安全IP去加速各种的安全协议。对于安全问题的共性在于对密钥算法的要求和密钥策略的要求以及传输协议的要求。也就是针对数据流转性的保护,Rambus提出了一套叫做secure data in motion的概念,通过运用MACsec技术来保护流动数据。 针对静态数据的保护(secure data at rest),Rambus有一个CryptoManager RoT(Root of Trust)的安全IP,可以在SoC中构建一个单独的岛式安全引擎。这个IP是一个单独的RISC-V内核,专门用来进行数据加解密工作。这种保护不仅仅限于单独密码学的计算,也可以抵抗物理层面的攻击,例如芯片侧信道功耗分析攻击、错误注入攻击或者是剖片攻击等。为什么考虑使用RISC-V来构建这样的安全IP呢?张岩先生分享到了一个在安全领域的设计原则叫作“clean sheet design”,即从一张白纸开始去做安全和构架设计。这样从设计最初阶段开始,继承的过去的设计越少,越有可能更少地遇到原来历史遗留的安全问题。   据苏雷先生分享,Rambus此前曾收购了Verimatrix的安全IP团队,再加上Rambus原有的安全团队,现在可以实现业界非常全面的安全IP产品,并具备全球领先的安全IP研发实力,这将为客户在数据安全方面提供保驾护航。 --- 目前中国正在加紧进行5G、数据中心和人工智能等新基础设施的建设,中国在全球高科技产业舞台上也在发挥着越来越重大的作用,这对 Rambus而言也孕育着巨大的接口IP的生意机会。苏雷先生表示,Rambus非常看重中国的机会,将会和中国主要云厂商在内的更多云服务商展开更密切的合作,同时也会和中国广泛的OEM、ODM合作,打造整个内存产业生态系统内的一系列合作。

    时间:2020-12-21 关键词: 数据 接口IP Rambus 内存

  • 英特尔正式发布全新一代内存和存储产品

    2020年12月16日,北京——在2020英特尔内存存储日活动上,英特尔重磅发布了六款全新内存和存储产品,旨在帮助客户驾驭数字化转型的重大机遇。 为进一步推动内存和存储创新,英特尔宣布推出两款新的傲腾固态盘产品,即全球运行速度最快的数据中心固态盘英特尔®傲腾™固态盘P5800X,以及能够为游戏和内容创作提供高性能和主流生产力,面向客户端的英特尔®傲腾™H20混合式固态盘。通过使内存更靠近处理器,英特尔傲腾能够满足现代计算的需求。此外,英特尔还透露,即将面向云和企业级用户发布代号为“Crow Pass”的第三代英特尔®傲腾™持久内存。 英特尔数据平台事业部副总裁兼傲腾事业部总经理Alper Ilkbahar指出:“对于英特尔内存和存储业务来说,今天是颇具里程碑意义的一天。随着几款全新傲腾产品的发布,我们不仅延续创新,进一步强化了我们的内存和存储产品组合,还将持续赋能客户更好地应对数字化转型的复杂性。现阶段,傲腾产品和技术正日益成为计算业务中的主流应用。而作为英特尔重要的组成部分,这些领先的内存和存储产品将进一步推动包括AI、5G网络、智能和自主边缘计算等重点增长领域的长足发展。” 本次大会上,英特尔还宣布推出三款采用144层存储单元的全新NAND固态盘,包括适用于主流计算的英特尔下一代144层QLC 3D NAND固态盘——英特尔固态盘670p;全球首个推向市场的144层TLC NAND设计的英特尔固态盘D7-P5510;采用业内首个144层QLC NAND并具备更高密度、更强持久性的英特尔固态盘D5-P5316。 随着当天傲腾技术的发布,英特尔继续在数据中心内存和存储金字塔中构建融合了DRAM和NAND特性的新的层级。其中,英特尔傲腾固态盘通过高速缓存和高速存储的性能,突破数据供应瓶颈并加速应用,提高每台服务器的性能可扩展性,并降低时延敏感型工作负载的交易成本。 英特尔傲腾持久内存是英特尔打造兼具持久性、大容量、经济性、低延迟和近内存速度性能的内存和存储解决方案愿景的最好体现。通过傲腾持久内存,英特尔重构了内存和存储层级,建立了以其为基础的容量和性能都有所不同的内存和存储层。这种方式同时使建立双层内存架构成为可能,其中以DRAM作为性能层,持久内存作为容量层。而从存储角度来看,傲腾持久内存可被用作基于NAND大容量存储层之上的性能层。 同时,英特尔傲腾持久内存通过双倍数据速率总线连接CPU,能够以DRAM的速度直接进行加载和存储访问,同时它也兼具非易失性,融合了内存和存储的最佳特性。 此外,英特尔将通过其代号为“Crow Pass”的第三代英特尔®傲腾持久内存和代号为Sapphire Rapids的英特尔®至强®可扩展处理器进一步增强和扩展其独特的内存和存储产品组合。 值得一提的是,当天发布的三款全新NAND固态盘,代表着TLC和QLC作为大容量固态盘的主流技术正式迈入全新时代。其中,英特尔®3D NAND固态盘670p是用于主流计算的英特尔下一代144层(QLC)3D NAND固态盘。英特尔固态盘D7-P5510是全球首个推向市场的144层TLC NAND固态盘,而英特尔固态盘D5-P5316则是业内首个采用了144层QLC NAND的数据中心级固态盘。过去十余年间,英特尔始终致力于推动和发展领先的内存和存储技术。

    时间:2020-12-17 关键词: 存储 内存

  • 电脑内存有何作用?内存适用类型包含哪些?

    电脑内存有何作用?内存适用类型包含哪些?

    内存是重要器件之一,很多厂商致力于生产高性能内存。但是,大家对电脑内存的作用真的了解吗?如果你对内存作用存在疑惑,本文即可为您解惑。此外,本文还将对内存适用类型予以探讨。小编相信,通过本文对内存的介绍,大家必定可以增进对内存的认识。那么,和小编共同往下阅读吧。 一、内存适用类型 根据内存条所应用的主机不同,内存产品也各自不同的特点。台式机内存是DIY市场内最为普遍的内存,价格也相对便宜。笔记本内存则对尺寸、稳定性、散热性方面有一定的要求,价格要高于台式机内存。而应用于服务器的内存则对稳定性以及内存纠错功能要求严格,同样稳定性也是着重强调的。 台式机内存 笔记本内存就是应用于笔记本电脑的内存产品,笔记本内存只是使用的环境与台式机内存不同,在工作原理方面并没有什么区别。只是因为笔记本电脑对内存的稳定性、体积、散热性方面的需求,笔记本内存在这几方面要优于台式机内存,价格方面也要高于台式机内存。 笔记本内存 笔记本诞生于台式机的486年代,在那个时代的笔记本电脑,所采用的内存各不相同,各种品牌的机型使用的内存千奇百怪,甚至同一机型的不同批次也有不同的内存,规格极其复杂,有的机器甚至使用PCMICA闪存卡来做内存。进入到台式机的586时代,笔记本厂商开始推广72针的SO DIMM标准笔记本内存,而市场上还同时存在着多种规格的笔记本内存,诸如:72针5伏的FPM;72针5伏的EDO;72针3.3伏的FPM;72针3.3伏的EDO。此几种类型的笔记本内存都已成为“古董”级的宝贝,早已在市场内消失了。在进入到“奔腾”时代,144针的3.3伏的EDO标准笔记本内存。在往后随着台式机内存中SDRAM的普及,笔记本内存也出现了144针的SDRAM。现在DDR的笔记本内存也在市面中较为普遍了,而在一些轻薄笔记本内,还有些机型使用与普通机型不同的Micro DIMM接口内存。 对于多数的笔记本电脑都并没有配备单独的显存,而是采用内存共享的形式,内存要同时负担内存和显存的存储作用,因此内存对于笔记本电脑性能的影响很大。 服务器内存 服务器是企业信息系统的核心,因此对内存的可靠性非常敏感。服务器上运行着企业的关键业务,内存错误可能造成服务器错误并使数据永久丢失。因此服务器内存在可靠性方面的要求很高,所以服务器内存大多都带有Buffer(缓存器),Register(寄存器),ECC(错误纠正代码),以保证把错误发生可能性降到最低。服务器内存具有普通PC内存所不具备的高性能、高兼容性和高可靠性。 二、电脑内存外观 可能大家看了以上关于内存含义的概述还是不怎么了解内存是什么,其实内存也是存储器,只是内存不同于硬盘等存储器,比一般的硬盘灯速度更快,采用的是闪存技术,不过内存上的数据在断电后将全部清空,也就是说内存是随机存储,在计算机中开机状态下内存最为CPU与外界数据交换的桥梁,所有诸如硬盘、主板上的数据均是通过内存与CPU进行数据交换的,一般来说内存越大,对数据交换越有利,因此速度也越快些。 其实我们只要知道内存就是暂时存储程序以及数据交换的地方,不同于硬盘,当电脑关机内存断点后,所有临时存储在内存的上的数据将全部清空,而保存在硬盘上的数据再次开机后还在,这主要是因为内存与硬盘采取的存储技术不同,内存的优点是高速但数据需要一直通电才能保存,有兴趣的朋友可以深入了解RAM与ROM数据读取与存储的关系。 三、电脑内存的作用 简单的说内存在电脑中的作用相当于一座桥梁,用以负责诸如硬盘主板上的数据与处理器之间数据交换与处理器。所有电脑数据都是通过内存与处理器进行交换的,可能有的朋友会想为什么数据不直接与处理器进行数据处理器与交换呢?其实大家只要了解内存就知道,内存的读取速度与存储速度是最快的,直接与主板上数据总线交换速度很慢,大家也可以将内存看作数据缓存区。 关于内存是什么就为大家介绍到这里,目前我们可以看到的内存均是2GB以上,内存已经发展到第3代也就是DDR3,预计2014年将上市第四代DDR4 内存。内存越大相应来说整机的速度就越快,但如果只是运行小程序,其实2G内存与8G内存是一样的,所以通常内存越大越好只是相对的,如果不是电脑中运行大程序或同时开很多程序一般2GB内存足够。 以上便是此次小编带来的“内存”相关内容,通过本文,希望大家对内存适用类型具备一定的了解。如果你喜欢本文,不妨持续关注我们网站哦,小编将于后期带来更多精彩内容。最后,十分感谢大家的阅读,have a nice day!

    时间:2020-12-03 关键词: 指数 电脑内存 内存

首页  上一页  1 2 3 4 5 6 7 8 9 10 下一页 尾页
发布文章

技术子站

更多

项目外包