strncpy和snprintf
[| 2012/02/29 19:06]
之前用strncpy总是感觉比较恶,老是要考虑最后\0的问题,今天仔细看了下,发现如果源串长度大于等于最大长度的话,strncpy会直接拷贝最大长度,不在后面加\0,也就是说在用一个字符串覆盖另一个字符串一部分的时候用strncpy是很不错的,但全覆盖的话比较麻烦,很容易出bug。
而snprintf会拷贝最大长度-1的字符数,并在后面加\0,使用一个字符串覆盖另一个时很不错。
看了一下资料,发现snprintf的效率也要高于strncpy。
日常字符串拷贝还是推荐snprintf。
而snprintf会拷贝最大长度-1的字符数,并在后面加\0,使用一个字符串覆盖另一个时很不错。
看了一下资料,发现snprintf的效率也要高于strncpy。
日常字符串拷贝还是推荐snprintf。
由一个503问题看配置文件重要性
[| 2012/02/26 23:55]
最近有一个困扰近两个月的bug终于解决了,心情愉快。503问题之前一直没找到头绪,压力一大就开始有,越大就越多。刚开始一直认为503是正常现象,后端负载能力不足的必然结果。加之之前后端用的自己写的模拟server,性能什么的没什么保证。所以一直在看是不是程序逻辑上有什么漏洞会导致封禁所有后端,一直找不到头绪。
周五测试同学突然说:现在用的后端server肯定能力要强于前边这个啊,怎么可能会因为负载力不足导致503呢?一想确实啊,肯定是流量调度部分的问题。开gdb仔细一找,结果大跌眼镜,原来是跟后端的连接池的最大允许并发连接数开的太小了。。
之前那个值设置的比较小是有道理的,因为php是每个进程处理一个请求的,所以并发数肯定不会超过启动的php进程数。但现在后端改用了lighttpd,lighttpd可以承载的并发数是很多的,这样的话在高并发请求的情况下后端连接池很容易就用完了。
由此有两个感触,一是不同场景下配置项一定要仔细想想如何调优。二是bug并不都是逻辑错误导致的,还有可能是配置错了。。。。
周五测试同学突然说:现在用的后端server肯定能力要强于前边这个啊,怎么可能会因为负载力不足导致503呢?一想确实啊,肯定是流量调度部分的问题。开gdb仔细一找,结果大跌眼镜,原来是跟后端的连接池的最大允许并发连接数开的太小了。。
之前那个值设置的比较小是有道理的,因为php是每个进程处理一个请求的,所以并发数肯定不会超过启动的php进程数。但现在后端改用了lighttpd,lighttpd可以承载的并发数是很多的,这样的话在高并发请求的情况下后端连接池很容易就用完了。
由此有两个感触,一是不同场景下配置项一定要仔细想想如何调优。二是bug并不都是逻辑错误导致的,还有可能是配置错了。。。。
http请求的host字段--关于根域
[| 2012/02/20 17:24]
今天qa说http请求host字段最后以"."结尾时会有问题,看了下发现lighttpd自动把点号去掉了,试了试nginx,也是这样。查了很多rfc,没找到为什么,跑到群里问,有人说是根域的问题,回想起来配dns的cname记录时,最后必须是有“点”结尾的,于是搜索了下。原来真正完整的域名最后是带点的,com、net、cn这都是顶级域名,点后面的“”是根域名。dns解析最顶部是找到根域,然后再找顶级域。之前以为com就是根域,由跟域名服务器解析。现在看是错的
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;
}
}
}
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;
}
}
}
由strcm参数为NULL看编译器优化
[| 2012/02/14 18:29]
今天程序出了一个core,是strcmp的时候有一个参数没有判断为NULL导致的,当我编写了一个小程序:
测试的时候发现程序跑的毫无问题。
编译参数是gcc -g -O0,没有开任何优化。
gdb进去后发现strcmp根本没有被执行,改成int a = strcmp(NULL, "");后,出core了。
看来编译器默认还是提供一定优化的。
另外需要注意strcmp不会检查参数(效率考虑),所以需要自己检查。
引用
#include
int main()
{
strcmp(NULL, "“);
return 1;
}
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等参数。
在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等参数。
由一个小概率出core看函数声明的重要性
[| 2012/01/14 23:39]
最近程序遇到一个小概率出core的bug,高压力下大概10分钟左右就会出core,gdb查看发现一个指针高四字节被置0xffffffff了,低四字节正常。
该指针是局部变量,存放在栈上,排除了线程间同步互斥写坏数据的可能。
该指针前后变量均正常,都是指针,排除了写越界的可能。
通过日志查看,在返回该指针的函数返回前,指针正常,返回后高四字节即被置0xffffffff了。推断应该是函数返回的过程中遭到了破坏。
但不知道为什么返回的过程中会出错,请教了下组内高工,高人就是高人,一听问题描述就表示应该是函数声明的问题。
原来,在调用另一个so文件中的函数时,如果没有该函数的声明,由于从该so的符号表里可以找到函数,所以编译可以通过,但gcc会把这个函数返回值按默认的int处理,这种情况下,32位机编译的程序是没问题的,但64位机上指针是8字节,导致高四字节数据丢失。但返回的指针超过int值域时,高四字节数据丢失,导致指针被破坏。
所以函数声明还是不可或缺的。
该指针是局部变量,存放在栈上,排除了线程间同步互斥写坏数据的可能。
该指针前后变量均正常,都是指针,排除了写越界的可能。
通过日志查看,在返回该指针的函数返回前,指针正常,返回后高四字节即被置0xffffffff了。推断应该是函数返回的过程中遭到了破坏。
但不知道为什么返回的过程中会出错,请教了下组内高工,高人就是高人,一听问题描述就表示应该是函数声明的问题。
原来,在调用另一个so文件中的函数时,如果没有该函数的声明,由于从该so的符号表里可以找到函数,所以编译可以通过,但gcc会把这个函数返回值按默认的int处理,这种情况下,32位机编译的程序是没问题的,但64位机上指针是8字节,导致高四字节数据丢失。但返回的指针超过int值域时,高四字节数据丢失,导致指针被破坏。
所以函数声明还是不可或缺的。
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
然后是串口设置:
波特率9600,字长8,停止位1,不校验,硬件流控无,开启信息收发,启用串口。
然后就可以用USART_SendData发送数据了。
需要注意的地方:不要忘记开启时钟,这里要两次时钟操作,一次给串口,一次给io口,缺一不可。
磨刀不误砍柴功。如果要写比较复杂的程序,调试是必需品。虽然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);
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口,缺一不可。
入手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程序麻烦不少,还要管时钟、中断之类的。不过很有意思,稍后会搞点有意思的东西出来。
调研了一下,发现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程序麻烦不少,还要管时钟、中断之类的。不过很有意思,稍后会搞点有意思的东西出来。
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了。
在看日志的时候发现一个有意思的现象,google和百度的蜘蛛今天很不活跃,对于站点的大规模改变似乎并不感兴趣,对css,js不屑一顾,而soso的spider非常活跃,把每个链接都详细爬了一遍,但发现一个问题:
新博客的url是采用base设置+相对url的模式,soso的spider似乎并不识别base标签,直接把相对url附加到当前url之后进行抓取,导致了很多404请求。查了一下,base属性是html标准属性,soso不支持这个属性应该算是个bug了。