题外话,PA里面也有很不错的Linux基础基础

传送门:https://nju-projectn.github.io/ics-pa-gitbook/ics2019/linux.html

静态库与动态库

什么玩意?

库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量函数

​ 库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行

​ 库文件有两种,静态库动态库(共享库)。

静态库与动态库的区别

复制代码到程序的时机不同

​ 静态库在程序的链接阶段被复制到了程序中;

​ 动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用

库的好处

1.代码保密

2.方便部署和分发

静态库的制作与使用

命名规则

Linux : libxxx.a(库文件的名字)

lib : 前缀(固定)

xxx : 库的名字,自己起(注意,在使用gcc编译时需要的是xxx,不含lib前缀

.a : 后缀(固定)

Windows : libxxx.lib

静态库的制作

◆ gcc 获得 .o 文件

◆将 .o 文件打包,使用 ar 工具(archive)

ar rcs libxxx.a xxx.o xxx.o

​ r –将文件插入备存文件中

​ c –建立备存文件

​ s –索引

root@ubuntu:/home/ag/cpp/Linux/lesson04/calc# tree
.
├── add.c
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c 0 directories, 6 files
root@ubuntu:/home/ag/cpp/Linux/lesson04/calc# gcc -c add.c div.c mult.c sub.c
root@ubuntu:/home/ag/cpp/Linux/lesson04/calc# ls
add.c div.c head.h mult.c sub.c
add.o div.o main.c mult.o sub.o
root@ubuntu:/home/ag/cpp/Linux/lesson04/calc# tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── head.h
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o 0 directories, 10 files
root@ubuntu:/home/ag/cpp/Linux/lesson04/calc# ar rcs libcalc.a add.o sub.o mult.o div.o
root@ubuntu:/home/ag/cpp/Linux/lesson04/calc# tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── head.h
├── libcalc.a
├── main.c
├── mult.c
├── mult.o
├── sub.c
└── sub.o 0 directories, 11 files

静态库的使用

在写项目的时候,一般会进行分包操作

一个常见的项目树如下:

root@ubuntu:/home/ag/cpp/Linux/lesson05/library# tree
.
├── include //放头文件
│ └── head.h
├── lib //放库文件
├── main.c //测试文件
└── src //源代码
├── add.c
├── div.c
├── mult.c
└── sub.c 3 directories, 6 files

把之前制作的静态库文件libcalc.a复制到 lib 目录下即可,然后去编译

ps:使用库文件时,还有同时有其依赖的头文件

编译文件

root@ubuntu:/home/ag/cpp/Linux/lesson05/library# gcc main.c -o app
main.c:2:10: fatal error: head.h: No such file or directory
#include "head.h"
^~~~~~~~
compilation terminated.

这里报错的原因是:编译时,需要把头文件的代码拷贝到main.c中,但是我们include的时候用的是相对路径,main.c与head.h并不在统一目录下,因此是识别不到的

解决方法1:让编译文件(main.c)与头文件(head.h)在同一目录下

解决方式2(优雅):使用gcc提供的参数选项 -I 在编译时查找指定目录下的头文件

gcc main.c -o app -I ./include

又报错

root@ubuntu:/home/ag/cpp/Linux/lesson05/library# gcc main.c -o app -I ./include
/tmp/cct81thC.o: In function `main':
main.c:(.text+0x3a): undefined reference to `add'
main.c:(.text+0x5c): undefined reference to `subtract'
main.c:(.text+0x7e): undefined reference to `multiply'
main.c:(.text+0xa0): undefined reference to `divide'
collect2: error: ld returned 1 exit status

原因:main中使用了上述函数,但是编译时只找到了 .h 中的声明,没有找到库文件中的定义

还需要追加gcc参数,使用 -l 指定使用哪个库文件(只需要库名字,前缀lib不需要)进行编译, -L 指定到某个目录下找

root@ubuntu:/home/ag/cpp/Linux/lesson05/library# gcc main.c -o app -I ./include -l calc -L ./lib
root@ubuntu:/home/ag/cpp/Linux/lesson05/library# ls
app include lib main.c src

./app可以正常执行

动态库的制作与使用

命名规则

Linux : libxxx.so(库文件的名字)

lib : 前缀(固定)

xxx : 库的名字,自己起(注意,在使用gcc编译时需要的是xxx,不含lib前缀

.so : 后缀(固定,在linux下是一个可执行文件)

Windows : libxxx.dll

动态库的制作

◆ gcc 获得 .o 文件,得到和位置无关的代码

gcc -c –fpic/-fPIC axx.c bxx.c

◆ gcc得到动态库

gcc -shared a.o b.o -o libcalc.so
root@ubuntu:/home/ag/cpp/Linux/lesson06/calc# gcc -c -fpic add.c div.c sub.c mult.c
root@ubuntu:/home/ag/cpp/Linux/lesson06/calc# ls
add.c add.o div.c div.o head.h main.c mult.c mult.o sub.c sub.o
root@ubuntu:/home/ag/cpp/Linux/lesson06/calc# gcc -shared add.o sub.o mult.o div.o -o libcalc.so
root@ubuntu:/home/ag/cpp/Linux/lesson06/calc# ls
add.c div.c head.h main.c mult.o sub.o
add.o div.o libcalc.so mult.c sub.c

然后把 .so 拷贝到要使用的地方

root@ubuntu:/home/ag/cpp/Linux/lesson06/calc# cd ..
root@ubuntu:/home/ag/cpp/Linux/lesson06# cd library/
root@ubuntu:/home/ag/cpp/Linux/lesson06/library# cp ../calc/libcalc.so ./lib/
root@ubuntu:/home/ag/cpp/Linux/lesson06/library# tree
.
├── app
├── include
│ └── head.h
├── lib
│ └── libcalc.so
├── main.c
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c 3 directories, 8 files

编译main.c

root@ubuntu:/home/ag/cpp/Linux/lesson06/library# gcc main.c -o main
main.c:2:10: fatal error: head.h: No such file or directory
#include "head.h"
^~~~~~~~
compilation terminated.

报错,和静态库使用的时候的原因一样

需要指定头文件位置、库文件位置、库文件名字

root@ubuntu:/home/ag/cpp/Linux/lesson06/library# gcc main.c -o main -I include/
/tmp/ccOgCtBV.o: In function `main':
main.c:(.text+0x3a): undefined reference to `add'
main.c:(.text+0x5c): undefined reference to `subtract'
main.c:(.text+0x7e): undefined reference to `multiply'
main.c:(.text+0xa0): undefined reference to `divide'
collect2: error: ld returned 1 exit status
root@ubuntu:/home/ag/cpp/Linux/lesson06/library# gcc main.c -o main -I include/ -L lib/ -l calc
root@ubuntu:/home/ag/cpp/Linux/lesson06/library# ls
include lib main main.c src

可见,成功编译了,但是运行编译后的文件又报错

root@ubuntu:/home/ag/cpp/Linux/lesson06/library# ./main
./main: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
动态库加载失败原因

造成上述错误的原因与动态库加载原理有关

库文件工作原理

静态库:GCC 进行链接时,会把静态库中代码打包到可执行程序中

动态库:GCC 进行链接时,动态库的代码不会被打包到可执行程序中

程序启动之后,动态库会被动态加载到内存中,通过 ldd (list dynamic dependencies)命令检查动态库依赖关系

root@ubuntu:/home/ag/cpp/Linux/lesson06/library# ldd main
linux-vdso.so.1 (0x00007fff808bb000)
libcalc.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efca544e000)
/lib64/ld-linux-x86-64.so.2 (0x00007efca5a41000)

