编译工具链及程序调试工具

目录
  1. 编译工具链
  2. 调试工具
    1. gdb
    2. addr2line
    3. c++filt

编译工具链

  • 编译:gcc g++ 常用
  • 链接:ld
  • 创建,修改和从归档文件中提取文件:ar
    1) 打包.o文件生成静态库:ar -crv libmylib.a *[obj1.o obj2.o]
    2) 查看静态库中的符号 ar -t libmylib.a
    3) 提取静态库中的文件 ar -x libmylib.a [file] (一般先用-t 选项看下要提取的文件名,然后进行提取)

  • 去掉符号表(debug info): strip, 去除后可以通过file命令查看,其用法如下:
    img
    想进一步看下strip做了什么,我们可以通过readelf -S file 查看下section head

  • 查看符号表:nm
  • 查看文件中的可打印字符串:strings
  • 查看堆栈地址对应的代码行:addr2line
  • 查看目标文件的信息:objdump
  • 拷贝和加工目标文件:objcopy
  • 查看ELF文件的信息:readelf —— 有点类似objdump,只适用于Executable and Linkable Format(可执行与可链接格式)

正常情况下,使用前三个工具就可以工程编译了

调试工具

gdb

这个工具就不介绍了,只言片语扯不清,可以参考用GDB调试程序 序列文章

addr2line

当程序出现core dump时,经常会打印出堆栈地址,这时侯可以根据该堆栈地址来查找程序中对应的出错行,使用方式如下:

1
addr2line -e filename addr

其中,filename为堆栈对应的库或者可执行文件(需要式no stripped形式的,否则结果会出现??), addr 为堆栈地址

c++filt

为了支持函数重载,c++引入了name mangle机制,将同名函数名称进行改编,来区分不同的函数。以c++中的find 函数来说,其存在多种重载形式,我们可以用下面的命令查看:

1
readelf -s /usr/lib64/libstdc++.so.6 |grep findE

其结果如下:
img1

然后用c++filt工具可以查看其对应的具体形式:

1
readelf -s /usr/lib64/libstdc++.so.6 |grep findE | awk '{print $NF}' |sed 's/@@.*$//g' | xargs c++filt

得到结果如下:
img2

为connect、send、recv设置超时时间

目录
  1. 非阻塞connect
  2. 为send/recv设置超时时长

非阻塞connect

  1. 调用fcntl设置套接字为非阻塞
  2. 发起非阻塞connect。期望的错误是EINPROCESS,表示连接已经启动但是尚未建立。
  3. 如果非阻塞connect返回0,表示连接已经建立,当客户端和服务端位于同一主机时连接很快,有可能出现这种情况
  4. 调用select设置超时时间,然后等待套接字变为可读或可写。当select返回0时,表示connect超时;
  5. 如果描述符变为可读或可写,由于错误情况下套接字也是可读可写的,所以要区分这种情况下是否真的连接成功,这时我们调用getsockopt取得套接字的待处理错误
  6. 还原套接字的阻塞状态并返回

注意:
使用 getsockopt 函数检查错误
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
在 sockfd 都是可读和可写的情况下,我们使用 getsockopt 来检查连接
是否出错。但这里有一个可移植性的问题。
如果发生错误,getsockopt 源自 Berkeley 的实现将在变量 error 中
返回错误,getsockopt 本身返回0;然而 Solaris 却让 getsockopt 返回 -1,
并把错误保存在 errno 变量中。所以在判断是否有错误的时候,要处理
这两种情况。

具体源程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec)
{
int flags, n, error, code;
socklen_t len;
fd_set wset;
struct timeval tval;
flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
error = 0;
if ((n == connect(sockfd, saptr, salen)) == 0) {
goto done;
} else if (n < 0 && errno != EINPROGRESS){
return (-1);
}
/* Do whatever we want while the connect is taking place */
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
tval.tv_sec = nsec;
tval.tv_usec = 0;
if ((n = select(sockfd+1, NULL, &wset,
NULL, nsec ? &tval : NULL)) == 0) {
close(sockfd); /* timeout */
errno = ETIMEDOUT;
return (-1);
}
if (FD_ISSET(sockfd, &wset)) {
len = sizeof(error);
code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
/* 如果发生错误,Solaris实现的getsockopt返回-1,
* 把pending error设置给errno. Berkeley实现的
* getsockopt返回0, pending error返回给error.
* 我们需要处理这两种情况 */
if (code < 0 || error) {
close(sockfd);
if (error)
errno = error;
return (-1);
}
} else {
fprintf(stderr, "select error: sockfd not set");
exit(0);
}
done:
fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
return (0);
}

为send/recv设置超时时长

1
2
3
struct timeval timeout={3,0};//3s
int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,(const char*)&timeout,sizeof(timeout));
int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout));

