504. 孤儿进程和僵尸进程是什么?怎么处理?
孤儿进程:当一个父进程结束,而他的一个或多个子进程还在运行时,那些子进程将成为孤儿进程。孤儿进程会被init进程(进程ID为1)自动领养,init进程会负责调用wait()来收集他们的退出状态。
僵尸进程:当一个子进程结束,在其父进程没有调用wait()或waitpid()获取子进程的状态信息之前,该子进程将变成僵尸进程。僵尸进程占用资源很小,但如果大量累计可能导致进程ID耗尽。
处理方法:
- 孤儿进程不需要手动处理,他们会被操作系统处理。
- 僵尸进程需由父进程通过wait()或waitpid()来处理和回收资源。如果父进程不做处理,可以试图用SIGCHLD信号提醒父进程处理僵尸进程,或者重启父进程。如果僵尸进程的父进程已经结束,那么这些僵尸进程将有init进程回收。
505. 计算机网络中的长连接和短连接
长连接:在客户端和服务端建立连接后,如果不主动断开,这个连接将会保持活动状态,多次通信可以复用同一个连接。长连接减少了频繁建立连接的开销,适用于需要频繁交换数据的场景。
短连接:每次通信都需要新建一个连接,数据传输完成后立即断开。短连接适用于请求-响应模式的通信,适合偶尔交换数据的场景。相较于长连接,他跟简单,但在高频通信时会增加额外的开销。
506. TIME_WAIT和CLOSE_WAIT的区别
TIME_WAIT
: 是TCP连接被动关闭(接收到对方发送的FIN报文)并发送了ACK后的状态。此状态通常等待一段时间以确保对方接收到了确认的ACK报文,通常是2MSL
(最长报文段生存时间)。
CLOSE_WAIT
: 是当一方接收到对方的FIN报文,发送了ACK响应,但本地应用程序还没有调用close关闭连接时的状态,这意味着等待本地程序关闭连接。
507. 栈的概念和应用
栈是一种后进先出的数据结构,只能在一端(栈顶)进行添加数据(压栈)和移除数据(弹栈)的操作。
应用:
- 函数调用的管理:保存函数返回点和局部变量。
- 表达式求值:编译器中用于计算后缀表达式。
- 撤销操作:编辑器中的撤销操作通常用栈实现。
- 页面导航历史:浏览器后退按钮的实现。
508. 堆的概念和应用,对堆的理解
堆是一种通常被实现为二叉树的数据结构,它满足父节点的键值总是大于或小于子节点的键值(最大堆或最小堆)。它允许快速的插入新数据和删除最大或最小的元素。
应用:
- 优先队列:快速访问和删除最高或最低优先级的元素。
- 堆排序:利用堆的性质进行高效的排序操作。
- 图算法:比如在Dijkstra和Prim算法中用于快速查找最小边。
- 动态数据集合的管理:动态的添加和删除集合中的元素。
509. 哈希表的概念和应用
哈希表是一种通过哈希函数将键(key)映射到表中一个位置来访问记录的数据结构,以支持快速的插入和删除操作。
应用:
- 字典:存储键值对,允许快速的键查询。
- 缓存:例如网页缓存或数据缓存,提供快速的数据检索。
- 数据库索引:提高数据库查询效率。
- 集合:实现集合数据结构,进行快速的成员检查。
510. 哈希表碰撞的概念以及如何解决
哈希表碰撞是指两个不同的键经过哈希函数处理后得到相同的哈希值,导致他们在哈希表中定位到同一个位置。
解决方法包括:
- 链接法:在哈希表的每个索引位置维护一个链表,发生碰撞时将元素添加到链表中。
- 开放地址法:发生碰撞时,按照某种探测序列在哈希表中寻找空位。
- 双重哈希:使用第二个哈希函数来决定探测序列。
- 增大哈希表:扩展哈希表的大小以减少碰撞概率。
511. 哈希表的种类
哈希表的种类主要包括:
- 直接寻址法:当键的范围很小且连续时,可以直接使用键作为数组索引。
- 链地址法哈希表:通过链表来解决冲突,每个索引处存储一个链表。
- 开放寻址哈希表:在表内寻找空闲位置来解决冲突,常见的探测方式有线性探测、二次探测和双重哈希。
- 完全哈希表:先用一个哈希函数将键分配到多个桶中,然后对每个桶使用另一个哈希函数进行再哈希,以减少冲突。
- 一致性哈希表:特别适用于分布式系统,通过一致性哈希算法保证节点的增减对系统影响最小。
512. OSI七层模型
- 物理层:负责原始数据的传输,如光纤、电缆等。
- 数据链路层:处理数据帧的寻址、错误检测和改错。
- 网络层:负责数据包的路由和转发。
- 传输层:确保数据完整性和有效传输,如TCP/UDP协议。
- 会话层:管理用户会话和对话控制。
- 表示层:处理数据的表示、加密和压缩。
- 应用层:为应用软件提供网络服务。
513. TCP当中的粘包和拆包的概念以及如何解决这两类问题
TCP中的粘包和拆包是因为TCP是基于字节流的,没有固定边界:
- 粘包 : 多次发送的数据被合并成一次接收。
- 拆包 : 一次发送的数据被分成多次接收。
解决方法:
- 定长包:每个包的大小固定,接收方按照固定长度接收数据。
- 包头加长度:每个包的开始部分增加表示数据长度的信息,接收方先读取这部分的信息,再根据长度读取整个包。
- 结束符:在包尾添加结束符,接收方通过这个字符判断包的终点。
514. MySQL中的varchar和char的概念以及区别
varchar和char是MySQL中用于存储字符串的两种数据类型。
char
: 是固定长度的字符串,当存储的字符串长度小于定义的长度时,会使用空格填充余下的空间;检索时会去除这些空格。适合存储长度几乎不变的数据。varchar
:是可变长度字符串,只占用必要的存储空间(加上一个额外的用于记录长度的字节或两个字节)。适合存储长度可变的数据。
区别在于存储方式和空间效率。char适合存储长度固定的字段,而varchar更节省空间,用于长度不固定的字段。
515. 删除表的方式有哪几种?
删除MySQL表的主要方式有以下几种:
DROP TABLE
: 直接删除表结构及其数据,无法恢复。TRUMCATE TABLE
: 删除表中的数据,重置自增计数器,但保留表结构。DELETE TABLE
: 删除表中的全部或部分数据行,可以使用 WHERE 子句指定条件,保留表结构和自增计数器的当前值。
516. MySQL的索引类型
MySQL中的主要索引包括:
-
按照功能分类:
- PRIMARY KEY(主键索引):唯一标识表中的每行数据,不能为NULL。
- UNIQUE(唯一索引):确保数据列中的每个值都是唯一的。
- INDEX(普通索引):加速数据的检索速度。
- FULLTEXT(全文索引):用于全文检索。
-
按照数据结构分类:
- B+树索引
- 哈希索引
-
按照存储位置分类:
- 聚簇索引
- 非聚簇索引
517. MySQL的行锁有哪些特点和类型
MySQL的行锁特点包括:
- 粒度小,仅锁定单个或部分行,减少锁竞争。
- 支持高并发事务处理。
- 可以防止脏读、不可重复读和幻读(取决于事务隔离级别)。
MySQL行锁的类型主要有:
- 共享锁(S锁):允许事务读一行数据。
- 排他锁(X锁):允许事务删除或更新一行数据。
- 意向锁:表明事务想要在表中的某些行上获取共享锁或排他锁,分为意向共享锁(IX)和意向排他锁(IS)。
518. MySQL中死锁的产生原因和处理机制
产生原因:
- 多个事务同时锁定不同资源后,又请求对方已锁定的资源,形成循环等待。
- 索引不足导致全表扫描,增加锁竞争。
- 插入,更新操作导致的锁升级。
- 事务隔离级别较高,增加所的范围和时间。
处理机制:
- 超时机制:设置事务等待锁的最大时间,超时后事务自动回滚。
- 死锁检测:MySQL会不断检测事务等待图中是否存在环,发现死锁立即回滚其中一个事务来进行破坏环。
- 主动回滚:用户可以选择主动回滚事务来解除死锁。
- 减少锁粒度:优化SQL,减少一次事务中锁定的数据量,降低死锁发生概率。
519. 操作系统中内存分页和分段的概念以及区别
内存分页和分段是操作系统中两种不同的内存管理机制。
分页(Paging):
- 概念:将物理内存分为固定大小的页,虚拟内存也分为同样大小的页。一个页表用于映射虚拟页到物理页。
- 区别:透明于用户,用户程序看不到分页的存在。
分段(Segmentation):
- 概念:将虚拟内存划分为逻辑上的段,每个段有其意义(如函数、数组等),段表实现虚拟段到物理内存的映射。
- 区别:分段是可见的,即用户定义了段的大小和内容。
两者的区别主要在于:
- 分页是物理内存的映射技术,而分段是逻辑上对内存的划分。
- 分页有固定大小、分段大小不一,依据程序逻辑结构而定。
- 分段能够更好地反映程序的逻辑结构,而分页主要解决内存碎片和扩展问题。
- 分页管理简单,CPU加载时不必了解数据意义,分段管理复杂但方便程序片段的保护和共享。
520. 操作系统中I/O多路复用的概念和工作原理
I/O多路复用是指操作系统中可以在一个进程内同时处理多个Socket网络I/O操作的技术。它允许多个I/O操作同时阻塞在一个系统调用上,从而提高处理并发I/O的效率。
工作原理如下:
- 应用程序将需要监听的文件描述符(通常是socket)注册到I/O多路复用函数(如select、poll、epoll)上。
- 当有数据到达或连接关闭等事件发生时,操作系统将唤醒阻塞在I/O多路复用函数上的进程或线程。
- 应用程序遍历多路复用函数提供的就绪文件描述符列表进行相关处理。
I/O多路复用的优势在于使用单个线程处理多个I/O事件,减少了线程的创建和销毁开销,提高了I/O并发的处理能力。
521. C++多态性如何体现?模板怎么实现的多态?
C++中的多态性主要通过两种方式体现:虚函数(动态多态性)和模板(静态多态性)。
- 虚函数实现的多态性:通过基类指针或者引用,调用由不同的派生类重载的虚函数,实现在运行时根据对象的实际类型调用相应的函数处理,这被称为动态多态。
- 模板实现的多态性:模板通过允许函数或类操作泛型类型,实现在编译时根据使用的实际类型生成具体的函数或实例,这被称为静态多态。模板多态不依赖于继承体系,因此提供了一种更为灵活的多肽实现方式。
522. 在C++的多继承里面,怎么知道调用的方法和属性属于哪一个父类?
如果需要指定调用特定父类的方法或者属性,应在方法或属性名称前加上相应的父类的名称,使用作用域解析操作符(::)。
523. C++指针和智能指针的区别?智能指针的实现原理是什么?指针和函数指针的区别?
- 指针和智能指针的区别:指针是C++的核心特性,用于存储对象的内存地址。使用时需要手动分配和释放内存,否则会导致内存泄漏等问题。智能指针则是一种对象,像常规指针一样,可以指向另一个对象,但它包含更加完善的内存管理机制,可以自动释放它所指向的资源,减少内存泄露和其他相关错误。
- 智能指针的实现原理:智能指针的实现主要基于RALL概念。其主要含义是,将一个对象的生命周期(从初始化到销毁)与某项资源的获取和释放(如内存、文件描述符、锁等)绑定起来。当对象销毁时,其对应的资源也随之被释放。例如,shared_ptr智能指针通过引用计数来管理资源,但没有任何指针引用该资源时,资源会被自动释放。
- 指针和函数指针的区别:指针是指向一个内存地址的变量,用于存储和访问对象的地址。函数指针是指向函数的指针,它可以用于调用函数或传递函数作为参数。
524. C++什么时候可能出现内存泄漏?如何定位到内存泄漏?
C++可能出现内存泄漏的情况包括但不限于:
- 动态分配的内存没有被正确释放,例如使用new分配内存后没有使用delete释放。
- 资源分配和释放之间程序提前退出(如异常抛出)导致释放代码未执行。
- 指针变量被覆盖,导致原有的内存地址丢失而无法释放。
- 在程序数据结构中的循环引用,特别是在智能指针未正确使用时。
定位内存泄露的方法有:
- 利用C++标准库中的< memory >头文件,使用智能指针如std::unique_ptr和std::shared_ptr以帮助自动管理内存。
- 使用内存管理工具,如Valgrind、AddressSanitizer等,他们可以在程序运行时检测内存泄漏。
- 在代码中使用断言和监测,追踪动态内存的分配和释放。
525. 析构函数必须是虚函数么?
析构函不是必须声明为虚函数,但如果你的类是多态基类,即预期会被其它类继承,并且通过基类指针或引用来删除派生类对象,那么你应该将析构函数声明为虚函数。这确保了对象的正确析构,触发动态绑定,以调用适当的派生类析构函数。如果不这么做,在通过基类指针删除派生类对象时,可能会导致资源泄露或未定义的行为。
526. new和malloc申请内存有什么差别?
- new是C++中的操作符,malloc是C中的函数。
- new不仅分配内存,还会创建对象(即会调用构造函数),而malloc只分配内存,不会初始化内存。
- new直接返回指定类型的正确指针,无需类型转换,而malloc返回void*,在使用前需要转换为正确的类型。
- 与new配套的是delete,与malloc配套的是free。
- 如果内存分配失败,new会抛出bad_alloc异常,而malloc返回NULL。
- 可以重载new和delete,但不能重载malloc和free。