可以看到,我们创建的动态库文件没有被找到,而标准的c库文件是被识别到的

如何定位共享库文件?

当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系

统的动态载入器来获取该绝对路径。

对于elf格式的可执行程序,是由 ld-linux.so 来完成的,它先后搜索 elf 文件的

DT_RPATH段 ——> 环境变量LD_LIBRARY_PATH ——> /etc/ld.so.cache文件列表 ——> /lib//usr/lib目录,找到库文件后将其载入内存。

解决动态库加载失败问题

临时添加环境变量
root@ubuntu:/home/ag# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ag/cpp/Linux/lesson06/library/lib
root@ubuntu:/home/ag# echo $LD_LIBRARY_PATH
:/home/ag/cpp/Linux/lesson06/library/lib

此时可以在当前终端看到,编译后的文件main已经与动态库链接

root@ubuntu:/home/ag/cpp/Linux/lesson06/library# ls
include lib main main.c src
root@ubuntu:/home/ag/cpp/Linux/lesson06/library# ldd main
linux-vdso.so.1 (0x00007ffd93bf7000)
libcalc.so => /home/ag/cpp/Linux/lesson06/library/lib/libcalc.so (0x00007f32c4b61000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f32c4770000)
/lib64/ld-linux-x86-64.so.2 (0x00007f32c4f65000)

关闭终端再打开其他终端,仍然会报错

永久配置环境变量
用户级别

回到home目录下(或者在当前目录下操作~/.bashrc也行)

