0%

调度指标

周转时间

T周转时间=T完成时间-T到达时间

如果所有任务在同一时间到达,那么 T完成时间 = 0,T周转时间 = T完成时间

先进先出(FIFO)

(1) 假设有 3 个工作 A,B和C在大致在同一时间到达,A 比 B 早一点,B 比 C 早一点,假设每个工作的运行 10s,计算周转时间?

阅读全文 »

通过配置密钥,可以免密登录远程主机。

在需要登录远程主机的终端输入命令:

阅读全文 »

假设当前目录下共有以下3个文件,接下来会利用以下代码来演示GDB的调试过程。

├── main.cpp

├── tool.h

└── tools.cpp

文件内容分别为:

tools.h:函数声明

1
2
3
4
5
6
#ifndef _TOOLS_H_
#define _TOOLS_H_
void greeting();
int sum(int a, int b);

#endif /* tools.h */

tools.cpp:函数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "tools.h"

using namespace std;

void greeting()
{
cout << "Hello World" << endl;
}

int sum(int a, int b)
{
return a + b;
}

main.cpp:主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string.h>
#include "tool.h"
using namespace std;

void greeting();
int main(int argc, char* argv[]){
int s = 0;
int m=0;
int n=0;
for(int i=1; i<=atoi(argv[1]); i++){
s+=i;
m++;
n++;
}
printf("sum=%d\n",s);
greeting();
cout<<sum(12,12)<<endl;
print_num();
return 0;
}

以上代码只是为了演示之用,并无特别之处。

编译方式

1
2
// -g 添加调试信息    -Wall 输出所有警告信息,例如定义了从未使用过的变量
$ gcc main.cpp -o main -g -Wall

启动

调试可执行程序

1
$gdb main

调试 core 文件,core 文件是程序运行过程中出现Segmentation fault (core dumped)错误时,程序停止运行时产生的。core文件是程序运行状态的内存映象。使用gdb调试core文件,可以帮助我们快速定位程序出现段错误的位置。可执行程序编译时应加上-g编译选项,生成调试信息。

1
$gdb <program> <core dump file>

调试服务程序

1
$gdb <program> <PID>

设置和获取参数

有时需要在执行程序时输入额外的参数,例如:像下面这样执行程序test

1
$ ./main 5

调试时,往往直接执行$ gdb main

那么怎么在gdb中输入该程序的参数呢?

1
(gdb) set args 5

还可以查看程序的参数

1
2
(gdb) show args
Argument list to give program being debugged when it is started is "5".

查看代码

GDB提供了两种查看源码的方式,分别是根据行号函数名查看。除了可以查看本文件源码,还可以查看其他文件的源码。

本文件

本文件指的是该程序对应的main函数所在文件,即main.cpp

1
2
3
(gdb) l        # 每执行1次显示10行,再执行1次显示次10行
(gdb) l 15 # 显示第15行,此时会将15行显示在屏幕窗口中央,方便查看前后的代码
(gdb) l main # 显示本文件的main函数

其他文件

共同编译的所有文件中,除了main函数所在文件的其它文件。在这里,除了main.cpp即tools.cpp

1
2
(gdb) l tools.cpp:15       		# 在tools.cpp中,显示第15行附近的代码
(gdb) l tools.cpp:sum # 在tools.cpp中,查看sum函数的代码

设置和获取显示行数

1
2
(gdb) show list    				# 显示行数
(gdb) set list 20 # 设置行数

断点

可以在一次调试中设置1个或多个断点,下一次只需让程序自动运行到设置断点位置,便可在上次设置断点的位置中断下来,极大的方便了操作,同时节省了时间。

可以根据行号,函数名设置断点,还可根据条件设置断点(一般用于循环中)

