记录学习过程中的点点滴滴
Linux编程
awk中ord的实现:将字符转化对应的ascii码
六 14th
在awk中并未直接提供ord函数,所以在将某个字符转换为码时,需要自己来实现
awk 'BEGIN{for (i = 0; i < 256; ++i) {ord[sprintf("%c", i)] = i;} }' ' { print ord['a']; ord ['b']; }'
这里主要是构造了ord为key,val的数据结构,key为char字符,val对应的ascii码
awk学习笔记之正则表达式
六 7th
正则在Awk中用处比较多,这里就其主要用法做个大概的介绍。
- 使用正则
awk中的regexp是用斜线包括,如/xx/, 就是一个比较简单的正则
awk '/foo/ {print $2}'
上面的示例是每行的记录只要包括foo字符串的,就将其第二个字段打印
注:/foo/ : 是指只要记录包含子串即可,不管其出现的位置
- 两种匹配方式~ !~
- str ~ /regexp/ str匹配正则表达式时,返回true
- str !~ /regexp/ str不匹配正则表达式时,返回true
- 正则中的转义字符
对于一些非可打印的字符,使用\进行转义。
如 \t : 代表tab \\ 代表\, \*代表* - 正则中使用的operator
- ^ : 头部开始匹配
如^Sep : 则str必须以Sep开头,正则匹配才会返回true - $ : 尾部匹配
如end$ : 则str必须以end结尾,则正匹配才会返回true - . : 匹配任意的字符
- [...] : 匹配[]包含字符的某一个
如[0-9] 匹配任意的一个数字
[a-zA-Z] 匹配任意的字母
[MVX] 匹配字符’M’ ‘V’ ‘X’中的某一个
内置的表示:[:alnum:] 代表所有的字母和数字,
[:alpha:] 代表所有的字母 [:digit:] 代表所有的数字
- [^...] : 中括号中加入^, 匹配非[]内的所有字符
- | : 或的关系,^P | [0-9] 匹配所有的以P开头的字符串,或者包含数字的字符串
注:| 的优先级最低 - () : 将正则括起来
- 出现次数的operator: * + ? {m,n}
- ^ : 头部开始匹配
- awk的正则匹配是大小写敏感,可使用tolower(str) ~ /regexp/
awk学习笔记【一】
六 4th
今天主要介绍下awk中内置的String函数和awk中内置的变量。
awk中内置的string函数
- index(in, find): 在 string “in”中查找 find 的第一次出现位置,返回的find在in中第一个字符出现的位置,从1开始
awk 'BEGIN { print index("peanut", "an") }' # 打印3
- length(string) : 返回字符串的长度,如果是number类型,返回对应字符的长度
- match(string, regexp) : 正则匹配regexp的规则,如果匹配,则返回对应第一个字符的位置
- split(string, array, fieldsep) : 按照fieldsep分割字符串,并将结果放置到array中,array的下标从1开始
split("yaronspace.cn", fields, ".") # fields[1] = "yaronspace" # fields[2] = "cn"
- sprintf(format, expression1, …): 格式化字符串
- sub(regexp, replacement, target): 将第一个匹配的regexp的部分使用replacement替代,替代后的结果还是保存在target变量中。所以target必须是变量,不可为字符常量
str = "yaronspace.cn"; sub(/yaronspace/, "xiaofangdeng", str); # str="xiaofengdeng.cn"
使用&符号引用匹配regexp部分的字符长,示例如下:
str = "yaronspace.cn"; sub(/yaronspace/, "&_xiaofangdeng", str); # str = "yaronspace_xiaofangdeng.cn"
- gsub(regexp, replacement, target): 与sub功能类似,但是会替换所有匹配regexp的子串,g代表”global”的含义
- substr(string, start, length) : 获取子串
- tolower(string) : 转化为小写
- toupper(string) : 转化为大写
awk中内置的变量
- FS: Input Field Seperator variable: 对每行记录的分割符,可以通过-F参数修改,或者在awk文件中,直接使用FS=”XX”进行修改
- OFS : Output Field Seperator variabble: 输出时默认每个记录的分隔符,默认为空格
$ awk -F':' '{print $3,$4;}' /etc/passwd # $3 $4以空格分割 $ awk -F':' 'BEGIN{OFS="_"} {print $3, $4}' # 此时$3 $4是以_分割
- RS : Input Record Seperator variable: 记录的默认分隔符,默认为\n换行符
可以修改,如将其修改为\n\n,以两个空行为分隔符 - ORS: Ouput Record Seperator variable: 默认的记录的输出的分隔符,默认为\n
- NR: Number of records variable: 当前已读取记录数,默认情况为当前的行数
- NF: Number of fields vairable : 当前记录的域的个数
- FILENAME : Name of the current input file
- FNR: Number of Records relative to the current input file : 当awk的输入文件只有一个时,NR == FNR;当awk的输入文件时多个时,FNR是当前文件的当前行数,NR是全局的行数
参考文章:
1. http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html
libmemcached的retry_timeout问题分析
三 26th
最近在线上中遇到使用libmemcached的svr连接memcached间隔性的出现47错误,每次在2s的时间内大量出现,
其宏定义为MEMCACHED_SERVER_TEMPORARILY_DISABLED,表示memcached临时性不可用.
- 当某个时刻,某台memcached机器出现连接超时情况时,会将server的状态标识为MEMCACHED_SERVER_STATE_IN_TIMEOUT
- 在接下来的retry_timeout(默认为2s)的时间内,libmemcached将不会重新连接该memcached,总是直接返回47错误码,表示服务临时不可用
我们遇到的情况就是当某个时刻网络抖动(这个在跨机房通信应该比较常见),可能会是在ms级的,但是libmemcached会直接将该server不可用,并且在2s内不再重试。这里就是所谓的“快隔离、慢恢复”的理念,会在2s内导致该memcached不可用,出现大量47错误的原因。
可以通过MEMCACHED_BEHAVIOR_RETRY_TIMEOUT将retry_timeout设置为1s来减少网络抖动的影响,但是不能设置为0,最少是1s.
snprintf函数的返回值问题
三 11th
平时在程序设计中,我们推荐使用snprintf, 主要是为了避免str写越界的情况发生,但是对snprintf的返回值理解有个误区,
今天特别记录下。
snprintf的函数原型为:
int snprintf(char *str, size_t size, const char *format, …);
说明:
之前以为snprintf的返回值是实际写入到str字符串的长度,其实不然
case 1 : 如果要输出的字符串的长度< size, 主要这里不包括=, 因为snprintf会自动将\0加入到str中,
snprintf的返回值是实际str的长度
case 2 : 如果要输出的字符串长度>= size, 则表明str的长度不够写入原有的长度,则snprintf的返回值
在理想情况下(即str的长度足够长)的字符串长度,所以其返回值可能会出现>= size的情况。
另外需要说明的snprintf会自动将’\0′追加到str的末尾,而snprintf的返回值是不包括’\0′的
这个在官方的manual里写的比较清楚:
If the output was truncated due to this limit then the return
value is the number of characters (not including the trailing ’\0’) which would have been written to the final string if enough space had been available.
Thus, a return value of size or more means that the output was truncated. (See also below under NOTES.) If an output error is encountered, a negative value
is returned.
使用Bash中Complete自动补全命令
十二 25th
大家在Linux下都用shell的TAB自动补全功能,非常方便,但是有时自己开发程序,可能需要自定义实现TAB自动补全功能,这时可能就会用到complete命令来实现对命令的自定义补全,本文主要介绍complete的用法,下一篇会着重介绍对ssh的自动补全的实现。
入门
首先先看一个非常简单的例子:
$ # Create a dummy command: $ touch ~/bin/myfoo $ chmod +x ~/bin/myfoo $ # Create some files: $ touch a.bar a.foo b.bar b.foo $ # Use the command and try auto-completion. $ # Note that all files are displayed: $ myfoo <TAB><TAB> a.bar a.foo b.bar b.foo $ # Now tell bash that we only want foo files. $ # This command tells bash args to myfoo are completed $ # by generating a list of files and then excluding $ # everything # that doesn't match *.foo: $ complete -f -X '!*.foo' myfoo $ # Tray again: $ myfoo <TAB><TAB> a.foo b.foo
- 首先自定义了command: myfoo, 然后测试对myfoo的自动补全功能,默认情况下,myfoo会自动列出当前目录的文件作为自动补全
内容 - complete -f -X ‘!*.foo’ myfoo : 使用complete命令对myfoo自定义补全功能,参数含义:-f 是列出当前的文件 -X pattern 将符合pattern条件的文件exclude,这里主要是显示*.foo的文件,将*.bar文件排除在外
complete参数详解
complete [-abcdefgjksuv] [-o comp-option] [-A action] [-G globpat] [-W wordlist] [-P prefix] [-S suffix] [-X filterpat] [-F function] [-C command] name [name ...]
- -W wordlist: 自动补全使用的wordlist, 使用IFS分割,会和当前用户输入的Word做前缀比较,提示那些匹配的word list.
- -S suffix: 向每个自动补全word后添加suffix 后缀.
- -P prefix: 向每个自动补全word后添加prefix 前缀.
- -X filterpat: 对于文件名,将匹配pattern的文件名从completion list中移除(exclude), pattern中使用!表示否定
- -G globpat: 对于文件名,将匹配pattern的文件名作为可能的completion. 与-X刚好相反,-X “!*.foo” 与 -G “*.foo”作用相同
- -C command: 将command命令的执行结果作为可能的completion.
- -F function: 执行shell function,在function中对COMPREPLY这个数组复制,作为可能的completion
- -p [name]: 打印当前自定义的complete
- -r [name]: 删除当前自定的complete
- -A action : 表示生成可能的completion的方式,包括alias, file, directory等,具体请参看文后的参看资料
使用样例
- 使用-C -G -X参数
complete -C "pwd" ls # ls的自动补全为当前目录 complete -G "*.py" ls # ls的自动补全为.py文件 complete -X "*.py" ls # ls的自动补全为非.py文件
-
使用-F 参数自定义补全函数
function _mycomplete_() { local cmd="${1##*/}" # 当前命令 local word=${COMP_WORDS[COMP_CWORD]} #当前输入的字符串 local line=${COMP_LINE} #整行字符串 local xpat='*.py' # pattern # 根据word的值来补全当前非.py的文件file. COMPREPLY=($(compgen -f -X "$xpat" -- "${word}")) } # _mycomplete_函数可定义在.bash_profile中,然后执行 source ~/.bash_profile # 当然也可多个命令共用一个function, 只要_myfunction_作区分即可,比如对cmd执行case语句 # 生成不同的xpat complete -F _mycomplete_ myfoo #使用_mycomplete 作为myfoo的自动补全
参考资料

c++中const变量问题的简单分析
十二 5th
今天同事问了关于const 值被更改的问题,这个问题在面试中也会经常被问到,所以专门写篇博客总结下。
代码如下
输出结果:a:10 p:20
简单分析:
其实针对该问题,原因比较简单,在printf调用中,由于a是常量,编译器做了优化,将a的值直接替换为10,所以在后续对a的内存位置进行更改为20时,并未对printf的a参数影响。当然,同事并未相信我的分析,我只能调用汇编后的代码进行分析。
深度分析:
1. 生成汇编代码:
g++ -S test.cpp # 输出的汇编代码位于test.s中
2. 汇编代码如下:
根据第26行,验证了我上述的分析,此时同事想让我讲下整个汇编代码的逻辑(我心里暗喜,刚好最近在上@林士鼎 的课程,顺便复习下)
3. 函数调用的知识
理解函数调用栈,有几个概念需要事先明确:
a. 栈的增长方式由高到低(大学课本里早就会背了,但是要真正理解)
b. 参数传递方式:寄存器 + 栈
c. %rsp : 栈头指针 , push pop会更改对应的指针值
d. %rbp : 帧指针, 这个概念非常重要,当前函数的栈的起始位置,
使用%rbp + offset访问局部变量及传递的参数,下面这张图是@林士鼎 老师的课件中某个图
4. 汇编分析:
main: .LFB2: pushq %rbp # 将原来的帧指针保存 .LCFI0: movq %rsp, %rbp # 将当前%rsp 保存至%rbp, 当前帧 .LCFI1: subq $32, %rsp # 将栈下移32Bytes, 这32字节保存了main的参数和main中局部变量 .LCFI2: movl %edi, -4(%rbp) # 取 argc的值 movq %rsi, -16(%rbp) # 取argv的值, 为何会占12Bytes呢? 后续分析 movl $10, -20(%rbp) # 将10赋值给a leaq -20(%rbp), %rax #取a的地址到%rax movq %rax, -32(%rbp) #将a的地址赋值给p(int *), 32的偏移有待分析 movq -32(%rbp), %rax # 去p的值到%rax movl $20, (%rax) # 对*p赋值 movq -32(%rbp), %rax movl (%rax), %edx #这两步相当于*p,同时将其赋值到寄存器作为printf的参数 movl $10, %esi #传递10参数(其实是a) movl $.LC0, %edi # "a:%d p:%d\n" movl $0, %eax #这个作用稍后分析 call printf movl $0, %eax #main函数的返回值 leave ret
变量在栈的位置分布情况:
5. 其他问题分析:
a. 为何argv 和 p在栈的位置偏移异常,这里主要是因为argc 和a 的大小为4Bytes, 而系统是64Bits, 所以存在8Bytes对齐,
这里可以验证,在a后面单独声明一个b变量,发现其在栈的空间刚好为间隙的位置
b. movl $0, %eax 这句话的作用:当对于变参的参数传递时,%eax标示的使用vector register的数目(可以向printf传递float类型参数进行验证)
参考资料:
1. http://stackoverflow.com/questions/6212665/why-is-eax-zeroed-before-a-call-to-printf
2. http://yaronspace.cn/blog/archives/1347
Unix/Linux设计思想学习笔记之一
三 30th
最近在看《Unix/Linux设计思想》这本书,有些地方读起来很容易,但是真正理解还需要细细品味,以后就把书中提到的一些准则简单记录下,主要是为了加深理解,今天主要记录下Unix哲学的概述。
Unix哲学概述:
(1) 小即是美。相对于同类庞然大物,小巧的事物有着无可比拟的优势,其中一点就是它们能够以独特有效的方式结合其他小事物,这和软件工程中提到的“模块化”的思想基本是一致的。
(2) 让每个程序只做好一件事情。通过集中精力应对单一任务,程序可以减少冗余代码,从而避免过高的资源开销、不必要的复杂性和缺乏灵活性。
(3) 尽快建立原型。大多数人都认同“建立原型”(prototyping)是任何项目的一个重要组成部分。在Unix环境中,它是达成完美设计的主要工具。
(4) 舍高效率而取移植性。当Unix作为第一个可移植性系统而开创先河时,它曾掀起过轩然大波。今天,可移植性早被视作为现代软件设计中一个理所当然的特性。
(5) 使用纯文本文件来存储数据。可以看到在类Unix的系统中,所有的配置文件都是使用文本数据直接存储,一方面用户可以直接修改配置,另一方面也是为移植性考虑的。
(6) 充分利用软件的杠杆效应。很多程序员对可重用代码模块的重要性只有一些肤浅的认识。代码重用能帮助人们充分利用软件的杠杆效应。一些Unix程序员正是遵循这个强大的理念,在相对较短的时间内编写出大量应用程序。
(7) 使用shell脚步来提高杠杆效应和可移植性。shell脚步在软件设计中可谓是一把双刃剑,它可以加强软件的可重用性和可移植性。无论什么时候,只要有可能,编写shell脚本来替代C语言程序都不失为一个良好的选择。
(8) 避免强制的用户界面
(9) 让每个程序都成为过滤器(filter)。所有软件程序共有的最基本特性就是,它们只修改而从不创造数据。因此,基于软件的过滤器本质,人们应该把它们编写成执行过滤器任务的程序,管道就是Unix这一个特性的非常好代表。
google sparse hash set/map
三 19th
google开源了hashtables的包,包括两个部分:
- sparse, 比较节省空间space-efficient;
- dense, 高效的查询效率 time-efficient;
同时接口兼容SCI的hash_map和hash_set, 并且提供了序列化的功能,非常赞~
下面是其官方的性能测试数据:可以看到sparse_hash_map比stl::map和hash_map节省2/3的空间,而dense_hash_map具备较高的查询,删除, insert性能,可以根据具体的应用
来使用不同的数据结构。
下载地址:http://code.google.com/p/sparsehash/
doc : http://google-sparsehash.googlecode.com/svn/trunk/doc/
SPARSE_HASH_MAP: map_grow 665 ns map_predict/grow 303 ns map_replace 177 ns map_fetch 117 ns map_remove 192 ns memory used in map_grow 84.3956 Mbytes DENSE_HASH_MAP: map_grow 84 ns map_predict/grow 22 ns map_replace 18 ns map_fetch 13 ns map_remove 23 ns memory used in map_grow 256.0000 Mbytes STANDARD HASH_MAP: map_grow 162 ns map_predict/grow 107 ns map_replace 44 ns map_fetch 22 ns map_remove 124 ns memory used in map_grow 204.1643 Mbytes STANDARD MAP: map_grow 297 ns map_predict/grow 282 ns map_replace 113 ns map_fetch 113 ns map_remove 238 ns memory used in map_grow 236.8081 Mbytes
linux gcc编译字节对齐设置方法
一 25th
使用#pragma pack(n)
- 伪指令#pragma pack (n),编译器将按照n 个字节对齐
- 伪指令#pragma pack (),取消自定义字节对齐方式
#pragma pack(1) struct my_align_struct { uint32 u0:8; uint32 u1:8; uint32 u2:8; uint32 u3:16; }; #pragma pack() sizeof(my_align_struct) == 5 // 默认情况下gcc是按照四字节对齐的,sizeof(my_align_struct) == 8.
使用__attribute__属性
struct my_align_struct { uint32 u0:8; uint32 u1:8; uint32 u2:8; uint32 u3:16; }__attribute__((packed, aligned(1)));
近期评论