root@ubuntu:/home/ag/cpp/Linux/lesson06/library# cd
root@ubuntu:~# ls
snap
root@ubuntu:~# ll
total 68
drwx------ 7 root root 4096 Feb 5 21:14 ./
drwxr-xr-x 24 root root 4096 Feb 2 16:50 ../
-rw------- 1 root root 8887 Feb 9 06:36 .bash_history
-rw-r--r-- 1 root root 3106 Apr 9 2018 .bashrc
drwx------ 4 root root 4096 Nov 9 04:38 .cache/
drwx------ 3 root root 4096 Nov 7 17:50 .gnupg/
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
drwx------ 6 root root 4096 Nov 8 03:06 snap/
drwx------ 2 root root 4096 Nov 7 18:41 .ssh/
-rw------- 1 root root 9616 Feb 5 21:14 .viminfo
drwxr-xr-x 5 root root 4096 Feb 9 19:51 .vscode-server/
-rw-r--r-- 1 root root 183 Nov 7 18:45 .wget-hsts
-rw------- 1 root root 52 Nov 7 18:38 .Xauthority

找到隐藏文件 .bashrc ,编辑该文件

vim .bashrc

使用shift+g跳转到最后一行,按o往下插入一行

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ag/cpp/Linux/lesson06/library/lib

保存退出,使修改后的bashrc生效:

root@ubuntu:~# . .bashrc
root@ubuntu:~#

注:

1、. .bashrc等于source .bashrc

2、在终端1中配置bashrc后,在另一终端2中也要重新source一次才可以正常链接

系统级别

这时需要配置系统的profile文件

sudo vim etc/profile

同样在最后一行加入export环境变量即可

然后source一下(不用加sudo)

root@ubuntu:/etc# source etc/profile

如果识别不到,就source一下

静态库与动态库对比

程序编译成可执行程序的过程

静态库处理过程

动态库处理过程

静态库的优缺点

优点:

◆静态库被打包到应用程序中加载速度快

◆发布程序无需提供静态库,移植方便

缺点:

◆消耗系统资源,浪费内存

◆更新、部署、发布麻烦

动态库的优缺点

优点:

◆可以实现进程间资源共享(共享库)

◆更新、部署、发布简单

◆可以控制何时加载动态库

缺点:

◆加载速度比静态库慢

◆发布程序时需要提供依赖的动态库

Makefile

什么是 Makefile

​ 一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,Makefile 文件定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 Makefile 文件就像一个 Shell 脚本一样,也可以执行操作系统的命令。

​ Makefile 带来的好处就是“自动化编译” ,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。make 是一个命令工具,是一个解释 Makefile 文件中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如 Delphi 的 make,Visual C++ 的 nmake,Linux 下 GNU 的 make。

Makefile 文件命名和规则

文件命名

makefile 或者 Makefile

Makefile 规则

一个 Makefile 文件中可以有一个或者多个规则

目标 ...: 依赖 ...
命令(Shell 命令)
...

目标:最终要生成的文件(伪目标除外)

依赖:生成目标所需要的文件或是目标

