1.
早期通过双绞线(只能有一台设备进行数据发送)
,通过10100…高低电平就能表示数据信号。标识:1–>3,3需要表明自己身份是3。
2.
通过集线器广播
给所有设备,2345自己分辨是我的消费了,不是我的数据包丢弃。如果1,2同时广播,4收到2个消息混合解析不出,导致1,2这两个数据包全没法用。针对上面问题提出CSMA/CD
协议:发送前进行载波侦听,检测这链路上有没有其他人正在发送数据,没有的话再进行数据发送防冲突。
hub集线器缺点:1.进行数据的广播
会导致带宽利用率较低。2.在链路上同时只能有一个
设备发送数据,链路利用率低。3.没有标识
,只是广播出去,让设备自己判断是不是自己的,工作效率低。
3.
如下机器1想发到机器3,通过SW寻址到3号口。SW记录了地址
(mac地址)和端口
(此处端口不是电脑端口而是交换机端口)的映射关系不用广播
(集线器),SW用的是网线,里面有8根线,正常情况至少4根线是在工作的,所以实现全双工。
买来交换机里是张空表,怎么建立映射关系?如下机器A插上来后要向B发送数据,发现是空表
,确定A是1号口,B找不到就往每个端口发,4号口对B做出了回应,表记录B对应4号口。桥接
(如没有映射关系,C和D都对应5,5口转到另一个SW,量大之后不断桥接引起消息洪泛)实现几千条存储,几千条不够至少几十亿。如下mac和端口的映射表
不是路由表,局域网
(家庭网,校园网等)使用交换机效率高。
win下ipconfig /all查看无线或有线网的mac地址。
SW交换机的映射表
只能实现几千存储,如果表中记录满了,新的来会把旧的替代,所以跨网用路由器(也称网关)
。如下家庭网是整个网络2,每台机器都有自己的路由表如ubuntu
有路由表,路由器也是linux系统也有路由表。
本台电脑的路由表会写默认网关是192.168.0.1(这个点在路由器上)
,本台电脑就会把数据包发到路由器上,这个路由器自己也有路由表
路由到1.52这个网卡,1.52和1.254和1.1
在同一个网段下很容易找到。路由器的路由表
比SW交换机的映射表
复杂用到了很多路由算法。
ping 192.168.1.254
可通,那么网络内传输如1.52—>1.254即网络内怎么传数据的呢?同一网段一找就找到这样的说法是错的,若是这样为什么有了IP地址还要mac地址呢?ip地址(抽象地址)
不能直接通信,只能用mac地址(真实地址)
通信,ARP
协议广播询问谁的ip是1.254,1.254收到这询问就会回复一下,说我的地址是1.254,我的mac地址
是。。。1.52知道了1.254对应的mac地址就会在mac层进行传输。
ip的数据包
就是mac的数据部分,越往上层(往里)
ip层包着还有tcp层,ip数据包里数据部分还会有tcp的头,再往上层(往里)
还可能有http的头,最后的数据才是我们要传的数据。
获取mac地址都是通过ARP协议(cat /proc/net/arp)
,如下ip的源目地址是不变的,一直为0.102和1.254,只有mac地址一直在切换(竖着对比)。有个特例NAT(网络地址转换协议)
:源地址ip也会进行切换。
如下手动修改eeprom中mac,和eth无关。
# config_mac
. /usr/local/bin/openbmc-utils.sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
prog=$(basename "$0")
usage()
{
echo "Usage: $prog "
echo
echo "Examples:"
echo " $prog XX:XX:XX:XX:XX:XX"
echo
exit 1
}
check_parameter()
{
mac=$1
strlen=${#mac}
if [ $# -ne 1 ] ;then
usage
fi
if [ "$strlen" -ne 17 ] ;then
usage
fi
}
set_mac()
{
mac=$(echo "$mac" | sed 's/://g'| tr '[:a-z:]' '[:A-Z:]')
/usr/local/bin/fruid-util base --dump /tmp/base_fru.bin
/usr/local/bin/fruid-util base --modify --PCD2 "$mac" /tmp/base_fru.bin > /dev/null 2>&1
set_mac_eeprom_wp
/usr/local/bin/fruid-util base --write /tmp/base_fru.bin > /dev/null 2>&1
reset_mac_eeprom_wp
}
check_mac(){
fru_mac=$(/usr/local/bin/fruid-util base |grep "Product Custom Data 2" | awk -F ': ' '{print $2}')
if [ "$mac" != "$fru_mac" ] ;then
echo "Set mac fail"
fi
}
check_parameter "$@"
set_mac
check_mac
root@bmc:~# fruid-util base
Product Custom Data 2 : 78D4F15F171D
root@bmc:~# ./config_mac 78:D4:F1:5F:17:1a
root@bmc:~# fruid-util base
Product Custom Data 2 : 78D4F15F171A
如下上电启动,从eeprom读出mac并写进eth。
# /recipes-plats/network/files/eth1_mac_fixup.sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
count=0
while [ $count -lt 3 ]
do
str=$(fruid-util cmm | grep "BMC Base Mac address" | awk -F ":" '{print $2}')
str=$(echo $str |sed 's/ //g')
if [ ${#str} -ne 12 ];then # 获取字符串长度
logger "Loop $count failed to read BMC FRU:$str"
count=$(($count + 1))
sleep 1
continue
fi
mac="${str:0:2}:${str:2:2}:${str:4:2}:${str:6:2}:${str:8:2}:${str:10:2}" #中间有冒号
if [ -n "$mac" -a "${mac/X/}" = "${mac}" ]; then # mac有值返回true,不用管X
logger "Loop $count success to configure BMC MAC: $mac"
#ip link set dev eth1 address "$mac"
#在/etc/rc.d/rc.local里加上如下三句,reboot后就不怕MAC复原了
/sbin/ifdown eth1
/sbin/ifconfig eth1 hw ether $mac # modprobe -r e1000 , modprobe e1000 , ethtool -i eth0
/sbin/ifup eth1 # ifconfig eth0 up , dhclient eth0
sleep 1
exit 0
fi
count=$(($count + 1))
sleep 1
done
if [ $count -ge 3 ]; then
echo "Cannot find out the BMC MAC" 1>&2
logger "Error: cannot configure the BMC MAC"
exit -1
fi
vi /etc/sysconfig/network-scripts/ifcfg-eth0 。MACADDR=00:11:22:33:44:55 。改好执行/etc/init.d/network stop ,/etc/init.d/network start。ifconfig eth0 down ,dhclient eth0,ifconfig eth0 up。
# vi /etc/network/interfaces 重启ip不丢失,改完后systemctl restart network
auto lo
iface lo inet loopback
auto eth1
iface eth1 inet static
address 10.75.159.201
netmask 255.255.255.0
gateway 10.75.159.1
如下TCP/IP架构:以太网协议mac
:把cpu想要发送的数据封装为以太网协议(网卡完成这功能)。ip协议
:实现路径的管理,传输过程中根据想要发送的目标地址,帮我们的报文在网络中选择一条传输路径(路由器完成这功能)。ip协议针对目标是机器与机器之间通信,平时利用网络过程中需要进程与进程的通信,所以传输层(tcp/udp协议)
这层封装有必要。
udp
:实验室内部交流终端,发信息时效性要高
如语音、视频、直播等,丢个一帧两帧影响不大,数据是不停的过来,在ip协议基础上增加了很少一部分功能同时它不是面向连接
的,不需要对方给我一个反馈,减少了传输的成本,相对来说时延也小得多。
tcp
:传一些重要内容
,如发一个公告或给谁发一个文件,这个过程对时效性没那么强,传文件稍微等一会也没事但要求传输的准确不能出错,TCP复杂面向连接
。
如下拆包和粘包,Client和Server间的Packet1被拆包,与Packet2粘包:
如下解决粘包拆包:头/定长/分隔符。
如下是第一种方法,粘包还是会出现,但可以区分开。
如下是第三种方法,自定义分隔符。
交换机 ,二层交换机 ,多接口网桥
是一个东西。路由器 ,三层交换机 ,网关
是一个东西。
从一个HTTP请求来看网络分层原理:内网
里通过网线
进行传输,连接到公网
的话会通过光纤
进行连接。要实现不同介质间信号的转换,还有从光纤到路由器无线脉冲转换
,距离远的话还有信号衰减
问题。如下把问题分层,不同层间定义标准化接口让它们间可进行数据通信。
1.
如下右边一个服务器部署了一个静态页面,通过nginx部署在公网上,看下浏览器里有没有域名对应DNS的缓存
,有的话直接拿到服务端的ip地址,没有的话去浏览器本地的host文件
看有没有配置,没有配置的话才会发起一个DNS请求
用来获取服务器ip地址。
2.
DNS也是台服务器也有自己的ip地址通常配在自己的操作系统上,这时应用层会构造一个DNS请求报文:应用层
会去调用传输层socket的API。传输层
会在DNS请求报文基础上加一个UDP的请求头。网络层
同样在UDP请求报文基础上加IP的请求头,网络层会将IP请求报文交给数据链路层。数据链路层
会将自己的mac头加上去并把对应的请求报文交给下一个机器的mac地址也会加上去。下一个机器的mac地址通过网络层ARP协议找到
,ARP会发送一些请求看下你对应的ip地址的mac地址是多少,最后通过物理层物理介质传出去,通常传到路由器上。
3.
路由器是三层设备(网络/数据链路/物理)从物理层开始连接,物理层交给数据链路层,数据链路层看下地址是不是给我的,是给我的进行解析,不是给我的就丢弃,报文再传给上面一层网络层,网络层把数据传到下一个路由器的地址是多少,会通过运营商的网络接口传到运营商的路由器上
。运营商有自己的DNS服务器,如果配的是运营商自己的DNS服务器的话会直接在这个DNS服务器里找自己对应的域名拿到对应的ip地址,也就是刚请求DNS报文地址,然后原路返回解析直到应用层拿到刚域名对应得ip地址,这样就可以进行HTTP请求报文
的发送。再调用传输层协议是TCP参数,同样每到一层加头。
主机名(hostname)和域名(domain)的区别在于,主机名
通常在局域网内使用,通过hosts文件,主机名就被解析到对应ip。域名
通常在internet上使用,但如果本机不想使用internet上的域名解析,这时就可以更改hosts文件,加入自己的域名解析。
如下是TCP报文,Source port源端口如果是发送端的话是随机生成的,tcp三次握手之前要知道对方端口目的Dest port
,和服务器建立连接web服务一般80端口如nginx。unused保留字段,CWR到FIN是报文标识flag,标识报文什么类型的,如果把syn的bit位设为1的话,当前报文是同步序列号
即建立连接的报文,ack的bit为1代表响应报文。Receive window是当前服务器可接受数据大小窗口的值。
如下加上TCP协议头
就是五元组
,基于TCP的基础上就是四元组。如下三次握手主要做了a和b两件事。
如下服务端先进入listen状态,如nginx的话会监听某个端口(如web服务就是80端口),客户端发送请求前会创建一个数据结构
(下面黄色)用来存储要发送的端口号等,客户端报文一发出去,客户端立马进入syn-sent状态,服务端收到syn(Synchronous number,同步序列号)报文时也会在本地创建一个对应的数据结构
。
客户端可以发送很多TCP报文,每个报文都有自己的随机生成算法生成自己的序列号,所以x+1是对x这个报文的响应。建立连接会消耗非常多系统资源(create tcb…),所以不用时要关闭(四次挥手)。中间SYN和ACK可以合在一起节省流量,也可以拆分开。
下面通过实验看三次握手怎么进行的:
如下nc命令会发一个TCP三次握手请求,输入服务器地址和端口。
如下查看tcp连接状态,-t参数查看当前tcp连接状态,-p显示进程,-n数字型显示ip和端口。如下就是win系统和linux系统建立的连接。
如下是tcp四次挥手,关闭连接(客户端或服务端都可以直接关,全双工),主动方会进入time_wait状态
,没有2MSL(报文一个来回时间)
立马关闭会造成第一(服)个
问题:ACK j+1这个报文丢失,服务端没收到ACK会不断重发FIN报文,服务端资源没法释放。第二(客)个
问题:关闭连接意味着资源被释放了,那么端口号被其他进程使用,报文到来时根据tcp的四元组恰好碰到刚释放掉那个连接,造成混乱。
Hash散列算法(应用于哈希表和摘要密码学),是把任意长度的输入通过特定的算法变换成固定长度的输出,输出的值就是hash值。这个特定的算法就叫hash算法,hash算法并不是一个固定不变的算法。只要是能达到这个目的的算法都可以说hash算法
。例如MD5,SHA,String.hashcode()都是hash算法。
不同的输入可能会得出相同的hash值,那么这种现象称为hash碰撞
,无论是采用那种hash算法,hash碰撞都是不可避免的,我们只能通过改进hash算法,把出现碰撞的概率降低。hash英语中的意思是剁碎的食物,反应在计算机领域大概就是把任意数据切割打碎,输出固定长度的数据。
先解AES,再用AES解image。
HTTPS利用摘要(也叫hash散列,用于校验信息完整性,确保文件没被修改)
和加密(对称【一个密钥】和非对称【2个密钥】)
算法完成加密通道。
如下用到的公私钥都存在Server端。
线程(操作系统级别概念)是cpu调度的最小单位
,cpu并不在意是哪个进程,cpu就是轮换着线程来运行并不需要知道这个线程是属于哪个进程的。左边单核cpu(不是单线程),3个线程(任务都是读取文件)交叉
运行完。
通过以下两点大大提高了cpu利用率,因而线程想提高效率和io密切相关。
1.
DMA过程中cpu一段时间不被线程阻塞。2.
DMA进行数据读取时可复用,因为cpu的总线程具有多条线路,所以DMA可充分利用这些线路,实现并行读取这些文件。
多线程需调用系统底层API才能开辟,在多线程开辟过程中浪费时间,并且在线程运行中上下文切换部分(左边切换多次,右边切换三次)有用户态和内核态
转换耗,效率浪费在cpu切换时间点上。所以服务端连接的客户端不活跃多(即io次数少)
,考虑单线程(io多路复用或nio)
或协程
。上面的1,2,3线程都有io,所以多线程效率高。
如何利用cpu资源?os给了我们两种抽象即进程和线程
。进程是系统资源分配,调度和管理的最小单位,比如去任务管理器查看使用内存时是看的哪个进程或哪个程序使用了多少内存而不是哪个线程,如果是哪个线程根本不知道是哪个程序里的线程,没法管理。一个进程的内存空间是一套完整的虚拟内存地址空间,这个进程中所有线程都共享这一套地址空间
。如下线程的5种状态,只有运行中
是占用cpu资源的。
线程执行有性能损耗,这些损耗来自线程的创建销毁和切换,线程本质向cpu申请计算资源,用户态转内核态
。
协程是用户自定义线程但与os的线程不同,协程不进入内核态
。自己创建一套API,协程利用线程资源。
如下程序一直没结束即while(a)这线程没结束:一个线程对a写了false,但是对另一个线程并不可见。
如下第一个core为主线程,第二个core为开辟的线程。
如上线程2不能立即读到线程1写后的最新变量值(线程1写,线程2读),多线程不可见性
。如何解决多线程不可见性:加volatile关键字使a在主存和localcache间强制刷新一致。
如果线程1和2都进行基于读的变量再对读的变量再进行写,最典型操作i++,T1和T2都进行i++操作。
一开始i=0,经过两个线程两次i++操作结果变成了1,这显然是不对的,并且这种情况下不能用volatile保证这样操作的正确性(两个线程既有读操作,又有基于读操作的写操作,可见性只保证一个线程写另一个线程读是正确的
,这里可见性不适用)。
现在想做的是将读操作和写操作合为一步,要么同时发生要么同时不发生(原子性)。在保证原子性同时一定以保证可见性为前提
(不是并列关系,AtomicInteger类里本质上就是volatile),本身不可见的话没办法保证原子性。
也可用synchronized同步关键字来保证原子性发生,同步关键字同一时间只有一个线程进入代码段。
volatile
可见性关键字最轻量级(保证一个线程写,一个线程读能读到最新的值),AtomicInteger
(保证既有读操作又有写操作如i++这种场景下能保证操作的原子性)基于volatile,synchronized
最重量级(能保证整个代码块中所有操作都是原子性的)。多线程情况下需要自增请使用Atomicxxx类来实现
。
内存,cpu,io
是编程中三个最重要的点。南桥(桥就是连接)连接带宽要求低的设备如是一些鼠标键盘硬盘usb设备等。北桥(集成到了cpu内部)负责带宽比较高的设备如pcie显卡,pcie硬盘,内存RAM需高速访问。如下是cpu常见参数,8核16线程(超线程)。
系统架构
=处理器指令集
,如下常见的6种指令集,X86_64基于X86,ARM不是其他嵌入式类,cortex A系列等。
2个物理cpu,1个物理cpu有38个逻辑核【76个线程/频率/处理单元processor)】。CPU就intel和amd。CPU(S):所有cpu的总逻辑核数。socket:物理cpu数量。top -d 1。
应对并发:1.
动静分离,cdn加速资源。2.
水平扩展,nginx集群。3.
微服务化,多用多分配资源。4.
缓存redis减少io寻找。5.
队列,秒杀系统采用。
如下A,B。。都是客户端,方框是服务端。首先想到应对并发,写一个多线程程序,每个传上来的请求都是一个线程,现在很多rpc框架用了这种方式,多线程存在弊端:cpu上下文切换,因而多线程不是最好的解决方案,转回单线程。如下while(1)…for…就是单线程。
while(1)中FD_ZERO将rset初始化0,用FD_SET将有数据的fd插个旗子,并赋给reset。
epoll_wait和前面select和poll不一样,有返回值。最后只遍历nfds,不需要轮询,时间复杂度为O(1)。epoll解决select的4个缺点。redis,nginx,javaNIO/AIO都用的是epoll,多路io复用借助了硬件上优势DMA。
IO模型(BIO/NIO/AIO):阻塞:发起io读取数据的线程中函数不能返回。同步:拿到io读取完的数据之后,对数据的处理是在接收数据线程的上下文后紧接着处理。异步:回调函数中进行数据处理
如上看出java比C语言系统调用多的多,因为java要启动jvm虚拟机,jvm要读jdk的lib库等很多操作。如上并没有发现open…xml操作,因为java程序主要启动jvm进程,jvm进程可能又起了很多线程去真正运行main函数,所以加-f。
io总线 (包括PCIE总线)
最常见的USB(通用串行总线),南桥PCH里有一个DMA控制器,PCH接不太占用带宽的设备如USB/硬盘/网卡/声卡。
Nodejs是单线程,但在读文件时,文件还没读完却可以执行下面几行程序,文件读完后触发一个回调。因为单线程按理来说cpu直接读磁盘中文件的话,应该一直读取这文件,读完前不能进行其他操作,它怎么做到执行其他操作的呢?需要有硬件支持即DMA,读文件操作是非常机械劳动,cpu资源宝贵不能干这种活,下面xxx是内存地址
。
读写文件和申请内存
是用户态转内核态的两个例子。malloc的两种实现方式brk和mmap,两者只选一种。brk和mmap申请的都是虚拟内存,不是物理内存,想真拿到物理内存空间还要第一次访问时发现虚拟内存地址未映射到物理内存地址
,于是促发一个缺页中断(也叫缺页异常,os中异常和中断有很多类似地方)
。C语言是malloc,而java和c++中new对象
申请内存空间,也是经过这么过程。
查看linux内核中有多少系统调用:man syscalls如下。第四类信息相关,如获取cpu信息等。管道pipe是进程间通信。open,read,write是文件相关,同时也是对磁盘操作,也可归到设备这类。mmap是文件和内存的映射,mmap申请内存也是对磁盘设备操作,也可属于第三类。
逻辑地址(逻辑/虚拟/进程内存)
:程序自身看到的内存地址空间,是抽象地址。逻辑地址需映射到物理内存
才能完成对内存操作,那为什么程序操作是虚拟的逻辑地址,不能直接操作物理地址即对内存条操作?因为程序是写死的(操作的地址是固定的),而硬件内存条哪些地址被占用了一直变化,因为os是多进程的,当前进程需要操作的地址,其他进程在使用,这样不能使用这块地址了,所以说除非是单进程机器,否则为了进程安全
必须做出逻辑地址和物理地址的映射。
所以必须要有逻辑地址,必须要有映射。如何映射?如下固定偏移量映射:程序1的偏移量(初始位置)是0
,程序2的偏移量(初始位置)200
:如果程序1操作的逻辑地址是100,那么映射的物理地址也100(因为偏移量0)
;如果程序2操作的逻辑地址是50,映射到物理内存250(因为偏移量200)
。
如上看上去简单高效,但存在两个缺陷,第一个缺陷
:程序使用的内存无法计算的,随时间推移,进程使用的内存不断变化。这里我们说程序1使用200的内存,这种说法本身不太对的,因为我们没法去限定一个程序使用的内存大小,当然你可以说我估算了这程序使用的最大内存就是200,但这也代表整个200的一段内存中,程序使用的内存绝大多数时间都小于200。蓝色区域
中内存使用率并不高,其中存在很多没有利用起来的内存,我们把没利用起来的内存叫内碎片
。
第二个缺陷
:当程序运行完,内存被释放,比如程序1执行完后,0-200这块地址被释放出来了,此时程序3
使用了内存大小是201,这时程序3没法直接使用0-200这段内存了,假设很长一段时间内都没有占用200以内的内存这样的程序被创建,那么0-200一直被闲置,称这段内存为外碎片
。
将内存空间包括逻辑内存(左,页,地址连续)和物理内存(右,帧,地址不连续)
都进行切分,分成固定大小很多片,每一片称它为页。页到帧的映射
需要有个表来维系,这个表就叫页表即pagetable(pagetable不仅存了页号帧号,还存了当前这一页读写权限等等)。
页表
是每个进程都需要维护的,因为每个进程映射关系是互相独立的,所以不能共用映射表,每个进程有自己的pagetable。
32位os物理地址有2的32次方个即4000000000个地址(内存的一个地址里住着一字节Byte数据)即4GB
。32位程序以为自己拥有4GB内存,如两个32位程序,一个使用了2GB内存,另一个使用了3GB内存。但整个物理机只有4GB内存,造成虚拟地址可能比物理地址大,多出来部分可将虚拟地址的页映射到磁盘上。但映射到磁盘上导致下一次读
映射到磁盘上这一页内存时会触发一个缺页中断进入到内核态
,整个会产生一个大(major)错误
。linux下这磁盘部分又叫swapping
(与物理帧交换)。
分页小结:1.
分页使得每个程序都有很大的逻辑地址空间,通过映射磁盘和高效置换算法,使得内存无限大。
2.
分页使不同进程的内存隔离,保证了安全(不同进程各自维系了一个页表,只要页表中value即帧号这一栏不互相冲突,保证不同程序间内存隔离,保证安全性)。
3.
分页降低了内存碎片问题。
4.
缺点:页表存在我们主存中即存在内存中,如果我们要对某一个内存进行访问的话其实要读取两次内存
。因为先读取页表,从页表中拿到对应帧号,再拿帧号去内存中再查询一遍,对内存操作有两次读取【时间上要优化(将页表集成到cpu中的mmu硬件中称为快表,先查快表,查不到查页表)】。页表存在主存中占空间【空间上要优化(多级页表)】。
不同程序共享内核(kernel space)这1G空间,共享内存如Libraries函数库(so/dll文件)。
函数参数传递是复制传递即将实参复制一份给形参,因而形参的改变不会影响实参,但是如果参数是指针,那么复制的就是地址的值,通过星号下钻该地址就能修改地址内变量的值。我们把包括结构体在内的基础类型传参称为值传递
,而指针的传递称为引用传递
。
如下从main函数开始。
像c、golang是有指针和地址概念,而对于java、python、js等语言,对象的变量名本身就是个指针,因而传递对象就是引用传递。
父函数main()
在子函数f()
入栈之前会留出子函数f()返回值的内存空间,子函数返回值与父函数的入参
(这里父函数没有入参,打比方)一样是复制传递。
但是返回值如果是指针,可能会导致父函数调用该地址内变量时,子函数已经出栈,导致访问错误
。同情况也会出现在全局变量属性赋值时。这些都属于变量逃逸,像go、rust、java会自动进行逃逸分析,将逃逸的变量创建到所有函数共享的全局空间中,这就是堆(heap)。
堆内存的释放复杂,像c语言需手动释放,忘记或多次释放都会带来问题,而像java、golang、js、python等是有gc机制能定期自动释放,这会导致性能下降,无法胜任系统级别和硬件编程。
上下文切换
:P1进程还没运行完,其中一些信息如程序计数器、变量、程序运行到哪了即context(执行环境,上下文)保存到内核的栈,P2再加载进来运行。调度算法
:FIFO
(first in first out)非抢占式,谁先来谁先被调度,缺点需等待长时间的进程执行完,其他进程才能分到CPU。STF
(short time first)非抢占式,谁的进程时间短,谁先被调度,需要同时到达,不然缺点是长时间进程先到达,后面进程也要等待。STCF
(short time complete first)抢占式,谁时间短先被执行,再切回去执行时间长的,缺点是如1000ms的进程一直被抢占导致3000ms才执行完,响应时间太长。RR
轮询即cpu将1s分成很多时间片,把这些时间片分给每一个进程,1s多个进程是在并行的。
一个cpu多个进程,这些进程放在哪?用什么数据结构存储?进程队列:全局队列(多个cpu一个队列)和局部队列(每个cpu有自己队列)。进程优先级:不是每一个进程都有相同优先级,如下不是ps aux(显示STAT列就是进程状态)。
linux调度器:O(n)调度器
即遍历进程队列,找到优先级最高的进程。O(1)调度器
即优先级0-139映射成140个格子即0或1的bitmap,cpu找到1的格子去执行进程队列(链表或其他数据结构),和hashmap一样。
如下free206M和available1.6G
能用的是哪个?是1.6G
。used包含shared,free是真正的空闲,没有任何东西在使用的大小。文件磁盘缓存指读过的文件暂时帮我们缓存到内存中
下次再读的时候直接从内存中拿出来就能加速对文件读写操作。比如说现在free的空间只有206M,我有个程序要用1G内存,能用吗?能
,buffer/cache这边1.6G中有800M扔出去释放掉+206M=1G给程序。
buff/cache中间为什么有个/,较早内核中free-h看分buffcache
(以磁盘扇区【硬件扇区】为单位直接对磁盘缓存)和pagecache
(以页【文件系统】为单位对磁盘文件缓存)两项。两项有重复的地方,文件本质也是磁盘。
C语言中有sbrk库函数是对brk的一个封装,如下brk申请内存,内存是连续的,并不是说在堆空间随便找内存就把空间给你。
当前我们对第5,6,7,8四个字节赋int值123。只有第一个字节通brk申请出,却给第5-8字节赋值,这样会不会报错呢?不会,主要原因是在上节讲到的操系内存的分页管理所导致的,也就是说brk申请内存申请最小单位为1页,一般系统中页大小4k,所以brk看似申请1字节其实申请了一页(4096个字节)
,所以第5-8字节也属于4096字节里,也是当前进程所能支配的内存,所以不报错。
如下+号相当于向后移动1024个int,如下报段错误。
mmap还有直接将磁盘文件映射到内存作用,类似read,不是malloc。
如下触发大错误因为对文件的映射,将文件映射到内存,也是惰性的,这文件没有直接读到内存里,而是当真正需读文件里内容
时才会映射到内存里。第一次触发是上面for循环里打印文件内容时到内存中读,发现这一页在查页表时对应是磁盘就触发一个缺页错误,对应是磁盘,触发majflt
,将磁盘内容加载到内存中,之后就是一些小错误了。-p:指定进程号。-r:显示各个进程的内存使用统计。grep -v grep过滤掉包含grep的行。最后1是输出1次信息。
read系统调用进入内核态,内核态将文件内容加载到内核空间(如下kernel space),内核空间给它复制到用户空间,再从内核态切换到用户态,然后用户的程序就可读到文件的内容了,有个文件-内核空间-用户空间
周转过程。
mmap直接将文件进行了映射,一开始在页表中填充的是磁盘disk即FILE文件,一开始mmap是惰性的直接对应磁盘文件,真正读取时触发缺页
将文件加载到内存。
mmap这么牛干嘛还用read函数?mmap虽减少了内核空间到用户空间拷贝(0拷贝)
,但mmap没法利用前面讲的buffer/cache对文件缓冲这么一块空间,而且mmap第一次触发的缺页异常耗时不一定比read少。
如下只有第一个线程安全
。
Boolean、Byte
的所有对象,都是预先创建好的(类加载的时候)。Character、Short、Integer、Long
是-128~127的对象是预先创建好的(Character没有负数)。
现象:
new Integer(1)则是从创建好的缓存中,直接拿出,因而是同一个:错
,new的是新的对象,原因:
为了节省内存,这些数字使用概率很高,早就创建好,之后都用同一个,是提高效率的做法。
如下是对象类型。func1结束后栈上清空了,但是堆上怎么清空呢?引出GC。main函数静态存方法区。java基础数据类型都是值类型,指针也是值类型,因而直接存到内存,不是存地址去寻址。
如下引用类型传地址,和上面形参a不同。
VMware15和CentOS6.9:
链接:https://pan.baidu.com/s/1HV6WqUTAwlOSjWkLXVrCRw ,提取码:1x8e 。VMware15【CG392-4PX5J-H816Z-HYZNG-PQRG2】直接下一步安装。右击图标属性-
兼容性-
更改所有用户的设置-
勾上以管理员身份运行此程序。仅主机模式(Host-only)网卡默认192.168.56…,NAT网卡默认10.0.2…。
1.点击创建新的虚拟机
:自定义(高级)-
稍后安装操作系统-
linux-
版本centos6 64位-
D:\vm.\cent0s6-
使用仅主机模式网络-
将虚拟磁盘存储为单个文件(动态分配20G硬盘)。
2.编辑虚拟机设置
:选中网络适配器添加-
CD/DVD使用CentOS6的iso文件(开启此虚拟机出问题:控制面板-卸载程序-Microsoft Visual C++的两个x64和x86文件右击卸载,不点卸载,点修复再重启计算机)。
3.开启虚拟机安装操系
:默认第一个install,方向键选择红色的Skip(跳过)回车。两个网卡注意要配置网络
自动连接。使用所有空间-
将修改写入磁盘-
Basic Server。鼠标退出虚拟机用ctrl+alt,将一个小窗口移至虚拟机前就可以用QQ截图。
NAT网络模式
:多台虚拟机和宿主组成一个小局域网,之间都可互相通信,虚拟机也可访问外网,如搭建hadoop 集群,分布式服务。桥接网络模式
:只需要一台虚拟机可以和宿主互通,并可以访问外网。如果不需要锁定静态IP(如hadoop不锁定IP很麻烦),那跳过下面步骤。如下同理本地回环配置文件 /etc/sysconfig/network-scriptis/ifcfg-lo。ifdown/ifup关闭/启动etho网卡。
ssh客户端软件SecureCRT8.5
:链接:https://pan.baidu.com/s/1Y74YVz2ysQ3rFjGjnthb1Q ,提取码:l8gb 。解压后如下所示:
右击以管理员身份运行上图的scrt…exe文件,安装完后桌面出现图标先不要点击运行,将上图注册机文件夹
里的keygen.exe复制到下图默认安装路径文件夹中,并右击以管理员
打开keygen.exe出现如下图黄色窗口。
在如下License中不用去除中括号,写入SecureCRT.exe中去除,Patch连到SecureCRT.exe和LicenseHelper.exe。
第一次打开下图进行填写注册信息按照上图黄色窗口对应写入,上面patch到就是下面这个.exe文件。
配置会话的属性,在会话上点击属标右键,选择Properties的Terminal。每30秒向服务器发送一次心跳。
分清Hostname,Username,Name
。
修改Centos的字符集,增加对中文的支持: 登录服务器,输入 su – root 回车后再输入密码,切换到root用户(超级用户,有的远程服务器没权限)。修改字符集:echo LANG="zh_CN.gbk" > /etc/sysconfig/i18n
。export LANG="en_US";export LANGUAGE="en_US";export LC_ALL="en_US";
。
修改时区为亚州上海时间,在root下执行并输入y:
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
。date (不是data)查看系统时间:date -s
修改时间,date -s 2019/07/31,date -s 10:24:00 。date -s ‘2020-01-01 11:11:11’。
win下任务管理器中explorer.exe进程(是Windows程序管理器或文件资源管理器,它用于管理Windows图形壳,包括桌面和文件管理。删除该程序会导致Windows图形界面无法使用)kill掉就不用重启安装软件时,cmd重新explorer.exe执行。
/usr/bin
里是系统预装的可执行程序,会随着系统升级而改变。/usr/local/bin
是给用户放置自己的可执行程序的地方,推荐放在这里,不会被系统升级而覆盖同名文件。/etc
存放的是管理文件用的相关配置文件,比较重要的 /etc/rc,用户信息文件/etc/passwd:
/usr
(Unix System Resource,不是User)放的是应用程序和文件,如果在安装软件的时候,选择默认安装的位置,通常就会默认在这个位置:
/var
用来存放系统运行的日志文件:
/dev
包含所有的设备文件。/proc
是虚拟目录,主要存放的是内存的映射,通过这个目录和内核的数据结构打交道比如修改内核参数,获取进程的相关信息:
/boot
存放了启动 Linux 的核心文件,包含镜像文件和链接文件,破坏后系统基本上就不能启动。/mnt
是移动设备文件系统的挂点。
/bin/sbin
当你装相关的软件或者安装包后,很多时候都会链接在这个目录下面,另外这里也存放了平时我们用的各种shell命令如 cp,ls,dd等。对于sbin,这里的s是super 的意思,意味着需要超级用户才能执行的命令。常见磁盘分区fdisk,创建文件系统的mkfs就在这里。
/lib
开发过程中,共享库文件等很多放在这里,这个目录会包含引导进程所需要的静态库文件,对于用户和系统来说“必需”的库(二进制文件)。/usr/lib一般存放的只是对用户和系统来说“不是必需的”库(二进制文件)。
/lost+found
保存丢失的文件。什么意思,如果我们不恰当的关机操作,可能导致一些文件丢失,这些丢失的临时文件可能就会存放在这里。当重新启动的时候,引导程序就会运行 fsck程序并发现这个文件。
tar只是打包,并不压缩或解压。
-c: 建立压缩档案
-x:解压
-t:查看内容
-j, --bzip2
-z, --gzip, --gunzip --ungzip
-v, --verbose 详细列出处理过的文件
-f, --file ARCHIVE 使用存档文件或设备存档(指定存档文件),切记,这个参数是最后一个参数,后面只能接档案名。
tar命令必须和-f命令连用, 在选项f之后的文件档名是自己取的,习惯用.tar 来作为辨识。 如果加z选项,则以.tar.gz或.tgz来代表gzip压缩过的tar包。
如下文件类型p是指管道文件。
查看几个文件的内容差异:diff -c file1 file2
。
将a.txt每5行分割为一个文件:split -5 a.txt
。
nohup java -jar a.jar & ( jobs命令查看这个后台任务的编号 )。
netstat -ntlp查看运行程序的端口。
kill -9 %1(这个1就是jobs命令的1编号)。
tcpdump数据包获取,nc网络调试,vmstat进程信息和内存使用。
内存条:dmidecode |grep -P -A5 “Memory\s+Device”|grep Size|grep -v Range,ps -ef | grep memtester | grep -v grep | cut -c 9-16 | xargs kill -9。
/etc/rc.local,Crontab,Systemd。on failure是执行失败重启,成功就退出。on shot是无论怎么样就只执行一次。crontab -e:@reboot sleep 300 && /home/wwwjobs/clean-static-cache.sh
(在启动 5 分钟后运行指定脚本)。
update-rc.d管理: mv new_service.sh /etc/init.d/,cd /etc/init.d/,sudo update-rc.d new_service.sh defaults 90(90表明一个优先级,越高表示执行的越晚)。在/etc/init.d目录下的可执行程序的优先级会高于/etc/systemd/system/下的.service文件(如果同时存在)。
serivice xxxx start|stop|restart
相当于是对/etc/init.d下的xxxx的封装,相当于是一个管理命令,实际执行的是/etc/init.d下的可执行程序。如果/etc/init.d下没有该服务的可执行程序,则使用.service文件。
Linux系统启动过程:ubuntu开机引导文件/etc/default/grub,如下cpu发出mov指令告诉内存把300那地方的东西取出来放到777显存地址上。
内核(=操作系统)
怎样启动第一个应用程序:1.
open(dev/console,printf/scanf/err)等设备文件,2.
run_init_process函数如下。
init进程
:是加载内核镜像文件后第一个进程,是Linux的根进程,所有的系统进程都是它的子进程。运行级别:0
(init0关机),1
(单用户模式,只允许root用户对系统维护),2到5
(多用户模式,3为字符界面,5为图形界面),6
(init6重启)。
busybox
:ls这些命令相当于应用程序(可执行二进制文件),成千上百个命令,源码找来再编译,显然很费事。busybox是这些命令的组合。想确定init进程做了什么事就要看busybox的源码。在kernel挂载根文件系统后,运行的第一个程序是根目录下的linuxrc,实际是一个指向/bin/busybox的链接, 也就是说系统起来后运行的第一个程序是busybox本身。busybox首先解析/etc/inittab
这个进行初始化的配置文件, 然后调用/etc/init.d/rcS
, 最后是执行/etc/profile
(/etc/profile.d , ~/.bashrc , ~/.bash_file
)。
如下将逗号
替换为空格
。s表示替换,g表示全局,即行中所有匹配项都被替换。sed -i ‘s/sys_led/sys_url/g’ /home/sysfs1/s3ip_sysfs_frame/sysurl_sysfs.c。
^匹配行首,$匹配行尾,如下d删除空行或只包含空格的行(因为行首行尾中间为空)。
sed常用于管道过滤,如下把x替换成y。
如下-r打开扩展正则,将逗号换成TAB。
如下按逗号分隔并打印分割后的第三列和第四列。
如下BEGIN指定了处理文本之前需要执行的操作。END指定了处理完所有行之后所需要执行的操作。
如下按逗号分隔并打印最后一列内容。
对比上面,如下一行一行读取并打印,列数>0打印。
如下-c统计个数。
2是个数,不是序号。
如下.中的\是转义符,.168
这样重复3次(注意168前面有.
,不是168,而是数字)。
1.插入
:以下命令都在命令行模式下。
2.上下左右
:j下,k上,h左,l右。
3.翻页
:
4.跳光标
:
5.删字符和行
:
6.复制粘贴
:
7.替换
:
8.撤,接
:
9.行,找
:
10.重复,大小写
:
11.存盘
:
12.列操作
::%d删除文件整个内容。
^
:整段字符串开头。$
:整段字符串结束。^[]$
:中括号内部可匹配一个字符。
如下自己输入1,1蓝色阴影则匹配。
\d
:相等于[0- 9]
,中括号里是什么或什么。
\D
:相等于[^0- 9]
,除了0到9外的任意字符。如下匹配 数字\d 或
数字外任意字符\D,也就是匹配任意字符。
\w
:字母,数字或下划线,常用于互联网用户名的命名上。
如下两个等价。
如下两个等价。
如下两个等价。
如下是邮箱的匹配规则。
组group:如上只想获取@前面的用户名,上面中括号,大括号都出现了,就差小括号
如下1就是组1。
如下first就是组名,右边是js语法,groups显示underfined因为没命名。
linux文件系统中iNode用来存储文件原数据信息
,不存储文件内容
,原数据信息
包括:
类型
:这个文件是个目录还是普通文件。
拥有者
:这个文件是owner还是group owner。
时间
:ctime
:上次inode变动时间。atime
:上次访问时间。mtime
:上次文件内容发生变动时间。
连接数
:有多少文件名同时指向inode。一个文件名只对应一个inode
,但一个inode可能被多个文件名同时指向。
文件内容所在的位置
:文件真正内容所在磁盘块的标号。
1.
文件系统fs在格式化好后,inode以什么样格式存储的呢?整个inode以数组形式存储,每个元素是一个inode
,每个inode大小根据当前文件系统以及整个磁盘大小,inode会有一个固定128或256字节
大小。
2.
除了inode数组,fs初始化好后还会生成一个Map映射关系表
(存储filename和inode index)。现在要读取/ect/1.txt,整个过程怎么样?
先根据文件名到Map中找到inode index,找到下标为假如是3的inode后拿出来如下图左边整个框。当前在读取/ect/1.txt,所以查看是否有读权限,如果有读权限就继续往下,找到文件内容
所在位置(磁盘上块的下标)。
文件内容在磁盘中存储区域如下:以块
进行分隔,每个块大小
也是根据当前fs和整个磁盘大小决定,并不是一个特定大小【扇区在磁盘生产时
有多少个扇区,每个大小是定的,早期扇区512byte,现在4k】。文件系统fs在文件访问过程中不可能直接使用扇区,扇区是硬件
的概念,所以抽象出一个概念:fs角度去看最小文件存储单元就是块
,一个块可以有一个或多个扇区组成(2的幂次方即1,2,4…个扇区)。
一个块采用多少扇区也是有权衡的,比如一个块有好几兆,存一个1k文件也要占一个文件块,造成磁盘空间浪费。块选择过小的话也不好,如果一个块大小1bit,导致一个文件假如是1kb,它所在的块由1千个块组成
,在inode中存储文件内容所在位置这个字段时候造成存1千个块信息(1千个块下标),一个inode(存1千个块下标)不可能128/256字节大小了
,一个inode会很大,进而导致inode数组会很大,整个inode区大,这样导致磁盘损耗大量空间存储inode信息,较少的空间存储真正文件内容。
即使进行权衡,目前存在问题,如经常听到inode用完了即inode数组初始化大小用完了
,声明完数组大小后不能增加或减少了。inode数组用完了即使磁盘还有额外空间也不能存储文件了,常见特别零碎文件数量又特别多占据磁盘大量inode导致整个inode用完。如早期docker采用overlay文件存储格式导致镜像的碎文件很多,导致inode用尽这样问题,后面采用overlay2文件存储格式一定程度上解决了这问题。
查看linux系统中inode数组以及每个文件所对应inode标号:df -i(inode)
,查看当前文件夹下文件所在的inode标号是什么ls -il
。访问1.txt先查文件名和inode标号映射即Map,1.txt能找到270306
这个标号。根据这个标号到1183200这个数组中拿取第270306个
标号的inode。根据这个inode信息查看权限,最终找到1.txt在磁盘中存储位置,最后把这些磁盘块进行读取,最终读取到1.txt这个文件。
如果是一块移动硬盘,在其他设备上创建了文件 把这个移动硬盘拿到linux上面,有没有inode呢?
1.
os可以支持多种fs。2.
inode是在ext2/3/4等linux支持的文件系统(fs)有的。所以移动硬盘看是什么文件系统
了,如果是ntfs或者exfat、fat32等等就是另一种访问形式了,inode其实是文件系统里的概念,而不是linux的概念。
过滤一下看文件大小:-print0将如上三行打印为1行并用null即‘\0’隔开,再用xargs -0即用‘\0’再分开(原因是默认管道到下一个里面空格会出错)。
如上/是整个系统搜索慢,如下是当前路径搜索快。
日志文件没清空非常大,要找到删除,如下找系统中大文件,超过10M。
如下查找文件夹,文件夹有相应名字或大小属性。
如下基于修改时间,time是天。-1
:今天一天之内。1
:1天前这一天。+1
:1天前。
如下指定最大文件深度。
如下是find指令总结。
echo “aa” >> 1.txt,追加内容。cat -n 1.txt(行号:number)。grep “a” 1.txt。
如下*
可换成 * .txt
。
-r:递归子路径,-n:显示行号。-i:忽略大小写。
如下用于java日志文件非常大,要grep出某个异常如ioexception,且需要打印出exception下面几行看什么出了错。
cat的文件非常大,非常占用cpu和内存,这时候可以每次读取一小部分。
如下通过空格往后翻页。
如下指定从第四行开始读。
如下查看前后10行。
如下打印文件最后两行,tail -f 阻塞监控。
df -h查看磁盘使用,占用率太高就需要使用前面find,grep指令并进行删除。
如上找出占空间的文件夹再去里面找。
题目:输出当前路径及当前路径子路径下所有.txt文件,要求大小超过1M,并且按照从大到小
顺序进行排序输出前10个?
先通过find . -name '*.txt' -size +1M -type f
查看是否有大于1M的txt文件,没有的话就不用继续了。
再通过find . -name '*.txt' -size +1M -type f -print0|xargs -0 du -m|sort -nr|head -10
。