程序员自我修炼指南——从删库到跑路

rm / -rf

一、linux相关

1.1 线程和进程

进程间都是相互独立的,产生一个新的进程,大部分的数据都被独立开来,每个进程拥有自己的堆区、栈区等数据,遵循“读时共享、写时复制”的原则。
多个线程间大部分数据是共享的,如全局变量、堆等,都是共享的,线程只有自己独立的栈以及线程ID等少量资源,线程还拥有自己独立的信号屏蔽字。

  • forkwait有什么作用,fork进程时的操作。

fork会产生一个子进程,在子进程中,fork返回0,父进程中,返回新创建的子进程id。
wait是父进程用来回收子进程资源用的,避免子进程不回收处在僵尸态。
fork后子进程和父进程采用“读时共享、写时复制”的方式来运行。

  • 父子进程、孤儿进程以及僵尸进程,僵尸进程如何产生和消除。

在进程中调用fork函数会产生一个子进程,当前进程即为新进程的父进程,父进程调用fork返回子进程ID,子进程返回0。
孤儿进程是指某个进程的父进程执行完退出了,但是子进程还在执行,此时子进程就是孤儿进程,会被1号进程init回收。
僵尸进程是指子进程运行完毕退出了,父进程没有调用wait或者waitpid回收子进程资源,导致子进程处于僵尸态。消除僵尸进程的方法是杀掉父进程,使子进程被1号进程收养并回收掉。

  • 进程间通信:共享内存、匿名管道、有名管道、消息队列、信号量和socket。
  • linux应用层常见的锁,什么是死锁、如何避免死锁。

互斥量、读写锁、自旋锁、信号量、屏障以及条件变量等
死锁是指进程间由于加锁不当导致程序hang住的现象,死锁只会发生在一个线程试图锁住另一个线程以相反顺序锁住的互斥量以及同一个进程对同一把锁重复加锁。
避免死锁:锁住的区域尽量少,加完即使释放,尽量不多多个进程同时加多把锁,加锁时注意顺序。

  • 守护进程,操作以及实现原理。

1.2 网络编程

  • socket API:connect/accept/bind/listen/send/sendto/recv/recvfrom/select/gethostbyname
  • 异步IO框架:select/poll/epoll
  • 什么是阻塞模式和非阻塞模式,两种模式下的socket在connectsend以及recv等行为上的区别,如何将socket设置为非阻塞的

阻塞模式:函数没有达到触发状态时,会一直卡在这里处于等待状态,直到又事件发生为止。
将socket设置为非阻塞的只要使用fcntl函数给文件描述符的状态加上O_NONBLOCK标记即可。如果是文件描述符,直接在打开时也可以设置非阻塞。

边沿触发(ET模式):epoll中产生可读事件后,如果没有读完缓冲数据域,剩余的数据部分将会在下次有数据过来时才能继续读。
水平触发(LT模式):epoll产生可读事件后,如果没有一次没有读完,epoll_wait()继续返回可读事件。
正常来说,ET模式比LT模式效率更高,因为它在epoll产生的触发事件更少,更多情况下都采用“ET+非阻塞”模式。

  • libeventrefactor模式。

libevent的核心思想便是refactor,当一个socket产生可读事件时,读取数据,然后设置可写事件,再对socket写数据。
工作流程:socket --> 添加到epoll --> 可读 --> 从epoll中摘下,读数据 --> 设置可写事件 --> 放回红黑树

  • 为什么select有文件句柄限制,pollepoll没有。

select的文件句柄限制是写死在头文件里面的,编译时就已经固定了。poll和select工作原理类似,它的出现就是为了解决select文件句柄限制的问题。
而epoll基于事件触发,和前面两者不同,它底层通过红黑树来存储文件描述符,插入删除的效率非常高。

  • epoll_event结构中的epoll_data_t的fd与ptr的使用场景。