命令:通过执行命令对依赖操作生成目标(命令前必须 Tab 缩进

Makefile 中的其它规则一般都是为第一条规则服务的。

一个简单的Makefile例子

app: sub.c add.c mult.c div.c main.c
gcc sub.c add.c mult.c div.c main.c -o app

工作原理

命令在执行之前,需要先检查规则中的依赖是否存在

  • 如果存在,执行命令
  • 如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的,如果找到了,则执行该规则中的命令

检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间

  • 如果依赖的时间比目标的时间晚,需要重新生成目标
  • 如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被

    执行

升级一下之前的简单例子,检测每个依赖是否存在并且重新生成被修改的目标

app: sub.c add.c mult.c div.c main.c
gcc sub.c add.c mult.c div.c main.c -o app sub.o : sub.c
gcc -c sub.c -o sub.o
add.o : add.c
gcc -c add.c -o add.o
add.o : div.c
gcc -c div.c -o div.o
mult.o : mult.c
gcc -c mult.c -o mult.o
main.o : main.c
gcc -c main.c -o main.o

但是这么写非常不简洁,文件一多就很麻烦

还得改进

变量

自定义变量

变量名 = 变量值

例如,var = hello

预定义变量
AR : 归档维护程序的名称,默认值为 ar

CC : C 编译器的名称,默认值为 cc

CXX : C++ 编译器的名称,默认值为 g++

$@ : 目标的完整名称

$< : 第一个依赖文件的名称

$^ : 所有的依赖文件
获取变量的值

$ (变量名)

例如,$(var)

模式匹配

' % '是通配符,可以匹配一个字符串

例如:%.o可以匹配出文件结尾为 .o 的所有文件

所以我们可以进行如下改写

add.o : add.c
gcc -c add.c -o add.o
#等于
%.o : %.c
gcc -c add.c -o add.o

gcc -c add.c -o add.o也应该简化掉

于是第三版简单例子可以写为:

src=sub.o add.o mult.o div.o main.o
target=app $(target): $(src)
$(CC) $(src) -o $(target) %.o : %.c
$(CC) -c $< -o $@

解释一下:

%.o : %.c 匹配到了src中的每一个文件

$< 则按顺序往下执行指令

src获取的方式还可以再优化

函数

$(wildcard PATTERN...)

​ 功能:获取指定目录下指定类型的文件列表

​ 参数:PATTERN 指的是某个或多个目录下的对应的某种类型的文件,如果有多个目录,一般使用空格间隔

​ 返回:得到的若干个文件的文件列表,文件名之间使用空格间隔

​ 示例:

$(wildcard *.c ./sub/*.c)

​ 返回值格式: a.c b.c c.c d.c e.c f.c

$(patsubst <pattern>,<replacement>,<text>)

​ 功能:查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。可以包括通配符%,表示任意长度的字串。如果中也包含%,那么,中的这个%将是中的那个%所代表的字串。(可以用\来转义,以\%来表示真实含义的%字符)

​ 返回:函数返回被替换过后的字符串

​ 示例:

$(patsubst %.c, %.o, x.c bar.c)

​ 返回值格式: x.o bar.o

于是第四版简单例子可以改成:

#定义变量
# sub.o add.o mult.o div.o main.o
src=$(wildcard ./*.c)
objs=$(patsubst %.c, %.o,$(src))
target=app
$(target): $(objs)
$(CC) $(objs) -o $(target) %.o:%.c
$(CC) -c $< -o $@ .PHONY:clean #伪目标,不生成clean
clean:
rm $(objs) -f

GDB调试

什么是 GDB

GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境。

一般来说,GDB 主要帮助你完成下面四个方面的功能:

​ 1.启动程序,可以按照自定义的要求随心所欲的运行程序

​ 2.可让被调试的程序在所指定的调置的断点处停住(断点可以是条件表达式)

​ 3.当程序被停住时,可以检查此时程序中所发生的事

​ 4.可以改变程序,将一个 BUG 产生的影响修正从而测试其他 BUG

准备工作

通常,在为调试而编译时,我们会()关掉编译器的优化选项(-O), 并打开调试选项(-g)。另外,-Wall在尽量不影响程序行为的情况下选项打开所有warning,也可以发现许多问题,避免一些不必要的 BUG。

gcc -g -Wall program.c -o program

-g 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件。

GDB 命令 –启动、退出、查看代码

启动和退出

gdb 可执行程序
quit

给程序设置参数/获取设置参数

set args 10 20
show args

查看当前文件代码

list/l (从默认位置显示)
list/l 行号 (从指定的行显示)
list/l 函数名(从指定的函数显示)

查看非当前文件代码

list/l 文件名:行号
list/l 文件名:函数名

设置显示的行数

show list/listsize
set list/listsize 行数

GDB命令 –断点操作

设置断点

b/break 行号
b/break 函数名
b/break 文件名:行号
b/break 文件名:函数

查看断点

i/info b/break

删除断点

d/del/delete 断点编号

设置断点无效/生效

dis/disable 断点编号
ena/enable 断点编号

设置条件断点(一般用在循环的位置)

b/break 10 if i==5 //比如有个for循环,这里是循环到i=5才会停在断点上

GDB命令 –调试命令

运行GDB程序

start(程序停在第一行)
run(遇到断点才停)

继续运行,到下一个断点停

c/continue

向下执行一行代码(不会进入函数体)

n/next

变量操作

p/print 变量名(打印变量值)
ptype 变量名(打印变量类型)

向下单步调试(遇到函数进入函数体)

s/step
finish(跳出函数体)

自动变量操作

display 变量名(自动打印指定变量的值)
i/info display
undisplay 编号

其它操作

set var 变量名=变量值 (循环中用的较多)
until (跳出循环)

【webserver 前置知识 01】Linux系统编程入门的更多相关文章

  1. Linux系统编程温故知新系列 --- 01

    1.大端法与小端法 大端法:按照从最高有效字节到最低有效字节的顺序存储,称为大端法 小端法:按照从最低有效字节到最高有效字节的顺序存储,称为小端法 网际协议使用大端字节序来传送TCP分节中的多字节整数 ...

  2. 读书笔记之Linux系统编程与深入理解Linux内核

    前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...

  3. LINUX系统编程 由REDIS的持久化机制联想到的子进程退出的相关问题

    19:22:01 2014-08-27 引言: 以前对wait waitpid 以及exit这几个函数只是大致上了解,但是看REDIS的AOF和RDB 2种持久化时 均要处理子进程运行完成退出和父进程 ...

  4. Linux网络编程入门 (转载)

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  5. 【转】Linux网络编程入门

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  6. 《转》Linux网络编程入门

    原地址:http://www.cnblogs.com/duzouzhe/archive/2009/06/19/1506699.html (一)Linux网络编程--网络知识介绍 Linux网络编程-- ...

  7. linux系统编程:cp的另外一种实现方式

    之前,这篇文章:linux系统编程:自己动手写一个cp命令 已经实现过一个版本. 这里再来一个版本,涉及知识点: linux系统编程:open常用参数详解 Linux系统编程:简单文件IO操作 /*= ...

  8. 《Linux系统编程(第2版)》

    <Linux系统编程(第2版)> 基本信息 作者: (美)Robert Love 译者: 祝洪凯 李妹芳 付途 出版社:人民邮电出版社 ISBN:9787115346353 上架时间:20 ...

  9. Linux系统编程博客参考

    通过看前人的博客更易于把握知识要点 http://www.cnblogs.com/mickole/category/496206.html <Linux系统编程> http://www.c ...

  10. linux系统编程之文件与io(一)

    经过了漫长的学习,C语言相关的的基础知识算是告一段落了,这也是尝试用写博客的形式来学习c语言,回过头来看,虽说可能写的内容有些比较简单,但是个人感觉是有史起来学习最踏实的一次,因为里面的每个实验都是自 ...

随机推荐

  1. [转帖]Centos7 nginx访问日志文件割接

    一.yum安装nginx 二.各文件路径( /etc/nginx/nginx.conf) 1.访问日志路径:access_log /var/log/nginx/access.log main; 2.p ...

  2. 从零开始配置vim(26)——LSP UI 美化

    之前我们通过几个实例演示如何配置其他语言的lsp服务,相信各位小伙伴碰到其他的编程语言也能熟练的配置它对应的lsp服务.本篇讲作为一个补充,我们来优化一下LSP 相关的显示 配置 UI 原始的 lsp ...

  3. vim 从嫌弃到依赖(13)——motion 进阶

    在最开始的时候我们介绍了一些vim中的motion 包括如何在字符间.单词间.行间以及多行间移动.·但是motion中的内容可远不止我们介绍的这些,平时用到的也远不止之间介绍的那些. 之所以没有一次介 ...

  4. Git 简单实用教程

    相关链接: 码云(gitee)配置SSH密钥 码云gitee创建仓库并用git上传文件 git 上传错误This oplation equires one of the flowi vrsionsot ...

  5. Windows10安装Apache2.4.54并配置PHP5.6.40/PHP8.1.11

    环境 Windows 10 Apache2.4.54 PHP5.6.40/PHP8.1.11 安装Microsoft Visual C++ 下载地址:https://learn.microsoft.c ...

  6. Arduino-电位器调节led

    Arduino-电位器调节led 电位器相关: 电位器是具有三个引出端.阻值可按某种变化规律调节的电阻元件.电位器通常由电阻体和可移动的电刷组成.当电刷沿电阻体移动时,在输出端即获得与位移量成一定关系 ...

  7. hv_balloon: Balloon request will be partially fulfilled. Balloon floor reached

    windows 的hyper-v 安装了 centos 或者龙蜥 操作系统,会一直提示这个信息: hv_balloon: Balloon request will be partially fulfi ...

  8. 【路由器】电信光猫中兴 F7010C 折腾记录

    目录 问题描述 解锁超管密码 前言 配置安卓抓包环境 抓包获取超管密码 IPv6 配置 光猫拨号 改用 SLAAC 路由器配置 wan6 配置 wan 配置 lan 配置 验证 参考资料 问题描述 近 ...

  9. Rating 算法

    Rating 算法 这是一种 OJ 常用的算法,下面进行介绍 公式 $ Rating $ 的计算公式如下: $ Rating = R + K(P - E) $ 这里 $ R $ 表示当前 $ Rati ...

  10. [JVM] CPU缓存一致性协议

    CPU缓存一致性协议 CPU高速缓存 CPU缓存是位于cpu和内存之间的临时数据交换器,它的容量比内存小的夺但是交换速度要比内存快得多,主要是为了解决cpu运行时的处理速度与内存读写速度不匹配的问题. ...