本文主要是介绍【面试总结】小灰灰求职进行曲(一)概念,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 1 字符编码ANSI和ASCII区别、Unicode和UTF-8区别
- 2 进程间通信
- 3 进程控制
- 4 进程与线程
- 5 死锁的概念与处理
- 6 TCP、UDP
- 7 select、poll和epoll(Linux系统中I/O复用的系统调用函数)
- 8 用户态和内核态的区别
- 9 LRU(Least Recently Used)算法
- 10 MySQL
- 11 浏览器输入一个URL后的全过程
- 12 DNS
- 13 并发模型
- 14 分布式概念:分布式计算(MapReduce、Stream、Actor、流水线)
- 15 <>和“”的区别
- 16 C/C++中内存模型(堆 栈 全 常 代)
- 17 select/pool/epool(复用IO)
- 18 编译器执行过程
- 19 NULL 与nullptr
- 20 美团面试
1 字符编码ANSI和ASCII区别、Unicode和UTF-8区别
ASCII码
American Standard Code for Information Interchange,ASCII码中,一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。
ANSI码
ANSI编码是一种对ASCII码的拓展:ANSI编码用0x00~0x7f (即十进制下的0到127)范围的1 个字节来表示 1 个英文字符,超出一个字节的 0x80~0xFFFF 范围来表示其他语言的其他字符。也就是说,ANSI码仅在前128(0-127)个与ASCII码相同,之后的字符全是某个国家语言的所有字符。值得注意的是,两个字节最多可以存储的字符数目是2的16次方,即65536个字符,这对于一个语言的字符来说,绝对够了。还有ANSI编码其实包括很多编码:中国制定了GB2312编码,用来把中文编进去另外,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准。受制于当时的条件,不同语言之间的ANSI码之间不能互相转换,这就会导致在多语言混合的文本中会有乱码。
Unicode编码
为了解决不同国家ANSI编码的冲突问题,Unicode应运而生:如果全世界每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。
Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。
但是问题在于,原本可以用一个字节存储的英文字母在Unicode里面必须存两个字节(规则就是在原来英文字母对应ASCII码前面补0),这就产生了浪费。那么有没有一种既能消除乱码,又能避免浪费的编码方式呢?答案就是UTF-8!
UTF-8编码
这是一种变长的编码方式:它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,如此一来UTF-8编码也可以是为视为一种对ASCII码的拓展。值得注意的是unicode编码中一个中文字符占2个字节,而UTF-8一个中文字符占3个字节。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换。
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。
2 进程间通信
- 管道
- 共享内存
- 消息队列
- Socket 套接字
3 进程控制
进程原语:创建、终止、阻塞、唤醒、切换
进程状态:就绪态、运行态、阻塞态
就绪态 to 运行态:获取到CPU时间片
运行态 to 就绪态:执行完毕进程的CPU时间片
运行态 to 阻塞态:等待IO或事件完成
阻塞态 to 就绪态:就绪IO或事件完成
4 进程与线程
简单区别
进程是资源分配的基本单位,线程是调度的基本单位,往往一个进程包含多个线程。线程并发,系统开销小,不需要切换系统资源。
线程间内存共享:一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
内存安全:一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
互斥锁:这就叫"互斥锁"–Mutex,防止两个线程同时读写某一块内存区域。
信号量:这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。
锁和信号量:互斥锁是信号量的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
标准区别
根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
开销方面:每个进程都有独立的代码和数据空间(程序上下文),进程之间切换开销大;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)。
进程调度方式
调度的产生是因为系统资源有限,没办法同时处理所有进程,需要特定的规则分配执行顺序,从而有了调度。
操作系统调度层次分为三类:高级调度、中级调度、低级调度。
1.先来先去服务
2.时间片轮转法
3.多级反馈队列算法
4.最短进程优先
5.最短剩余时间优先
6.最高响应比优先
7.多级反馈队列调度算法
5 死锁的概念与处理
死锁的4个条件
-
互斥条件:对必须互斥使用的资源的争抢才会导致死锁
-
不剥夺条件:进程所获得的资源未使用完之前,不能被其他进程强行夺走,只能主动释放。
-
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己有的资源保持不放。就像很窄的桥,两个人都要去对面,但谁又都无法让出位置来
-
循环等待条件:存在一种进程资源的循环等待,链中的每一个进程已获得的资源同时被下一个进程所请求。想象有一个闭环,闭环上每个人都需要下一个人手上的某个资源,那么所有人都没办法满足
死锁的处理方式
-
破坏互斥条件(创建一个队列,所有的请求都会被快速响应,然后队列逐渐将请求发送到处理器进行整理)
-
破坏不剥夺条件(进程的某个资源得不到满足时,就必须立刻释放所持有的资源)
-
破坏请求和保持条件(静态分配,进程在运行前就一次性申请全部的资源,不满足就不让允许,就像过桥时保证桥上没人才让通行,否则禁止通行)
-
破坏循环等待条件(资源编号,进程必须按照编号递增的顺序请求资源,这样就不会出现持有大资源请求小资源的情况,也就不会有循环的等待)
银行家算法:进程提出资源申请时,先判断这次分配会不会导致系统进入不安全状态,如果会则不答应请求,让该进程阻塞。简而言之,请求不能大于手中的资源。这种算法也叫银行家算法。
6 TCP、UDP
区别
两者都是通信协议,TCP、UDP 是传输层协议。TCP(Transmission Control Protocol),又叫传输控制协议,UDP(User Datagram Protocol),又叫用户数据报协议。
TCP:面向连接、可靠的、慢
UDP:面向非连接、不可靠的、快
TCP三次握手和四次挥手
参考博客文章
OSI七层模型 & TCP/IP五层模型
知道各个层使用的是哪个数据交换设备。(交换机、路由器、网关)
应用层
传输层(TCP、UDP)
数据链路层
7 select、poll和epoll(Linux系统中I/O复用的系统调用函数)
参考博客文章
8 用户态和内核态的区别
参考博客文章
9 LRU(Least Recently Used)算法
参考博客文章
LRU是一种缓存淘汰机制策略。
也就是说我们认为最近使用过的数据应该是有用的,很久都没用过的数据应该是无用的,缓存满了就优先删除那些很久没有用过的数据。
双向链表和哈希表的结合体
哈希表 o(1)的查找和删除
双向链表,删除自身,方便找到前驱节点
10 MySQL
订单表,唯一的订单号可以保证不重复,那么是否还需要自增长的ID呢?
即便你有了唯一的订单ID,那也一定要有主键自增id。
乐观锁与悲观锁
乐观锁
:总是假设最好的情况,每次拿数据都认为别人不会修改数据,所以不会加锁,但是更新的时候,会判断在此期间有没有人修改过;一般基于版本号机制实现。
悲观锁
:总是假设最坏的情况,每次拿数据都认为别人会修改数据,所以要加锁,别人只能等待,直到我释放锁才能拿到锁;数据库的行锁、表锁、读锁、写锁都是这种方式。
使用场景
:乐观锁适用于读多写少的情况,即冲突很少发生;如果是多写的情况,应用会不断重试,反而会降低系统性能,这种情况最好用悲观锁,因为等待到锁被释放后,可以立即获得锁进行操作。
11 浏览器输入一个URL后的全过程
参看博客文章
12 DNS
域名系统(Domain Name System)将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。
DNS同时占用UDP和TCP端口53是公认的,这种单个应用协议同时使用两种传输协议的情况在TCP/IP栈也算是个另类。
TCP支持的应用协议主要有:Telnet、FTP、SMTP等。
UDP支持的应用层协议主要有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。
13 并发模型
高并发常见的面试题
5 种常见的并发模型
详细解释—
1 Future模型
Future模型是将
异步请求和代理模式
结合的产物。【电商平台,用户在网站下单。】
2 Fork/Join模型
将任务分割成足够小的小任务,然后让不同的线程来做这些分割出来的小事情,然后完成之后再进行join,将小任务的结果组装成大任务的结果。【分治思想,比如多线程下载文件、读取文件】
3 Actor模型(多线程/分布式编程-Actor模型)
详细解释Actor
所有的线程(或进程)通过消息传递
的方式进行合作,这些线程(或进程)称为Actor。共享内存更适合单机多核的并发编程,而且共享带来的问题很多,编程也困难。随着多核时代和分布式系统的到来,共享模型已经不太适合并发编程,因此几十年前就已经出现的Actor模型又重新受到了人们的重视。MapReduce就是一种典型的Actor模式。
到了分布式系统时代,工厂已经用流水线了,每个人都有明确分工,这就是Actor模式。每个线程都是一个Actor,这些Actor不共享任何内存,所有的数据都是通过消息传递的方式进行的。
如果用Actor模型实现统计素数个数,那么我们需要1个actor做原料的分发,就是提供要处理的整数,然后10个actor加工,每次从分发actor那里拿一个整数进行加工,最终把加工出来的半成品发给组装actor,组装actor把10个加工actor的结果汇总输出。
4 Master-Worker模型
Nginx采用的模型是master-worker模型
系统有两个进程协作工作,Master进程,负责接收和分配任务,Worker进程,负责处理子任务。
当Worker进程将子任务处理完成后,结果返回给Master进程,由Master进程做归纳汇总,最后得到最终的结果。
5 生产者消费者模型
核心是使用一个缓存来保存任务。开启一个/多个线程来生产任务,然后再开启一个/多个线程来从缓存中取出任务进行处理。
这样的好处是任务的生成和处理分隔开,生产者不需要处理任务,只负责向生成任务然后保存到缓存。而消费者只需要从缓存中取出任务进行处理。使用的时候可以根据任务的生成情况和处理情况开启不同的线程来处理。
生成的任务速度较快,那么就可以灵活的多开启几个消费者线程进行处理,这样就可以避免任务的处理响应缓慢的问题
线程同步方式
详细信息-大炳
互斥量,信号量,消息队列,事件,临界区
在WIN32中(区别于Linux,其实也差不多),同步机制主要有以下几种
(1)临界区(Critical section);
任意时刻只允许一个线程对共享资源进行访问。
(2)事件(Event);
(3)信号量(semaphore);
信号量是维护0到指定最大值之间的同步对象。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。
(4)互斥量(mutex)。
等价于二元信号量。
采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
14 分布式概念:分布式计算(MapReduce、Stream、Actor、流水线)
分布式概念:分布式计算(MapReduce、Stream、Actor、流水线)
MapReduce 模式(Hadoop海量数据的处理 — Actor模式)
核心思想:将一个复杂的、难以直接解决的大问题,分割成一些规模较小的、可以比较简单的或直接求解的子问题,这些子问题之间相互独立且与原问题形式相同,递归地求解这些子问题,然后将子问题的解合并得到原问题的解。
MapReduce 分为 Map 和 Reduce 两个核心阶段,其中 Map 对应“分”,即把复杂的任务分解为若干个“简单的任务”执行;Reduce 对应着“合”,即对 Map 阶段的结果进行汇总。
Actor 模式
分布式并行计算模型。这种模型有自己的一套规则,规定了 Actor 的内部计算逻辑,以及多个 Actor 之间的通信规则。在 Actor 模型里,每个 Actor 相当于系统中的一个组件,都是基本的计算单元。
优点:非阻塞性,无需使用锁,并发度高。
缺点:不适用于对消息处理顺序有严格要求的系统,工程中不易实现 Actor 模型。
流水线模式(TenserFlow)
数据并行计算的一种形式,就是将一个任务拆分为多个步骤(子任务),然后多个这样的任务通过对步骤(子任务)的重叠执行,以实现数据并行处理的场景。
流水线计算模式的原理
TensorFlow 运用了流水线模式对输入数据进行预处理,其数据输入流水线主要包含 3 个步骤:
提取(Extract)。通过多种途径读取数据,比如内存、本地的 HDD 或 SSD、远程的 HDFS、GCS 等。数据的种类也有很多,比如图像数据、文本数据、视频数据等。
转换(Transform)。使用 CPU 处理器对输入的数据进行解析以及预处理操作,包括混合重排(shuffling)、批处理(batching), 以及一些特定的转换。比如图像解压缩和扩充、文本矢量化、视频时序采样等。
加载(Load)。将转换后的数据加载到执行机器学习模型的加速器设备上,比如 GPU 或 TPU。
因此 TensorFlow 的数据输入流水线也称为 ETL 流水线。
15 <>和“”的区别
16 C/C++中内存模型(堆 栈 全 常 代)
C++内存分为5个区域(堆 栈 全 常 代
):
-
堆 heap :
由new分配的内存块,其释放编译器不去管,由我们程序自己控制(一个new对应一个delete)。如果程序员没有释放掉,在程序结束时OS会自动回收。涉及的问题:“缓冲区溢出”、“内存泄露”。 -
栈 stack :
是那些编译器在需要时分配,在不需要时自动清除的存储区。存放局部变量、函数参数。存放在栈中的数据只在当前函数及下一层函数中有效,一旦函数返回了,这些数据也就自动释放了。 -
全局/静态存储区 (.bss段和.data段) :
全局和静态变量被分配到同一块内存中。在C语言中,未初始化的放在.bss段中,初始化的放在.data段中;在C++里则不区分了。 -
常量存储区 (.rodata段) :
存放常量,不允许修改(通过非正当手段也可以修改) -
代码区 (.text段) :
存放代码(如函数),不允许修改
(类似常量存储区),但可以执行
(不同于常量存储区)
17 select/pool/epool(复用IO)
- 阻塞等待
- 非阻塞轮询
- 高复用IO
18 编译器执行过程
C语言中编译器工作的过程
菜鸟-编译器的工作过程
动态链接库和静态链接库
关于C/C++中的预处理
19 NULL 与nullptr
参考博客文章:C++中NULL和nullptr的区别
C程序中的NULL
#define NULL ((void *)0)int *pi = NULL; // void* -> int*
char *pc = NULL;
NULL实际上是一个空指针,如果在C语言中写入以上代码,编译是没有问题的,因为在C语言中把空指针赋给int和char指针的时候,发生了隐式类型转换,把void指针转换成了相应类型的指针。
C++程序中的NULL
C代码如果使用C++编译器来编译则是会出错的,因为C++是强类型语言,void是不能隐式转换成其他类型的指针的,所以实际上编译器提供的头文件做了相应的处理*,在C++中,NULL实际上是0。
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
// 在c++中NULL -> int*
nullptr -> void*
C++中的nullptr
C++11版本(2011年发布)中特意引入了nullptr这一新的关键字来代指空指针。
const class nullptr_t
{
public:template<class T>inline operator T*() const{ return 0; }template<class C, class T>inline operator T C::*() const{ return 0; }private:
void operator&() const;
} nullptr = {};
20 美团面试
正则表达式贪婪匹配
K-V型数据库
k-v数据库是指Key-value数据库,是一种以键值对存储数据的一种数据库,类似java中的map。可以将整个数据库理解为一个大的map,每个键都会对应一个唯一的值。
key-value分布式存储系统查询速度快、存放数据量大、支持高并发,非常适合通过主键进行查询,但不能进行复杂的条件查询。
- HBase
- Redis
redis是一个key-value存储系统,支持存储的value类型相对更多,包括string(字符串)、list(列表)、set(集合)和zset(有序集合)。另外redis是一种内存型的数据库,所以可以对外提供很好地读写操作,但是内存占用高,数据持久化不容易。
KV型内存数据库Redis
Redis数据库看这一篇文章就够了
这篇关于【面试总结】小灰灰求职进行曲(一)概念的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!