目录
一、简述GDB常见的调试命令,什么是条件断点,多进程下如何调试
1、GDB调试
2、命令格式
3、条件断点
4、多进程下如何调试
二、简述什么是“大端小端”及如何判断
1、小端模式
2、大端模式
3、如何判断
三、简述进程调度算法有哪些
1、分类
2、区别
四、简述操作系统如何申请及管理内存
五、简述Linux系统态和用户态,何时进入系统态
六、简述LRU算法及其实现方式
一、简述GDB常见的调试命令,什么是条件断点,多进程下如何调试
1、GDB调试
gdb调试的是可执行文件,在gcc编译时加入 -g,告诉gcc在编译时加入调试信息,这样gdb才能调试这个被编译的文件。 gcc -g test.c -o test
2、命令格式
①quit:退出gdb,结束调试。
②list:查看程序源代码。
list 5,10: 显示5到10行的代码。
list test.c:5,10:显示源文件5到10行的代码,在调试多个文件时使用。
list get_sum:显示get_sum函数周围的代码。
list test.c get_sum:显示源文件get_sum函数周围的代码,在调试多个文件时使用。
③reverse-search:字符串用来从当前行向前查找第一个匹配的字符串。
④run:程序开始执行。
⑤help list/all:查看帮助信息。
⑥break:设置断点。
break 7:在第7行设置断点。
break get_sum:以函数名设置断点。
break 行号或者函数名if条件:以条件表达式设置断点。
⑦watch 条件表达式:条件表达式发生改变时程序就会停下来。
⑧next:继续执行下一条语句,会把函数当作一条语句执行。
⑨step:继续执行下一条语句,会跟踪进入函数,一次一条的执行函数内的代码。
3、条件断点
break if条件。以条件表达式设置断点。
4、多进程下如何调试
用set follow-fork-mode child调试子进程或者set follow-fork-mode parent 调试父进程。
二、简述什么是“大端小端”及如何判断
1、小端模式
低的有效字节存储在低的存储器地址。小端一般为主机字节序,常用的X86结构是小端模式。很多的ARM,DSP都是小端模式。
2、大端模式
高的有效字节存储在低的存储器地址。大端为网络字节序,KEIL C51为大端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
3、如何判断
我们可以根据联合体来判断系统是大端还是小端。因为联合体变量总是从低地址存储。
int fun1(){
union test{
char c;
int i;
}
test t;
t.i = 1;
//如果是大端,则t.c为0x00,则t.c != 1,反之则是小端
return (t.c == 1);
}
补充:
1、进行网络通信时是否需要进行字节序转换?
相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。
原因是:网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。而相同平台进行通信时,如果双方都进行转换最后虽然能够正确收发数据,但是所做的转换是没有意义的,造成资源的浪费。而不同平台进行通信时必须进行转换,不转换会造成错误的收发数据,字节序转换函数会根据当前平台的存储模式做出相应正确的转换,如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收到得网络字节序进行转换。
2、网络字节序
网络上传输的数据都是字节流。对于一个多字节数值,在进行网络传输的时候,先传递哪个字节。也就是说,当接收端收到第一个字节的时候,它将这个字节作为高位字节还是低位字节处理是一个比较有意义的问题。UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高字节(即:高位字节存放在低地址)。由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的。所以说,网络字节序是大端字节序,比如,我们经过网络发送整型数值0x12345678时,在X86平台中,它是以小端发存放的,在发送之前需要使用系统提供的字节序转换函数htonl()将其转换成大端法存放的数值。
三、简述进程调度算法有哪些
1、分类
主要有先来先服务调度算法、短作业(进程)优先调度算法、高优先级优先调度算法、时间片轮转算法、多级反馈队列调度算法。
①先来先服务调度算法:每次调度都是从后备作业(进程)队列中选择一个或多个最先进入该队列的作用(进程),将它们调入内存,为其分配资源、创建进程,然后放入就绪队列。
②短作业(进程)优先调度算法:短作业优先(SIF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业(进程),将它们调入内存运行。
③高优先级优先调度算法:当把该算法用于作业调度时,系统将从后备队列中选择若干个优先级最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程。
④时间片轮转算法:每次调度时,把CPU分配给队首进程,并令其执行一个时间片。时间片的大小从几十ms到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便依据该信号停止该进程的执行,并将其送往就绪队列的末尾。然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。
⑤多级反馈队列调度算法:综合前面多种调度算法。
2、区别
区别:抢占式和非抢占式。
①抢占式优先权调度算法(Preemptive)
允许将逻辑上可继续运行的在运行过程暂停的调度方式可防止单一进程长时间独占,CPU系统开销大(降低途径:硬件实现进程切换,或扩充主存以贮存大部分程序)。
②非抢占式优先权调度算法(Nonpreemptive)
让进程运行直到结束或阻塞的调度方式,容易实现,适合专用系统,不适合通用系统。
四、简述操作系统如何申请及管理内存
1、物理内存。分为四个层次:寄存器、高速缓存、主存、磁盘。
寄存器:速度最快、容量最少、价格最贵。
高速缓存:次之。
主存:再次之。
磁盘:速度最慢、容量最多、价格便宜。
操作系统会对物理内存进行管理,有一个部分称为内存管理器(memory manager),它的主要工作是有效管理内存,记录哪些内存是正在使用的,在进程需要时分配内存以及在进程完成时回收内存。
2、虚拟内存。操作系统为每一个进程分配一个独立的地址空间,但是虚拟内存。虚拟内存和物理内存存在映射关系,通过页表寻址完成虚拟地址和物理地址的转换。
五、简述Linux系统态和用户态,何时进入系统态
1、系统态(又称内核态)与用户态是操作系统的两种运行级别。内核态拥有最高权限,可以访问所有系统指令;用户态只能访问其中一部分指令。
2、进入系统态的方式有三种:①系统调用;②异常;③设备中断。其中,系统调用是主动的,其余两种是被动的。
3、为何区分系统态和用户态:在CPU所有指令中,有一些指令非常危险,如果错用,将导致整个系统崩溃。比如:清内存、设置时钟等。所以区分系统态和用户态主要是出于安全考虑的。
六、简述LRU算法及其实现方式
1、LRU算法用于缓存淘汰。思路是将缓存中最近最少使用的对象删除掉。
2、实现方式:利用链表和hashmap。
当需要插入新的数据项时,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部,如果不存在,则新建一个节点,放到链表头部,若缓存满了,则把链表最后一个节点删除即可。
在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来,在链表尾部的节点就是最近最久未访问的数据项。
3、C++实现代码:
class LRUCache{
list<pair<int, int>> cache; //创建双向链表
unordered_map<int, list<pair<int, int>>::iterator> map; //创建哈希表
int cap;
public:
LRUCache(int capacity){
cap = capacity;
}
int get(int key){
if(map.count(key) > 0){
auto temp = *map[key];
cache.erase(map[key]);
map.erase(key);
cache.push_front(temp);
map[key] = cache.begin(); //映射头部
return temp.second;
}
return -1;
}
void put(int key, int value){
if(map.count(key) > 0){
cache.erase(map[key]);
map.erase(key);
}
else if(cap == cache.size()){
auto temp = cache.back();
map.earse(temp.first);
cache.pop_back();
}
cache.push_front(pair<int, int>(key, value));
map[key] = cache.begin(); //映射头部
}
};
/*
Your LRUCache object will be instantiated and called as such:
LRUCache* obj = new LRUCache(capacity);
int param_1 = obj->get(key);
obj->put(key,value);
*/