strncpy和snprintf

[| 不指定 2012/02/29 19:06]
    之前用strncpy总是感觉比较恶,老是要考虑最后\0的问题,今天仔细看了下,发现如果源串长度大于等于最大长度的话,strncpy会直接拷贝最大长度,不在后面加\0,也就是说在用一个字符串覆盖另一个字符串一部分的时候用strncpy是很不错的,但全覆盖的话比较麻烦,很容易出bug。
    而snprintf会拷贝最大长度-1的字符数,并在后面加\0,使用一个字符串覆盖另一个时很不错。
    看了一下资料,发现snprintf的效率也要高于strncpy。
    日常字符串拷贝还是推荐snprintf。
Tags: ,
    最近有一个困扰近两个月的bug终于解决了,心情愉快。503问题之前一直没找到头绪,压力一大就开始有,越大就越多。刚开始一直认为503是正常现象,后端负载能力不足的必然结果。加之之前后端用的自己写的模拟server,性能什么的没什么保证。所以一直在看是不是程序逻辑上有什么漏洞会导致封禁所有后端,一直找不到头绪。

    周五测试同学突然说:现在用的后端server肯定能力要强于前边这个啊,怎么可能会因为负载力不足导致503呢?一想确实啊,肯定是流量调度部分的问题。开gdb仔细一找,结果大跌眼镜,原来是跟后端的连接池的最大允许并发连接数开的太小了。。

    之前那个值设置的比较小是有道理的,因为php是每个进程处理一个请求的,所以并发数肯定不会超过启动的php进程数。但现在后端改用了lighttpd,lighttpd可以承载的并发数是很多的,这样的话在高并发请求的情况下后端连接池很容易就用完了。

    由此有两个感触,一是不同场景下配置项一定要仔细想想如何调优。二是bug并不都是逻辑错误导致的,还有可能是配置错了。。。。
Tags:
    今天qa说http请求host字段最后以"."结尾时会有问题,看了下发现lighttpd自动把点号去掉了,试了试nginx,也是这样。查了很多rfc,没找到为什么,跑到群里问,有人说是根域的问题,回想起来配dns的cname记录时,最后必须是有“点”结尾的,于是搜索了下。原来真正完整的域名最后是带点的,com、net、cn这都是顶级域名,点后面的“”是根域名。dns解析最顶部是找到根域,然后再找顶级域。之前以为com就是根域,由跟域名服务器解析。现在看是错的
Tags: , ,

lighttpd的超时参数详解

[| 不指定 2012/02/14 21:00]
Lighttpd配置中,关于超时的参数有如下几个(篇幅考虑,只写读超时,写超时参数同理):


server.max-keep-alive-idle = 5
server.max-read-idle = 60
server.read-timeout = 0
server.max-connection-idle = 360


这几个参数意思相近,配置的时候很容易搞混。




对于一个keep-alive连接上的连续请求,发送第一个请求内容的最大间隔由参数max-read-idle决定,从第二个请求起,发送请求内容的最大间隔由参数max-keep-alive-idle决定。请求间的间隔超时也由max-keep-alive-idle决定。发送请求内容的总时间超时由参数read-timeout决定。Lighttpd与后端交互数据的超时由max-connection-idle决定。



例子:


下面是模拟客户端代码:


$fp = fsockopen("127.0.0.1", 8902, $errno, $errstr, 30);

fwrite($fp, "GET / HTTP/1.1\r\n");
sleep(3);                                             //$1这个时间必须小于max-read-idle,否则会超时
fwrite($fp, "Host: a.com\r\n");
sleep(3);                                             //$2这个时间必须小于max-read-idle,否则会超时。且$1+$2时间之和必须小于read-timeout,否则超时
fwrite($fp, "Connection: Keep-Alive\r\n\r\n");
echo fread($fp, 1024);



sleep(7);                                             //$3 这个时间必须小于max-keep-alive-idle,否则超时



