深圳app网站建设哪家好,西安seo外包,wordpress 极简博客,百度软件商店文章目录 一、线程1.什么是进程#xff0c;线程#xff0c;彼此有什么区别?2.多进程、多线程的优缺点3.什么时候用进程#xff0c;什么时候用线程4.多进程、多线程同步#xff08;通讯#xff09;的方法5.父进程、子进程的关系以及区别6.什么是进程上下文、中断上下文7.一… 文章目录 一、线程1.什么是进程线程彼此有什么区别?2.多进程、多线程的优缺点3.什么时候用进程什么时候用线程4.多进程、多线程同步通讯的方法5.父进程、子进程的关系以及区别6.什么是进程上下文、中断上下文7.一个进程可以创建多少线程和什么有关8.什么是线程同步和互斥9.进程的空间模型10.进程线程的状态转换图 什么时候阻塞什么时候就绪11.线程同步与阻塞的关系同步一定阻塞吗阻塞一定同步吗12.并发同步异步互斥阻塞非阻塞的理解13.孤儿进程、僵尸进程、守护进程的概念14.正确处理僵尸进程的方法15.如何创建守护进程 二、C/C高频面试题1.new和malloc的区别2.malloc的底层实现3.在1G内存的计算机中能否malloc(1.2G)为什么4.指针与引用的相同和区别如何相互转换5.C语言检索内存情况 内存分配的方式6. extern”C” 的作用⭐⭐⭐7.头文件声明时加extern定义时不要加 因为extern可以多次声明但只有一个定义⭐⭐⭐⭐8.函数参数压栈顺序即关于__stdcall和__cdecl调用方式的理解⭐⭐⭐9.重写memcpy()函数需要注意哪些问题⭐⭐10.数组到底存放在哪里⭐⭐⭐11.struct和class的区别 ⭐⭐⭐⭐⭐12. char和int之间的转换⭐⭐⭐13. static的用法定义和用途⭐⭐⭐⭐⭐14.const常量和#define的区别编译阶段、安全性、内存占用等 ⭐⭐⭐⭐15.c/c中变量的作用域⭐⭐⭐⭐⭐16.volatile作用和用法 ⭐⭐⭐⭐⭐17.有常量指针 指针常量 常量引用 没有 引用常量⭐⭐⭐18.c中类型转换机制各适用什么环境dynamic_cast转换失败时会出现什么情况⭐⭐⭐19.char、short、int、long、float、double、struct的大小 三、继承、多态相关面试题1.继承和虚继承 ⭐⭐⭐⭐⭐2.多态的类内存布局是怎么样的 ⭐⭐⭐⭐⭐3.被隐藏的基类函数如何调用或者子类调用父类的同名函数和父类成员变量 ⭐⭐⭐⭐⭐4.多态实现的三个条件、实现的原理 ⭐⭐⭐⭐⭐5.对拷贝构造函数 深浅拷贝 的理解 拷贝构造函数作用及用途什么时候需要自定义拷贝构造函数⭐⭐⭐6.析构函数可以抛出异常吗为什么不能抛出异常除了资源泄露还有其他需考虑的因素吗⭐⭐⭐7.什么情况下会调用拷贝构造函数三种情况⭐⭐⭐8.析构函数一般写成虚函数的原因⭐⭐⭐⭐⭐9.构造函数为什么一般不定义为虚函数⭐⭐⭐⭐⭐10.什么是纯虚函数⭐⭐⭐⭐⭐11.静态绑定和动态绑定的介绍⭐⭐⭐⭐12.C所有的构造函数 ⭐⭐⭐13.重写、重载、覆盖的区别⭐⭐⭐⭐⭐14.成员初始化列表的概念为什么用成员初始化列表会快一些性能优势⭐⭐⭐⭐15.如何避免编译器进行的隐式类型转换explicit⭐⭐⭐⭐ 四、网络编程1.TCP、UDP的区别 ⭐⭐⭐⭐⭐2.TCP、UDP的优缺点⭐⭐⭐3.TCP UDP适用场景⭐⭐⭐4.TCP为什么是可靠连接⭐⭐⭐⭐5.典型网络模型简单说说有哪些⭐⭐⭐6.Http1.1和Http1.0的区别⭐⭐⭐7.URI统一资源标识符和URL统一资源定位符之间的区别⭐⭐8.什么是三次握手⭐⭐⭐⭐⭐9.为什么三次握手中客户端还要发送一次确认呢可以二次握手吗⭐⭐⭐⭐10.为什么服务端易受到SYN攻击⭐⭐⭐⭐11.什么是四次挥手⭐⭐⭐⭐⭐12.为什么客户端最后还要等待2MSL⭐⭐⭐⭐13.为什么建立连接是三次握手关闭连接确是四次挥手呢⭐⭐⭐⭐ 五、常见算法1.各种排序算法的时间空间复杂度、稳定性⭐⭐⭐⭐⭐2.各种排序算法什么时候有最好情况、最坏情况尤其是快排 ⭐⭐⭐⭐3.冒泡排序⭐⭐⭐⭐4.选择排序⭐⭐⭐⭐5.插入排序⭐⭐⭐⭐6.希尔排序⭐⭐⭐⭐7.归并排序⭐⭐⭐⭐8.快速排序⭐⭐⭐⭐⭐9.快排的partition函数与归并的Merge函数⭐⭐⭐10. vector list异同⭐⭐⭐⭐⭐11. vector内存是怎么增长的vector的底层实现⭐⭐⭐⭐12.几种模板插入的时间复杂度 ⭐⭐⭐⭐⭐13. vector和deque的比较⭐⭐⭐⭐14.为什么stl里面有sort函数list里面还要再定义一个sort⭐⭐⭐15. STL底层数据结构实现⭐⭐⭐⭐16.利用迭代器删除元素会发生什么⭐⭐⭐⭐17. map是如何实现的查找效率是多少⭐⭐⭐⭐⭐ 六、Linux操作系统1. Linux内核的组成⭐⭐2.用户空间与内核通信方式有哪些⭐⭐⭐⭐⭐3.系统调用read()/write()内核具体做了哪些事情⭐⭐4.系统调用的作用⭐⭐⭐⭐⭐5.内核态用户态的区别⭐⭐⭐⭐⭐6. bootloader内核 根文件的关系⭐⭐⭐⭐7. Bootloader多数有两个阶段的启动过程⭐⭐⭐8. linux的内核是由bootloader装载到内存中的⭐⭐⭐9.为什么需要BootLoader⭐⭐⭐⭐10. Linux内核同步方式总结⭐⭐⭐⭐11.为什么自旋锁不能睡眠 而在拥有信号量时就可以⭐⭐⭐⭐12. linux下检查内存状态的命令⭐⭐⭐13.大小端的区别以及各自的优点哪种时候用⭐⭐⭐⭐⭐14. 一个程序从开始运行到结束的完整过程四个过程⭐⭐⭐⭐⭐15.什么是堆栈内存泄漏和内存溢出⭐⭐⭐⭐16.硬链接与软链接的区别⭐⭐⭐⭐⭐17.虚拟内存虚拟地址与物理地址的转换⭐⭐⭐⭐18.计算机中32bit与64bit有什么区别⭐⭐⭐19.中断和异常的区别⭐⭐⭐⭐⭐20.中断怎么发生中断处理大概流程⭐⭐⭐⭐21. Linux 操作系统挂起、休眠、关机相关命令⭐⭐22.数据库为什么要建立索引以及索引的缺点⭐⭐23.堆和栈的区别⭐⭐⭐⭐⭐24.死锁的原因、条件 创建一个死锁以及如何预防⭐⭐⭐⭐⭐ 单片机1 CPU 内存 虚拟内存 磁盘/硬盘 的关系⭐⭐⭐2 CPU内部结构⭐⭐⭐⭐3 ARM结构处理器简析 ⭐⭐4波特率是什么为什么双方波特率要相同高低波特率有什么区别⭐⭐⭐⭐5.arm和dsp有什么区别⭐⭐6 ROM RAM的概念浅析⭐⭐⭐7 IO口工作方式上拉输入 下拉输入 推挽输出 开漏输出⭐⭐⭐⭐8扇区 块 页 簇的概念⭐⭐⭐⭐9简述处理器在读内存的过程中CPU核、cache、MMU如何协同工作画出CPU核、cache、MMU、内存之间的关系示意图加以说明⭐⭐10请说明总线接口USRT、I2C、USB的异同点串/并、速度、全/半双工、总线拓扑等⭐⭐⭐⭐⭐11什么是异步串口和同步串口⭐⭐⭐⭐⭐12 I2C时序图⭐⭐⭐⭐⭐ 一、线程
1.什么是进程线程彼此有什么区别?
概念: 进程是并发执行的程序在执行过程中分配和管理资源的基本单位。
线程处理器任务的基本单位。线程也被称为轻量级进程。
协程是一种比线程更加轻量级的存在。C 20的协程是一个特殊函数。只是这个函数具有挂起和恢复的能力可以被挂起挂起后调用代码继续向后执行而后可以继续恢复其执行。
区别: 地址空间进程之间是独立的地址空间,线程共享本进程的地址空间。
健壮性一个进程崩溃后在保护模式下不会对其他进程产生影响但是一个线程崩溃整个进程都死掉。
执行过程或者切换时进程执行开销大。线程执行开销小。
最小单位: 进程是管理资源的基本单位。线程是处理器调度的基本单位
2.多进程、多线程的优缺点 3.什么时候用进程什么时候用线程
进程与线程的选择取决以下几点
需要频繁创建销毁的优先使用线程因为对进程来说创建和销毁一个进程代价是很大的。线程的切换速度快所以在需要大量计算切换频繁时用线程还有耗时的操作使用线程可提高应用程序的响应因为对CPU系统的效率使用上线程更占优所以可能要发展到多机分布的用进程多核分布用线程并行操作时使用线程如C/S架构的服务器端并发线程响应用户的请求需要更稳定安全时适合选择进程需要速度时选择线程更好。
4.多进程、多线程同步通讯的方法
多进程、多线程同步通讯的方法 进程间通讯
管道( pipe)管道是一种半双工的通信方式数据只能单向流动而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系有名管道 (named pipeline) 有名管道也是半双工的通信方式但是它允许无亲缘关系进程间的通信。信号量( semaphore)信号量是一个计数器可以用来控制多个进程对共享资源的访问。它常作为一种锁机制防止某进程正在访问共享资源时其他进程也访问该资源。因此主要作为进程间以及同一进程内不同线程之间的同步手段。消息队列( message queue)消息队列是由消息的链表存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。信号 ( signal ) 信号是一种比较复杂的通信方式用于通知接收进程某个事件已经发生。共享内存( shared memory)共享内存就是映射一段能被其他进程所访问的内存这段共享内存由一个进程创建但多个进程都可以访问。共享内存是最快的 IPC方式它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制如信号量配合使用来实现进程间的同步和通信。套接字(socket ) 套接口也是一种进程间通信机制与其他通信机制不同的是它可用于不同及其间的进程通信。
线程通讯
互斥锁提供了以排他方式防止数据结构被并发修改的方法。读写锁允许多个线程同时读共享数据而对写操作是互斥的。条件变量可以以原子的方式阻塞进程直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。信号量机制(Semaphore)包括无名线程信号量和命名线程信号量。 信号机制(Signal)类似进程间的信号处理。
5.父进程、子进程的关系以及区别
父子不同处: 1.进程ID 2.fork返回值 3.父进程ID 4.进程运行时间 5.闹钟(定时器) 6.未决信号集 父子进程间遵循读时共享写时复制的原则。
6.什么是进程上下文、中断上下文
处理器总处于以下三种状态之一
、内核态运行于进程上下文内核代表进程运行于内核空间 、内核态运行于中断上下文内核代表硬件运行于内核空间 、用户态运行于用户空间。
什么是中断上下文 当执行一个中断处理函数时内核处于中断上下文。 所谓的“ 中断上下文”其实也可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境主要是当前被打断执行的进程环境。 中断上下文
1中断上文硬件通过中断触发信号导致内核调用中断处理程序进入内核空间。这个过程中硬件的一些变量和参数也要传递给内核内核通过这些参数进行中断处理。中断上文可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境主要是当前被中断的进程环境。 2中断下文执行在内核空间的中断服务程序。
什么是进程上下文 用户空间的应用程序通过系统调用进入内核空间。这个时候用户空间的进程要传递 很多变量、参数的值给内核内核态运行的时候也要保存用户进程的一些寄存 器值、变量等。所谓的“进程上下文”可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。
进程上下文
1进程上文其是指进程由用户态切换到内核态是需要保存用户态时cpu寄存器中的值进程状态以及堆栈上的内容即保存当前进程的进程上下文以便再次执行该进程时能够恢复切换时的状态继续执行。 2进程下文其是指切换到内核态后执行的程序即进程运行在内核空间的部分。
7.一个进程可以创建多少线程和什么有关
理论上一个进程可用虚拟空间是2G默认情况下线程的栈的大小是1MB所以理论上最多只能创建2048个线程。如果要创建多于2048的话必须修改编译器的设置。 一个进程可以创建的线程数由可用虚拟空间和线程的栈的大小共同决定只要虚拟空间足够那么新线程的建立就会成功。如果需要创建超过2K以上的线程减小你线程栈的大小就可以实现了。
8.什么是线程同步和互斥
线程同步每个线程之间按预定的先后次序进行运行协同、协助、互相配合。可以理解成“你说完我再做”。有了线程同步每个线程才不是自己做自己的事情而是协同完成某件大事。
线程互斥当有若干个线程访问同一块资源时规定同一时间只有一个线程可以得到访问权其它线程需要等占用资源者释放该资源才可以申请访问。线程互斥可以看成是一种特殊的线程同步。
9.进程的空间模型
1、常量区
2、全局变量区
3、静态变量区
4、代码区
两个动态区域这就是
堆和栈。
10.进程线程的状态转换图 什么时候阻塞什么时候就绪 创建态、终止态、就绪态、运行态、阻塞态。
11.线程同步与阻塞的关系同步一定阻塞吗阻塞一定同步吗
同步是个过程阻塞是线程的一种状态。多个线程操作共享变量时可能会出现竞争。这时需要同步来防止两个以上的线程同时进入临界区在这个过程中后进入临界区的线程将阻塞等待先进入的线程走出临界区。 线程同步不一定发生阻塞线程同步的时候需要协调推进速度互相等待和互相唤醒会发生阻塞。
12.并发同步异步互斥阻塞非阻塞的理解
并发(concurrency)在操作系统中是指一个时间段中有几个程序都处于已启动运行到运行完毕之间且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。 1.互斥是指某一资源同时只允许一个访问者对其进行访问具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序即访问是无序的。 2.同步是指在互斥的基础上大多数情况通过其它机制实现访问者对资源的有序访问。在大多数情况下同步已经实现了互斥特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。 异步(asynchronous)异步和同步是相对的同步就是顺序执行执行完一个再执行下一个需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成从而可以让主线程干其它的事情。
阻塞和非阻塞是针对于进程在访问数据的时候根据IO操作的就绪状态来采取的不同方式说白了是一种读取或者写入操作函数的实现方式。阻塞方式下读取或者写入函数将一直等待而非阻塞方式下读取或者写入函数会立即返回一个状态值。 一般来说IO模型可以分为同步阻塞同步非阻塞异步阻塞异步非阻塞。 同步阻塞IO 用户进程在发起一个IO操作以后必须等待IO操作的完成只有当真正完成了IO操作以后用户进程才能运行。 同步非阻塞IO 用户进程发起一个IO操作以后可返回做其它事情 但是用户进程需要时不时的询问 IO操作是否就绪这就要求用户进程不停的去询问从而引入不必要的CPU资源浪费。应用发起一个IO操作以后使用阻塞select系统调用 来等待 I/O可用的通知。 select 调用非常有趣的是它可以用来为多个IO描述符提供通知而不仅仅为一个描述符提供通知。 异步阻塞IO 应用发起一个IO操作以后不等待内核IO操作的完成 等内核完成IO操作以后会通知应用程序这其实就是同步和异步最关键的区别同步必须等待或者主动的去询问IO是否完成。 异步非阻塞IO 用户进程只需要发起一个IO操作然后立即返回 等IO操作真正的完成以后应用程序会得到IO操作完成的通知 此时用户进程只需要对数据进行处理就好了不需要进行实际的IO读写操作因为真正的IO读取或者写入操作已经由内核完成了。
同步与异步是对应的它们是线程之间的关系两个线程之间要么是同步的要么是异步的。 阻塞与非阻塞是对同一个线程来说的在某个时刻线程要么处于阻塞要么处于非阻塞。 阻塞是使用同步机制的结果非阻塞则是使用异步机制的结果。
13.孤儿进程、僵尸进程、守护进程的概念
一个父进程退出而它的一个或多个子进程还在运行那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养并由init进程对它们完成状态收集工作。 一个进程使用fork创建子进程如果子进程退出而父进程并没有调用wait或waitpid获取子进程的状态信息那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。 Linux Daemon守护进程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
14.正确处理僵尸进程的方法
1.子进程退出时向父进程发送SIGCHILD信号父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。 2.把父进程杀掉。父进程死后僵尸进程成为孤儿进程过继给进程initinit始终会负责清理僵尸进程。它产生的所有僵尸进程也跟着消失。 fork两次父进程fork一个子进程然后继续工作子进程fork一 个孙进程后退出那么孙进程被init接管孙进程结束后init会回收。不过子进程的回收 还要自己做。 两次fork的原理是将子进程成为孤儿进程从而其的父进程变为init进程通过init进程可以处理僵尸进程。
15.如何创建守护进程
1、fork()创建子进程父进程exit()退出
2、在子进程调用setsid()创建新会话
3、再次 fork() 一个子进程父进程exit退出
4、在子进程中调用chdir()让根目录“/”成为子进程的工作目录
5、在子进程中调用umask()重设文件权限掩码为0
6、在子进程中close()不需要的文件描述符
7、守护进程退出处理
二、C/C高频面试题
1.new和malloc的区别 malloc和free是库函数而new和delete是C操作符 new自己计算需要的空间大小比如’int * a newmalloc需要指定大小例如’int * a malloc(sizeof(int))’ new在动态分配内存的时候可以初始化对象调用其构造函数delete在释放内存时调用对象的析构函数。而malloc只分配一段给定大小的内存并返回该内存首地址指针如果失败返回NULL。 new是C操作符是关键字而operate new是C库函数 opeartor new /operator delete可以重载而malloc不行 new可以调用malloc来实现但是malloc不能调用new来实现 对于数据C定义new[]专门进行动态数组分配用delete[]进行销毁。new[]会一次分配内存然后多次调用构造函数delete[]会先多次调用析构函数然后一次性释放。 分配数组不同之处 int char* pa new char[100]; int char* pb malloc(sizeof(char) * 100); malloc能够直观地重新分配内存 使用malloc分配的内存后如果在使用过程中发现内存不足可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间如果有原地扩大可分配的内存地址并且返回原来的地址指针如果空间不够先按照新指定的大小分配空间将原有数据从头到尾拷贝到新分配的内存区域而后释放原来的内存区域。 new没有这样直观的配套设施来扩充内存。
2.malloc的底层实现
malloc()工作机制 malloc函数的实质体现在它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后将该内存块一分为二一块的大小与用户请求的大小相等另一块的大小就是剩下的字节。接下来将分配给用户的那块内存传给用户并将剩下的那块如果有的话返回到连接表上。调用free函数时它将用户释放的内存块连接到空闲链上。到最后空闲链会被切成很多的小内存片段如果这时用户申请一个大的内存片段那么空闲链上可能没有可以满足用户要求的片段了。于是malloc函数请求延时并开始在空闲链上翻箱倒柜地检查各内存片段对它们进行整理将相邻的小空闲块合并成较大的内存块。
3.在1G内存的计算机中能否malloc(1.2G)为什么
是有可能申请1.2G的内存的。
回答这个问题前需要知道malloc的作用和原理应用程序通过malloc函数可以向程序的虚拟空间申请一块虚拟地址空间与物理内存没有直接关系得到的是在虚拟地址空间中的地址之后程序运行所提供的物理内存是由操作系统完成的。
4.指针与引用的相同和区别如何相互转换
都是地址的概念指针指向某一内存、它的内容是所指内存的地址引用则是某块内存的别名。从内存分配上看两者都占内存程序为指针会分配内存一般是4个字节而引用的本质是指针常量指向对象不能变但指向对象的值可以变。两者都是地址概念所以本身都会占用内存。指针转引用把指针用*就可以转换成对象可以用在引用参数当中。引用转指针把引用类型的对象用取地址就获得指针了。
5.C语言检索内存情况 内存分配的方式
内存分配方式
从静态存储区域分配。内存在程序编译的时候就已经分配好这块内存在程序的整个运行期间都存在。例如全局变量static变量。在栈上创建。在执行函数时函数内局部变量的存储单元都可以在栈上创建函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中效率很高但是分配的内存容量有限。从堆上分配亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存程序员自己负责在何时用free或delete释 放内存。动态内存的生存期由程序员决定使用非常灵活但如果在堆上分配了空间就有责任回收它否则运行的程序会出现内存泄漏频繁地分配和释放不同大 小的堆空间将会产生堆内碎块。
程序的内存空间
栈区(stack)由编译器自动分配释放存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址。其操作方式类似于数据结构中的栈。堆区(heap)一般由程序员分配释放若程序员不释放程序结束时可能由OS回收。分配方式类似于链表。全局区(静态区)(static)存放全局变量、静态数据、常量。程序结束后由系统释放。文字常量区常量字符串就是放在这里的。程序结束后由系统释放。程序代码区存放函数体(类成员函数和全局函数)的二进制代码。
6. extern”C” 的作用⭐⭐⭐
extern “C”的作用是告诉C编译器用C规则编译指定的代码除函数重载外extern “C”不影响C其他特性。
7.头文件声明时加extern定义时不要加 因为extern可以多次声明但只有一个定义⭐⭐⭐⭐
8.函数参数压栈顺序即关于__stdcall和__cdecl调用方式的理解⭐⭐⭐
__stdcall和__cdecl都是函数调用约定关键字。
__stdcall参数由右向左压入堆栈堆栈由函数本身清理。
__cdecl参数也是由右向左压入堆栈但堆栈由调用者清理
9.重写memcpy()函数需要注意哪些问题⭐⭐
10.数组到底存放在哪里⭐⭐⭐
1.数组就是一片地址连续且空间大小一致的存储空间 (但是每个空间存的还是其他数据的地址) 2.数组存在于堆内存中但凡在堆中存储的数据都称之为 对象但凡在堆内存中创建的对象都会有默认初始值
11.struct和class的区别 ⭐⭐⭐⭐⭐
默认的继承访问权。class默认的是private,strcut默认的是public。默认访问权限struct作为数据结构的实现体它默认的数据访问控制是public的而class作为对象的实现体它默认的成员变量访问控制是private的。3.“class”这个关键字还用于定义模板参数就像“typename”。但关键字“struct”不用于定义模板参数class和struct在使用大括号{ }上的区别 关于使用大括号初始化 1.class和struct如果定义了构造函数的话都不能用大括号进行初始化 2.如果没有定义构造函数struct可以用大括号初始化。 3.如果没有定义构造函数且所有成员变量全是public的话class可以用大括号初始化
12. char和int之间的转换⭐⭐⭐
1.1、char转int
char a 1;
int b a - 0; // b 1;1.2、int转char
int a 1;
char b a 0; // b 1;13. static的用法定义和用途⭐⭐⭐⭐⭐
在全局变量前加上关键字static该变量就被定义成为一个静态全局变量。该变量在全局数据区分配内存静态全局变量在声明它的整个文件都是可见的而在文件之外是不可见的;未经初始化的静态全局变量会被程序自动初始化为0在局部变量前加上关键字static该变量就被定义成为一个静态局部变量。 • 该变量在全局数据区分配内存 • 静态局部变量在程序执行到该对象的声明处时被首次初始化即以后的函数调用不再进行初始化 • 静态局部变量一般在声明处初始化如果没有显式初始化会被程序自动初始化为0 • 它始终驻留在全局数据区直到程序运行结束。但其作用域为局部作用域当定义它的函数或语句块结束时其作用域随之结束在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同它只能在声明它的文件当中可见不能被其它文件使用。静态数据成员 在类内数据成员的声明前加上关键字static该数据成员就是类内的静态数据成员。 • 静态数据成员是该类的所有对象所共有的 • 静态数据成员存储在全局数据区。与静态数据成员一样我们也可以创建一个静态成员函数它为类的全部服务而不是为某一个类的具体对象服务。
14.const常量和#define的区别编译阶段、安全性、内存占用等 ⭐⭐⭐⭐
define是在编译的预处理阶段起作用而const是在 编译、运行的时候起作用。const 定义的常数是变量 也带类型,#define 定义的只是个常数 不带类型。define只是简单的字符串替换没有类型检查。而const有对应的数据类型是要进行判断的可以避免一些低级的错误。const常量可以进行调试的define是不能进行调试的因为在预编译阶段就已经替换掉了define可以用来防止头文件重复引用而const不能const不足的地方是与生俱来的const不能重定义而#define可以通过#undef取消某个符号的定义再重新定义。
15.c/c中变量的作用域⭐⭐⭐⭐⭐ 16.volatile作用和用法 ⭐⭐⭐⭐⭐
首先volatile修饰的变量作用在编译阶段影响编译出的结果其修饰的变量是随时可能被修改的volatile告诉编译器这个变量是重要人物不要偷懒的去走捷径每次认认真真的去从内存拿值。 一般说来volatile用在如下的几个地方
中断服务程序中修改的供其它程序检测的变量需要加 volatile多任务环境下各任务间共享的标志应该加 volatile存储器映射的硬件寄存器通常也要加 volatile 说明因为每次对它的读写都可能由不同意义
17.有常量指针 指针常量 常量引用 没有 引用常量⭐⭐⭐
常量指针(const int* p)常量指针本质上是一个指针是一个指向“常量”的指针即不能通过指针改变指向对象的值不能解除引用但可以更改指向常量引用(const int a)本质上是一个引用对常量的引用不能通过引用改变绑定对象的值指针常量(int* const p)指针常量本质上是一个常量const是修饰p的即指针的值自身是一个常量不可改变始终指向一个地址在创建的同时必须进行初始化
18.c中类型转换机制各适用什么环境dynamic_cast转换失败时会出现什么情况⭐⭐⭐
http://t.csdn.cn/wBwuS 若对指针进行dynamic_cast失败返回nullptr成功返回正常cast后的对象指针
19.char、short、int、long、float、double、struct的大小 结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。 偏移量指的是结构体变量中成员的地址和结构体变量地址的差。 1、结构体变量中成员的偏移量必须是成员大小的整数倍0被认为是任何数的整数倍 2、结构体大小必须是所有成员大小的整数倍。
三、继承、多态相关面试题
1.继承和虚继承 ⭐⭐⭐⭐⭐
虚继承的目的是让某个类做出声明承诺愿意共享它的基类。其中这个被共享的基类就称为虚基类Virtual Base Class本例中的 A 就是一个虚基类。在这种机制下不论虚基类在继承体系中出现了多少次在派生类中都只包含一份虚基类的成员。
2.多态的类内存布局是怎么样的 ⭐⭐⭐⭐⭐
https://blog.csdn.net/yuupengsun/article/details/104136210 和类中的数据有关和函数无关若是有虚函数内存加上虚指针的大小
3.被隐藏的基类函数如何调用或者子类调用父类的同名函数和父类成员变量 ⭐⭐⭐⭐⭐
4.多态实现的三个条件、实现的原理 ⭐⭐⭐⭐⭐
1.继承2.虚函数重写3.父类指针或引用指向子类对象
5.对拷贝构造函数 深浅拷贝 的理解 拷贝构造函数作用及用途什么时候需要自定义拷贝构造函数⭐⭐⭐
拷贝构造函数一般用于以下三种情况 1.当用类的一个对象去初始化该类的另外一个对象时。 2.如果函数的形参是类的对象调用函数时是值传递。引用传递并不调用拷贝构造函数 3.如果函数的返回值是类的对象函数执行完成时会返回调用者时。 如果是浅拷贝那么只会拷贝指针ba它们都指向D内存区域。如果是深拷贝不仅拷贝指针ba还会申请一块新的内存空间E原来的a指向D新的b指向E。 当类存在指针类成员变量时默认拷贝构造函数是浅拷贝会导致二次析构问题所以要自定义拷贝构造函数。
6.析构函数可以抛出异常吗为什么不能抛出异常除了资源泄露还有其他需考虑的因素吗⭐⭐⭐
1如果析构函数抛出异常则异常点之后的程序不会执行如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源则这些动作不会执行会造成诸如资源泄漏的问题。
2通常异常发生时c的机制会调用已经构造对象的析构函数来释放资源此时若析构函数本身也抛出异常则前一个异常尚未处理又有新的异常会造成程序崩溃的问题。
7.什么情况下会调用拷贝构造函数三种情况⭐⭐⭐
1.当用类的一个对象去初始化该类的另外一个对象时。 2.如果函数的形参是类的对象调用函数时是值传递。引用传递并不调用拷贝构造函数 3.如果函数的返回值是类的对象函数执行完成时会返回调用者时。
8.析构函数一般写成虚函数的原因⭐⭐⭐⭐⭐
在派生类中申请的资源就不会得到释放就会造成内存泄漏
9.构造函数为什么一般不定义为虚函数⭐⭐⭐⭐⭐
存储空间角度虚函数对应一个vtablevtable存储于对象的内存空间 若构造函数是虚的则需要通过 vtable来调用若对象还未实例化即内存空间还没有无法找到vtable
使用角度虚函数主要用于在信息不全的情况下能使重载的函数得到对应的调用。 构造函数本身就是要初始化实例那使用虚函数就没有实际意义
从实际含义上看在调用构造函数时还不能确定对象的真实类型因为子类会调父类的构造函数而且构造函数的作用是提供初始化在对象生命期只执行一次不是对象的动态行为也没有太大的必要成为虚函数
10.什么是纯虚函数⭐⭐⭐⭐⭐
纯虚函数只有函数的名字而不具备函数的功能不能被调用。在虚函数后面添加 0 虚函数就成为纯虚函数
虚函数定义形式成员函数前添加 virtual 关键字纯虚函数在虚函数后添加 0 含有纯虚函数的类称为抽象类只含有虚函数的类不能称为抽象类。虚函数既可以直接使用也可以被子类重载实现后以多态的形式调用而纯虚函数必须被子类重载实现才能以多态的形式调用因为纯虚函数在基类中只有声明。无论虚函数还是纯虚函数定义中都不能有 static 关键字。因为 static关键字修饰的内容在编译前就要确定而虚函数、纯虚函数是在运行时动态绑定的。
11.静态绑定和动态绑定的介绍⭐⭐⭐⭐
静态类型对象在声明时采用的类型在编译期既已确定 动态类型通常是指一个指针或引用目前所指对象的类型是在运行期决定的 静态绑定绑定的是静态类型所对应的函数或属性依赖于对象的静态类型发生在编译期 动态绑定绑定的是动态类型所对应的函数或属性依赖于对象的动态类型发生在运行期
12.C所有的构造函数 ⭐⭐⭐
C中的构造函数可以分为4类 1默认构造函数。以Student类为例默认构造函数的原型为 Student(//没有参数 2初始化构造函数 Student(int numint age//有参数 3复制拷贝构造函数 Student(Student//形参是本类对象的引用 4转换构造函数 Student(int r) //形参时其他类型变量且只有一个形参
13.重写、重载、覆盖的区别⭐⭐⭐⭐⭐
Overload重载重载是指不同的函数使用相同的函数名但是函数的参数个数或类型参数列表不同。调用的时候根据函数的参数来区别不同的函数。有以下特征
1相同的范围在同一个类中
2函数名字相同
3参数不同
4virtual关键字可有可无
Override覆盖是指派生类函数覆盖基类函数有以下特征
1不同的范围分别位于派生类与基类
2函数名字相同
3参数相同
4基类函数必须有virtual关键字 Overwrite重写是值在派生类中重新对基类中的虚函数重新实现。即函数名和参数都一样只是函数的实现体不一样 覆盖就是重写
14.成员初始化列表的概念为什么用成员初始化列表会快一些性能优势⭐⭐⭐⭐
如果有些成员是类那么在进入构造函数之前会先调用一次默认构造函数进入构造函数后所做的事其实是一次赋值操作(对象已存在)所以如果是在构造函数体内进行赋值的话等于是一次默认构造加一次赋值而初始化列表只做一次赋值操作。
15.如何避免编译器进行的隐式类型转换explicit⭐⭐⭐⭐
将构造函数声明为explicit来防止隐式类型转换。
explicit关键字只能用于类内部的构造函数声明上而不能用在类外部的函数定义上。
四、网络编程
1.TCP、UDP的区别 ⭐⭐⭐⭐⭐ 2.TCP、UDP的优缺点⭐⭐⭐
3.TCP UDP适用场景⭐⭐⭐
UDP 常用于以下几个方面 1.包总量较少的通信DNS、SNMP等 2.视频、音频等多媒体通信即时通信 3.限定于 LAN 等特定网络中的应用通信 4.广播通信广播、多播
4.TCP为什么是可靠连接⭐⭐⭐⭐
检验和通过检验和的方式接收端可以检测出来数据是否有差错和异常假如有差错就会直接丢弃TCP 段重新发送。序列号/确认应答机制过程如下 发送端每次发送数据时TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收端对分配的这个序列号进行确认如果发送端在一个特定时间内没有收到接收端的确认则发送端会重传此数据包。 接收端利用序列号对接收的数据进行确认以便检测对方发送的数据是否有丢失或者乱序等接收端一旦收到已经顺序化的数据它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。超时重传原理 TCP协议要求在发送端每发送一个报文段就启动一个定时器并等待确认信息接收端成功接收新数据后返回确认信息。若在定时器超时前数据未能被确认TCP就认为报文段中的数据已丢失或损坏需要对报文段中的数据重新组织和重传。
5.典型网络模型简单说说有哪些⭐⭐⭐
网络模型一般是指OSI七层参考模型和TCP/IP四层参考模型。
OSI分层(7层)物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP分层(4层)网络接口层、网际层、运输层、应用层。
网络层IP协议、ICMP协议、ARP协议、RARP协议。
传输层UDP协议、TCP协议。
应用层FTP(文件传送协议)、Telnet(远程登录协议)、DNS(域名解析协议)、SMTP(邮件传送协议)POP3协议(邮局协议)HTTP协议。
6.Http1.1和Http1.0的区别⭐⭐⭐
7.URI统一资源标识符和URL统一资源定位符之间的区别⭐⭐
8.什么是三次握手⭐⭐⭐⭐⭐ 三次握手的目的是建立可靠的通信通道说到通信简单来说就是数据的发生与接收而三次握手最主要的目的就是双方确认自己与对方的发送与接收是否正常
第一次握手Client什么都不能确认Server确认了对方发送正常自己接收正常
第二次握手Client确认了自己发送、接收正常对方发送正常、接收正常Server确认了对方发送正常自己接收正常
第三次握手Client确认了自己发送、接收正常对方发送正常接收正常Server 确认了对方发送正常接收正常自己发送正常接收正常。
9.为什么三次握手中客户端还要发送一次确认呢可以二次握手吗⭐⭐⭐⭐
下面解释明明两次就可以建立连接的为什么还要加第三次的确认。
如果发送两次就可以建立连接话那么只要客户端发送一个连接请求服务端接收到并发送了确认就会建立一个连接。
可能出现的问题如果一个连接请求在网络中跑的慢超时了这时客户端会从发请求但是这个跑的慢的请求最后还是跑到了然后服务端就接收了两个连接请求然后全部回应就会创建两个连接浪费资源
如果加了第三次客户端确认客户端在接受到一个服务端连接确认请求后后面再接收到的连接确认请求就可以抛弃不管了。
10.为什么服务端易受到SYN攻击⭐⭐⭐⭐
服务端的资源分配是在二次握手时分配的而客户端的资源是在三次握手时分配的。 SYN攻击即客户端在短时间内伪造大量不存在的IP地址并向SERVER端不断的发送SYN包SERVER收到请求即回复确认并等待客户端的确认由于源地址不存在因此SERVER需要不断的重发直到超时。 这些伪造的SYN包长时间占用未连接队列导致正常的SYN请求因为队列满而丢弃因为网络拥塞。 一般服务端会重试5次。 分类: 嵌入式100题
11.什么是四次挥手⭐⭐⭐⭐⭐ 第一次挥手 Client端发起挥手请求向Server端发送标志位是FIN报文段设置序列号seq此时Client端进入FIN_WAIT_1状态这表示Client端没有数据要发送给Server端了。
第二次分手Server端收到了Client端发送的FIN报文段向Client端返回一个标志位是ACK的报文段ack设为seq加1Client端进入FIN_WAIT_2状态Server端告诉Client端我确认并同意你的关闭请求。
第三次分手 Server端向Client端发送标志位是FIN的报文段请求关闭连接同时Client端进入LAST_ACK状态。
第四次分手 Client端收到Server端发送的FIN报文段向Server端发送标志位是ACK的报文段然后Client端进入TIME_WAIT状态。Server端收到Client端的ACK报文段以后就关闭连接。此时Client端等待2MSL的时间后依然没有收到回复则证明Server端已正常关闭那好Client端也可以关闭连接了
12.为什么客户端最后还要等待2MSL⭐⭐⭐⭐
TIME_WAIT状态的时长设置为2MSL的主要原因
确保被动关闭TCP连接的一端能收到第四次挥手的ACK 避免上一次TCP连接的数据包影响到下一次的TCP连接。
13.为什么建立连接是三次握手关闭连接确是四次挥手呢⭐⭐⭐⭐
建立连接时因为当Server端收到Client端的SYN连接请求报文后可以直接发送SYNACK报文。其中ACK报文是用来应答的SYN报文是用来同步的。所以建立连接只需要三次握手。
由于TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议TCP是全双工模式。这就意味着关闭连接时当Client端发出FIN报文段时只是表示Client端告诉Server端数据已经发送完毕了。当Server端收到FIN报文并返回ACK报文段表示它已经知道Client端没有数据发送了但是Server端还是可以发送数据到Client端的所以Server很可能并不会立即关闭SOCKET直到Server端把数据也发送完毕。当Server端也发送了FIN报文段时这个时候就表示Server端也没有数据要发送了就会告诉Client端我也没有数据要发送了之后彼此就 会愉快的中断这次TCP连接。
五、常见算法
1.各种排序算法的时间空间复杂度、稳定性⭐⭐⭐⭐⭐ 2.各种排序算法什么时候有最好情况、最坏情况尤其是快排 ⭐⭐⭐⭐
3.冒泡排序⭐⭐⭐⭐
https://www.runoob.com/w3cnote/bubble-sort.html
4.选择排序⭐⭐⭐⭐
5.插入排序⭐⭐⭐⭐
6.希尔排序⭐⭐⭐⭐
7.归并排序⭐⭐⭐⭐
8.快速排序⭐⭐⭐⭐⭐
9.快排的partition函数与归并的Merge函数⭐⭐⭐
void Merge(vectorint Array, int front, int mid, int end) {// preconditions:// Array[front...mid] is sorted// Array[mid1 ... end] is sorted// Copy Array[front ... mid] to LeftSubArray// Copy Array[mid1 ... end] to RightSubArrayvectorint LeftSubArray(Array.begin() front, Array.begin() mid 1);vectorint RightSubArray(Array.begin() mid 1, Array.begin() end 1);int idxLeft 0, idxRight 0;LeftSubArray.insert(LeftSubArray.end(), numeric_limitsint::max());RightSubArray.insert(RightSubArray.end(), numeric_limitsint::max());// Pick min of LeftSubArray[idxLeft] and RightSubArray[idxRight], and put into Array[i]for (int i front; i end; i) {if (LeftSubArray[idxLeft] RightSubArray[idxRight]) {Array[i] LeftSubArray[idxLeft];idxLeft;} else {Array[i] RightSubArray[idxRight];idxRight;}}
}void MergeSort(vectorint Array, int front, int end) {if (front end)return;int mid (front end) / 2;MergeSort(Array, front, mid);MergeSort(Array, mid 1, end);Merge(Array, front, mid, end);
}template typename T
void quick_sort_recursive(T arr[], int start, int end) {if (start end)return;T mid arr[end];int left start, right end - 1;while (left right) { //在整个范围内搜寻比枢纽元值小或大的元素然后将左侧元素与右侧元素交换while (arr[left] mid left right) //试图在左侧找到一个比枢纽元更大的元素left;while (arr[right] mid left right) //试图在右侧找到一个比枢纽元更小的元素right--;std::swap(arr[left], arr[right]); //交换元素}if (arr[left] arr[end])std::swap(arr[left], arr[end]);elseleft;quick_sort_recursive(arr, start, left - 1);quick_sort_recursive(arr, left 1, end);
}
template typename T //整數或浮點數皆可使用,若要使用物件(class)時必須設定小於()、大於()、不小於()的運算子功能
void quick_sort(T arr[], int len) {quick_sort_recursive(arr, 0, len - 1);
}10. vector list异同⭐⭐⭐⭐⭐
vector底层实现是数组list是双向链表。vector支持随机访问list不支持。vector是顺序内存list不是。vector在中间节点进行插入删除会导致内存拷贝list不会。vector一次性分配好内存不够时才进行2倍扩容list每次插入新节点都会进行内存申请。vector随机访问性能好插入删除性能差list随机访问性能差插入删除性能好。
11. vector内存是怎么增长的vector的底层实现⭐⭐⭐⭐
vector是按照2倍方式扩容的扩容需要经过以下步骤
开辟新空间 拷贝元素 释放旧空间 使用新空间
12.几种模板插入的时间复杂度 ⭐⭐⭐⭐⭐ 13. vector和deque的比较⭐⭐⭐⭐ 两端都能快速安插和删除元素这些操作可以在分期摊还的常数时间amortized constant time内完成。 元素的存取和迭代器的动作比vector稍慢。 迭代器需要在不同区块间跳转所以它非一般指针。 因为deque使用不止一块内存而vector必须使用一块连续内存所以deque的max_size()可能更大。 不支持对容量和内存重新分配时机的控制。不过deque的内存重分配优于vector因为其内部结构显示deque不必在内存重分配时复制所有元素。 除了头尾两端在任何地方安插或删除元素都将导致指向deque元素的所有pointers、references、iterators失效。 deque的内存区块不再被使用时会自动被释放。deque的内存大小是可自动缩减的。 deque与vector组织内存的方式不一样。在底层deque按“页”page或“块”chunk来分配存储器每页包含固定数目的元素。而vector只分配一块连续的内存。例如一个10M字节的vector使用的是一整块10M字节的内存而deque可以使用一串更小的内存块比如10块1M的内存。所以不能将deque的地址如deque[0]传递给传统的C API因为deque内部所使用的内存不一定会连续。
deque的下述特性与vector差不多 在中部安插、删除元素的速度较慢。 迭代器属于random access iterator随机存取迭代器。
14.为什么stl里面有sort函数list里面还要再定义一个sort⭐⭐⭐
虽然STL中提供了标准的sort函数但是它只适用于随机存取的容器而显然list并不是这样的容器因此list提供了专用的链表排序函数虽然同样名为sort但是这个sort函数是list类的成员函数。
15. STL底层数据结构实现⭐⭐⭐⭐
C STL 的实现
1.vector 底层数据结构为数组 支持快速随机访问
2.list 底层数据结构为双向链表支持快速增删
3.deque 底层数据结构为一个中央控制器和多个缓冲区详细见STL源码剖析P146支持首尾中间不能快速增删也支持随机访问 deque是一个双端队列(double-ended queue)也是在堆中保存内容的.它的保存形式如下: [堆1] -- [堆2] --[堆3] -- … 每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品.
4.stack 底层一般用list或deque实现封闭头部即可不用vector的原因应该是容量大小有限制扩容耗时
5.queue 底层一般用list或deque实现封闭头部即可不用vector的原因应该是容量大小有限制扩容耗时
stack和queue其实是适配器,而不叫容器因为是对容器的再封装
6.priority_queue 的底层数据结构一般为vector为底层容器堆heap为处理规则来管理底层容器实现
7.set 底层数据结构为红黑树有序不重复
8.multiset 底层数据结构为红黑树有序可重复
9.map 底层数据结构为红黑树有序不重复
10.multimap 底层数据结构为红黑树有序可重复
11.hash_set 底层数据结构为hash表无序不重复
12.hash_multiset 底层数据结构为hash表无序可重复
13.hash_map 底层数据结构为hash表无序不重复
14.hash_multimap 底层数据结构为hash表无序可重复
16.利用迭代器删除元素会发生什么⭐⭐⭐⭐
1对于关联容器如mapsetmultimapmultiset删除当前的iterator仅仅会使当前的iterator失效只要在erase时递增当前的iterator即可。这是因为map之类的容器使用了红黑树来实现插入删除一个结点不会对其他结点造成影响。 2对于序列式容器如vectordequelist等删除当前的iterator会使后面所有元素的iterator都失效。这是因为vectordeque使用了连续分配的内存删除一个元素导致后面所有的元素会向前移动一个位置。不过erase方法可以返回下一个有效的iterator。
17. map是如何实现的查找效率是多少⭐⭐⭐⭐⭐
C STL中的map用红黑树实现搜索效率是O(lgN)
六、Linux操作系统
1. Linux内核的组成⭐⭐
Linux内核主要由五个子系统组成
进程调度内存管理虚拟文件系统网络接口进程间通信。
2.用户空间与内核通信方式有哪些⭐⭐⭐⭐⭐
系统调用提供特定的用户空间与内核空间的信息传递。 信号内核空间出现一些异常时候会发送信号给进程如SIGSEGV、SIGILL、SIGPIPE等。 /procproc可以读取内核空间的信息并且设置部分属性的值需要循环检测。 文件可以通过指定文件的读写操作来实现通信但是流程不够实时需要循环检测来实现。 netlink类似socket通信方式可以读写大量的数据实现稍微复杂。 ioctl可以实现数据量比较少时候的通信。
3.系统调用read()/write()内核具体做了哪些事情⭐⭐
用户空间read()–内核空间sys_read()–scull_fops.read–scull_read()
该过程分为两个部分用户空间的处理和核心空间的处理。在用户空间中通过 0x80 中断的方式将控制权交给内核处理内核接管后经过6个层次的处理最后将请求交给磁盘由磁盘完成最终的数据拷贝操作。在这个过程中调用了一系列的内核函数。
4.系统调用的作用⭐⭐⭐⭐⭐
(1) 系统调用可以为用户空间提供访问硬件资源的统一接口以至于应用程序不必去关 注具体的硬件访问操作。
比如读写文件时应用程序不用去管磁盘类型甚至于不用关心是哪种文件系统。
(2) 系统调用可以对系统进行保护保证系统的稳定和安全。系统调用的存在规定了用 户进程进入内核的具体方式
换句话说用户访问内核的路径是事先规定好的只能从规定位置进入内核而不准许肆意跳入内核。有了这样的进
入内核的统一访问路径限制才能保证 内核的安全。
5.内核态用户态的区别⭐⭐⭐⭐⭐
用户空间就是用户进程所在的内存区域相对的系统空间就是操作系统占据的内存区域。用户进程和系统进程的所有数据都在内存中。 开机加电系统启动后就对物理内存进行了划分。 当一个任务进程执行系统调用而陷入内核代码中执行时我们就称进程处于内核运行态或简称为内核态。 此时处理器处于特权级最高的0级内核代码中执行。 当进程处于内核态时执行的内核代码会使用当前进程的内核栈。 每个进程都有自己的内核栈。 当进程在执行用户自己的代码时则称其处于用户运行态用户态。
6. bootloader内核 根文件的关系⭐⭐⭐⭐
7. Bootloader多数有两个阶段的启动过程⭐⭐⭐
8. linux的内核是由bootloader装载到内存中的⭐⭐⭐
linux的内核的确是由bootloader装载到内存中的。 linux的bootloader有2个部分组成bootstrap和uboot。
9.为什么需要BootLoader⭐⭐⭐⭐
10. Linux内核同步方式总结⭐⭐⭐⭐
原子操作 信号量semaphore 读写信号量rw_semaphore Spinlock Mutex BKL(Big Kernel Lock只包含在2.4内核中不讲) Rwlock brlock只包含在2.4内核中不讲 RCU只包含在2.6内核及以后的版本中 seqlock只包含在2.6内核及以后的版本中
11.为什么自旋锁不能睡眠 而在拥有信号量时就可以⭐⭐⭐⭐
12. linux下检查内存状态的命令⭐⭐⭐
13.大小端的区别以及各自的优点哪种时候用⭐⭐⭐⭐⭐
大端序Big-Endian将数据的低位字节存放在内存的高位地址高位字节存放在低位地址。这种排列方式与数据用字节表示时的书写顺序一致符合人类的阅读习惯。 小端序Little-Endian将一个多位数的低位放在较小的地址处高位放在较大的地址处则称小端序。小端序与人类的阅读习惯相反但更符合计算机读取内存的方式因为CPU读取内存中的数据时是从低地址向高地址方向进行读取的。 小端模式 强制转换数据不需要调整字节内容1、2、4字节的存储方式一样。 大端模式 符号位的判定固定为第一个字节容易判断正负。
14. 一个程序从开始运行到结束的完整过程四个过程⭐⭐⭐⭐⭐
预处理.i文件。在预编译的过程中主要处理源代码中的预处理指令引入头文件去除注释处理所有的条件编译指令宏的替换添加行号保留所有的编译器指令。当进行预编译以后的文件中将不再存在宏所有的宏都已经被替代。 编译.s文件。在预处理结束后进行的是编译。编译过程所进行的是对预处理后的文件进行语法分析词法分析语义分析符号汇总然后生成汇编代码。 汇编.o文件。汇编过程将汇编代码转成二进制文件二进制文件就可以让机器来读取。每一条汇编语句都会产生一句机器语言。这个阶段会形成符号表并给这些符号分配虚拟地址。 链接链接程序的主要工作就是将有关的目标文件彼此相连接也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体
15.什么是堆栈内存泄漏和内存溢出⭐⭐⭐⭐
堆heap是由malloc之类函数分配的空间所在地。地址是由低向高增长的。
栈stack是自动分配变量以及函数调用的时候所使用的一些空间。地址是由高向低减少的。
内存溢出out of memory通俗理解就是内存不够通常在运行大型软件或游戏时软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小就叫内存溢出。
内存泄漏Memory Leak是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放造成系统内存的浪费导致程序运行速度减慢甚至系统崩溃等严重后果。
16.硬链接与软链接的区别⭐⭐⭐⭐⭐
1.软链接是存放另一个文件的路径的形式存在。 2.软链接可以 跨文件系统 硬链接不可以。 3.软链接可以对一个不存在的文件名进行链接硬链接必须要有源文件。 4.软链接可以对目录进行链接。
17.虚拟内存虚拟地址与物理地址的转换⭐⭐⭐⭐
虚拟内存借助于地址转换操作系统可以给应用程序一种假象独占整个计算机内存可以使用超过实际物理大小的内存应用程序之间互不干扰。 进程隔离地址转换可以用来构建沙盒SandBoxes技术让第三方代码在沙盒中运行限制其对内存的访问从而避免操作系统内核和应用程序受到病毒或者恶意代码的攻击。 进程间通信地址转换可以将不同进程空间的地址映射到同一段物理内存从而实现进程间通信。 共享代码段动态加载so库可以在多个程序实例之间进行共享。 程序初始化使用地址转换技术可以让应用程序只加载部分代码和数据就可以运行内核在后台继续加载剩余部分若执行到未加载部分则发生中断挂起应用程序待内核将剩余部分加载到内存后再恢复应用程序的执行。 缓存管理内核通过合理安排程序所在的内存位置来提高缓存效率。 内存映射文件将文件内容映射到应用程序地址空间这样一来文件的内容就可以被应用程序直接访问。 内存的两种视角
虚拟地址进程看到的内存地址称为虚拟地址他们不对应任何物理实体每个进程有自己的地址空间。 物理地址内存系统看到的地址称为物理地址他们用实际的地址去查找和存储内容。
虚拟寻址的方法。CPU生成一个虚拟地址(VA)然后MMU(Memory Management Unit 内存管理单元) 将虚拟地址翻译成实际的物理地址然后再进行物理寻址。 虚拟内存是计算机系统中的一种技术它通过将物理内存和磁盘空间结合起来为每个进程提供了一个统一的连续地址空间。在虚拟内存中使用虚拟地址来访问内存而不是直接使用物理地址。虚拟地址需要通过地址转换机制转换为对应的物理地址以实现内存的访问。
18.计算机中32bit与64bit有什么区别⭐⭐⭐
对于电脑CPU来说64位和32位就是CPU一次出来数据的能力32位就是32bit即一次可以处理4个字节的数据64位就是64bit即一次可以处理8个字节。 32位的系统许多支持4G的内存而64位则可以支持上百G的内存。
19.中断和异常的区别⭐⭐⭐⭐⭐
中断和异常
相同点都是CPU对系统发生的某个事情做出的一种反应。
区别中断由外因引起异常由CPU本身原因引起。
把中断分为外中断和内中断。
外中断——就是我们指的中断——是指由于外部设备事件所引起的中断如通常的磁盘中断、打印机中断等 内中断——就是异常——是指由于 CPU 内部事件所引起的中断如程序出错(非法指令、地址越界)。内中断(trap)也被译为“捕获”或“陷入”。 异常是由于执行了现行指令所引起的。由于系统调用引起的中断属于异常。 中断则是由于系统中某事件引起的该事件与现行指令无关。
20.中断怎么发生中断处理大概流程⭐⭐⭐⭐
21. Linux 操作系统挂起、休眠、关机相关命令⭐⭐
Linux 操作系统挂起、休眠、关机相关命令 立刻关机
shutdown -h now
shutdown -h 0
定时/延时关机
shutdown -h 10:00 shutdown -h 30 //单位为分钟
重启
reboot
待机/挂起
sudo pm-suspend
sudo pm-suspend-hybrid
echo “mem” /sys/power/state
sudo hibernate-ram
休眠
sudo pm-hibernate
echo “disk” /sys/power/state
sudo hibernate-disk
22.数据库为什么要建立索引以及索引的缺点⭐⭐
23.堆和栈的区别⭐⭐⭐⭐⭐
1管理方式不同。栈由操作系统自动分配释放无需我们手动控制堆的申请和释放工作由程序员控制容易产生内存泄漏
2空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。理论上程序员可申请的堆大小为虚拟内存的大小进程栈的大小 64bits 的 Windows 默认 1MB64bits 的 Linux 默认 10MB
3生长方向不同。堆的生长方向向上内存地址由低到高栈的生长方向向下内存地址由高到低。
4分配方式不同。堆都是动态分配的。栈有2种分配方式静态分配和动态分配。静态分配是由操作系统完成的比如局部变量的分配。动态分配由alloca函数进行分配但是栈的动态分配和堆是不同的他的动态分配是由操作系统进行释放无需我们手工实现。
5分配效率不同。栈由操作系统自动分配会在硬件层级对栈提供支持分配专门的寄存器存放栈的地址压栈出栈都有专门的指令执行这就决定了栈的效率比较高。堆则是由C/C提供的库函数或运算符来完成申请与管理实现机制较为复杂频繁的内存申请容易产生内存碎片。显然堆的效率比栈要低得多。
6存放内容不同。栈存放的内容函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候要对当前函数执行断点进行保存需要使用栈来实现首先入栈的是主函数下一条语句的地址即扩展指针寄存器的内容EIP然后是当前栈帧的底部地址即扩展基址指针寄存器内容EBP再然后是被调函数的实参等一般情况下是按照从右向左的顺序入栈之后是被调函数的局部变量注意静态变量是存放在数据段或者BSS段是不入栈的。出栈的顺序正好相反最终栈顶指向主函数下一条语句的地址主程序又从该地址开始执行。堆一般情况堆顶使用一个字节的空间来存放堆的大小而堆中具体存放内容是由程序员来填充的。
从以上可以看到堆和栈相比由于大量malloc()/free()或new/delete的使用容易造成大量的内存碎片并且可能引发用户态和核心态的切换效率较低。栈相比于堆在程序中应用较为广泛最常见的是函数的调用过程由栈来实现函数返回地址、EBP、实参和局部变量都采用栈的方式存放。虽然栈有众多的好处但是由于和堆相比不是那么灵活有时候分配大量的内存空间主要还是用堆。
无论是堆还是栈在内存使用时都要防止非法越界越界导致的非法内存访问可能会摧毁程序的堆、栈数据轻则导致程序运行处于不确定状态获取不到预期结果重则导致程序异常崩溃这些都是我们编程时与内存打交道时应该注意的问题。
24.死锁的原因、条件 创建一个死锁以及如何预防⭐⭐⭐⭐⭐
产生死锁的原因两个 由竞争资源引起死锁多个进程共享资源资源不足竞争资源。 进程推进顺序不当引起死锁进程运行过程中请求和释放资源的顺序不当而导致进程死锁。
产生死锁的四个必要条件
① 互斥条件——进程要求对所分配的资源进行排它性控制即在一段时间内某资源仅为一进程所占有。
② 请求和保持条件——当进程因请求资源而阻塞时对已获得的资源保持不放。
③ 不剥夺条件——进程已获得的资源在未使用完之前不能被剥夺只能在使用完时由自己释放。
④ 环路等待条件——在发生死锁时必然存在一个“进程—资源”的环形链。
解决死锁的基本方法四种 ① 预防死锁——通过设置某些限制条件以破坏产生死锁的四个必要条件中的一个或几个来防止发生死锁。
② 避免死锁——在资源的动态分配过程中使用某种方法去防止系统进入不安全状态从而避免了死锁的发生。
③ 检测死锁——检测死锁方法允许系统运行过程中发生死锁。但通过系统所设置的检测机构可以及时检测出死锁的发生并精确地确定与死锁有关的进程和资源然后采取适当措施从系统中消除所发生的死锁。
④ 解除死锁——解除死锁是与检测死锁相配套的一种设施用于将进程从死锁状态下解脱出来。常用的方法是撤销或者挂起一些进程以便于释放出一些资源再将它分配给已经处于阻塞的进程使其转换为就绪状态可以继续运行。解决死锁的基本方法四种 ① 预防死锁——通过设置某些限制条件以破坏产生死锁的四个必要条件中的一个或几个来防止发生死锁。
② 避免死锁——在资源的动态分配过程中使用某种方法去防止系统进入不安全状态从而避免了死锁的发生。
③ 检测死锁——检测死锁方法允许系统运行过程中发生死锁。但通过系统所设置的检测机构可以及时检测出死锁的发生并精确地确定与死锁有关的进程和资源然后采取适当措施从系统中消除所发生的死锁。
④ 解除死锁——解除死锁是与检测死锁相配套的一种设施用于将进程从死锁状态下解脱出来。常用的方法是撤销或者挂起一些进程以便于释放出一些资源再将它分配给已经处于阻塞的进程使其转换为就绪状态可以继续运行。
单片机
1 CPU 内存 虚拟内存 磁盘/硬盘 的关系⭐⭐⭐ CPU从内存或缓存中取出指令放入指令寄存器并对指令译码分解成 系统指令的执行。 内存(即物理 内存是相对于硬盘这个“外存”而言)作为硬盘和CPU的“中转站”对电脑运行速度有较大影响。 当运行数据超出物理内存容纳限度的时候部分数据就会自行“溢出”虚拟内存运行的程序或 不使用的数据存放到这部分空间之中等待需要的时候方便及时调用。 由于内存是带电存储的(一旦断电数据就会消失)而且容量有限有了硬盘
2 CPU内部结构⭐⭐⭐⭐ 3 ARM结构处理器简析 ⭐⭐
4波特率是什么为什么双方波特率要相同高低波特率有什么区别⭐⭐⭐⭐
波特率是指数据传送时每秒传送数据二进制代码的位数它的单位是位/秒b/s。1波特就是一位每秒。
5.arm和dsp有什么区别⭐⭐
ARM具有比较强的事务管理功能可以用来跑界面以及应用程序等其优势主要体现在控制方面它的速度和数据处理能力一般但是外围接口比较丰富标准化和通用性做的很好而且在功耗等方面做得也比较好所以适合用在一些消费电子品方面
而DSP主要是用来计算的比如进行加密解密、调制解调等优势是强大的数据处理能力和较高的运行速度。由于其在控制算法等方面很擅长所以适合用在对控制要求比较高的场合比如军用导航、电机伺服驱动等方面。
如果只是着眼于嵌入式应用的话嵌入式CPU和DSP的区别应该只在于一个偏重控制一个偏重运算了。
DSP的优势主要是速度它可以在一个指令周期中同时完成一次乘法和一次加法这非常适合快速傅立叶变换的需求。DSP有专门的指令集,主要是专门针对通讯和多媒体处理的而ARM使用的是RISC指令集当然ARM的E系列也支持DSP指令集是通用处理用的。
6 ROM RAM的概念浅析⭐⭐⭐
只读存储器(Read Only MemoryROM)。ROM所存数据一般是装入整机前事先写好的整机工作过程中只能读出而不像随机存储器那样能快速地、方便地加以改写。ROM所存数据稳定断电后所存数据也不会改变。随机存取存储器(Random Access MemoryRAM)又称作“随机存储器”是与CPU直接交换数据的内部存储器也叫主存(内存)。它可以随时读写而且速度很快通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。当电源关闭时RAM不能保留数据。如果需要保存数据就必须把它们写入一个长期的存储设备中(例如硬盘)。RAM和ROM相比两者的最大区别是RAM在断电以后保存在上面的数据会自动消失而ROM不会自动消失可以长时间断电保存。
7 IO口工作方式上拉输入 下拉输入 推挽输出 开漏输出⭐⭐⭐⭐ 浮空输入 当IO口没有接输入的时候此时的电平会是一个不确定的值也就是我们所说的浮空。电平会处于一个跳变的状态一会高一会低。只有输入了一个高/低电平才会确定下来。 上拉输入 在没有信号输入的时候此时的电平就是VDD的电平此时读取到的电平就是高电平。如果输入了一个高电平VDD和O点(最上面的图中的O点)之间就几乎没有电势差此时O点的电平就仍然是高电平读取到的电平就是高电平。当输入信号是一个低电平的时候此时O点的电平的电平就会变成低电平那么VDD和O点之间形成了电势差但是因为上拉电阻的存在所以不会出现一个大电流。此时单片机读取到的一个电平就是一个低电平。上拉输入的好处就是输入的电平不会上下浮动而导致输入信号不稳定在没有信号输入的情况下可以稳定在高电平。 下拉输入 : 在没有信号输入的时候根据电路知识电平就是VSS的电平此时读取到的电平就是低电平。此时输入的电平如果是一个低电平就没有办法和之前的情况进行区分。但如果输入的是一个高电平O点和VSS之间同样形成了电势差O点的电平会变成外部的高电平那么单片机得到的就是一个高电平信号。 下拉输入的好处就是输入的电平不会上下浮动而导致输入信号不稳定在没有信号输入的情况下可以稳定在低电平。 模拟输入 : 模拟输入需要走的路径如图所示。首先得知道模拟输出走的这一条路径是我们需要对一个模拟信号进行读取。 在我们使用单片机的时候我们有时候需要用AD采集到IO口上面的真实电压。这就有了我们所需要的模拟输入。为了让外部的电压真实的读取到单片机的AD模块我们既不能闭合上拉和下拉的开关也不能让信号经过施密特触发器。 优势可以让AD读取电压。还可以在低功耗模式下运行实现省电的作用。 开漏输出 我们可以把这一个MOS管当成一个三极管对于图中所示的这种三极管我们可以简单的理解成一个水龙头左侧就是一个水龙头开关当给一个高电平的时候 O点和GND就会导通。O点的输出就是一种反向器的输出也就是O点的电平会和左侧MOS的栅极(三极管的基极)相反 所以说开漏输出就很好理解了。当我们给一个低电平的时候MOS管关闭此时输出的电压就是一个浮空即不确定的电压。如果给一个高电平那么MOS管导通相当于IO口与VSS相连此处就输出了一个低电平电压。 优势① 虽然我们可以看到开漏输出是没有办法在内部输出一个高电平但是这一个看似是缺点。其实实际上是一种优点。我们可以得到当给一个低电平的时候MOS管没有导通此时电压不确定导致无法输出高电平但是一旦我们在外部增加一个上拉那么这一个缺点就会被有效避免。并且因为是我们自己设计一个上拉这个上拉的电压是由我们自己确定这样我们就可以根据外部电路需要多少V的高电平来给这一个上拉的电压可以更好的适应更多情况。如下图我们可以给定任意的VDD电压来适应我们实际所需要的情况。 优势② 开漏输出的实质其实就是一个OD门OD漏极输出(Open Drain)。而在数电中OD门有一个非常重要的特性就是可以实现线与的功能简单来说就是在像IIC这样的总线协议中只要有一个给低电平那么总线都会被拉低。 推挽输出 推挽输出就是可以需要利用两个不同的MOS管来实现输出。P-MOS和N-MOS是不同的控制方式当给一个高电平的时候N-MOS不导通P-MOS导通此时IO口接通在VDD此时输出的是高电平。当给一个低电平的时候N-MOS导通P-MOS不导通此时IO口接通在VSS 电源上面此时输出的是低电平。 一定要把这个MOS管理解成开关控制的水龙头 优势 带载能力强。
8扇区 块 页 簇的概念⭐⭐⭐⭐
扇区是磁盘中最小的物理存储单位。通常情况下每个扇区的大小是512字节。 块是操作系统中最小的逻辑存储单位。操作系统与磁盘打交道的最小单位是磁盘块。 与内存操作是虚拟一个页的概念来作为最小单位。与硬盘打交道就是以块为最小单位。吧
9简述处理器在读内存的过程中CPU核、cache、MMU如何协同工作画出CPU核、cache、MMU、内存之间的关系示意图加以说明⭐⭐
10请说明总线接口USRT、I2C、USB的异同点串/并、速度、全/半双工、总线拓扑等⭐⭐⭐⭐⭐
UART通用异步串行口速率不快可全双工结构上一般由波特率产生器、UART发送器、UART接收器组成硬件上两线一收一发。
I2C双向、两线、串行、多主控接口标准。速率不快半双工同步接口具有总线仲裁机制非常适合器件间近距离经常性数据通信可实现设备组网。
SPI高速同步串行口高速可全双工收发独立同步接口可实现多个SPI设备互联硬件3~4线。
USB通用串行总线高速半双工由主机、hub、设备组成。设备可以与下级hub相连构成星型结构。
11什么是异步串口和同步串口⭐⭐⭐⭐⭐
串行通信进行数据传送时是将要传送的数据按二进制位依据一定的顺序逐位发送到接收方。其有两种通信方式异步通信和同步通信。
异步通信是指数据传送以字符为单位字符与字符间的传送是完全异步的位与位之间的传送基本上是同步的。异步通信采用固定的通信格式数据以相同的帧格式传送。每一帧由起始位、数据位、奇偶校验位和停止位组成。异步串行通信的特点可以概括为
以字符为单位传送信息。 相邻两字符间的间隔是任意长。 因为一个字符中的比特位长度有限所以需要的接收时钟和发送时钟只要相近就可以。 异步方式特点简单的说就是字符间异步字符内部各位同步。 异步位系统是面向字符来传输信息的也就是我们一般情况下的一个字符8位1bit当然了传输的时候还要加上起始位和结束位没有这两位接收方就不知道什么时候开始接收数据什么时候结束了。
同步通信是指数据传送是以数据块一组字符为单位字符与字符之间、字符内部的位与位之间都同步。同步通信时通信双方共用一个时钟这是同步通信区分于异步通信的最显著的特点。同步串行通信的特点可以概括为
以数据块为单位传送信息。 在一个数据块信息帧内字符与字符间无间隔。 因为一次传输的数据块中包含的数据较多所以接收时钟与发送进钟严格同步通常要有同步时钟。
12 I2C时序图⭐⭐⭐⭐⭐