如果recv或者send返回-1,而且errno为EAGIN表示超时。具体可以使用man recv查看说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
RECV(2) BSD System Calls Manual RECV(2)
NAME
recv, recvfrom, recvmsg -- receive a message from a socket
...
ERRORS
The calls fail if:
[EAGAIN] The socket is marked non-blocking, and the receive
operation would block, or a receive timeout had been
set, and the timeout expired before data were
received.

参考链接:
非阻塞connect的实现

IPD_process

目录

IPD强调以市场需求作为产品开发的驱动力,将产品开发纳入投资管理的一种流程:
TR1 需求(概念)分析、需求设计
TR2 需求分解、架构设计
TR2~TR4 迭代开发
TR5 功能验证
TR6 发布商用
其流程图如下:
IPD流程
实施IPD流程的关键要素:

  • 跨部门团队:包括进行产品管理的评审委员会及具体执行开发过程的产品开发团队(PDT)
  • 结构化的流程:IPD流程分为6个阶段及4个主要决策评审点(DCP),这些阶段和决策评审点由跨部门团队进行计划和管理。6个阶段包括概念、计划、开发、验证、发布 及生命周期,每个阶段有其阶段性的目标、关注点及需交付的成果。
  • 一流的子流程:包括计划与控制、阶段决策、技术评审、以用户为中心的设计、CBB-重用、文档管理、质量控制、物料管理、软硬件设计、技术管理及管道管理
  • IPD工具:IPD实施需要借助信息化工具。需要制定一系列的流程、制度、方法。典型的工具有项目管理系统(包括需求、设计、开发、PPM计划及人力安排)、代码构建工具(保障代码质量)、版本自动构建工具及发布平台。
  • 考评:包括团队和个人绩效考核两个方面:首先是基于产品开发团队(PDT)的指标,如上市时间(TTM)、盈利时间和公用构建模块(CBB)等;其次是基于个人的指标,包括进度或计划完成率、质量、公用构建模块、关键行为指标等。

python语法

目录
  1. 语法
    1. 条件判断if/else
    2. 循环
      1. while 语句
      2. for 语句
    3. 推导式
      1. 列表推导式
      2. 字典推导式
      3. 集合推导式
    4. 函数
      1. 匿名lambda函数
  2. 运算符
  3. 字符串操作
    1. 引号
    2. 类型转换
    3. 拼接
    4. 字符串复制
    5. 提取字符
    6. 提取子串
    7. 获取长度
    8. 分割与合并
    9. 文件操作
    10. 二进制转换
    11. python执行shell命令
    12. 其他操作

语法

  • python中使用缩进来区分语句块,而不是c++,java中的{}
  • 条件表达式(if)及函数后面需要加”:”
  • 对于数组变量,其下标从0开始,可以使用array[index, index + n + 1]来取第index - 1 ~ index + n + 1个元素,如array[0,2]代表取array[0]、array[1],注意,这边和stl中的迭代器类似,取得不是是begin到end-1个元素
  • 条件判断与或非:and or not ,而不是c语言的那一套

    条件判断if/else

1
2
3
4
5
6
>>>if condition:
>>> do something
>>>elif condition:
>>> do something
>>>else:
>>> do something

其中,condtion为条件表达式,可以使用逻辑操作符进行判断。

循环

while 语句

1
2
while condition
do something

python 中使用缩进来控制逻辑结构,所以while中有缩进的部分都是while的执行体,其中可以使用break跳出循环或使用continue跳到循环开始

for 语句

1
2
for var in array
do something

经常可以使用zip函数及range函数来生成序列,配合for语句使用:

1
2
3
4
5
6
7
days=['Monday', 'Tuesday']
foods=['chicken', 'beef']
for day, food in zip(days, foods):
print(day, "eat", food)
for x in range(0:3)
print(x)

推导式

推导式是从一个或者多个迭代器快速简洁地创建数据结构的一种方法

列表推导式

  • [expression for item in iterable]
1
2
#number_list = list(range(1,6))等价于
number_list1 = [number for number in range(1,6)]
  • [expression for item in iterable if condition]
1
2
#取16中偶数
number_list1 = [number for number in range(1,6) if number % 2 == 0]

字典推导式

{key_expression : value_expression for expression in iterable }
例子如下:

1
2
3
word = 'letters'
letter_counts = {letter: word.count(letter) for letter in set(word)}
letter_counts

输出结果如下:
{'s': 1, 'r': 1, 'e': 2, 'l': 1, 't': 2}

集合推导式

{expression for expression in iterable}
集合推导式和列表推导式类似,只是[]换成了{}

1
{number for number in range(1, 6)}

函数

函数定义语法:

1
2
3
4
def function_name(arg1, arg2...):
do something
# call function
function_name()

函数中可以指定默认参数.
对于函数参数中*和**特别说明下:

  • *
    星号将一组可变数量的位置参数转换成元组对象