fwrite($fp, "GET / HTTP/1.1\r\n");
fwrite($fp, "Host: a.com\r\n");
sleep(15);                                           //$4  这个时间必须小于max-keep-alive-idle,否则超时,可以大于max-read-idle,但仍然不能超过read-timeout
fwrite($fp, "Connection: Keep-Alive\r\n\r\n");
echo fread($fp, 1024);
fclose($fp);

                                                  //以上时间均不受max-connection-idle限制


下面是模拟后端server代码:



$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if($sock == NULL)
{
    echo "can't create socket";
    exit;
}
if(!socket_bind($sock, "0.0.0.0", 8904))
{
    echo "can't bind socket";
    exit;
}
socket_listen($sock, 100);

while(1)
{
    if($new_conn = socket_accept($sock))
    {  
        $recv = socket_read($new_conn, 100000);
        //echo $recv;
        echo "begin sleep\n";
        sleep(10);                                                                                  //这个时间必须小于max-connection-idle,否则会超时
        echo "end sleep\n";
        socket_write($new_conn, "HTTP/1.1 200 OK\r\nDate: Tue, 01 Nov 2011 05:58:25 GMT\r\nServer: TestServer/1.0\r\nContent-Length: 1\r\nContent-Type: text/html;charset=gb2312\r\nConnection: Keep-Alive\r\n\r\na");
    }
    else
    {
        echo "accept failed!";
    }
}




下面是lighttpd中关于这几个参数实现的代码:

if (con->recv->is_closed) {                                                                        
                                                                 if (srv->cur_ts - con->read_idle_ts > con->conf.max_connection_idle) {                                              //对于客户端已经发送完请求数据的情况下,超时时间max-connection-idle
                                                                           /* time - out */
#if 1
                                                                           WARNING("(connection process timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf));
#endif
                                                                           connection_set_state(srv, con, CON_STATE_ERROR);
                                                                           changed = 1;
                                                                 }
                                                        }
                                                        else {

                                                                 if (con->request_count == 1) {                  
                                                                           if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) {                                              //对于第一个请求,发送的数据最大时间间隔:max_read_idle
                                                                                    /* time - out */
#if 1
                                                                                    if (con->conf.log_timeouts) {
                                                                                             WARNING("(initial read timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf));
                                                                                    }
#endif
                                                                                    connection_set_state(srv, con, CON_STATE_ERROR);
                                                                                    changed = 1;
                                                                           }
                                                                 } else {                                                                                               //从第二个请求开始,发送的数据最大时间间隔:keep_alive_idle
                                                                           if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) {
                                                                                    /* time - out */
#if 1
                                                                                    if (con->conf.log_timeouts) {
                                                                                             DEBUG("(keep-alive read timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf));
                                                                                    }
#endif
                                                                                    connection_set_state(srv, con, CON_STATE_ERROR);
                                                                                    changed = 1;
                                                                           }
                                                                 }

                                                                 if (con->conf.read_timeout > 0 && con->read_start_ts > 0)                                                    //在read_timeout设置不为0的情况下,发送数据的最大总时间:read_timeout
                                                                 {
                                                                           used_time = srv->cur_ts - con->read_start_ts;
                                                                           if (used_time > con->conf.read_timeout)
                                                                           {
                                                                                    WARNING ("read timeout, client[%s], time=%lu",
                                                                                             SAFE_BUF_STR(con->dst_addr_buf), used_time);
                                                                                    connection_set_state(srv, con, CON_STATE_ERROR);
                                                                                    changed = 1;
                                                                           }
                                                                 }
                                                        }
    今天程序出了一个core,是strcmp的时候有一个参数没有判断为NULL导致的,当我编写了一个小程序:

引用
#include
int main()
{
strcmp(NULL, "“);
return 1;
}

测试的时候发现程序跑的毫无问题。
编译参数是gcc -g -O0,没有开任何优化。

gdb进去后发现strcmp根本没有被执行,改成int a = strcmp(NULL, "");后,出core了。

看来编译器默认还是提供一定优化的。

另外需要注意strcmp不会检查参数(效率考虑),所以需要自己检查。

run-parts命令的用法及原理

[| 不指定 2012/02/01 22:10]
    在很多系统中,用户目录下都有cron.daily之类的文件夹,里面的可执行文件每天都会被执行一次。也就是说如果想添加一个每天都被执行的任务的话,在目录下放置该任务的脚本即可。使用很方便,原理是什么呢,就是run-parts命令。

    在centos5下,run-parts命令位于/usr/bin/run-parts,内容是很简单的一个shell脚本,就是遍历目标文件夹,执行第一层目录下的可执行权限的文件。
    

#!/bin/bash

# run-parts - concept taken from Debian

# keep going when something fails
set +e

if [ $# -lt 1 ]; then
    echo "Usage: run-parts <dir>"
    exit 1
fi

if [ ! -d $1 ]; then
    echo "Not a directory: $1"
    exit 1
fi

# Ignore *~ and *, scripts
for i in $1/*[^~,] ; do
    [ -d $i ] && continue
    # Don't run *.{rpmsave,rpmorig,rpmnew,swp} scripts
    [ "${i%.rpmsave}" != "${i}" ] && continue
        [ "${i%.rpmorig}" != "${i}" ] && continue
        [ "${i%.rpmnew}" != "${i}" ] && continue
        [ "${i%.swp}" != "${i}" ] && continue
    [ "${i%,v}" != "${i}" ] && continue

    if [ -x $i ]; then
        $i 2>&1 | awk -v "progname=$i" \
                  'progname {
                   print progname ":\n"
                   progname="";
                   }
                   { print; }'
    fi
done

exit 0

     在ubuntu下,该文件位于/bin/run-parts,是个二进制文件,功能更为强大,支持--test等参数。
Tags:
    最近程序遇到一个小概率出core的bug,高压力下大概10分钟左右就会出core,gdb查看发现一个指针高四字节被置0xffffffff了,低四字节正常。
    该指针是局部变量,存放在栈上,排除了线程间同步互斥写坏数据的可能。
    该指针前后变量均正常,都是指针,排除了写越界的可能。

    通过日志查看,在返回该指针的函数返回前,指针正常,返回后高四字节即被置0xffffffff了。推断应该是函数返回的过程中遭到了破坏。

    但不知道为什么返回的过程中会出错,请教了下组内高工,高人就是高人,一听问题描述就表示应该是函数声明的问题。

    原来,在调用另一个so文件中的函数时,如果没有该函数的声明,由于从该so的符号表里可以找到函数,所以编译可以通过,但gcc会把这个函数返回值按默认的int处理,这种情况下,32位机编译的程序是没问题的,但64位机上指针是8字节,导致高四字节数据丢失。但返回的指针超过int值域时,高四字节数据丢失,导致指针被破坏。

   所以函数声明还是不可或缺的。
Tags: ,

stm32之串口

[| 不指定 2011/12/02 00:53]
    在最基本的GPIO使用熟练后,开始下一步:调试。

    磨刀不误砍柴功。如果要写比较复杂的程序,调试是必需品。虽然jlink可以断点调试,但是效率低、查看变量值麻烦。程序比较大的时候相当复杂。对于普通程序,可以用日志的方式来进行调试,将日志打印到标准输出或者是文件,然后进行调试。在单片机开发中,可以通过串口通讯来完成这个事情,单片机将日志通过串口输出到开发机上,在开发机端使用串口工具进行查看。

    首先是串口的概念,在网上搜索资料相当恼火,大部分都是直接就说串口怎么怎么用,如何如何好,要么就是直接大段代码。很蛋疼的是,到底啥是串口?单片机上哪个引脚是串口?

    首先明确串口的概念,pc端串口分为25针和9针两种,9针为简化版,在台式机上一般都有,笔记本上已经绝迹了。
    对于单片机来说,如果要串口通讯,最简单的方式其实是两根线:rx,tx。

    一般要问了,为啥两根线就能干的活要9根甚至25根线呢?原来两根线只是最基本的串口通讯需要的。如果需要硬件流控等功能,还需要其他引脚的帮助。在这里简单起见,先使用最简单的。

    由于笔记本没有串口,手头有两块usb转串口板,一块是pl2303的,非常便宜,10块钱。但功能也最简单,只有4根线,rx,tx,vcc,gnd,兼容性一般,需要安装驱动才能使用。还有一块基于ft232的,将近50块,功能也比较强大,支持9针插头,兼容性好,插上后可使用windows update自动搜索驱动并安装。

    这次选用了p2303。

    下一步,就是怎么连接的问题,板上密密麻麻近百个引脚,应该如何连接呢?这时需要查询datasheet。

    stm32f103ze有5个串口通道。usart1最快。在这里我准备先拿2试试。datasheet上显示a2为tx,a3为rx。很快将线插好。vcc和gnd也接上。现在电路连通了(注意,插拔串口的时候需要断电操作,否则容易损坏串口)

    然后就是编程。至于程序网络上就大把了。随便找个文档就是大堆。挑个有注释的看。大致流程如下:(最简单的,不带中断)


    首先启用usart时钟:RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    然后是初始化gpio引脚:a3设置为GPIO_Mode_IN_FLOATING,a2设置为GPIO_Mode_AF_PP

    然后是串口设置:

    
引用
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART2 , &USART_InitStructure);
USART_Cmd(USART2, ENABLE);


   波特率9600,字长8,停止位1,不校验,硬件流控无,开启信息收发,启用串口。

    然后就可以用USART_SendData发送数据了。

需要注意的地方:不要忘记开启时钟,这里要两次时钟操作,一次给串口,一次给io口,缺一不可。
Tags: , , ,

入手stm32最小系统板

[| 不指定 2011/11/28 13:50]
    之前在大学的时候弄过一段时间的嵌入式,后来因为小批量的制作电路板成本实在很高,后来没有继续搞了。最近看到有同事在用arduino,又激发了我的兴趣,准备搞一下。

    调研了一下,发现arduino虽然ide看起来很好玩,但是性能和价格都不靠谱。性能是8位mcu,20mhz主频,2ksram,128-2048k flash,总体性能差了不少。并且由于现在在国内属于小众,价格也很不给力。

    在调研的过程中发现了stm32,这是基于arm Cortex-M3内核的一个mcu,32位mcu,主频72mhz,应用的比较多,资料也不少,于是决定入手一个这个。

    先是看了开发板,发现附带的外设过多,很乱,不喜欢,决定买个最小系统版,然后自己买别的扩展板来扩展。为了扩展性考虑选择了flash和sram容量都比较大的一款:stm32f103ze的最小系统板,512k flash,64k sram。足够跑一个比较复杂的程序了,咨询了一下嵌入式的同学,这个配置跑ucos之类的系统也是足够了。

    除了买板,一些其他的配件也是必须的。jlink调试器、面包板、跳线、直插电阻、发光二级管,都买了一些。同时还买了红外感应模块、isp下载模块、光感应模块,用来练手。一共200出头,很便宜。

    为了时效性考虑,卖家选的是北京的,不过查快递信息的时候发现貌似卖家在上地八街。。。。这么近,还不如自取呢。



    周日晚上搞了一会,装了keil,jlink驱动等等,鼓捣了一下,发现比搞普通c程序麻烦不少,还要管时钟、中断之类的。不过很有意思,稍后会搞点有意思的东西出来。
    
Tags: , ,

Soso Spider 不支持base属性

[| 不指定 2011/10/27 19:17]
    今天博客新迁移,由于对静态化url的改动非常大,难免有遗漏的地方,所以非常关注access日志,看看爬虫们遇到了哪些困扰。

    在看日志的时候发现一个有意思的现象,google和百度的蜘蛛今天很不活跃,对于站点的大规模改变似乎并不感兴趣,对css,js不屑一顾,而soso的spider非常活跃,把每个链接都详细爬了一遍,但发现一个问题:

     新博客的url是采用base设置+相对url的模式,soso的spider似乎并不识别base标签,直接把相对url附加到当前url之后进行抓取,导致了很多404请求。查了一下,base属性是html标准属性,soso不支持这个属性应该算是个bug了。




Tags: , , ,
分页: 11/31 第一页 上页 6 7 8 9 10 11 12 13 14 15 下页 最后页 [ 显示模式: 摘要 | 列表 ]