以refactor模式为例,可以把fd设置为当前的文件描述符,ptr设置为函数指针,当socket有可读或者可写事件的时候,自动调用函数指针执行,从而达到异步IO的效果。

  • select函数可以检测网络异常吗

可以用过select设置超时时间来检测网络异常,很多时候可以通过非阻塞socket+select来对函数添加超时逻辑。

  • send/recv以及read/write返回值大于0、等于0、小于0的区别,错误码EINTR如何处理。

大于0表示读到n字节的数据,等于0表示对端已经关闭连接,小于0表示错误,需要判断错误码。
常见的错误码EINTR表示遇到中断了,重新读。EAGAIN也要重新读。

  • 发送数据缓冲区与接收数据缓冲区如何设计,如何编写正确的收数据代码与发数据代码。
  • shutdown与优雅关闭

close会直接关闭两端的socket连接,并销毁内存对象。shutdown只会关掉本机的连接,对向还可以传输数据。也就是TCP的瓣关闭状态

  • 如何解决tcp粘包问题。

添加协议,最简单的就是添加一个数据包头,然后写明该条数据包的长度n。读取时根据长度来读数据。

  • http代理、socks4代理与socks5代理如何编码实现。
  • gethostbyname阻塞与错误码获取问题

gethostbyname默认是阻塞的

  • 如何清除无效的死链(端与端之间的线路故障)

长连接,持续发送心跳包检测。

  • 长连接和短连接

长连接表示连接一直处于打开状态,并且定时发送心跳包,当一定时间内心跳包如果发送失败或者没有收到回包,就认为连接发生故障。长连接的目的就是为了实时检测网络故障,避免出现连接异常断开,但是另一端并不知情还处在打开的状态。

1.3 linux操作系统

1.4 linux系统和shell命令

  • crontabiptables等命令的的使用
  • 进程、线程状态查看命令top/strace/pstack`
  • 内存状态查看命令memstat/free
  • IO状态查看命令iostat/df/du`
  • linux文件的权限、用户、时间(ctime/mtime/atime)和inode
  • 文件传输命令scp/rz/sz命令
  • 文件定位命令find/whereis命令
  • 硬链接和软链接,ln命令的用法,硬链接和软连接区别
  • lsof命令
  • kill用法,某个进程杀不掉的原因(进入内核态,忽略kill信号)
  • 系统管理命令(如查看内存使用、网络情况)
  • 管道的使用|
  • grep的使用
  • 文本处理命令awk/sed/grep
  • 本编辑工具vi/vim
  • 了解shell基本语法、变量操作、函数、循环/条件判断等程序结构
  • linux环境下的几种文件类型
  • linux常用端口和网络命令

二、C/C++语言相关

2.1 语言

  • 程序的内存布局:(堆、栈、静态/全局/局部变量)
  • 函数的堆栈,函数参数的堆栈
  • 指针数组,数组指针,函数指针,n级指针
  • 结构体对齐
  • 静态数组和动态数组区别

    > 静态数组是编译时就分配好大小的数组,内存在栈区,能分配的大小有限。动态是程序运行后动态调用`malloc`或者`new`生成的,内存在堆区,可分配大小相较于栈区较大。
    > 在网上又看到一种说法,说动态数组会执行类的默认构造函数,内置类型不会被初始化。但是实际测试中发现不管静态动态数组都会执行默认构造函数,并且动态分内的内置数据类型空间也被初始化了,怀疑可能是编译器的原因(`gcc 4.4`)。静态分配的确实是随机值。
    
  • 重载overlode和重写override的区别

重载是同一个函数名的不同种实现方式,通过入参的不同调用不同的函数。注意:重载函数不能以返回值为区别。
重写是指子类覆盖父类的函数,隐藏父类实现达到多态的机制。