1
2
3
4
5
def print_args(*args):
print('args = ', args)
#call print_args
print_args(3,2,1)

输出结果为 (‘args = ‘, (3, 2, 1))

  • **
    双星号将参数收集到一个字典结构中,参数名为字典的key,参数值为字典的value。
1
2
3
4
5
def print_kwargs(**kwargs):
print("kwargs = ", kwargs)
#call print_kwargs
print_kwargs(china='BJ', britain='London')

输出结果为 (‘kwargs = ‘, {‘britain’: ‘London’, ‘china’: ‘BJ’})

匿名lambda函数

lambda param: operation(param)

tips:
为了读取全局变量而不是函数中的局部变量,需要在变量前面显式地加关键字global

运算符

+,-,,/,%和编程语言一样,特殊的运算符有://(整除)、\*(求幂)

字符串操作

引号

python中可以使用单引号及双引号来表示字符串;另外,可以使用’’’来创建带\n的字符串,如下:

1
2
3
4
5
>>> str='''hello,
... world!'''
>>> print(str)
hello,
world!

类型转换

使用str()可以将其他Python数据类型转换为字符串

拼接

  • 使用 ‘+’ 可以将多个字符串或字符串变量拼接起来,如:
1
2
>>> "hello" + "," + "world"
'hello,world'
  • 或者直接将各个字符串放在一起,如:
1
2
"hello" "," "world"
'hello,world'

字符串复制

使用*可以进行字符串复制,如:

1
2
>>> 'NA'*4
'NANANANA'

提取字符

与c语言类似,可以使用[]运算符来提取指定位置的字符,从左边开始的下标为0;也可以从右边开始,下标从-1开始,依次为-2, -3
当访问越界时会出现异常提示:

