字节提前批(一面)

linux进程和线程同步的方式有哪些

进程同步的方式:
1)互斥锁
2)条件变量
3)读写锁
4)记录锁(文件锁)

为了增加并行性,我们可以在读写锁的基础上进一步细分被锁对象的粒度。比如一个文件中,读进程可能需要读取该文件的前1k个字节,写进程需要写该文件的最后1k个字节。我们可以对前1k个字节上读锁,对最后1k个自己上写锁,这样两个进程就可并发工作了。记录锁中的所谓"记录"其实是"内容"的概念。使用读写锁可以锁定一部分,而不是整个文件。
文件锁可以认为是记录锁的一个特例,当使用记录锁锁定文件的所有内容时,此时的记录锁就可以称为文件锁了。

5)信号量
线程同步的方式:
1)互斥锁
2)自旋锁

互斥锁和自旋锁是锁的最基本处理方式,更高级的锁都会选择其中一个来实现,比如读写锁既可以选择互斥锁实现,也可以基于自旋锁实现。

3)信号量
4)条件变量

常用于生产者消费者模型,有pthread_cond_signal和pthread_cond_broadcast两种通知方式

5)读写锁
6)屏障
拓展点:
同步和通信的区别

进程竞争资源时要实施互斥,互斥是一种特殊的同步,实质上需要解决好进程同步问题,进程同步是一种进程通信,通过修改信号量,进程之间可建立起联系,相互协调运行和协同工作。但是信号量与PV操作只能传递信号,没有传递数据的能力。有些情况下进程之间交换的信息量虽很少,例如,仅仅交换某个状态信息,但很多情况下进程之间需要交换大批数据,例如,传送一批信息或整个文件,这可以通过一种新的通信机制来完成,进程之间互相交换信息的工作称之为进程通信IPC (InterProcess Communication)(主要是指大量数据的交换)。
参考链接:https://blog.csdn.net/weixin_41413441/article/details/80548683

进程同步和线程同步的区别

线程同步:多线程编程中,解决共享资源冲突的问题
进程同步:多进程编程中,解决共享资源冲突的问题
互斥锁和条件变量出自Posix.1线程标准,它们总是可以用来同步一个进程内的各个线程的。如果一个互斥锁或者条件变量存放在多个进程共享的某个内存区中,那么Posix还允许它用在这些进程间的同步。
线程同步和进程同步的本质区别在于锁放在哪,放在私有的进程空间还是放在多进程共享的空间,并且看锁是否具备进程共享的属性。

互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

参考链接:https://zhuanlan.zhihu.com/p/246114725

条件变量如果是广播的话,会导致惊群效应,怎么能够通过加锁的方式来竞争?

   惊群效应也有人叫做雷鸣群体效应,不过叫什么,简言之,惊群现象就是多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只可能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群。

原文链接:https://blog.csdn.net/lyztyycode/article/details/78648798
条件变量的惊群效应的解决方式,加锁,while判断
参考链接:https://blog.csdn.net/u012351051/article/details/123783424
扩展(关于条件变量、避免惊群、虚假唤醒):https://www.cnblogs.com/dream397/p/14690724.html

AVL树插入时间复杂度是怎么算的

添加和删除的时间复杂度都是O(logn)的原因是,要先查询到对应添加和删除结点的位置,而查询的时间复杂度就是O(logn)。
参考链接:https://blog.csdn.net/u014453898/article/details/112390002
红黑树是一种弱平衡二叉树(由于是弱平衡,可以看到,在相同的节点情况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来说,它的旋转次数少,所以对于搜索,插入,删除操作较多的情况下,我们就用红黑树
** 参考链接:**https://blog.csdn.net/u010899985/article/details/80981053

有几个排序算法的时间复杂度是O(nlogn)的

image.png

select、poll、和epoll的区别,epoll性能好在哪,只是因为它能节约拷贝吗?epoll怎么解决惊群效应的

阻塞IO


在 IO 阻塞逻辑中,存在下面三个问题:

  1. 进程在 recv 的时候大概率会被阻塞掉,导致一次进程切换;
  2. 当 TCP 连接上的数据到达服务端的网卡、并从网卡复制到内核空间 socket 的数据等待队列时,进程会被唤醒,又是一次进程切换;并且,在用户进程继续执行完 recvfrom() 函数系统调用,将内核空间的数据拷贝到了用户缓冲区后,用户进程才会真正拿到所需的数据进行处理;
  3. 一个进程同时只能等待一条连接,如果有很多并发,则需要很多进程;

总结:一次数据到达会进行两次进程切换,一次数据读取有两处阻塞,单进程对单连接

非阻塞 IO


到这里,阻塞 IO 模型的“两次进程切换,两处阻塞,单进程对单连接”问题,通过非阻塞 IO 和多路复用技术,就只剩下了“一处阻塞”这个问题,即 Linux 服务器上用户进程一定要等待数据从内核空间拷贝到用户空间,如果这个步骤也变成非阻塞的,也就是进程调用 recvfrom 后立刻返回,内核自行去准备好数据并将数据从内核空间拷贝到用户空间、再 notify 通知用户进程去读取数据,那就是 IO 异步调用,不过,Linux 没有提供异步 IO 的实现,真正意义上的网络异步 IO 是 Windows 下的 IOCP(IO 完成端口)模型,这里就不探讨了。

详解 select、poll、epoll 实现原理

select 的缺点


从上面两图描述的执行过程,可以发现 select 实现多路复用有以下缺点:
1.性能开销大
1)调用 select 时会陷入内核,这时需要将参数中的 fd_set 从用户空间拷贝到内核空间,select 执行完后,还需要将 fd_set 从内核空间拷贝回用户空间,高并发场景下这样的拷贝会消耗极大资源;(epoll 优化为不拷贝)
2)进程被唤醒后,不知道哪些连接已就绪即收到了数据,需要遍历传递进来的所有 fd_set 的每一位,不管它们是否就绪;(epoll 优化为异步事件通知)
3)select 只返回就绪文件的个数,具体哪个文件可读还需要遍历;(epoll 优化为只返回就绪的文件描述符,无需做无效的遍历)
2.同时能够监听的文件描述符数量太少。受限于 sizeof(fd_set) 的大小,在编译内核时就确定了且无法更改。一般是 32 位操作系统是 1024,64 位是 2048。(poll、epoll 优化为适应链表方式)
第 2 个缺点被 poll 解决,第 1 个性能差的缺点被 epoll 解决。

