linux开机的步骤
linux开机步骤
运维的一些小题目
在/tmp/目录下创建test.txt文件,内容为: Hello,World! ,用一个命令写出来:
echo “Hello,World!” > /tmp/test.txt
用vi命令编辑test.txt,如何跳转到末行,首行,行首、行末,如何在光标行下一行插入,如何复制5行,删除10行,查找jingfeng的字符、把jingfeng替换为jfedu.net:
末行: G
首行: gg
行首: ^
行末: $
光标行下一行插入: o
复制5行:5yy
删除10行:10dd
替换::%s/jingfeng/jfedu.net/g
查找linux系统下以txt结尾,30天没有修改的文件大小大于20K同时具有执行权限的文件并备份到/data/backup/目录下。
find / -name *txt -mtime +30 -type f -size +20k -perm a=x -exec cp {} /data/backup/ \;
当前test.txt所属的用户为root,组为abc,请将test.txt使拥有者为abc,组为root,写出命令。
chown abc:root test.txt
每次开机在/tmp目录下创建一个当天的日期文件夹(提示:当前日期表示的方法为:date +%Y%m%d)
echo “mkdir /tmp/date +%Y%m%d” >> /etc/rc.d/rc.local
将普通用户test加入root组的命令是?
usermod -G root test
ps: -aux, - ef:显示所有进程的相关信息,-ef是按照bsd版本的格式显示出来
top:
top -p:显示指定的进程号的进程信息
top -S:以累积模式显示程序运行的时间
TCP为什要进行三次握手
如果采用两次的话,会出现下面这种情况。比如是A机要连到B机,结果发送的连接信息由于某种原因没有到达B机;于是,A机又发了一次,结果这次B收到了,于是就发信息回来,两机就连接。传完东西后,断开。 结果这时候,原先没有到达的连接信息突然又传到了B机,于是B机发信息给A,然后B机就以为和A连上了,这个时候B机就在等待A传东西过去。但是实际上此时A没有数据要传输,造成服务器事件白白的被浪费了。
多重继承为什么会含有多个虚表指针而不是一个?
这个属于编译器厂商做的事情,标准并未规范。C++的父亲就做出过这样的一款编译器原型,通过增大vtbl的体积,每个slot上不只有一个指针,还有一个offset,用来调整this指针的指向。 这样做的弊端是:所有vtbl中的虚函数指针都包含这样一个offset,并且假设不需要调整this指向,调用时还是要做offset的加法操作,尽管offset此时为0。另外,vtbl中每个slot体积的膨胀。这些都是效率问题。
TCP的错误处理
- connect导致套接字从CLOSED状态转换到SYN_SENT,若失败,则此套接字不可用,必须关闭,不能再次调用connect,需要重新调用socket。
- 服务器端用listen等待建立连接,在accept之前。维护两个队列:未完成连接队列(SYN_RECV)与已完成连接队列(ESTABLISHED)。
- 客户发出的SYN在中间路由器发生了“destination unreachable”ICMP错误,客户主机内核保存该信息,按照第一种情况所述时间间隔发送SYN。超时未收到相应,则把保存信息EHOSTUNREACH或ENETUNREACH错误返回给进程。
- 客户SYN到达之后,TCP在未完成连接队列中创建新的一项,然后响应以三路握手的第二个分节:服务器SYN,在队列中保持直到第三个分节到达或者超时(与connect类似的75s)。
- 对客户端的响应若是RST,则表明服务器端对应端口没有进程等待与之连接,硬错误,返回ECONNREFUSED错误
- 如果服务器端队列满,收到客户端SYN,会忽略,而不是发送RST,因为情况是暂时的,可能下次就能够有空闲队。RST会让connect立即返回一个错误。
- 此处是SYN攻击的发生位置。黑客可以伪造大量不存在的IP地址发送SYN建立连接,服务器需要不断回复确认,但是没有响应,继续重发,占用了等待队列,无法为正常连接服务,堵塞网络,其属于典型的DDOS攻击。如果检测到许多随机的半连接IP地址,基本可以判断为SYN攻击(netstat -n -p TCP | grep SYN_RECV)
- 如果一方已经关闭或异常终止,而另一方不知道,此时TCP连接为半打开。不传输数据,另一方就不会检测到,(此时可以设置TCP的KEEPALIVE选项,来定时检测),如果发送数据,另一方会返回RST分节。
- 第四个分节ACK丢失,服务器端(被动关闭)会重发FIN分节。(对照前文,第一个分节发送之后如果没有ack,应该也是超时重发,但此点不确定)
- TIME_WAIT状态的必要性:(1) 可靠的实现TCP全双工连接的终止。如果四次交换的最后ACK丢失,服务器将重发FIN,客户端需要能够正确处理,发送ACK,如不维护此状态,将发送RST信息,被解释为一个错误。为了可靠结束,必须应对不得不重传最终的ACK。(2)允许老的重复分节在网络中消逝。如果在相同的端口与IP之间建立了新的连接,原来的连接的包经过路由处理又重新发送过来,可能被认为是原来的包,因此TCP不给TIME_WAIT状态建立新的连接。2MSL可以保证所有老的包消逝。
- RST产生的三个条件: 1) SYN到达目的端口,但是服务器没有对应的监听进程; 2) TCP想取消一个已有连接; 3) TCP收到一个根本不存在的连接上的分节。
- Nagle算法:其会将很多小的包缓冲,从而避免网络中存在太多的小包(协议头比例非常大)造成拥塞,可以用TCP_NODELAY选项关闭。
TRUNCATE,DELETE,DROP比较:
TRUNCATE TABLE:删除内容、释放空间但不删除定义。
DELETE TABLE:删除内容不删除定义,不释放空间。
DROP TABLE:删除内容和定义,释放空间。
速度: drop> truncate > delete。
HTTP请求所经历的步骤
- 建立TCP连接:这其中设计到了DNS协议,ARP协议(先为DNS然后是ARP)
- Web浏览器向Web服务器发送请求命令:一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。
- Web浏览器发送请求头信息:浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
- Web服务器应答:客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
- Web服务器发送应答头信息:正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
- Web服务器向浏览器发送数据:Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
- Web服务器关闭TCP连接一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive,TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。(1.1里面支持keep-alive)
数据库索引的好处以及坏处
好处: 帮助用户提高查询速度, 利用索引的唯一性来控制记录的唯一性, 可以加速表与表之间的连接, 降低查询中分组和排序的时间
坏处: 存储索引占用磁盘空间, 执行数据修改操作产生索引维护
实现一个简单的Malloc,Free
malloc,free的简单实现
GDB常用的调试命令
backtrace(或bt) 查看各级函数调用及参数
finish 执行到当前函数返回,然后停下来等待命令
frame(或f) 帧编号 选择栈帧
info(或i) locals 查看当前栈帧局部变量的值
list(或l) 列出源代码,接着上次的位置往下列,每次列10行
next(或n) 执行下一行语句
print(或p) 打印表达式的值,通过表达式可以修改变量的值或者调用函 数
set var 修改变量的值
start 开始执行程序,停在main函数第一行语句前面等待命令
step(或s) 执行下一行语句,如果有函数调用则进入到函数中
break(或b) 行号 在某一行设置断点
break 函数名 在某个函数开头设置断点
break…if… 设置条件断点
continue(或c) 从当前位置开始连续而非单步执行程序
delete breakpoints 删除断点display 变量名
disable breakpoints 禁用断点
enable breakpoints 启用断点
info(或i) breakpoints 查看当前设置了哪些断点
run(或r) 从头开始连续而非单步执行程序
undisplay 取消对先前设置的那些变量的跟踪
watch 设置观察点
info(或i) watchpoints 查看当前设置了哪些观察点
Trie树的相关构造
节点如下:
class TrieNode { public: TrieNode():isLeaf(false) { for(auto & t : dic){ t = NULL; }
}
TrieNode * dic[26];
bool isLeaf;
};
ARP以及DNS,PING,Traceroute的工作原理
ARP: 主机向自己网络上广播一个ARP请求,其中包含目标的网络地址。这个这个网络上的所有主机都会收到这个请求,但是只有被请求的机器会应答一个ARP应答。
DNS: 在浏览器中输入www.qq.com域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。否则查找本地DNS解析器缓存,是否有这个网址映射关系,没有的话再去,根据/etc/resolv.conf文件,询问DNS服务器,由服务器按照自己的规则去查找对应的IP地址,返回给主机
PING:ping程序向指定的IP地址发送ICMP数据包,通过返回的信息来判断网络的连接状况.发送的ICMP(IP)报文上TTL字段会被设置成一个固定值,Linux里面是64.然后没经过一个路由器减去1.当TTL为0但是还没有到达目的地址的话,那么会回送一个ICMP unreachable报文,通知发其端无法连接到dst。src可能会增大TTL重新发送ICMP报文,几次之后还是连不通的话会报dst不可达的消息。
Traceroute程序的设计是利用ICMP及IP header的TTL。首先traceroute送出一个TTL是1的IP datagram到目的地,当路径上的第一个路由器收到这个datagram时,它将TTL减1变为0,所以该路由器会将此datagram丢掉,并送回一个「ICMP time exceeded」消息。traceroute 收到这个消息后,便知道这个路由器存在于这个路径上,接着traceroute再送出另一个TTL是2 的datagram,发现第2 个路由器…… traceroute每次将送出的datagram的TTL加1来发现另一个路由器,这个重复的动作一直持续到某个datagram 抵达目的地。当
长连接和短连接
连接->传输数据->关闭连接,短连接是指SOCKET连接后,发送接收完数据后马上断开连接。 因为连接后接收了数据就断开了,所以每次数据接受处理不会有联系。这也是HTTP协议无状态的原因之一。
连接->传输数据->保持连接 -> 传输数据->………..->直到一方关闭连接,多是客户端关闭连接。长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。
什么时候用长连接,短连接:
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,而且频繁的socket创建也是对资源的浪费。 比如说qq的维持在线显示状态使用的好像就是长连接,或者是网络游戏这样的,smtp,pop3,telnet这种就可以认为是长连接。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。比如百度的搜索服务,搜索完成之后建立的连接就会断开。
事件与消息的区别
事件是一个动作——用户触发的动作,消息是一个信息——传递给系统的信息。
事件:只能由用户的操作产生
消息:由操作系统产生。由用户触发的事件转换而来。由另一个消息产生。
Struct与Union之间的区别
结构体每个属性都可以有默认值,union只能有一个有默认值
关于虚表
C++ 虚函数表解析
Linux中进程调度方法
SCHED_OTHER: 分时调度方法
SCHED_FIFO: 先来先服务调度算法
SCHED_RR: 实时调度策略,采用的是时间片轮转
TCP三次握手的漏洞
当服务器接受到Syn的时候会会送SYN+ACK,这是连接就处于等待队列(backlog),服务器维护这种半连接也是消耗资源的。这样如果有恶意大量的连接请求SYN到来二一直不发ACK的话,那么服务器的半连接队列就会被占满,会组织正常的用户的访问。这就是SYN FLOOD攻击:通过网络服务所在的端口发送大量伪造原地址的攻击报文,发送到服务端,造成服务端上的半开连接队列被占满,从而阻止其他用户进行访问。它的数据报特征是大量syn包,并且缺少最后一步的ACK回复。
解决方法:
(1).无效连接监控:不停监视半开连接和不活动连接,当半开连接数和不活动连接数到达一定值时候,就释放系统资源。(这样实际上会将正确的连接也释放,不好)
(2).延缓TCB方法:SYN FLOOD的关键是利用了,syn数据报一到,系统就分配TCB资源。那么我们有两种方法资源问题:
Syn cache:这种技术在收到Syn时不急着分配TCB(TCP Control Block),而是先回应一个ACK报文,并在一个专用的HASH表中保存这种连接,直到收到正确的ACK,才分配TCB。
(3).Syn Cookie用一种特殊的算法生成sequence number,算法考虑到对方的信息和己方信息,收到对方的ACK报文后,验证之后才决定是否生成TCB
HTTP协议特点
1.支持客户/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTPS与HTTP的区别
HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。细节不再赘述
端口:前者是80,后者是443。
加密:https协议需要到ca申请证书,一般免费证书很少,需要交费。http是超文本传输协议,信息是明文传输
速度:http速度大概为https的8倍左右
hash相关
- 线性探测可能会造成主集团问题,但是二次探测可能会造成次集团的问题。
- STL中的冲突解决方法是开链法。
- STL中的hash表的大小是质数,源码中已经事先将28特定大小的质数计算好,以便随时取用。
- hashset和multi_hashset插入的时候调用的是insert_unique以及insert_equal,这样决定了他们插入的时候,前者不能包含重复的,后者可以。
虚表相关
- 虚函数表相关
- C++虚函数及虚函数表解析
- 只有虚函数,或者父类虚函数子类重写这个虚函数的时候会在虚表中出现或者替换父类在虚表中的位置,其他函数不会出现在虚表中。
- 父子类都存在虚函数的时候,父类的虚函数较子类的虚函数会放在虚表的前面
求两个树的最大公共子树
什么叫做MVC
- 模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
- Model(模型)表示应用程序核心(比如数据库记录列表).View(视图)显示数据(数据库记录).Controller(控制器)处理输入(写入数据库记录)
- MVC 分层有助于管理复杂的应用程序,因为您可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。MVC 分层同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。
url加载后台的过程
- DNS过程(查浏览器缓存,本机缓存,路由器缓存,DNS服务器缓存等等)
- 三次握手建立连接
- 发送http请求
- 服务器发送http响应(可能会存在永久重定向等等过程)
- 断开TCP连接
- 从输入url到显示网页,后台发生了什么?
static关键字在c++作用
- 文件中作用:作用域
- 函数中作用:长生存期,保持值
- 类中作用:类变量或者类函数,没有this指针,变量需要在外部初始化,否则链接错误,static可以用作callback
- 默认初始化值为0
static关键字在java中作用
- static方法:类方法,不需要this可以被调用,所以可以直接被static的方法调用
- static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行初始化。
- static代码块:static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次
QT与java在跨平台区别
- Qt的frameWork基于纯粹的c++语言编写,所以可以在不同的平台上运行,使得其上传量软件可以做到跨平台,而java在不同的平台上按照java虚拟机标准重新实现了不同的虚拟机标准,从而使得上层的应用可以实现跨平台。Qt的跨平台应该是偏向语言层次的,而java更加底层一点。
- java的运行是在虚拟机上运行的,而Qt程序的运行依旧靠的是原来的操作系统,所以不同的系统有不同的执行文件,但是java虚拟机由于对上层提供了统一的接口,所以执行文件都是一样的。
僵尸进程处理的两种方法
- 通过信号机制: 子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。测试程序如下所示:
static void sig_child(int signo);
int main()
{
pid_t pid;
//创建捕捉子进程退出信号
signal(SIGCHLD,sig_child);
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am child process,pid id %d.I am exiting.\n",getpid());
exit(0);
}
printf("I am father process.I will sleep two seconds\n");
//等待子进程先退出
sleep(2);
//输出进程信息
system("ps -o pid,ppid,state,tty,command");
printf("father process is exiting.\n");
return 0;
}
static void sig_child(int signo)
{
pid_t pid;
int stat;
//处理僵尸进程
while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
printf("child %d terminated.\n", pid);
}
- fork两次:原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。测试程序如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid >0)
{
printf("first procee is exited.\n");
exit(0);
}
sleep(3);
printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
exit(0);
}
if (waitpid(pid, NULL, 0) != pid)
{
perror("waitepid error:");
exit(1);
}
exit(0);
return 0;
}
http协议与https协议
HTTP协议请求响应过程和HTTPS工作原理
冯诺依曼体系结构
- 计算机处理的数据和指令一律用二进制数表示
- 顺序执行程序:计算机运行过程中,把要执行的程序和处理的数据首先存入主存储器(内存),计算机执行程序时,将自动地并按顺序从主存储器中取出指令一条一条地执行,这一概念称作顺序执行程序。
- 计算机硬件由运算器、控制器、存储器、输入设备和输出设备五大部分组成:运算器:实现算术运算和逻辑运算的部件。 控制器:计算机的指挥系统。控制器通过地址访问存储器,从存储器中取出指令,经译码器分析后,根据指令分析结果产生相应的操作控制信号作用于其他部件,使得各部件在控制器控制下有条不紊地协调工作。存储器:是计算机中用来存放所有数据和程序的记忆部件,它的基本功能是按指定的地址存(写)入或者取(读)出信息。 计算机中的存储器可分成两大类:一类是内存储器,简称内存或主存;另一类是外存储器(辅助存储器),简称外存或辅存。 存储器由若干个存储单元组成,每个存储单元都有一个地址,计算机通过地址对存储单元进行读写。输入设备:是向计算机中输入信息(程序、数据、声音、文字、图形、图像等)的设备。常见的输入设备有:键盘、鼠标、图形扫描仪、触摸屏、条形码输入器、光笔等。 外存储器也是一种输入设备。输出设备:主要有显示器、打印机和绘图仪等。外存储器也当作一种输出设备。
怎么用C++去设计java的GC
- 具体可以参见cocos2d-x的实现方法,简单的说就是:创建一个自动释放池,所有的对象创建的时候要求添加到池中,每个对象有一个引用计数,然后定期或者按照某个触发条件去check下释放池,如果池中变量只有一个引用的时候那么释放掉。
有八个外形一样的铁球,其中一个比另外七个稍微重些,给你一个天平称,称两次,把那个重点的球找出来
- 先称 3 3, 如果相等, 那么从剩下两个里面和这三个里面的任意一个对比两次得到重的求。不相等,那么找重的那一边随便两个和剩下两个求沉重,平衡的话那么重的在三个里面剩下的那个球上。不平衡的话那么,从这两个里面随便取一个球,与标准的称,相等那么重的是剩下的哪个,否则找到这个重球
64位的CPU,64代表
- 64位就是CPU的地址线长度,CPU一个时钟周期内接受的最大数据量。
vim查找,替换以及全局替换
- 查找 /str 替换 s/str1/str2 全局s/str1/str2/g
有两台电脑,一台A时间准确,一台B时间不准,怎么才能把B调到最准?
- B连续访问A的time服务100次,求出平均延迟,以防偶然误差,然后最后在访问时间上加上延迟时间的一半(准确时间从A到B只经过了一半路程)
hash_map和map的性能区别
- hash_map在hash方法选取良好的时候效率较好,但是可能因为hash方法选取不当或者是hash表中元素的数量太大而导致性能下降,可能达到O(N)。map在最好的情况下的性能还是没有hash_map好的,但是其插入以及查找的复杂度是很稳定的(因为其树是平衡的)。页只能说二者各有优劣。
可以chmod 077吗
UDP调用connect有什么作用
- 因为UDP可以是一对一,多对一,一对多,或者多对多的通信,所以每次调用sendto()/recvfrom()时都必须指定目标IP和端口号。通过调用connect()建立一个端到端的连接,就可以和TCP一样使用send()/recv()传递数据,而不需要每次都指定目标IP和端口号。但是它和TCP不同的是它没有三次握手的过程。
- 还可以通过在已建立连接的UDP套接字上,再次调用connect()实现以下功能: a.指定新的IP地址和端口号。 b.断开连接。 这也与TCP有所不同,TCP套接字只能调用一次connect()函数。
TIME_WAIT状态过多的解决办法
- 在 /etc/sysctl.conf中加入
net.ipv4.tcp_tw_recycle = 1
(表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭),局域网环境下,700ms就回收
- 或者是让TCP的TIME_WAIT状态可以重用:这样即使TIME_WAIT占满了所有端口,也不会拒绝新的请求造成障碍:
echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
- ps: 查看本地TCP连接状态的一条语句
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
Connect会造成阻塞在,怎么解决这个问题
- 也可以采用非阻塞模式。设置非阻塞,返回之后用select检测状态。
如果select返回可读,结果只读到0字节,什么情况
- 某个套接字集合中没有准备好,可能会select内存用FD_CLR清该位为0
TCP常见的选项字段有哪些
- 窗口扩大因子
- TCPNODELAY
- SACK选择确认选项
- MSS字段
- 时间戳
SOCKET那些情况下可读,哪些情况下可写
- 下列四个条件中的任何一个满足时,socket准备好读:
- socket的接收缓冲区中的数据字节大于等于该socket的接收缓冲区低水位标记的当前大小。对这样的socket的读操作将不阻塞并返回一个大于0的值(也就是返回准备好读入的数据)。我们可以用SO_RCVLOWATsocket选项来设置该socket的低水位标记。对于TCP和UDP .socket而言,其缺省值为1.
- 该连接的读这一半关闭(也就是接收了FIN的TCP连接)。对这样的socket的读操作将不阻塞并返回0
- socket是一个用于监听的socket,并且已经完成的连接数为非0.这样的soocket处于可读状态,是因为socket收到了对方的connect请求,执行了三次握手的第一步:对方发送SYN请求过来,使监听socket处于可读状态;正常情况下,这样的socket上的accept操作不会阻塞;
- 有一个socket有异常错误条件待处理.对于这样的socket的读操作将不会阻塞,并且返回一个错误(-1),errno则设置成明确的错误条件.这些待处理的错误也可通过指定socket选项SO_ERROR调用getsockopt来取得并清除;
- 下列三个条件中的任何一个满足时,socket准备好写:
- socket的发送缓冲区中的数据字节大于等于该socket的发送缓冲区低水位标记的当前大小。对这样的socket的写操作将不阻塞并返回一个大于0的值(也就是返回准备好写入的数据)。我们可以用SO_SNDLOWAT socket选项来设置该socket的低水位标记。对于TCP和UDPsocket而言,其缺省值为2048
- 该连接的写这一半关闭。对这样的socket的写操作将产生SIGPIPE信号,该信号的缺省行为是终止进程。
- 有一个socket异常错误条件待处理.对于这样的socket的写操作将不会阻塞并且返回一个错误(-1),errno则设置成明确的错误条件.这些待处理的错误也可以通过指定socket选项SO_ERROR调用getsockopt函数来取得并清除;
- Socket可读可写条件
从socket读数据时,socket缓存里的数据,可能超过用户缓存的长度,如何处理? 例如,socket缓存有8kB的数据,而你的缓存只有2kB空间。
- 可以考虑使用select或者epoll来实现,当发现写入的数据量小于待发送的数据大小的时候,使用epoll或者select继续监听socket上的写时间,等待下次socket可写的时候再进行写数据,如上往复,直到所有的内容都已经写完。
系统如何将一个信号通知到进程
- 内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。进程检查信号的时机是:进程即将从内核态返回用户态时。执行用户自定义的信号处理函数的方法很巧妙。把该函数的地址放在用户栈栈顶,进程从内核返回到用户态的时候,先弹出信号处理函数地址,于是就去执行信号处理函数了,然后再弹出,才是返回进入内核时的状态。进程处理信号的时机就是从内核态即将返回用户态度的时候。
宏的展开
- 首先用实参替换形参,将实参代入宏文本中; 然后如果实参也是宏,则展开实参。最后再继续处理宏替换后的宏文本,若宏文本也包含宏则继续展开,否则完成展开。
指针和数组名之间的关系
指针的退化
内存分配的三种方法
- 从静态存储区域分配。内存在程序加载之后就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
- 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
- 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
高危的C函数
- strcat():非常类似strcpy,它可以将一个字符串合并到缓冲区末尾
解决方法:可以使用方法 fgets()。它可以做与 gets() 所做的同样的事情,但它接受用来限制读入字符数目的大小参数,因此,提供了一种防止缓冲区溢出的方法。
- sprintf()、vsprintf():它们可以用直接的方式模仿 strcpy() 行为
- gets()该函数从标准输入读入用户输入的一行文本,它在遇到 EOF 字符或换行字符之前,不会停止读入文本。gets() 根本不执行边界检查。因此,使用 gets() 总是有可能使任何缓冲区溢出。
解决方法:可以使用方法 fgets()。它可以做与 gets() 所做的同样的事情,但它接受用来限制读入字符数目的大小参数,因此,提供了一种防止缓冲区溢出的方法。
- getchar()、fgetc()、getc()、read():如果在循环中使用这些函数,确保检查缓冲区边界
- scanf()系列 : sscanf()、fscanf()、vfscanf()、vscanf()、vsscanf(),目的地缓冲区也可能会发生溢出。
解决方法:我们用设置宽度也可以解决这个问题。
linux的文件系统
- /bin 基础系统所需要的命令位于此目录
- /boot 包含Linux内核及系统引导程序所需要的文件
-/dev 设备文件存储目录
- /etc 存放系统程序或者一般工具的配置文件
- /lib 库文件存放目录这里包含了系统程序所需要的所有共享库文件
- /lost+found :当系统意外崩溃或机器意外关机,而产生一些文件碎片放在这里
- /media 即插即用型存储设备的挂载点自动在这个目录下创建
- /opt 表示的是可选择的意思,有些软件包也会被安装在这里,也就是自定义软件包
- /proc 操作系统运行时,进程(正在运行中的程序)信息及内核信息(比如cpu、硬盘分区、内存信息等)存放在这里
- /root Linux超级权限用户root的家目录;
- /sbin 大多是涉及系统管理的命令的存放,是超级权限用户root的可执行命令存放地
- /tmp 临时文件目录,有时用户运行程序的时候,会产生临时文件。
- /usr 这个是系统存放程序的目录,比如命令、帮助文件等
- /var 这个目录的内容是经常变动的
永久重定向和临时重定向的区别
- 301(永久移动) 请求的网页已被永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置。当网页A用301重定向转到网页B时,搜索引擎可以肯定网页A永久的改变位置,或者说实际上不存在了,搜索引擎就会把网页B当作唯一有效目标。
- 302(临时移动) 服务器目前正从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。会自动将请求者转到不同的位置。
- 304(未修改) 自从上次请求后,请求的网页未被修改过。服务器返回此响应时,不会返回网页内容。
Get与Post的区别
- GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。
- GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。POST把提交的数据则放置在是HTTP包的包体中。
- GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据,IIS4中最大为80KB,IIS5中为100KB”??!
- POST比GET安全,因为数据在地址栏上不可见。
- GET请求会被浏览器缓存,存储在用户的浏览记录中
IPCS命令
- unix/linux下的共享内存、信号量、队列信息管理
- 在unix/linux下,经常有因为共享内存、信号量,队列等共享信息没有干净地清楚而引起一些问题。
查看共享信息的内存的命令是ipcs [-m|-s|-q]。
进程与线程的区别
使用场景:
- 需要频繁创建销毁的优先用线程:web服务器。来一个建立一个线程,断了就销毁线程。要是用进程,创建和销毁的代价是很难承受的。
- 需要进行大量计算的优先使用线程:图像处理、算法处理,所谓大量计算,当然就是要消耗很多cpu,切换频繁了,这种情况先线程是最合适的。
- 强相关的处理用线程,弱相关的处理用进程:一般的server需要完成如下任务:消息收发和消息处理。消息收发和消息处理就是弱相关的任务,而消息处理里面可能又分为消息解码、业务处理,这两个任务相对来说相关性就要强多了。因此消息收发和消息处理可以分进程设计,消息解码和业务处理可以分线程设计。
可能扩展到多机分布的用进程,多核分布的用线程
一个qq好友列表的同步设计(比如在A地做了添加操作,在B地做了删除操作,在C地怎么拿到最新的好友列表)
- A的添加操作提交给数据库,B的删除操作提交给数据库,数据库将两个操作的结果发送给C.
写一个程序判断系统是16位还是32位
int k=~0;
if((unsigned int)k > 65536)
cout<<"at least 32bits"<<endl;
else
cout<<"16 bits"<<endl;
const的含义及实现机制,比如:const int i,是怎么做到i只可读的
- C++把const内置类型看做常量,(g++)编译器会使用常数直接替换掉对i的引用
- 但结构体类型不是内置数据类型,编译器如何直接替换,因此必须要访问内存去取数据,而访问内存去取数据必然会取到被指针q改变后的值。所以没有任何机制保证了const声明的常量的不可修改性。
死锁的四个必要条件
- 互斥使用(资源独占) 一个资源每次只能给一个进程使用
- 不可强占(不可剥夺) 资源申请者不能强行的从资源占有者手中夺取资源,资源只能由占有者自愿释放
- 请求和保持(部分分配,占有申请) 一个进程在申请新的资源的同时保持对原有资源的占有
- 循环等待 存在一个进程等待队列
死锁的原因以及处理
exit()与_exit()的区别
- _exit终止调用进程,但不关闭文件,不清除输出缓存,也不调用出口函数。exit函数将终止调用进程。在退出程序之前,所有文件关闭,缓冲输出内容将刷新定义,并调用所有已刷新的“出口函数”(由atexit定义)。
创建一个守护进程的步骤
- (1)用来启动守护进程的终端在启动守护进程之后,需要执行其他任务。(2)(如其他用户登录该终端后,以前的守护进程的错误信息不应出现)由终端上的一些键所产生的信号(如中断信号),不应对以前从该终端上启动的任何守护进程造成影响。要注意守护进程与后台运行程序(即加&启动的程序)的区别。
- 调用fork创建子进程。父进程终止,让子进程在后台继续执行。
- 子进程调用setsid产生新会话期并失去控制终端调用setsid()使子进程进程成为新会话组长和新的进程组长,同时失去控制终端。
- 忽略SIGHUP信号。会话组长进程终止会向其他进程发该信号,造成其他进程终止。
- 调用fork再创建子进程。子进程终止,子子进程继续执行,由于子子进程不再是会话组长,从而禁止进程重新打开控制终端。
- 改变当前工作目录为根目录。一般将工作目录改变到根目录,这样进程的启动目录也可以被卸掉。
- 关闭打开的文件描述符,打开一个空设备,并复制到标准输出和标准错误上。 避免调用的一些库函数依然向屏幕输出信息。
- 重设文件创建掩码清除从父进程那里继承来的文件创建掩码,设为0。
- 用openlog函数建立与syslogd的连接。
void daemon_init(const char* pname,int facility)
{
int i;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
umask(0);
if(getrlimit(RLIMIT_NOFILE,&rl) < 0)
{
perror("getrlimit() error");
exit(-1);
}
if((pid = fork()) < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
exit(0);
setsid();
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL) < 0)
{
perror("sigaction() error");
exit(-1);
}
if((pid = fork()) < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
exit(0);
if(chdir("/")<0)
{
perror("chdir() error");
exit(-1);
}
if(rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for(i=0;i<rl.rlim_max;++i)
close(i);
openlog(pname, LOG_PID, facility);
}
- 守护进程实现时间服务器
- 后台的文件描述符也是继承于父进程,例如shell,所以它也可以在当前终端下显示输出数据。
但是daemon进程自己变成了进程组长,其文件描述符号和控制终端没有关联,是控制台无关的。守护进程肯定是后台进程,但反之不成立。守护进程顾名思义,主要用于一些长期运行,守护着自己的职责(监听端口,监听服务等)。
linux的内存管理机制
- 内存分配回收机制、缓存和刷新机制、请求页机制、交换机制和内存共享机制
五种I/O 模式
- 阻塞I/O (Linux下的I/O操作默认是阻塞I/O,即open和socket创建的I/O都是阻塞I/O)
- 非阻塞 I/O (可以通过fcntl或者open时使用O_NONBLOCK参数,将fd设置为非阻塞的I/O)
- I/O 多路复用 (I/O多路复用,通常需要非阻塞I/O配合使用)
- 信号驱动 I/O (SIGIO)
- 异步 I/O
IO 多路技术使用场景
- 当一个客户端需要同时处理多个文件描述符的输入输出操作的时候(一般来说是标准的输入输出和网络套接字),I/O多路复用技术将会有机会得到使用。
- 当程序需要同时进行多个套接字的操作的时候。
- 如果一个 TCP 服务器程序同时处理正在侦听网络连接的套接字和已经连接好的套接字。
- 如果一个服务器程序同时使用 TCP 和 UDP 协议。
- 如果一个服务器同时使用多种服务并且每种服务可能使用不同的协议(比如 inetd就是这样的)。
数据库的底层结构
- 一个数据库存储引擎,从底层到高层,依次可能用到:堆(外存存储记录的实体):定长页和变长页。定长页使用记录长度和下标可以定位到内容,变长页头部和记录是反向存储的,根据记录下标可以定位到记录头,根据记录头定位到记录存储。索引(外存存储索引的实体):B+树内存记录缓存:分级的内存池,每块记录空间的大小固定。采用LRU或者近似淘汰算法,异步将不常用页面刷出到索引和堆。(并非所有数据库都有这一层)
数据库优化
- 建立索引,建立分区,尽量使用固定长度的字段,限制字段长度;增加缓存使用连接池;减少SQL语句的比较次数,限制返回的条目数
对两个很大的文件中的字符串求交集
洗牌算法
- 简单的可以使用下面这个,每次和范围中的最后一个交换(随机的),这样可以得到一个随机的序列
void MySwap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
void Shuffle(int n)
{
for(int i=n-1; i>=1; i--)
{
MySwap(num[i], num[rand()%(i+1)]);
}
}
hashtable桶的个数为什么一般都是素数
- 因为素数的平均性能一般比较好,虽然合数有的数字情况表现的也很好,但是在一些特殊的情况下会将很多值hash到一个桶里面
socket编程,如果client断电了,服务器如何快速知道
TCP连接的保持情况
- TCP的长连接理论上只要连接建立后,就会一直保持着。但有时有一些防火墙之类的软件会自动检查主机的网络连接状况,比如说如果发现某个连接在几分钟之内都没有数据通讯,则会关闭这个连接。有时客户端与服务器需要实时的检测连接状态,就是需要知道对方是否还在线,如果对方不在线了,需要做相应的处理,这是就需要通过发送心跳包的方法监测链路的状态。
- 防火墙是一种装置,有多种不同的实现方式(软件实现、硬件设备实现或是软硬件相结合实现),它需要依据一系列规则对进出的信息流进行扫描,并允许安全(符合规则)的信息交互、阻止不安全(违反规则)的信息交互。防火墙的工作特性决定了要维护一个网络连接就需要耗费较多的资源,并且企业防火墙常常位于企业网络的出入口,长时间维护非活跃的 TCP 连接必将导致网络性能的下降。因此,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 TCP 连接断连。
设计淘宝双11的架构
- 减少http请求,将js,css文件打包成一个文件。其实还有页面静态化,之前项目有涉及。
- 内容分发CDN,我回答的是根据用户的IP,将用户请求负载均衡到就近的数据中心。追问:如何负载均衡?可以根据IP hash生成,根据请求响应延时负载均衡。追问:这几种负载均衡属于哪种负载均衡,答不知道。他说属于软负载均衡。然后我问他还有哪种负载均衡策略。他说还有硬负载均衡。然后我再问,这样的话是不是用软件做负载均衡就是软负载,硬件做负载均衡就是硬负载。他说也可以这么理解,哈哈。最后提示我其实可以用DNS做负载均衡,这就是内容分发的思想了。
- 在每个数据中心中建立缓存。web代理的思想
- 数据库优化,具体方法之前问过
什么叫做面向对象
- 面向对象就是把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。
关闭TCP连接的两种方式
- close以及shutdown
- shutdown是优雅关闭,会让所有的数据都发完。
一些简单的shell命令
- 找出当前目录下最大的前十个文件:du -s | sort -nr | head
- 找出特定文件名的文件并且文件中包含特定字符串:find . -name “*.txt” | xargs grep -H “hello”
- grep -iR –include=”*.cpp” “val1” : 查找当前文件夹下所有cpp文件中的val1字符(递归搜索),看代码的时候很有用
gdb调试core dump文件
- 首先打开core dump文件生成, ulimit -c unlimited,gcc -g test.c -o test, 运行生成core文件之后,gdb test coreFile, 运行到crash地方,会打印crash的信息,bt查看栈的信息,一般都是段错误:访问了错误的内存段或者没权限,或者根本不存在对应的内存段
动态连接以及静态链接
- 静态链接:代码的装载速度快,执行速度也比较快,因为编译时它只会把你需要的那部分链接进去,应用程序相对比较大。但是如果多个应用程序使用的话,会被装载多次,浪费内存。
- 动态链接:可以动态的安装和卸载,更加灵活。
判断两个结构体是否相等的快捷方法
关于格林码
- 编码规则:该二进制数左边第一位不变照写,往后的每一位的二进制与其前一位的二进制进行异或得到该位的格雷码。 所以二进制1111的格雷码是1000.
- 所以求格林吗的代码就如下所示:
class Solution {
public:
vector<int> grayCode(int n) {
int total = 1 << n;
vector<int> ret;
for(int i = 0; i < total; ++i){
ret.push_back(i>>1^i);
}
return ret;
}
};
系统调用的实现原理
系统调用的实现原理
进程和线程的简单比较
- 进程是分配资源的基本单位(CPU、内存等),有独立的地址空间。线程是进程中的一个实体,共享进程的地址空间,是轻量级的进程,是CPU调度和分配的基本单位,但是各个线程拥有自己的栈空间。
- (1)单线程遇到阻塞,会卡死,影响交互;
- (2)发挥多核CPU的计算能力;
- (3)简化程序结构,使程序便于维护;
- (4)与进程相比,线程的创建和切换开销更小
Linux下进程通信的几种方式
- (1)管道:具有亲缘关系的进程通信,命名管道允许无亲缘关系的进程通信,shell命令中的”|”就是一种管道通信,一个命令的输出作为另外一个进程的输入;
- (2)信号:程序运行过程中可以随时被各种信号打断,从而去处理信号,Linux下面的kill命令就是通过信号的方式和其它进程通信,比如kill -l可以查看信号的分类,kill -9强制杀死对应进程;
- (3)消息队列:有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点;
- (4)共享内存:非常快的进程通信方式,和信号量结合起来,达到进程间的同步与互斥;
- (5)socket:套接字可以让不同主机之间的进程进行通信,肯定还会讲到,很重要。
线程之间同步的方式
- 因为线程之间共享内存,所以线程之间需要进行同步。
- 同步的方式有互斥锁、条件变量、读写锁。
- 互斥锁用于保护临界区,信号量同理(注意和信号之间的区别);
- 条件变量用于发送信号和等待信号;
- 读写锁是在互斥锁的基础上做的改进,又被成为共享-独占,非常适合于读数据的频率远大于写数据的频率的应用。
- 线程同步方式
linux中内存分配的策略
- malloc,first fit、best fit,分配的时候自然会产生内存碎片,可以在若干次free之后进行碎片合并。
- 如何实现一个malloc
- malloc浅析
nginx负载均衡的策略
- 轮询、最少连接优先、ip地址哈希(同一客户端的web请求会到同一个服务器上,可以解决session的问题–一般解决是通过数据库持久化session,然后用分布式的数据库)、基于权重的负载均衡。
两种MySql引擎
- Myisam和InnoDB(默认)。基本只要你写会用mysql,这个问题是必问的。myisam是不支持事务安全的,但是插入速度非常快,innoDB支持事务安全,但是插入速度相对较低。像12306这种涉及交易的网站,肯定会用到事务的。 Myisam支持全表索引但是InnoDB不支持。
数据库常用的优化技巧
- (1)索引:优点是读速度快;带来的缺点是体积变大、插入性能变差(需要创建索引);
- (2)缓存配置(缓存查询语句,缓存查询数据);
- (3)分库分表,分表又分为垂直和水平拆分;
- (4)子查询优化。
where 1=1和where 1=0语句的作用
- where 1=1是在表单提交中如果有多个查询条件在拼凑sql语句时如果没有填查询条件时可能会出异常,这个语句规范了sql语句。 where 1=0,这个语句恒为false,表示不返回结果集,节省了内存,可以用于快速建表
存储过程
- 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。优点是:灵活、可编程、执行速度快(预编译)、减少网络流量等。
QQ、SKYPE等的多客户端登陆软件,服务器一般是如何设计开发的?
后台开发的注意点
- 尽量少的线程切换,尽量少的共享冲突,尽量无锁,工作中的线程数尽量等于CPU核心数,尽量没有等待时间片的线程,逻辑尽量简化,避免不必要的封装和转发,工程不是学术,OOP要给简单易用高性能好维护让道~~! EPOLL:Edge Trigger、OneShot!!!,不要在epoll_wait()线程中用太长时间处理非epoll_wait()的事情。能用atomic的就不要用mutex(atomic是乐观锁)
epoll的ET模式与LT模式
- 对LT模式。epoll_wait检测到其上有事件发生并且将此事件通知应用的时候,应用程序可以不立即处理,那么下一次调用epoll_wait的时候,epoll_wait还是会将这个时间通知上层应用。直到这个时间被处理。
- 对LT模式。当epoll_wait第一次通知上层这个事件的时候,应用程序必须立即处理,因为后续epoll_wait不会再向上层通知这个事件。epoll再很大程度上降低了同一个事件被多次触发的概率
- EPOOL如果触发一个线程开始读取socket上的一个事件的时候,在数据处理的过程中这个socket可能又有新的数据到来,epoll可能触发另一个线程来处理,如果期望一个socket在任意时刻只能被一个线程处理的话,那么可以是使用epoll的EPOLLONESHOT来实现。当然了,当一个线程处理完成了注册了EPOLLONESHOT的socket上的时间之后,需要立即将EPOLLONESHOT重置,这样才能让socket的后续事件得到处理。例如下面这样
void reset(int epollfd, int fd){
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET | EPOLLONESHUT;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}
非阻塞的connect
- 对非阻塞的socket调用connect,而连接又没有建立的时候,会返回EINPROGRESS(表示连接还正在进行),这时可以使用epoll监听这个连接失败socket上的可读可写事件。epoll返回的时候,再用getsockopt清除这个socket上的错误,错误码为0的话代表简历连接成功否则表明建立连接失败。
关于Unicode
- Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字”严”。
- 但是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。比如,汉字”严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多的字节也是有可能的。
- UTF-8是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。
- UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
- UTF-8的编码规则很简单,只有二条:1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
Unicode符号范围 | UTF-8编码方式
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- 跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
- 下面,还是以汉字”严”为例,演示如何实现UTF-8编码。
已知”严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此”严”的UTF-8编码需要三个字节,即格式是”1110xxxx 10xxxxxx 10xxxxxx”。然后,从”严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,”严”的UTF-8编码是”11100100 10111000 10100101”,转换成十六进制就是E4B8A5。
字符编码笔记:ASCII,Unicode和UTF-8
伙伴算法和slab分配器以及vmalloc
- Linux使用伙伴算法来进行内存的分配(现代操作系统434),但是其会导致内存碎片,比如需要65页面大小的块,那么必须要申请到128页面的块。也就是内存分配会按照2^n的情况来进行划分
- 缓解这个问题Linux使用的是slab分配器。其使用伙伴算法分配内存,但是切分成更加细化的块来进行管理。其基本思想是:一次向内核获取整数页,slab根据数据结构的大小进行划分为一个个小的数据结构,当需要时直接从该链表上摘取一个返回应用程序,当应用程序释放时,而非真正释放,只需要该空间放回到链表中,当分散的一页多块又聚集一页时,又会拼成一页,同时判断slab空闲的页数,如果空闲页超过一定的页数,就会向系统释放一定的页数。一个slab分配器只能管理一个指定大小的数据结构分配。
- 一个slab分配器管理许多页,而每一页又管理许多的对象。为了有效管理页,即便于分配与释放,又根据页内对象的数量将页分为3个状态:
- 空闲(free)状态:该页没有分配出去任何对象
- 部分分配(partial)状态:该页只有部分对象被分配
- 完全分配(full)状态:该页所有对象都被分配出去。
- vmalloc用于仅仅需要虚拟地址空间连续的请求,常用于动态的插入内核模块。
Slab以及Buddy算法之间的区别
Slab分配器概述
Slab分配器
TCP的拥塞避免算法
- 慢开始:在主机刚刚开始发送报文段时可先将拥塞窗口cwnd设置为一个最大报文段MSS的数值。在每收到一个对新的报文段的确认后,将拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送端的拥塞窗口cwnd,可以分组注入到网络的速率更加合理。
- 拥塞避免:当拥塞窗口值大于慢开始门限时,停止使用慢开始算法而改用拥塞避免算法。拥塞避免算法使发送的拥塞窗口每经过一个往返时延RTT就增加一个MSS的大小。
- 快重传算法规定:发送端只要一连收到三个重复的ACK即可断定有分组丢失了,就应该立即重传丢手的报文段而不必继续等待为该报文段设置的重传计时器的超时。
- 快恢复算法:当发送端收到连续三个重复的ACK时,就重新设置慢开始门限 ssthresh与慢开始不同之处是拥塞窗口 cwnd 不是设置为 1,而是设置为ssthresh,
若收到的重复的AVK为n个(n>3),则将cwnd设置为ssthresh
若发送窗口值还容许发送报文段,就按拥塞避免算法继续发送报文段。
若收到了确认新的报文段的ACK,就将cwnd缩小到ssthresh
- 乘法减小:是指不论在慢开始阶段还是拥塞避免阶段,只要出现一次超时(即出现一次网络拥塞),就把慢开始门限值 ssthresh 设置为当前的拥塞窗口值乘以 0.5。当网络频繁出现拥塞时,ssthresh 值就下降得很快,以大大减少注入到网络中的分组数。
加法增大:是指执行拥塞避免算法后,在收到对所有报文段的确认后(即经过一个往返时间),就把拥塞窗口 cwnd增加一个 MSS 大小,使拥塞窗口缓慢增大,以防止网络过早出现拥塞。
拥塞避免算法小结
LinuxC小结
linux c 小结
对云的理解
- 计算、存储能力虚拟化。可以把云计算想象成一种电,一种从“发电厂”自由流动的信息流,我们需要的只是一个接入这种流的端口,所有的个人PC都不再需要了就像不需要发电机一样。如果云计算像电能一样普及,那我们看到的可能真的就是和黑客帝国里面的世界一样,信息流在世界流动,驱动各种设备运转,人工智能,虚拟现实都在这个基础上实现。而且电能的普及也经过了从独立发电到集中供电的这样一个过程,是不是很像今天的云计算,先是各个企业建立自己的数据中心,后来发现自己维护数据中心过于昂贵于是购买专业的云服务。事物的发展何其相似。
DDos攻击
- 过大量合法的请求占用大量网络资源,以达到瘫痪网络的目的。 这种攻击方式可分为以下几种:
- 通过使网络过载来干扰甚至阻断正常的网络通讯;
- 通过向服务器提交大量请求,使服务器超负荷;
- 阻断某一用户访问服务器;
- 阻断某服务与特定系统或个人的通讯。
单例的double check的问题
public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
- 上面这种单例的问题在于,优先高级语言的new 以及赋值是分开的。也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
- A、B线程同时进入了第一个if判断
- A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
- 由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
- B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
- 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
- 可以使用一个内部类来克服double check的缺点:
public class Singleton {
private Singleton() {
}
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonFactory.instance;
}
public Object readResolve() {
return getInstance();
}
}
Comparator属于设计模式的哪种
- 用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
- Comparator比Comparator更加灵活
软负载均和和硬负载均衡
- 硬负载均衡:例如通过智能交换机实现。处理能力更强,系统无关,缺点:首先是贵,这个贵不仅是体现在一台设备上,而且体现在冗余配置上.很难想象后面服务器做一个集群,但最关键的负载均衡设备却是单点配置,一旦出了问题就全趴了. 第二是对服务器及应用状态的掌握:硬件负载均衡,一般都不管实际系统与应用的状态,而只是从网络层来判断,所以有时候系统处理能力已经不行了,但网络可能还来得及反应
所以硬件方式更适用于一大堆设备、大访问量、简单应用。
- 软负载均衡:很多种,例如DNS负载均衡,例如RoundRobin等等
DNS与TCP以及UDP
- DNS同时占用UDP和TCP端口53,UDP报文的最大长度为512字节,而TCP则允许报文长度超过512字节。当DNS查询超过512字节时,协议的TC标志出现删除标志,这时则使用TCP发送。通常传统的UDP报文一般不会大于512字节。
- 区域传输:DNS的规范规定了2种类型的DNS服务器,一个叫主DNS服务器,一个叫辅助DNS服务器。在一个区中主DNS服务器从自己本机的数据文件中读取该区的DNS数据信息,而辅助DNS服务器则从区的主DNS服务器中读取该区的DNS数据信息。当一个辅助DNS服务器启动时,它需要与主DNS服务器通信,并加载数据信息,这就叫做区传送(zone transfer)。
- 区域传送时使用TCP,主要有一下两点考虑:
- 辅域名服务器会定时(一般时3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,则会执行一次区域传送,进行数据同步。区域传送将使用TCP而不是UDP,因为数据同步传送的数据量比一个请求和应答的数据量要多得多。
- TCP是一种可靠的连接,保证了数据的准确性。
- 客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过TCP三次握手,这样DNS服务器负载更低,响应更快。虽然从理论上说,客户端也可以指定向DNS服务器查询的时候使用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。
系统为什么要分成用户态和内核态
- 内核态: CPU可以访问内存所有数据, 包括外围设备. CPU也可以将自己从一个程序切换到另一个程序
- 用户态: 只能受限的访问内存, 且不允许访问外围设备. 占用CPU能力被剥夺, CPU资源可以被其他程序获取
- 由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络
- 安全性,如果用户可以直接访问到操作系统生命相关的重要组件,不当的修改可能会导致系统彻底崩溃。
- 高地址是内核地址空间,对32位:
0xC0000000到0xFFFFFFFF
, 0x00000000到0xBFFFFFFF
是用户的地址空间
java的float是精确的吗:
- 不是特别精确,float和double只能用来作科学计算或者是工程计算,但在商业计算中我们要用java.math.BigDecimal
统计城市的公交车数量
HTTP协议
HTTP请求响应的格式
HTTP1.0与HTTP1.1
内排序和外排序
- 内排序:指在排序期间数据对象全部存放在内存的排序。
- 外排序:指在排序期间全部对象太多,不能同时存放在内存中,必须根据排序过程的要求,不断在内,外存间移动的排序。
- 常见的排序实际上都是内排序:(直接插入,希尔)->(插入排序),(简单选择,堆排序)->(选择排序), (冒泡,快速)->(交换排序),归并排序,基数排序
crontab的格式
- crontab的格式:分 时 日 月 星期 要运行的命令
CGI
- 运行在服务器上的程序,提供给客户端HTML页面的接口
- CGI常用的环境变量:
- CONTENT_TYPE:传递来信息的类型
- HTTP_COOKIE:客户机的COOKIE
- REMOTE_ADDR : 请求客户机的IP地址
- REQUEST_METHOD: 脚本被调用的方法
Group使用细节
- 所用包含于SELECT 列表中,而未包含于组函数中的列都必须包含于 GROUP BY 子句中。
- 不能在 WHERE 子句中使用组函数,可以在 HAVING 子句中使用组函数。
数据库相关原理的要点
- 事务由以下的部分组成:
- 一个或多个DML 语句
- 一个 DDL(Data Definition Language – 数据定义语言) 语句
- 一个 DCL(Data Control Language – 数据控制语言) 语句
- 以第一个 DML 语句的执行作为开始
- 以下面的其中之一作为结束:
- COMMIT 或 ROLLBACK 语句
- DDL 或 DCL 语句(自动提交)
- 用户会话正常结束
- 系统异常终了
- 其他用户不能看到当前用户所做的改变,直到当前用户结束事务
- DML语句所涉及到的行被锁定, 其他用户不能操作。
- DROP table不能回滚。
- TRUNCATE TABLE 语句:删除表中所有的数据,释放表的存储空间,同样不能回滚
- 可以使用 DELETE 语句删除数据, 这样可以回滚
数据库隔离级别
并发问题分析
- 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
- 不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
- 幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
数据库提供的 4 种事务隔离级别:
常见的数据库对象
- 表:基本的数据存储集合,由行和列组成。
- 视图:从表中抽出的逻辑上相关的数据集合。
- 序列:提供有规律的数值。
- 索引:提高查询的效率
- 同义词 :给对象起别名
数据库约束
- NOT NULL,UNIQUE ,PRIMARY KEY,FOREIGN KEY,CHECK
- 列级约束只能作用在一个列上,而表约束可以作用在多个列上(当然表约束也可以作用在一个列上)。
- 列约束必须跟在列的定义里后面,表约束不与列一起,而是单独定义。
- 非空(not null) 约束只能定义在列上
- 例:添加一个外键约束:
ALTER TABLE employees ADD CONSTRAINT emp_manager_fk FOREIGN KEY(manager_id) REFERENCES employees(employee_id);
视图
- 视图是一种虚表,是一组相关联数据的集合
- 视图建立在已有表的基础上, 视图赖以建立的这些表称为基表
- 向视图提供数据内容的语句为 SELECT 语句, 可以将视图理解为存储起来的 SELECT 语句
- 视图向用户提供基表数据的另一种表现形式。
使用数据库解决TopK问题
SELECT ROWNUM as RANK, last_name, salary FROM (SELECT last_name,salary FROM employees ORDER BY salary DESC) WHERE ROWNUM <= 3;
关于索引
- 一种独立于表的模式对象, 可以存储在与表不同的磁盘或表空间中
- 索引被删除或损坏, 不会对表产生影响, 其影响的只是查询的速度
- 索引一旦建立, Oracle 管理系统会对其进行自动维护, 而且由 Oracle 管理系统决定何时使用索引. 用户不用在查询语句中指定使用哪个索引
- 在删除一个表时, 所有基于该表的索引会自动被删除
- 通过指针加速 Oracle 服务器的查询速度
- 通过快速定位数据的方法,减少磁盘 I/O
- 以下情况可以创建索引:
- 列中数据值分布范围很广
- 列经常在 WHERE 子句或连接条件中出现
- 表经常被访问而且数据量很大 ,访问的数据大概占数据总量的2%到4%
- 下列情况不要创建索引:
- 表很小
- 列不经常作为连接条件或出现在WHERE子句中
- 查询的数据大于2%到4%
- 表经常更新
请说出你所知道的线程同步的方法
- wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
- notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
- notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
设计一个BigInteger
public class BigInt(){
int[] ArrOne = new ArrOne[1000];
String intString="";
给定一个能够生成0,1两个数的等概率随机数生成器”,如何生成⼀个产生0,1,2,3的等概率随机数生成器?和上题类似,如何用rand7生成rand9?
- 答案:将两个0,1随机生成器级联,每次产生两个数,则可能的结果有(0,0), (0,1), (1,0), (1,1),分别映 射到0, 1, 2, 3即可 两个rand7可以产生49种可能,扔掉后面的4种,保留前45个,并平均分成9份,每次产生一个结果时,假如没落在对应区间中就扔掉,否则根据落在哪个区间判断是0–8中哪个
段式存储,页式存储
- 页式存储:
- 基本原理。将程序的逻辑地址空间划分为固定大小的页(page),而物理内存划分为同样大小的页框(pageframe)。程序加载时,可将任意一页放人内存中任意一个页框,这些页框不必连续,从而实现了离散分配。该方法==需要CPU的硬件支持,来实现逻辑地址和物理地址之间的映射==。在页式存储管理方式中地址结构由两部构成,前一部分是页号,后一部分为页内地址
- 优点是,不会产生外部碎片,因为分配的页面大小都是固定的,每个内碎片不超过页大比前面所讨论的几种管理方式的最大进步是,一个程序不必连续存放。这样就便于改变程序占用空间的大小(主要指随着程序运行,动态生成的数据增多,所要求的地址空间相应增长)。缺点是仍旧要求程序全部装入内存,没有足够的内存,程序就不能执行。
- 页式系统中进程建立时,操作系统为进程中所有的页分配页框。当进程撤销时收回所有分配给它的页框。在程序的运行期间,如果允许进程动态地申请空间,操作系统还要为进程申请的空间分配物理页框。操作系统为了完成这些功能,必须记录系统内存中实际的页框使用情况。操作系统还要在进程切换时,正确地切换两个不同的进程地址空间到物理内存空间的映射。这就要求操作系统要记录每个进程页表的相关信息。
- 相关的必须的信息有:进程页表(完成逻辑页号到物理页号的映射),物理页页表(描述物理内存空间的分配使用状况,其数据结构可采用位示图和空闲页链表。),请求表(整个系统有一个请求表,描述系统内各个进程页表的位置和大小,用于地址转换也可以结合到各进程的PCB(进程控制块)里。)
- 段式存储:
- 基本原理:段式存储管理中,将程序的地址空间划分为若干个段(segment),这样每个进程有一个二维的地址空间(实际上是由若干个页面来组成)。在前面所介绍的动态分区分配方式中,系统为整个进程分配一个连续的内存空间。而在段式存储管理系统中,则为每个段分配一个连续的分区,而进程中的各个段可以不连续地存放在内存的不同分区中。程序加载时,操作系统为所有段分配其所需内存,这些段不必连续,物理内存的管理采用动态分区的管理方法。
- 在为某个段分配物理内存时,可以采用首先适配法、下次适配法、最佳适配法等方法。==在回收某个段所占用的空间时,要注意将收回的空间与其相邻的空间合并。==段式存储管理也需要硬件支持,实现逻辑地址到物理地址的映射。程序通过分段划分为多个模块,如代码段、数据段、共享段。这样做的优点是:可以分别编写和编译源程序的一个文件,并且可以针对不同类型的段采取不同的保护,也可以按段为单位来进行共享。
- 总的来说,段式存储管理的优点是:没有内碎片,外碎片可以通过内存紧缩来消除;便于实现内存共享。缺点与页式存储管理的缺点相同,进程必须全部装入内存。
- 为了实现段式管理,操作系统需要如下的数据结构来实现进程的地址空间到物理内存空间的映射:
- 进程段表:描述组成进程地址空间的各段,可以是指向系统段表中表项的索引。每段有段基址(baseaddress)。
- 系统段表:系统所有占用段。
- 空闲段表:内存中所有空闲段,可以结合到系统段表中。
二者之间的异同点:
- 页式和段式系统有许多相似之处。比如,两者都采用离散分配方式,且都通过地址映射机构来实现地址变换。
页是信息的物理单位,分页是为了实现离散分配方式,以减少内存的外零头,提高内存的利用率。或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了更好地满足用户的需要。
页的大小固定且由系统决定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的。段的长度不固定,且决定于用户所编写的程序,通常由编译系统在对源程序进行编译时根据信息的性质来划分。
- 页式系统地址空间是一维的,即单一的线性地址空间,程序员只需利用一个标识符,即可表示一个地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址。
段页式存储管理
- 是基本分段存储管理方式和基本分页存储管理方式原理的结合,即先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。
- 在段页式系统中,为了获得一条指令或数据,须三次访问内存。第一次访问是访问内存中的段表,从中取得页表始址;第二次访问是访问内存中的页表,从中取出该页所在的物理块号,并将该块号与页内地址一起形成指令或数据的物理地址;第三次访问才是真正从第二次访问所得的地址中,取出指令或数据。
- 显然,这使访问内存的次数增加了近两倍。为了提高执行速度,在地址变换机构中增设一个高速缓冲寄存器。每次访问它时,都须同时利用段号和页号去检索高速缓存,若找到匹配的表项,便可从中得到相应页的物理块号,用来与页内地址一起形成物理地址;若未找到匹配表项,则仍须再三次访问内存。
- 参考内存的存储管理 页式和段式存储的区别
Web中的几种概念
- 网关:网关是资源和应用程序之间的粘合剂,可以向数据库发送查询语句,或者生成动态的内容,就像一个门一样:进去一条请求,出来一个响应。第一个流行的应用程序网关API就是通用网关接口(CommonGateway Interface,CGI)。CGI是一个标准的接口集,Web服务器用它来状态程序以响应对特定URL的HTTP请求,并收集程序的输出数据,将其放在HTTP响应中回送。
- 隧道:隧道可以通过HTTP应用程序访问使用非HTTP协议的应用程序,WEB隧道允许用户通过HTTP连接发送非HTTP流量,这样就可以在HTTP上捎带其他协议数据了,使用WEB隧道最常见原因就是要在HTTP连接中嵌入非HTTP流量,这样,这类流量就可以穿过只允许Web流量通过的防火墙了。
- 中继:是没有完全遵循HTTP规范的简单HTTP代理,中继负责处理HTTP中建立连接的部分,然后对字节进行盲转发。