阿里C++一面

文章目录[x]
  1. 0.1:1.自我介绍
  2. 0.2:2.项目的介绍
  3. 0.3:3.进程线程协程是什么,区别
  4. 0.4:4.用户态和内核态
  5. 0.5:5.死锁
  6. 0.6:6.jwt实现原理,改进
  7. 0.7:7.cookie窃取会发生什么
  8. 0.8:8.开源社区关注吗
  9. 0.9:9.dockerfile打包容器
  10. 0.10:10.使用docker时候遇到什么困难
  11. 0.11:11.mysql优化的方法
  12. 0.12:12.索引失效原因
  13. 0.13:13.mysql主从机
  14. 0.14:14.悲观锁乐观锁
  15. 0.15:15.TCP为什么可靠
  16. 0.16:16.滑动窗口
  17. 0.17:17.算法题

1.自我介绍

2.项目的介绍

3.进程线程协程是什么,区别

  • 进程是资源的分配和调度的独立单元。进程拥有完整的虚拟地址空间,当发生进程切换时,不同的进程拥有不同的虚拟地址空间。而同一进程的多个线程是可以共享同一地址空间

  • 线程是CPU调度的基本单元,一个进程包含若干线程。

  • 线程比进程小,基本上不拥有系统资源。线程的创建和销毁所需要的时间比进程小很多

  • 由于线程之间能够共享地址空间,因此,需要考虑同步和互斥操作

  • 一个线程的意外终止会影像整个进程的正常运行,但是一个进程的意外终止不会影像其他的进程的运行。因此,多进程程序安全性更高。

  • 协程(Coroutine,又称微线程)是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制。

  • 协程可以比作子程序,但执行过程中,子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。协程之间的切换不需要涉及任何系统调用或任何阻塞调用

  • 协程只在一个线程中执行,是子程序之间的切换,发生在用户态上。而且,线程的阻塞状态是由操作系统内核来完成,发生在内核态上,因此协程相比线程节省线程创建和切换的开销

  • 协程中不存在同时写变量冲突,因此,也就不需要用来守卫关键区块的同步性原语,比如互斥锁、信号量等,并且不需要来自操作系统的支持。

  • 协程适用于IO阻塞且需要大量并发的场景,当发生IO阻塞,由协程的调度器进行调度,通过将数据流yield掉,并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去运行。

4.用户态和内核态

因为操作系统的资源是有限的,如果访问资源的操作过多,必然会消耗过多的资源,而且如果不对这些操作加以区分,很可能造成资源访问的冲突。所以,为了减少有限资源的访问和使用冲突,Unix/Linux的设计哲学之一就是:对不同的操作赋予不同的执行等级,就是所谓特权的概念。简单说就是有多大能力做多大的事,与系统相关的一些特别关键的操作必须由最高特权的程序来完成。Intel的X86架构的CPU提供了0到3四个特权级,数字越小,特权越高,Linux操作系统中主要采用了0和3两个特权级,分别对应的就是内核态和用户态。运行于用户态的进程可以执行的操作和访问的资源都会受到极大的限制,而运行在内核态的进程则可以执行任何操作并且在资源的使用上没有限制。很多程序开始时运行于用户态,但在执行的过程中,一些操作需要在内核权限下才能执行,这就涉及到一个从用户态切换到内核态的过程。比如C函数库中的内存分配函数malloc(),它具体是使用sbrk()系统调用来分配内存,当malloc调用sbrk()的时候就涉及一次从用户态到内核态的切换,类似的函数还有printf(),调用的是wirte()系统调用来输出字符串,等等。

1)系统调用:原因如上的分析。

2)异常事件: 当CPU正在执行运行在用户态的程序时,突然发生某些预先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的异常事件,典型的如缺页异常。

3)外围设备的中断:当外围设备完成用户的请求操作后,会像CPU发出中断信号,此时,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。

5.死锁

什么是死锁? 所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁

产生死锁的原因?

a. 竞争资源

系统中的资源可以分为两类: 可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源; 另一类资源是不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。 产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞) 产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁

b. 进程间推进顺序非法

若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁 例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁

死锁产生的4个必要条件? 产生死锁的必要条件:

互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。

6.jwt实现原理,改进

JWT包包含三部分数据Header + Payload + Signature:  ①Header:头部,通常头部有两部分信息:申明类型,这里是JWT,会对头部进行base64编码,得到第一部分数据。  ②Payload: 载荷,就是有效数据,一般包含下面信息:a.用户身份信息(注意,因为这里采用的是base64编码,可解码,所以不要存放敏感信息);b.注册申明:比如token的签发时间,过期时间,签发人等;这一部分也是base64编码,得到第二部分数据。  ③Signature:签名,是整个数据的认证信息,一般根据前两部的数据,再加上服务的秘钥(secret)(最好是周期性的更换),通过加密算法生成,用于验证整个数据的完整性和可靠性。

如果加强 JWT 的安全性?

根据我的使用,总结以下几点:

  1. 缩短 token 有效时间

  2. 使用安全系数高的加密算法

  3. token 不要放在 Cookie 中,有 CSRF 风险

  4. 使用 HTTPS 加密协议

  5. 对标准字段 iss、sub、aud、nbf、exp 进行校验

7.cookie窃取会发生什么