virtual是实现多态的前提,对函数使用virtual将在类中产生虚函数表。对类使用virtual该类将变成虚基类。

  • 虚函数机制,画出并解释虚函数表运行机制,虚表的布局
  • 菱形继承:B和C继承A,D继承B和C)时每个对象的空间结构分布,比如问D有几份虚表,D中B和C的成员空间排布。
  • 指针和引用的区别
  • new/delete/new[]/delete []/malloc/free的区别
  • RAII/pimpl的惯用方式
  • struct 和类的区别(不只是默认访问权限)
  • C中的struct和C++的 struct 的区别
  • 接口和抽象类区别
  • 构造函数的调用顺序(子类父类之间)

先调用父类的构造函数,然后调用子类的构造函数。

  • 静态类静态方法
  • 内存泄漏的原因及避免措施

内存泄漏的原因是因为申请了内存没有被释放掉,写代码时应有良好的编程习惯,对申请的内存一定要即时释放。
如果程序中很难分析出内存泄漏的话,可以利用工具来检测,如gprofile和Valgrind memcheck等。

  • 递归的使用场景及注意事项
  • static的作用
  • sizeof
  • gdb的使用方法(断点、查看内存、执行跟踪、了解CPU主要寄存器作用…)
  • 性能分析工具(gprof)

2.2 C++ 11相关

  • 智能指针的实现原理
  • boost库使用
  • auto关键字、for-each循环、右值及移动构造函数

2.3 STL

  • 迭代器的使用方式,迭代器失效
  • set承载结构体
  • 容器内存扩充原理
  • vecotr 和list 的区别,适用情况
  • vector的扩容机制

2.4 实现函数

strcpy/memcpy/memmov/atoi

2.5 设计模式

  • OO思想、常见设计模式(单例模式、工厂设计模式、装饰者模式、Builder模式、生产者消费者模式、策略模式等)
  • Observer模式的思想,策略模式的思想,两者区别,优缺点,适用情况
  • OOP特性,通过哪些机制实现的

三、计算机网络

3.1 基础网络知识

  • TCP/UDP报头格式,tcp和ip包头常见有哪些字段
  • TCP/IP协议栈层次结构
  • TCP三次握手和四次挥手,各种状态的细节点:CLOSE_WAIT、TIME_WAIT、MSL
  • TCP与UDP的区别与适用场景
  • nagle / keepalive / linger等选项的区别
  • 拥塞控制慢开始、拥塞避免、快重传、滑动窗口协议、停止等待协议、超时重传机制
  • IP地址子网划分
  • DNS解析过程
  • 地址解析协议ARP
  • 交换机和路由器的区别
  • http和https区别,https在请求时额外的过程,https是如何保证数据安全的
  • SESSION机制、cookie机制

session和cookie的目的相同,都是为了克服http协议无状态的缺陷,但完成的方法不同。 session通过cookie,在客户端保存session id,而将用户的其他会话消息保存在服务端的session对象中, 与此相对的,cookie需要将所有信息都保存在客户端。因此cookie存在着一定的安全隐患,例如本地cookie 中保存的用户名密码被破译,或cookie被其他网站收集(例如:1. appA主动设置域B cookie,让域B cookie获取;2. XSS,在appA上通过javascript获取document.cookie,并传递给自己的appB)。

  • HTTP请求方法(GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE)
  • 熟悉tcpdump命令,windows下wireshark使用

3.2 常见问题

  • A与B建立了正常连接后,从未相互发过数据,这个时候B突然机器重启,问A此时的tcp状态处于什么状态?如何消除服务器程序中的这个状态
  • 聊天用什么协议(UDP),为什么使用UDP
  • 微信qq用什么协议
  • 跟多个人聊天怎么实现的(多线程),多线程怎么判断和哪个人聊天,需要设置什么全局变量

四、数据库