本文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(gdb) b 10              # 将第10行设置为断点
(gdb) b main # 将main函数入口处设置为断点
(gdb) l # 可以看到在main.cpp中含有greeting函数的声明
11
12 void greeting();
13 int main(int argc, char* argv[]){
14 int s = 0;
15 int m=0;
16 int n=0;
17 for(int i=1; i<=atoi(argv[1]); i++){
18 s+=i;
19 m++;
20 n++;
(gdb) b greeting # 此时设置的断点并不是函数的声明处,而是函数的定义处,greeting函数定义在tools.cpp文件中
Breakpoint 1 at 0xaa2: file tools.cpp, line 13.

其他文件

1
2
b tools.cpp:12            # 将tools.cpp的第12行设置为断点
b tools.cpp:sum # 将tools.cpp的sum函数设置为断点

设置条件断点

条件断点一般用于循环中

本文件

1
2
3
4
# 设置i==2时,第18行为断点
# 行号必须在变量的作用域范围内
(gdb) b 18 if i==2
Breakpoint 1 at 0x9d8: file main.cpp, line 18.

其他文件

1
2
3
4
# 设置tools.cpp文件内,i==5时,第22行为断点
# 22必须在i的作用域范围
(gdb) b tools.cpp:22 if i==5
Breakpoint 2 at 0xafb: file tools.cpp, line 22.

查看和删除断点

1
2
3
4
5
6
7
(gdb) i b            # 显示所有断点
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000009a4 in main(int, char**) at main.cpp:14
2 breakpoint keep y 0x00000000000009ec in main(int, char**) at main.cpp:22
3 breakpoint keep y 0x0000000000000a39 in main(int, char**) at main.cpp:25

d 1 # 删除第1个断点

设置断点无效和有效

1
2
3
4
clear n							# 清除第n行的断点
dis 2 # 将第2个断点设置为无效
ena 2 # 将第2个断点设置有效
delete breakpoints # 删除所有断点

运行

有两种运行方式,一种是从主函数开始运行,一种是运行到第1个断点处

从主函数运行

程序从main函数开始

1
(gdb) run

运行到第1个断点处

程序停在第一个断点处

1
(gdb) start

执行流控制

1
2
3
4
5
6
7
c/continue         # 向下运行到下一个断点处
n/next # 执行下一行代码,不进入调用的函数,直接返回结果
s/step # 执行下一行代码,进入调用的函数
finish # 跳出函数体
until # 跳出当前循环 在执行完循环体内的最后一条语句之后执行 until,才可以跳出循环
until+行号 # 运行至某行,不仅仅用来跳出循环
call 函数名称(参数) # 调用程序中可见的函数,并传递参数,如:call gdb_test(67)

打印变量

1
2
p  变量名                      # 打印变量值,可以答应在当前作用域之内的变量名
ptype 变量名 # 打印变量类型

查询

自动变量操作

可以在每次执行时都打印该变量的值,常用于循环体中

1
2
3
4
5
6
7
8
9
10
11
12
13
display 变量名                 # 自动打印指定变量的值
i display # 查看所有设置自动打印的变量值
undisplay 编号 # 取消自动打印变量值
print 表达式 # 打印表达式的值,可以是任何有效表达式
print num # 打印整数num
print ++num # 打印num+1 后的值
print gdb_test(1024) # 以整数 22 作为参数调用 gdb_test()函数
print gdb_test(num) # 以变量 num 作为参数调用 gdb_test() 函数
watch 表达式 # 设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如: watch num
whatis 变量名 # 显示某个变量的类型
info function # 查询函数信息
info locals # 显示当前堆栈页的所有变量

运行时信息

1
2
3
4
where/bt 						# 当前运行的堆栈列表;
bt backtrace # 显示当前调用堆栈
up/down # 改变堆栈显示的深度
info program # 查看程序的是否在运行,进程号,被暂停的原因。

设置变量值

1
set var 变量名=变量值           # 可以临时改变该变量的值               

多窗口调试

使用 layout 可以同时在多个窗口调试代码

1
2
3
4
5
layout src        # 显示源代码窗口
layout asm # 显示反汇编窗口
layout regs # 显示源代码/反汇编和CPU寄存器窗口
layout split # 显示源代码和反汇编窗口
Ctrl + L # 刷新窗口

多进程调试

GDB 默认调试的是父进程,使用以下命令切换调试的进程

1
2
3
set follow-fork-mode child      # 调试子进程
set follow-fork-mode parent # 调试父进程
show follow-fork-mode # 显示设置信息

GDB 可以同时调试一个进程,也可以同时调试多个进程,使用以下命令设置:

1
set detach-on-fork <mode>       # 当 mode 为 on 时,表示程序只调试一个进程(可以是父进程、子进程)。当 mode 为 off 时,父子进程都在gdb的控制之下,其中一个进程正常的调试,另一个会被设置为暂停状态。

GDB 将每一个被调试程序的执行状态记录在一个名为 inferior 的结构中。一般情况下一个 inferior 对应一个进程,不同的 inferior 有不同的地址空间。inferior 有时候会在进程没有启动的时候就存在。

1
info inferiors                 # 显示所有的 inferior,当前调试的进程前有 "*"。

切换进程使用命令 inferior

1
inferior <num>                 # 切换到 num 号进程

设置捕获点中断,调用 fork 函数时将产生中断

1
catch fork

查看内存

命令格式:例如 x/<n/f/u> <addr>

例如:

1
2
(gdb) x/3cb 0x8049098
0x8049098 <Snippet>: 75 'K' 65 'A' 78 'N'

x(examine)为命令的简称,用于检查内存数据。

命令的意思是:查看从内存地址 0x8049098 开始的数据,每字节为单位(b),以ASCII字母形式显示(c),显示 3 个单位的数据(3)。

下面对 3cb 三个字符对应的含义进行讲解:

  1. 3

    表示显示的单位数据个数,可根据需要自定义,和第三个字符参数有关。

  2. c

    用什么格式来解析这些二进制位,因为内存本质上是二进制位,它可被解释为多种含义,例如:0111 0101,可表示十进制数 117,也可以表示十六进制数 75,也可以表示字符 K(ASCII 字母)

    可用的格式字符如下:

    c 按字符格式显示变量。

    x 按十六进制格式显示变量。

    d 按十进制格式显示变量。

    u 按十进制格式显示无符号整型。

    o 按八进制格式显示变量。

    t 按二进制格式显示变量。

    a 按十六进制格式显示地址,并显示距离前继符号的偏移量(offset)。常用于定位未知地址(变量)。

    f 按浮点数格式显示变量。

  3. b

    表示从当前地址往后请求的位宽大小。如果不指定的话,GDB默认是4个bytes。也可以指定以下字符

    b表示单字节,h表示双字节,w表示四字 节,g表示八字节。

其它工具

cgdb

cgdb可以看作gdb的界面增强版,用来替代gdb的 gdb -tui。cgdb主要功能是在调试时进行代码的同步显示。

二叉树的性质

性质1: 在二叉树的第i层上至多有2i-1个节点(i>=1)

性质2: 深度为k的二叉树至多有2k-1个节点(k>=1)

性质3: 对任何一棵二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0=n2+1

满二叉树:深度为k且含有2k-1个节点的二叉树

阅读全文 »

LR分析

分析过程

  • 三元式表示
    三元式子:(状态栈,符号栈,输入符号串)

    阅读全文 »

文法的直观概念和语言概述

当我们表述一种语言时,无非是说明这种语言的句子,如果语言只含有有穷多个句子,则只需列出句子的有穷集就行了,但对于含有无穷句子的语言来讲,存在着如何给出它的有穷表示的问题。

以自然语言为例,人们无法列出全部句子,但是人们可以给出一些规则,用这些规则来说明(或者定义)句子的组成结构,比如汉语句子可以是由主语后随谓语而成,构成谓语的是动词和直接宾语,我们采用第2章所介绍的EBNF来表示这种句子的构成规则:

阅读全文 »

GCC全称GNU C Compiler,是以GPL协议发布的自由软件,其创始人理查德·马修·斯托曼是自由软件运动的精神领袖。

GNU 编译器套件包括 C、C++、Objective-C、Java、Ada 和 Go 语言前端,也包括了这些语言的库(如 libstdc++,libgcj等)

GCC 不仅支持 C 的许多“方言”,也可以区别不同的 C 语言标准;可以使用命令行选项来控制编译器在翻译源代码时应该遵循哪个 C 标准。例如,当使用命令行参数-std=c99 启动 GCC 时,编译器支持 C99 标准。

阅读全文 »

注释

;;;; 四个分号用于文件头注释
;;; 三个分号用于一大段特定代码的作用的描述
;; 某几行代码的功能性描述,注释与被注释的代码具有相同的缩进
; 用于单行注释

S-表达式

阅读全文 »

定义新函数

新函数一般用defun宏来定义。其基本结构为:
(defun name
“一些说明文字”
body-form*)

任何符号都可以作为函数名,但通常函数名仅包含字典字符和连字符,但在特定的命名约定里,其它字符也允许使用。例如+函数可以将一个或多个数字相加。

阅读全文 »