跨站请求伪造,获得用户登录态,伪造身份

8.开源社区关注吗

9.dockerfile打包容器

dockerfile文件编写

10.使用docker时候遇到什么困难

11.mysql优化的方法

  • 为查询缓存优化查询,有一些函数例如now,rand等不会开启查询缓存,需要用一个变量来替换函数

  • 当只要一条数据时候使用limit 1,已经知道结果只有1时候

  • 为搜索字段创建索引

  • 在join表的时候,为两个表中的join的字段建立索引

  • 不使用order by rand()

  • 避免select * ,需要什么就取什么

  • 为每个表设置一个ID为主键

  • 使用enum而不是varchar,如果确定这些字段的取值是有限且固定情况下

  • 尽可能使用not null,null需要额外空间,而且会使得进行比较时候程序变得很复杂

  • 固定长度的表查询更快

  • 拆分大的delete或insert,因为这两个操作会锁表

12.索引失效原因

  • 在索引列有其他操作

  • 使用了覆盖操作,如select *,访问了非索引列的查询

  • 使用了!=和<>时候无法使用索引,会看进行全表扫描

  • is null,is not null无法使用索引

  • 字符串不加单引号

  • 使用or会使索引失效

13.mysql主从机

MySQL主从配置又叫Replication或者AB复制,简单讲就是A和B两台机器做主从后,在A上写数据,另一台B也会跟着写数据,两台数据实时同步。

MySQL主从是基于binlog的,主上须开启binlog才能进行主从。

主从过程大致有3个步骤主将更改操作记录到Binlog里

从将主的Binlog事件(sql语句)同步到从本机上并记录在relaylog里

从根据relaylog里面的sql语句按顺序执行主上有一个logdump线程,用来和从的i/o线程传递binlog

从上有两个线程,其中i/o线程用来同步主的binlog并生成relaylog,另外一个SQL线程用来把relaylog里面的sql语句执行一遍。

适用场景:

(1)做数据库数据备份,仅仅是备份,当主机宕机时,马上从机可以代替主机。

(2)还是做数据备份,但是和主机一样,会向web服务器提供服务。但是不能往从机上写数据。

14.悲观锁乐观锁

悲观锁:认为很悲观,每次去拿数据的时候都认为别人回修改,所以每次都会在拿数据的时候上锁,这样别人想拿数据的时候就会阻塞,直到她拿到锁。

在关系型数据库当中就用到了这种锁机制,如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁

乐观锁:乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。

15.TCP为什么可靠

  • ACK和超时重发机制

  • 滑动窗口

  • 拥塞窗口

    • 一般来说,计算机网络都处在一个共享的环境中。因此也有可能因为其他主机之间的通信使得网络拥堵。如果网络拥堵的时候,我们发送端再发送一个较大的数据,极有可能就使得整个网络瘫痪了。又或者路由器没有足够的缓存空间,那么就会丢弃一些新来到的分组。然后TCP又进行了多次重传,造成了网络拥堵家具。

      TCP为了防止这类问题,引入了一个新的窗口变量称为“拥塞窗口”,拥塞窗口的大小取决于网络环境。

      在调节拥塞窗口大小的时候,首先会经历一个慢启动过程,首先将这个拥塞窗口的大小设置一个较小值,之后受到每一次ACK拥塞窗口的值会慢慢增加。发送数据包的时候,将拥塞窗口的大小与接收端主机通知的滑动窗口的大小作比较,然后取他们当中较小的值来确定发送端滑动窗口的值。

16.滑动窗口

现在假设我们有一个http报文被分成了9组发送1、2、3、4、5、6、7、8、9。如果按照刚刚的逻辑来看,肯定先发送1。当1确认了之后再发送2以此类推。

这时候我们限定滑动窗口的大小为5,就规定了滑动窗口覆盖这个范围1~5的数据可以并行发送出去

如果这时候接收端收到了1,并且返回了1的ACK,这时候窗口就会滑动一下,这时候6又可以被发送了.

当然接收方也可以进行累计确认,并不一定对每个分组都发送ACK。假设接收方收到了1、2、3这3个分组,接收方只需要对分组3进行确认。这时候发送方收到了3的ACK,就代表3之前的数据你都已经收到了,我就将窗口移动到4。

当然滑动窗口也有不足的地方,如果接收方收到分组1,2,4,5的数据而分组3丢失了,那么这时候只能返回分组2的ACK。4,5不能返回。因为如果一旦返回了4或5就代表4或5之前的数据我都已经收到了。当然发送发需要重新发送3、4、5。

TCP的窗口分为发送窗口和接受窗口,发送窗口的大小由接受端的能力决定。一般接收端报文确认的时候,会在TCP首部设置窗口大小。发送端接收到了确认会调整自己发送端涌口的大小,直到发送窗口的大小变为0就会暂停发送数据。这种发送暂停的状态会持续到发送端发送一个新的窗口值为止。

17.算法题

剑指Offer27:二叉树镜像原题

func mirrorTree(root *TreeNode) *TreeNode {
    if root==nil{
        return nil
    }
    tmp:=root.Left
    root.Left=mirrorTree(root.Right)
    root.Right=mirrorTree(tmp)
    return root
}

力扣24:两两交换链表的节点

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像