备战嵌入式软件工程师技术面试day3

Linux篇

1.1 Linux内核的组成⭐⭐ 

 1.2用户空间与内核通信方式有哪些?⭐⭐⭐⭐⭐ 

1.3系统调用read()/write(),内核具体做了哪些事情⭐⭐ 

1.4系统调用的作用⭐⭐⭐⭐⭐ 

C++ STL库篇:

2.1 vector list异同⭐⭐⭐⭐⭐ 

2.2 vector内存是怎么增长的vector的底层实现⭐⭐⭐⭐ 

2.3 vector和deque的比较⭐⭐⭐⭐ 

2.4为什么stl里面有sort函数list里面还要再定义一个sort⭐⭐⭐ 

Linux内核的组成⭐⭐

linux内核主要有五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。

 用户空间与内核通信方式有哪些?⭐⭐⭐⭐⭐ 

系统调用,提供特定的用户空间与内核空间的信息传递。

信号,内核空间出现一些异常的时候会发送信号给进程,如SIGSEGV、SIGILL、SIGPIPE

/proc,proc可以读取指定内核空间的操作信息并且设置部分属性的值,需要循环检测。

文件,可以通指定的文件的读写操作来实现通信,但是流程不够实时,也与需要循环检测。

Netink,  类似与socket通信方式,可以读取大量的数据,实现稍微复杂。

Ioctl,可以实现数据量比较少时候的通信。

系统调用read()/write(),内核具体做了哪些事情?⭐⭐

用户空间read()-->内核空间sys_read()-->scull_fops.read-->scull_read();

用户空间的处理和核心空间的处理。在用户空间中通过 0x80 中断的方式将控制权交给内核处理,内核接管后,经过6个层次的处理最后将请求交给磁盘,由磁盘完成最终的数据拷贝操作。在这个过程中,调用了一系列的内核函数。

系统调用的作用⭐⭐⭐⭐⭐ 

Linux内核中设置了一组用来是实现系统功能的子程序,称为系统调用。用户可以通过使用系统调用命令在自己的应用程序中调用它们。

作用:系统调用是操作系统提供给应用程序(程序员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序发出系统调用请求来获得操作系统的服务。

vector list异同⭐⭐⭐⭐⭐ 

vector和数组类似,拥有一段连续的内存空间,并且起始位置都是由索引下标0开始,因此可以高效的进行随机存取,时间复杂度o(1);

不足就是内存空间是连续的,在进行插入和删除操作的同时,会造成内存的拷贝,时间复杂度o(n);当数组中内存不够时,会重新申请一块内存空间并进行内存拷贝。

List:本质上就是双向链表,因此决定了空间的不连续,而且只能通过指针访问数据,所以list的随机存储非常没有效率,时间复杂度o(n);但由于链表的特点,能高效的进行插入和删除。

从应用上,因为vector拥有一段连续的内存空间,能够很好的支持随机存取,因此vector<int>::iterator支持“+”,“+=”,“<”等操作符。

list的内存空间可以是不连续,它不支持随机访问,因此list<int>::iterator则不支持“+”、“+=”、“<”等

vector<int>::iterator和list<int>::iterator都重载了“++”运算符。

总之,如果需要高效的随机存取,而不在乎插入和删除的效率,使用vector;
如果需要大量的插入和删除,而不关心随机存取,则应使用list。

vector内存是怎么增长的vector的底层实现⭐⭐⭐⭐ 

STL 众多容器中,vector 是最常用的容器之一,其底层所采用的数据结构非常简单,就只是一段连续的线性内存空间。

//_Alloc 表示内存分配器,此参数几乎不需要我们关心
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector{
    ...
protected:
    pointer _Myfirst;
    pointer _Mylast;
    pointer _Myend;
};

其中,_Myfirst指向的是容器对象的起始字节位置;Mylast指向当前最后一个元素的末尾字节;_Myend指向整个vector容器所占的内存空间的末尾字节。

如图 1 所示,通过这 3 个迭代器,就可以表示出一个已容纳 2 个元素,容量为 5 的 vector 容器。


在此基础上,将 3 个迭代器两两结合,还可以表达不同的含义,例如:

  • _Myfirst 和 _Mylast 可以用来表示 vector 容器中目前已被使用的内存空间;
  • _Mylast 和 _Myend 可以用来表示 vector 容器目前空闲的内存空间;
  • _Myfirst 和 _Myend 可以用表示 vector 容器的容量。

通过灵活运用这 3 个迭代器,vector 容器可以轻松的实现诸如首尾标识、大小、容器、空容器判断等几乎所有的功能

template <class _Ty, class _Alloc = allocator<_Ty>>
class vector{
public:
    iterator begin() {return _Myfirst;}
    iterator end() {return _Mylast;}
    size_type size() const {return size_type(end() - begin());}
    size_type capacity() const {return size_type(_Myend - begin());}
    bool empty() const {return begin() == end();}
    reference operator[] (size_type n) {return *(begin() + n);}
    reference front() { return *begin();}
    reference back() {return *(end()-1);}
    ...
};

vector和deque的比较⭐⭐⭐⭐ 

deque与vector非常相似。它也采用动态数组管理元素,提供随机存取,有着和vector几乎一样的接口。不同的是deque的动态数组头尾都开放,因此能在头尾两端进行快速安插和删除。

deque与vector的主要不同之处在于:

1. 两端都能快速安插和删除元素,这些操作可以在分期摊还的常数时间(amortized constant time)内完成。

2. 元素的存取和迭代器的动作比vector稍慢。

3. 迭代器需要在不同区块间跳转,所以它非一般指针。

4. 因为deque使用不止一块内存(而vector必须使用一块连续内存),所以deque的max_size()可能更大。

5. 不支持对容量和内存重新分配时机的控制。不过deque的内存重分配优于vector,因为其内部结构显示,deque不必在内存重分配时复制所有元素。

6. 除了头尾两端,在任何地方安插或删除元素,都将导致指向deque元素的所有pointers、references、iterators失效。

7. deque的内存区块不再被使用时,会自动被释放。deque的内存大小是可自动缩减的。

8. deque与vector组织内存的方式不一样。在底层,deque按“页”(page)或“块”(chunk)来分配存储器,每页包含固定数目的元素。而vector只分配一块连续的内存。例如,一个10M字节的vector使用的是一整块10M字节的内存,而deque可以使用一串更小的内存块,比如10块1M的内存。所以不能将deque的地址(如&deque[0])传递给传统的C API,因为deque内部所使用的内存不一定会连续。

为什么stl里面有sort函数list里面还要再定义一个sort⭐⭐⭐ 

1.对于STL来说,它本身的设计指导的思想是GP,即模板编程。所以在此思想的前提下,少了面向镀对象的类继承,虚函数,多态等的设计,取而代之的是数据与方法的分离,表现在STL中将容器和算法分离,两者闭门造车,中间依靠迭代器联系。

2.故sort算法被单独剥离出来,与所有的容器分开。虽然想的挺好但是总有例外,list容器就是那个例外。从源码可以看出,sort函数用到的迭代器的操作链表是不可能做到的,故list需要设计自己的sort函数。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值