4.1 MySql

  • 数据表结构设计(三范式、字段属性)
  • 查询优化(索引的概念与创建、sql优化)
  • 事务四大特性(ACID)
  • 数据库隔离级别,每个级别会引发什么问题,mysql默认是哪个级别
  • MYSQL的两种存储引擎区别(事务、锁级别等等),各自的适用场景
  • 数据库的优化(从sql语句优化和索引两个部分回答)
  • 索引有B+索引和hash索引,各自的区别
  • B+索引数据结构,和B树的区别
  • 索引的分类(主键索引、唯一索引),最左前缀原则,哪些情况索引会失效
  • 聚集索引和非聚集索引区别
  • 有哪些锁(乐观锁悲观锁),select时怎么加排它锁
  • 关系型数据库和非关系型数据库区别
  • 数据库的主从复制
  • long_query怎么解决
  • 使用explain优化sql和索引
  • 内连接、外连接、交叉连接、笛卡儿积等
  • MVCC机制
  • 死锁怎么解决
  • varchar和char的使用场景。
  • mysql并发情况下怎么解决(通过事务、隔离级别、锁)
  • 字典树
  • 平时怎么写数据库的模糊查询

4.2 Redis

  • redis基本数据结构
  • redis队列应用场景
  • redis和Memcached的区别(支持数据持久化)
  • 分布式使用场景(储存session等)
  • 发布/订阅使用场景
  • redis的数据存储结构、rehash;
  • redis的事务与集群。
  • redis的持久化
  • redis热备
  • redis和的hash和string选择的优劣
  • redis单线程设计的原因
  • redis淘汰策略

五、数据结构和算法

5.1 排序和查找

5.2 树

  • AVL和红黑树的区别和原理实现
  • 哈夫曼树,哈夫曼编码。哈夫曼译码
  • AVL树和B树的概念、细节
  • 红黑树的概念、平均算法复杂度、最好最坏情况下的算法复杂度,左右旋转、颜色变换
  • 层次遍历、求深度、求两个节点距离、翻转二叉树、前中后序遍历
  • B+树和B树的区别,插入节点怎么分裂

5.3 哈希表

  • 建表以及解决哈希冲突
  • 哈希表的冲突检测、哈希函数常用实现、算法复杂度
  • 哈希表插入元素算法,元素类型是任意类型
  • hashmap的实现原理 采用什么方法能保证每个bucket中的数据更均匀

5.4 链表

  • 删除一个节点
  • 单链表倒转
  • 两个链表找相交的部分
  • 插入节点、链表逆置、使用链表进行大数字的加减,双向链表实现队列、寻找链表中的环

5.5 堆

  • 大量数据中寻找最大N个数字几乎每次都会问
  • 堆在插入时进行的调整

5.6 栈和队列

  • 两个栈实现队列

5.6 其他

  • KMP算法描述

5.7 常见问题

  • 单纯反转,hello world! :!!dlrow olleh 剑指offer原题。然后问,如果不用栈怎么做。指针找空格,后往前反转
  • 1000万个数,取出top100 堆实现和二分
  • 有100W个数字存放在一个文件中,然后让你随机生成100个数字不要与文件中的数字重复。(关键代码实现)典型的大文件处理
  • 两个有序数组,a[],b[],大小分别为m,n,求第k 大的数
  • 在一组数里面找到一个数,比它左右相邻的数都要大
  • 一个文本文件中每一行中有一个URL,最多一万行,统计每一个URL的次数,输出到另外一个文件中,每一行前面是URL,后面是个数。
  • 一个函数实现给定字符串,去除前面和后面的空格,比如“ ab cd ”,最后得到的结果是”ab cd”,不能改变字符串的地址
  • 对比cookie和session,有一个值错误则不正确
  • 查找10的阶乘后面有几个0
  • 字符串移位,给出字符串abc##dfg##gh,实现将所有#移至字符串串头。输出####abcdfggh(个人认为可以用后向移位,减少移位次数)
  • 给出一颗二叉树,两个叶节点,找到这两个叶节点互连通的一条最短路径
  • 两个日期计算天数差
  • 100个有序数组合并
  • 在一个字符串中找出第一个字符出现的位置,保证高效

最后修改:2019 年 06 月 16 日 05 : 38 PM

发表评论