进程、线程、协程比较
-
进程
是拥有系统资源的(基本单位),拥有独立的地址空间资源,上下文切换开销大(回收分配资源),但相对稳定安全(一个进程的崩溃不会影响其他进程)。(一个独立运行程序)
资源:静态资源 ,如地址空间、打开的文件句柄集,文件系统状态,信号等。 -
线程
是一个进程的实体,是CPU调度和分配的基本单位,不拥有系统资源,隶属于同一进程的线程共享进程的全部资源,上下文切换开销小,不够稳定易丢失数据(一个线程死掉等于所有线程死掉)。
资源:动态资源(运行相关)、运行栈、调度相关控制信息、待处理信号集等。亲缘性: 进程/线程只在某个CPU上运行(多核cpu),使用亲缘性防止进程/线程在CPU核上频繁切换,避免CPU的cache失效,从而降低程序性能。
-
协程
用户态轻量级线程(程序控制、不被操作系统控制)。协程可中断,执行其他协程后再返回来继续执行。
优势
1.一个线程内执行,切换无开销,程序控制,效率高,
2.共享资源的控制不需要加锁,状态判断。
协程调度切换时先进性上下文保存,在切回来时重新恢复,几乎没内核开销,上下文切换快。异步机制,可保留上次调用时的状态,每次重入时相当时进入上一次调用的状态。
//在js中的实现
function ascynjob(){//一个协程
var y=yield read();//交出执行权
return y;
}
function* gen(x){
try{
var y=yield x+2;
}catch(e){
console.log();
}
return y;
}
var g=gen(1);
g.next(); //分阶段执行
g.throw()//错误抛出
进程间通讯方式
管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket
1.管道 - 半双工方式,数据单向流动
- 无名管道(pipe) - 允许有亲缘关系的进程通信
- 有名管道(FIFO) - 允许非亲缘关系的进程通信
2.消息队列
存放于内核中的消息链表,可被多个进程共享,使用消息队列是为了通过异步处理提高系统性能和削峰、降低系统耦合性。
3.信号量
通过信号机制来进行进程间的资源请求、释放、互锁
4.共享存储
是UNIX系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,
僵尸进程 孤儿进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
为什么不推荐使用递归?
递归使用消耗大量的内存空间和指令空间而且如果层数过深有栈溢出的风险
虚拟内存
虚拟内存是计算机内存管理的一种技术,它使得计算机认为它拥有连续可用的内存(即一个连续完整的地址空间),而实际上它通常是被分隔的多个物理内存碎片,还有部分在外部磁盘,需要时可进行数据交换。
为了解决问题
内存空间利用率、内存读写效率、安全性、进程间的安全问题
内存泄漏 内存溢出?
内存泄露 :是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄漏,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
(C++)
主要原因:是在使用new或malloc动态分配堆上的内存空间,而并未使用delete或free及时释放掉内存。
我们知道了内存泄漏的原因而内存溢出则有可能是因为我们我们多次内存泄漏堆积后的后果则变成了内存溢出
内存溢出: 指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出,简单来说就是自己所需要使用的空间比我们拥有的内存大内存不够使用所造成的内存溢出
堆内存和栈内存的区别?
一般情况下程序存放在Rom(只读内存,比如硬盘)或Flash中,运行时需要拷到RAM(随机存储器RAM)中执行,RAM会分别存储不同的信息,如下图所示:内存分配中的堆栈
image
内存中栈区 栈为高地址向低地址增长,向下增长,占中分配局部变量空间。
内存中堆为低地址 向上增长的用于 用户手动申请的内存空间(malloc、new)
区别:
1.申请方式
栈:系统自动分配,自动分配自动回收,生命周期在函数运行过程中。
堆:程序员手动申请、需要手动释放,否则会造成内存泄漏
2.空间申请响应
栈:只要栈的剩余空间大雨申请空间,系统自动分配,否则提示栈溢出。
堆:程序发出申请后,系统会查找内存空闲链表中符合要求的返回节点链表首地址(分配的堆大小有可能大于申请,则多的部分自动放回空闲链表)
3.申请效率比较
栈:系统自动分配速度快,无法控制。
堆:速度慢,容易产生碎片,操作方便。
4.申请大小限制
栈:连续内存区域,windows系统中最大容量预定好的,空间较小
堆:不连续,因为是链表,大小取决于有效的虚拟内存。
5.存储内容
栈:执行语句首地址,然后是参数从右到左入栈,然后是局部变量,出栈顺序正常出栈。
堆:头部存放堆的大小,内容自行设计
6.存取效率
栈快一些
一个程序从编译到执行的过程
main.cpp
预编译(处理预编译指令,宏替换)
main.i
编译(词法分析、语法语义分析及优化生成汇编文件)
main.S
汇编(汇编代码转为机器码文件)
main.o
链接
可执行文件
C++与C语言的区别在哪里?
首先,在思想上:
C++是面向对象的语言,而C语言是面向过程的结构化编程语言。
在语法上:
C++具有封装、继承、多态三种特性。
C++相比C,增加多许多类型安全的功能,比如强制类型转换。
C++支持范式编程,比如模板类、函数模板等。
sizeof 指针
在32位系统里占有4个字节
野指针
就是指向一个已删除的对象或者未申请访问受限内存区域的指针。
引用和指针的区别和联系
指针为一个变量,指向一块内存,存储的是地址,可以为空,可以修改,自身有大小
引用为一个别名,实际上就是原来的对象属于同一个,不可为空,不可修改
常量指针、常量引用 最前面加const 表示指向的值/引用对象不能修改。
指针常量、引用常量 表示指针不能修改 且必须初始化,引用本身就是这样
定义和声明的区别
变量只能被定义一次但可以被多次声明
变量声明使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。
定义负责创建与名字关联的实体。
变量声明规定了变量的类型和名字,但定义还申请存储空间,也可能会为变量赋一个初始值。
如果只声明不定义 可加上extern关键字
static关键字作用、static修饰的变量、函数?
用static修饰的函数、变量,限定在本源码文件中,不能被本源码文件以外的代码文件调用,其他文件中可以定义相同名字的函数,不会发生冲突。
- 未经过初始化的全局/局部静态变量会被初始化为0,且被放置在静态存储区中
》 局部静态变量作用域仍为局部且不会随着函数的运行结束释放掉仍被保存再次调用仍然存在。全局静态变量作用域为定义处起到文件结尾。
数据共享
成员变量(实例变量)和静态变量(类变量)的区别
-
两个变量的生命周期不同
成员变量随对象的创建而存在,随对象被回收而释放
静态变量随类的加载而存在,随类的消失而消失 -
调用方式不同
成员变量只能被对象调用
静态变量还可以被类名调用 -
数据存储位置不同
成员变量–>堆内存的对象中,也叫对象的特有数据
静态变量–>方法区(共享数据区)的静态区,也叫对象的共享数据
修饰变量:
每个对象都共有的属性就可以设置为static,被修饰的成员被所有的对象共享,且可以直接用 类名.X静态成员 的方式调用
static优先于对象存在,因为static成员随类的加载就已经存在了 -
修饰方法:静态方法
静态方法只能访问静态成员(非静态既可以访问静态也可以访问非静态)
静态方法中不可以使用this或者super关键字(对象不存在) -
修饰代码块:静态代码块
随着类的加载而运行,而且只运行一次
作用:用于类的初始化
当类中的变量全部是静态的时候才用静态代码块去初始化变量
用static修饰的函数,限定在本源码文件中,不能被本源码文件以外的代码文件调用,其他文件中可以定义相同名字的函数,不会发生冲突。
extern、volatile关键字作用
extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
静态函数和虚函数区别
静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销。
字节序
计算机硬件的数据存储方式
大端字节序、小端字节序
对于数值 0x2211
大端字节序 :高位字节为0x22,低位为0x11,高位在前低位在后
小端字节序: 低位在前高位在后 即以0x1122形式存储
枚举类型的大小
struct结构体变量大小等于结构体中的各个成员变量所占内存大小总和,
union共用体变量大小等于共用体结构中占用内存最大的成员的内存大小
enum枚举变量只顾盯内存大小的别名,他的大小其实取决于定义该变量的数据类型决定的,如int、float :4 字节 char :1 字节 double、long long int: 8字节
union和struct的区别和联系
union中的各个成员共用一块内存,大小为union中所占空间最大的元素所占大小
struct中 大小为所有成员长度的总和
在任何同一时刻,union只存放一个成员,struct可存放所有成员
对union的不同成员赋值会对其他成员重写,而结构体中的赋值互不影响
struct的对齐方式
对齐的原因:为了提高计算机的数据读取效率,有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误
默认 32位机位4字节、64位为8字节对齐
- 未指定#pragma pack
a.第一个成员起始于0偏移处;
b.每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐;
c.结构体总长度必须为所有对齐参数的整数倍;
d.对于数组,可以拆开看做n个数组元素。
struct A{
int a;
char b;
short c;
};
struct B{
char b;
int a;
short c;
};
sizeof(A) = 8;sizeof(B) = 12。
a b c
A的内存布局:1111, 1*, 11
b a c
B的内存布局:1***, 1111, 11**
即 结构体内的每一个成员变量的地址起点需为其自身长度的整数倍,且整个结构题的长度应为结构体内最长成员的长度的整数倍。
- 指定#pragma pack (1,2,4,8)
则按照设定的pragma pack进行每个成员对齐
new和malloc的区别
new/delete 主要是用在类对象的申请和释放。申请的时候会调用构造器完成初始化,释放的时候,会调用析构器完成内存清理。new/delete是关键字,效率高于malloc和free。
1.new和delete是C++关键字,需要编译器支持;
malloc和free是库函数,需要头文件支持。
2.使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
- new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
- new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。 内存泄漏对于new和malloc都能检测出来,而new可以指明是哪个文件的哪一行,malloc确不可以。
vector 和list
vector拥有一段连续的内存,适用于高效的随机存取
list双向链表实现 内存空间不连续,适用于大量的插入删除
重载和重写(虚函数/多态)的区别
虚函数 virtual关键字 动态绑定的
重载参数、顺序、个数、内容不同 函数名相同,根据参数列表决定函数调用
重写是派生类中存在重新定义的函数,函数名、参数及返回类型必须相同,函数内容可不一样。且基类同名函数需为虚函数。调用子类同名函数时会执行子类的方法,体现了多态。即动态链接 在任意点根据所调用对象类型来调用子类函数,也叫做晚绑定。
静态链接即程序运行之前就已经被定义好为基类函数。
虚函数底层实现
虚函数表(V-table 虚函数的地址表)+虚表指针
为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针(vptr),这种数组成为虚函数表(virtual function table, vtbl),即,每个类使用一个虚函数表,每个类对象用一个虚表指针。
基类对象包含一个指针,该指针指向基类所有虚函数的地址表,派生类对象将包含一个指向独立地址表的指针,如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,如果派生类没有重新定义虚函数,该虚函数表将保存函数原始版本的地址
纯虚函数
将一个函数定义为纯虚函数,实际上是将这个类定义为抽象类,不能实例化对象。
纯虚函数通常没有定义体,但也完全可以拥有, 甚至可以显示调用。
析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
.so文件
linux下的动态链接库文件
网络部分
http 和https区别
应用层
1.HTTPS依靠(CA)证书(需要申请并交费)来验证服务器的身份,并为浏览器和服务器之间的通信加密。
2.HTTP 是不安全的 信息是明文传输、连接简单,而 HTTPS 是安全的 具有安全性的ssl加密传输
- HTTP 标准端口是 80 ,而 HTTPS 的标准端口是 443
4.在 OSI 网络模型中,HTTP 工作于应用层,而 HTTPS 工作在传输层
TLS是SSL的升级版,使用https,通过证书认证网站真实性,加密信息,数据完整性。
加密方式:对称加密(DES、AES、IDEA)非对称加密(RSA、DSA、DH)
http1.0 http1.1区别
区别在于1.0版本每次请求都回建立一个新的TCP连接,从1.1开始在一个TCP连接上进行多个请求和应答。
UDP TCP 区别
位于 运输层
udp包头
udp
udp面向无连接的 速度快、基于数据包,沟通简单 可进行广播、需要的资源少,容易丢包
适用于直播、实时游戏、物联网
tcp 面向连接 - 需要先建立连接
tcp
面向字节流、保证数据正确性、顺序
TCP报头中的
序号 使数据按序到达、
确认序号保证不丢包、累计确认、超时重传拥有流量控制(滑动窗口)
拥塞控制机制(拥塞窗口)
TCP三次握手/四次挥手
-
三次握手
首先服务端监听一个端口,
1.客户端发出请求syn=x
2.服务端响应发送ack=x+1 seq=y,
3.客户端再发送ack=y+1 seq=x+1,
握手开始通信 -
四次挥手
1.客户端发出连接释放报文,停止发送数据 seq=p
2.服务端接收到释放报文 返回ack=p+1,客户端等待第二个结束信号,因为服务端有的时候还有数据没有传输完成不能立刻结束
3.服务端再次发送连接释放报文,ack=p+1 seq=q
4.客户端接收到报文后发出确认 ack=q+1,服务端收到后close,客户端要等2MSL(一个片段在网络中的存活时间,两个是一收一发)等待测小TCB后进入close。
设置2MSL原因:最后的ack有可能丢失 所以等待服务器重发fin,如果有fin发来就再次发送ack到服务器。
为何两次握手?
这样的两次握手过程, A 向 B 打招呼得到了回应,即 A 向 B 发送数据,B 是可以收到的。
但是 B 向 A 打招呼,A 还没有回应,B 没有收到 A 的反馈,无法确保 A 可以收到 B 发送的数据
网友评论