poll 实现原理

管理多个描述符也是进行轮询,根据描述符的状态进行处理,但 poll 无最大文件描述符数量的限制因其基于链表存储
select 和 poll 在内部机制方面并没有太大的差异。相比于 select 机制,poll 只是取消了最大监控文件描述符数限制,并没有从根本上解决 select 存在的问题。

epoll 实现原理

epoll 是对 select 和 poll 的改进,解决了“性能开销大”和“文件描述符数量少”这两个缺点,是性能最高的多路复用实现方式,能支持的并发量也是最大。
epoll 的特点是:
1)使用红黑树存储一份文件描述符集合,每个文件描述符只需在添加时传入一次,无需用户每次都重新传入;—— 解决了 select 中 fd_set 重复拷贝到内核的问题
2)通过异步 IO 事件找到就绪的文件描述符,而不是通过轮询的方式;
3)使用队列存储就绪的文件描述符,且会按需返回就绪的文件描述符,无须再次遍历;

参考链接:https://mp.weixin.qq.com/s/5xj42JPKG8o5T7hjXIKywg

进程和线程的区别

**根本区别:**进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
**资源开销:**每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
**包含关系:**如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
**内存分配:**同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
**影响关系:**一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
**执行过程:**每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
原文链接:https://blog.csdn.net/ThinkWon/article/details/102021274

死锁产生的条件

互斥条件、持有等待条件、不可剥夺条件、环路等待条件

快速排序的复杂度和初始顺序有关系吗?

有关系,最好的情况是O(nlogn),最坏的情况是O(n^2)
**最坏的情况:**为正序或逆序排列,二叉树画出来应该是一棵斜树,并且需要经过n-1次递归调用才能完成,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置

可重入性

重入一般可以理解为一个函数在同时多次调用,例如操作系统在进程调度过程中,或者单片机、处理器等的中断的时候会发生重入的现象。
(1)可以在执行的过程中可以被打断;
(2)被打断之后,在该函数一次调用执行完之前,可以再次被调用(或进入,reentered)。
(3)再次调用执行完之后,被打断的上次调用可以继续恢复执行,并正确执行。
参考链接:https://blog.csdn.net/acs713/article/details/20034511
扩展:可重入性和线程安全的区别
线程安全是多个线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取结束并且释放了锁,其他线程才可使用,保证了数据的一致性。
(1)一个线程安全的函数不一定是可重入的;
(2)一个可重入的函数缺也不一定是线程安全的!
(3)一个不可重入的函数一定不是线程安全的。这是收到多线程并发环境的限制,但可重入本身与线程安全无必然联系。
参考链接:https://zhuanlan.zhihu.com/p/352218464

解决哈希冲突的方法

开放定址法:我们在遇到哈希冲突时,去寻找一个新的空闲的哈希地址。

举例:就是当我们去教室上课,发现该位置已经存在人了,所以我们应该寻找新的位子坐下,这就是开放定址法的思路。如何寻找新的位置就通过以下几种方法实现。

线性探测法image.png
平方探测法

image.png

再哈希法:同时构造多个不同的哈希函数,等发生哈希冲突时就使用第二个、第三个……等其他的哈希函数计算地址,直到不发生冲突为止。虽然不易发生聚集,但是增加了计算时间。
链地址法:将所有哈希地址相同的记录都链接在同一链表中。
建立公共溢出区:将哈希表分为基本表和溢出表,将发生冲突的都存放在溢出表中。

消息队列是全局的还是程序级别的

没太懂面试官的意思,正常情况下应该是全局的吧。

  • 消息队列的本质其实是一个内核提供的链表,内核基于这个链表,实现了一个数据结构

手撕:中文数字转换为阿拉伯数字

需要利用递归解决,手撕失败,隔天就进人才库了。
http://data.biancheng.net/view/147.html
扩展点:阿拉伯数字转中文—>链接:http://data.biancheng.net/view/146.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值