1
2
3
4
5
6
7
>>> str1="hello"
>>> str1[0]
'h'
>>> str1[6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range

提取子串

  • [:]提取从开头到结尾的整个字符串
  • [start:]从start提取到结尾
  • [:end]从开头提取end个字符,即索引为end - 1
  • [start:end]从start提取到end - 1
  • [start:end:step]从start提取到end - 1,每step个字符提取一个

获取长度

使用len()获取字符串长度

分割与合并

  • split()进行字符串分割,默认使用空白符,使用方法如下:
1
2
3
4
5
>>> str="hello, world"
>>> str.split(',')
['hello', ' world']
>>> str.split()
['hello,', 'world']
  • join()用于将字符串合并,用法如下:
1
2
3
4
>>> substr
['hello', ' world']
>>> ','.join(substr)
'hello, world'

文件操作

  • open
  • file.close
  • file.write
  • file.read
  • file.seek
  • file.tell
  • file.readline
  • file.writelines

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import os
>>> fp=open("test",'w+')
>>> fp.read()
''
>>> fp.write("hello, world")
>>> fp.tell()
12
>>> fp.read()
''
>>> fp.seek(os.SEEK_SET)
>>> fp.read()
'hello, world'
>>> fp.close()>>> fp.close()

二进制转换

文本字符串 –> 二进制 : binascii.hexlify
二进制 —> 文本字符串 : binascii.unhexlify
示例如下:

1
2
3
4
5
6
7
8
>>> str="fffff"
>>> import binascii
>>> str1=binascii.hexlify(str)
>>> print str1
6666666666
>>> binascii.unhexlify(str1)
'fffff'
>>>

python执行shell命令

  • result=os.popen(command).read() #返回文件描述符,所以可以执行read操作读取结果,相当于fp=os.popen(“command”); fp.read()
  • os.system(“”) #成功返回0
    检查路径是否存在 os.path.exists

    其他操作

  • find()在字符串中查找子串,返回第一个匹配位置的索引,对应的rfind()为最后一个匹配的索引
  • count(word)返回给定word在字符串中的匹配数
  • capitalize()将字符串首字母改成大写
  • upper()将字符串转换为大写
  • lower()将字符串转换为小写
  • swapcase()颠倒大小写
  • replace(oldstr, newstr[, count]) 将oldstr替换成newstr,count参数可以指定,最大替换count个,若未指定,则表示全部替换

tips:
使用python交互环境的话可以使用bpython,具有自动补全及命令提示功能,很适合新手使用

使用python搭建web服务器

目录
  1. Mac下安装python pip程序
  2. 使用python搭建简单的web服务器
  3. 使用python web框架
    1. Bottle
    2. Flask

Mac下安装python pip程序

easy_install 是python自带的一个工具,用于给python安装第三方的扩展包。省去了我们安装python模块时需要手动下载然后执行python setup.py install来安装的麻烦。
安装pip时,只需执行如下命令即可:

sudo easy_install pip

使用python搭建简单的web服务器

使用python可以方便部署一个web服务器,假设当前目录为/web,则可以使用

1
python -m SimpleHTTPServer 8000

来启动一个python服务器,/web目录下则自动成为web服务器的资源目录。启动成功会答应下面的信息:

Serving HTTP on 0.0.0.0 port 8080 …

当在浏览器输入http://localhost:8000可以测试web服务器是否正常工作,这时web服务器会打印如下日志:

127.0.0.1 - - [20/Jun/2017 23:31:21] “GET / HTTP/1.1” 200 -

其中,
127.0.0.1表示客户端的IP;
第一个”-“表示远程用户名,这边为空;
第二个“=”表示登录用户名,这边是可选的,为空;
[20/Jun/2017 23:31:21] 这个是访问日期
“GET / HTTP/1.1” 200 这个是HTTP请求响应行


这里的“Web服务器模块”有如下三种:

BaseHTTPServer: 提供基本的Web服务和处理器类,分别是HTTPServer和BaseHTTPRequestHandler。
SimpleHTTPServer: 包含执行GET和HEAD请求的SimpleHTTPRequestHandler类。
CGIHTTPServer: 包含处理POST请求和执行CGIHTTPRequestHandler类。

使用python web框架

Bottle

Flask

使用python操作redis

目录
  1. 安装redis-py
  2. 使用python操作redis

安装redis-py

Python默认不支持Redis,需要自行安装redis-py模块。
安装步骤:

1
2
1、 Git clone https://github.com/andymccurdy/redis-py.git
2、 sudo python setup.py install

这样就能正常使用

1
>>> import redis

在python中使用redis了.

使用python操作redis

1
2
3
4
5
6
7
>>> import redis
>>> conn = redis.Redis() #可以通过redis.Redis('localhost', port)来指定具体端口,默认为6379
>>> conn.keys('*') #相当于在redis-cli 中执行'keys *'
>>> conn.get('name')
>>> conn.set('name','value') #设置之前检查key是否存在value的话,用setnx
>>> conn.getset('name', 'value') #getset会返回旧的值,然后赋新的值
... #命令太多,就不一一说明了

详细的接口说明redis-py’s documentation可以在http://redis-py.readthedocs.io/en/latest/ 上找到

Redis安装和启动.md

目录
  1. 安装
  2. 运行

redis官网有详细的说明:

安装

Download, extract and compile Redis with:

1
2
3
4
$ wget http://download.redis.io/releases/redis-3.2.9.tar.gz
$ tar xzf redis-3.2.9.tar.gz
$ cd redis-3.2.9
$ make

运行

The binaries that are now compiled are available in the src directory. Run Redis with:

1
$ src/redis-server

You can interact with Redis using the built-in client:

1
2
3
4
5
$ src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"

Gtest测试框架

目录

在代码开发中,对代码进行测试是很重要的一个环节,测试分为黑盒测试及白盒测试。针对测试而言,多种语言都有自己成熟的测试框架,如java的Juint测试框架,c++的unittest及Google test单元测试框架。

其中,Google test测试框架是一款开源软件,android开发源码中也自带该测试框架,软件用起来还算不赖的。操作也相对简单,给大家推荐下。

如果只是简单的想了解如何使用测试框架的话:可以直接到github上看[README.md](https://github.com/google/googletest]

如果想了解下gtest测试框架的实现的话,可以看下下面的这篇文章:
玩转Google test测试框架

高性能mysql

目录
  1. 数据库结构图
  2. 索引
    1. 为排序使用索引扫描
  3. 查询优化

数据库结构图

存储引擎层

索引

B-Tree索引
hash索引
innodDB中的自适应hash索引
使用where语句手动定义hash函数,增加查找速度,例子如url查找

select id from url where url=”www.mysqlcom” and url_crc=CRC32(“http://www.mysql.com“);

这种方法的一个缺点就是要维护哈希值,可以使用触发器进行维护:
create table pseudohash(
id int unsigned NOT NULL auto_increment,
url varchar(255) NOT NULL,
url_crc int unsigned NO NULL DEFAULT 0,
primary key(id)
);

创建触发器:

可以使用的伪hash函数如下:
CRC32 right(MD5(),n) left(MD5(), n)
像md5,sha1之类的函数返回的值太长,可以取其结果的部分值进行说明

sql 分隔符,delimiter关键字?

为排序使用索引扫描

按照索引对结果进行排序,只有当索引的顺序和order by子句中的顺序完全一致,并且所有列的方向(升序或降序)一样才可以。如果查询联接了多个表,只有在order by子句的所有列引用的是第一个表才可以。查找查询中的order by子句也有同样的局限:它要使用索引的最左前缀,在其他所有情况下,mysql使用文件排序。

查询优化

group by:如果运行了很多很长的聚合查询以生成汇总,那么磁盘使用会因支持group by查询的覆盖索引而得益。
的使用

to be continue

本站总访问量