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函数。