大话面试题--基本概念篇

用最简单的道理说清楚常见的面试题

1、异步编程的事件循环
异步编程事件循环是专门针对javascript的,之前javascript只支持同步模式,但是网络的事情谁也说不准,假如连最起码的鼠标点击,定时器触发等操作都要等着同步返回才能去做,那也许我们的页面就会出现很多卡顿的现象(鼠标或键盘的输入无法及时响应)。
为了防止这种情况频繁出现,开发了异步编程事件循环。
比如说两件事情:
1、在网页上点击链接:
2、滚动网页。
我们收到的情况可能有两种:一种是网页先滚动了,另一种是网页加载回来滚动了。异步事件循环如下:

	div.onclick = () => { console.log('click')}
	req = new XMLHttpRequst();

假如主线程没有别的事件,那么执行完这两行之后程序实际上就在等异步事件,这会哪个先到异步队列中就先添加哪个,由于主线程没有同步事件,就会先执行这个异步事件。因此有可能网页先加载也有可能先滚动。
未完:javascript同步事件一般都有哪些?

2、操作系统为什么分用户态和内核态?
笔者就是搞内核态开发的,对内核态开发深恶痛绝,原因之一就是经常crash,动辄半小时重启机器。
为什么会有这么多crash呢?操作系统本身就是C语言所写,里面各种纷繁的指针操作精妙无比。假如,操作系统不分用户态和内核态,我们自己写一个小程序:

 	int main()
   {
	       int a[10] ={0};
	       a[11] = 1;	
   }

a[11]数组越界,结果恰好这个a[11]对应的地址是一个整个操作系统依赖的全局变量比如是cpu时钟的地址,那么整个cpu运行就乱了。系统分分钟crash。
因此,对a[11]这个地址的操作,不能直接对内核态地址赋值,而是由操作系统内核态先分配出10个int类型的空间,之后假如对第11个空间赋值,由于用户态的程序并没有权限去访问内核态地址,系统会报空间溢出的错误,不会允许系统内存被随意篡改。

3、为什么要有page cache,操作系统怎么设计的page cache
page cache简单来说就是为了提高读写性能。cache的作用大家都知道,内存的速度要高于磁盘,因此读写cache总要比读写磁盘快。
基于这个原因,linux在读写文件的时候,都会把这个文件的内容缓存在内存中,不仅如此,还会多缓存一些页面(预读)。对于读来说,下次读取的时候如果能够命中缓存,读取的速度就会快很多;对于写来说,先写入缓存也会很快,后面下发fsyc()时再统一下盘。
由于缓存中有page读入,那么就一定会占用内存,如果没有管制,必然会导致内存紧张,因此缓存中page也有淘汰机制,可以简单认为成lru淘汰,读到了就命中,就放在队首,长时间不命中且引用计数为0,就从队尾开始淘汰。(实际要略微复杂,有两个list,先从activelist放到inactivelist,再从inactivelist中淘汰)。
4、mmap和page cache有什么关系么?
mmap能够将内核态的地址映射到用户态供用户态进程去使用,就像我们使用自己malloc出来的地址那样方便。试想之前没有mmap的时候,我们对一个文件的操作只能是读,写,追加写,你能对一个打开的文件进行某个字节的修改么?而使用mmap就可以,我们真正面对的就是真实的一个一个字节,我们可以非常方便的按照我们自己的意愿修改(但注意并不是内核态的地址,是内核态映射到用户态的地址)。
mmap和page cache什么关系呢?问题3中提到了,linux内核读写文件的时候都会建立page cache,建立mmap映射也会先将文件的内容缓存下来。因此我们实际上可以短期读取映射出来的地址之外的,构建到缓存中的page。举例说明:

5、resize()和reserve()的区别
在C++的STL容器中,有预分配机制,例如我们new一个vector,不同库函数也许会预分配不同大小的空间,比如预分配了2个,当你pushback第三的时候,就会触发系统自动预分配4个,然后是8个,等等。每次增加预分配都会将之前的数据进行删除,拷贝,空间释放等,很耗费时间。因此,C++允许使用reserve函数进行人工预分配。调用了reserve(size),系统会预先分配出size*type大小的空间,例如reserve(16),那么插入16个数据,一定不会引起系统的自动预分配。这也可以解释,先执行reserve(16),再执行reserve(10),stl的capacity不会发生变化,因为预分配的空间如果不释放,该容器的capacity是不会变化的(已经分配出来了)。
由此说来,capacity可以理解为此容器经过预分配之后,最多可以存放多少元素(永远大于等于size)。
resize的作用可以简单理解成截断。假如容器中已经有10个元素,resize(8),那么后两个就被删除了,但是不会影响capacity,因为无论是自己预分配还是系统预分配,分配好的空间都不会释放。如果resize(20),那么会扩张容器的大小,如果预分配的空间是10,capacity会涨为20。毕竟整体大小都扩大了,能够最多存放的元素也就变多了。
一句话总结,capacity是能放多少,size是放了多少。resize是截断或者增加容量(已经初始化元素完成),reserve是预分配空间(不初始化元素)。

6、手动实现一个lower_bound,并使得最差的时间复杂度是log(n)
这道题实际很简单,就是一道普通的二分查找法。只需要搞清楚,lower_bound是基于本身有序的队列来实现的
代码如下:

		int Self_Lowerbound(int *a, int size, int val)
		{
			int startpos = 0;
			int endpos = size;
			int mid = (startpos + endpos) / 2;
			while (startpos < endpos - 1)
			{
				if (a[mid] > val)
				{
					endpos = mid;
					mid = (startpos + endpos) / 2;
				}
				else if (a[mid] < val)
				{
					startpos = mid;
					mid = (startpos + endpos) / 2;
				}
				else
				{
					return mid;
				}
			}
			return startpos + 1;
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值