yanbin's Blog

MySQL: Specified key was too long; max key length is 767 bytes

MySQL 使用了InnoDB存儲引擎 UNIQUE index 長度限制是 767 byes; 使用了MyISAM存儲引擎長度限制是 1000 bytes。
使用了 utf-8 字符集兩種引擎下的 vchar 型 UNIQUE index 最大長度分別是 vchar(255) 和 vchar(333),
utf-8 最多使用 3 bytes 表示一個字符: 255 * 3 = 765 ...
使用了 utf-16 字符集兩種引擎下的 vchar 型 UNIQUE index 最大長度分別是 vchar(191) 和 vchar(250)。

django 有時遇到這個問題。
應當懷疑使用了 utf-8 之類的字符集並且 Field(unique=true, max_length=greater_than_255);
或者是否有 unique_together = ['field0', 'field1']; field0_max_length + field1_max_length > 255;

參見:mysql-specified-key-was-too-long-max-key-length-is-767-bytes

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

简单 Shell 编程 FAQ(〇)

0.sed 命令如何使用 shell 脚本某个变量的值?
  使用 '“'括起表达式,或者即不用 '‘' 也不用 '“' 括起表达式。
  'expression'  是使用 sed 时常用/常见的方式。
  可以用 "expression" 的形式给 sed 指定表达式参数。
  shell 对待参数的方式:
  1)使用 '‘'  括起来的字符以它的文本形式对待。$ 就是 $ 这个字符,不会对待为展开变量的值;
  2)使用 '“' 括起来的 '$ '用于展开变量的值,'\' 用来转义,其它字符仍然以文本形式对待;
  3)执行命令时指定参数而不用'‘' 或'“'  括起来与使用 '“'类似;
# '$' 在这里用于匹配 '$'字符。
sed 's/A $foobar value/foobar/g' foobar.txt
# $ 展开 foobar 这个变量的值。转义的 \$ 匹配 '$'。
# 参数在传递给 sed 程序时已经完成变量值展开和转义了。
# 完成转义和变量展开的是 shell 而不是 sed.
sed "s/A $foobar \$value/foobar/g" foobar.txt
# 这个与用 '“'括起来的效果是相同的。
sed s/A $foobar \$value/$/g foobar.txt
 
1.sed 如何从文件中直接删除一行或多行?
  使用 sed 的 d 命令。
# 不熟悉 sed 时一般会写这样的代码。这种方式容易出错且耗费资源。
cat foobar.txt | sed 's/patter to match//gp' > tmp_file.txt
mv tm_file.txt foobar.txt
# sed 的 d 命令 加 -i 参数 可以完成直接修改文件的操作。
sed -i '/pattern to match/d' foobar.txt
# 只输未匹配即没有被删除的行,而不修改文件
sed 'pattern to match/d' foobar.txt
  -i[SUFFIX], --in-place[=SUFFIX]
  edit files in place (makes backup if SUFFIX supplied)
# -i 参数接受一个可选的 suffix, 指定这个 suffix, sed 会修改文件前备份文件
sed -i.bak '/pattern to match/d' foobar.txt
 

继续阅读

shell 脚本中,程序的标准输出重定向到 FIFO, 需要注意的问题

FIFO, 又称为命名管道 (named pipes)。与 PIPE 不同和相同之处:
1. PIPE 只能用于关联进程之间,比如: 父子进程之间,两个子进程之间。
2. 关于读写:
   (a) 写一个没有读端的 PIPE,会触发 SIGPIPE(Broken pipe: write to pipe with no readers).
   (b) 写一个没有读端的 FIFO,没有设置 O_NONBLOCKING 会阻塞,直到有进程读;
        设置了 O_NONBLOCKING, 将会返回 -1.
   (c) 没有写端时读 PIPE/FIFO 都是会阻塞,而设置了 O_NONBLOKING 读都会返回 -1.
 
由于 FIFO 没有读端写端会阻塞的特性。
在 shell 脚步中,一个程序的标准输出重定向到 FIFO, 即写 FIFO 的程序启动之时或之后,
确保读 FIFO 的程序是否存在并且及时存在了,否则可能会有如下问题:
1. 读 FIFO 的程序没有启动过,写 FIFO 的程序永远阻塞在 FIFO 操作上。
2. 读 FIFO 的程序后来启动了,写 FIFO 的程序可能丢失了大量数据当其阻塞在写 FIFO 操作之上时。
3. 假如写 FIFO 的程序是一个多线程程序,她的产生数据操作写数据操作是分开,并且有内部数据缓存,
   当读 FIFO 的程序启动较晚时,会有大量数据要读,而且写 FIFO 程序也有大量数据要写,
   这至少会耗费时间。
4. 实在要考虑写 FIFO 程序读 FIFO 程序的启动先后顺序问题,要考虑时间窗口。
5. Linux Programmer's Manual, pipe(7) 给出的设计思想:
   Applications should not rely on a particular capacity: an application should be designed so that
   a reading process consumes data as soon as it is available, so that a writing process does not
   remain blocked.

shell脚本使用 timeout + wait 完成: 超时退出执行,等待执行完毕并处理执行结果

具体需求是:
1.从文件中读取 seq, 使用 pub 程序将 seq 推送给定阅读了 cmd topic 的 peer client.
   client 将处理结果(message)推送到 cmdresp topic. 这个过程是经过 server 的,可以不考虑。
   pub 成功后记录 seq.
2. sub 程序订阅 cmdresp topic, 简单处理接收到的消息,并且记录到文件。
3. 对比 pub 成功的 seq 记录与接收到的 message, 获得一个 pub/sub 成功率。
 
问题是:
1. pub 和 sub 分别使用两个不同的管道,pub cmd 成功后并不等待 cmdresp.
   甚至可以说: pub 程序并不知道 cmdresp 的存在,甚至不知道 sub 的存在.
2. sub 是阻塞的:没有任何消息也不退出。
3. pub message 到 peer client 到 cmdresp 返回之间的时间是不确定的。
   消息在两个管道都有可能出现延迟。
4. sub 程序本身没有超时退出选项。
 
伪代码是:
timeout time sub cmdresp_topic > cmdresp_record
while seq in read seqs:
     seq = parse_seq(seq)
     message = create_message(seq)
     pub cmd_topic message
     record(message, msg_record)
     // do something, maybe sleep 1s

wait sub

parse(cmdresp_record, msg_record)

shell 代码是:

继续阅读

Lua 调用的 C 函数保存 state 的两种方式: Storing State in C Functions 笔记

使用 Lua call C 编程,不免需要保存一些 non-local data 在 C 函数中; non-local data 来自 Lua state.
然而,直接使用 C static/global 变量保存 state 是有问题的: (a) C 变量不能保存一个 generic Lua value;
(b) static/global 变量的方式也无法用于 multiple Lua states, 因为不同 Lua state 调用相同函数/库时,
需要 C 函数保存的 state 是独立不相关的。
 
C API 提供了两个地方来保存 non-local data: registry 和 upvalues.
 
Registry
Registry 是一个 global table, 只能在 C 函数中使用 Lua C API 提供的函数和 pseudo-index 访问。
pseudo-index 就像一个普通的 stack index 一样,不同的是它所关联的 value 并不真正的存放在 stack 之上。
访问 registry 的 psedu-index 定义 LUA_REGISTRYINDEX. 对 registry 的访问就像是对 stack 访问一样,
Lua C API 提供的大多数函数都可以用于 registry —— 指定 LUA_REGISTRYINDEX 作为 index ——,
只有 lua_remove(), lua_insert() 这些操作 stack 本身的函数除外。例如:
lua_getfiled(L, LUA_REGISTRYINDEX, "SOME_KEY");
 
既然 registry 是一个 regular table, 那么就可以用所有 index 访问它,当然 nil 是不行的。
使用 registry  index 的选用一定要谨慎,毕竟 registry 是全局的,而且所有库和函数都可以共享的 table;
为避免 key 冲突,应当:
(a) 不要使用 number 作为 key;
(b) 使用 string 作为 key, 尽量加上一些特殊的前缀,比如: LIBRAY_NAME_ 这样的;
(c) 使用 reference system;
 
Reference system 是 auxilibrary 提供的一系列函数,使用这些函数可以保证存储 value 到 table
无须关心 key 的唯一性。如下方式可创建一个新的 reference:
int r = luaL_ref(L, LUA_REISTRYINDEX);
这个函数 pop 一个 value 从 stack, 并用新的 index 保存到指定的 table 中,这里 LUA_REISTRYINDEX
关联到 registry, 所以这个调用可保存一个 value 到 registry.
luaL_ref() 函数会将 key 返回,用于以后的对 table 的访问以及 release reference.
luaL_rawgeti(L, LUA_REGISTRYINDEX, r);
luaL_unref(L, LUA_REGISTRYINDEX, r);
 
指定给 luaL_ref() 的 value 是 nil —— 或者说栈顶的 valu 为 nil ——,luaL_ref() 返回 LUA_REFNIL,
使用 LUA_REFNIL 调用 luaL_unref() 和 lua_rawgeti() 都没有任何效果,算得上人畜无害。
另外一个 reference system 定义的常量是 LUA_NOREF, 这个常量保证不同于任何 valid reference,
可用来判断一个 reference 是否为 invalid reference.
 
reference system 有用之处就在于: 我们不能使用 C pointers 指向任何一个 lua object(包括 string),
这样的引用是未定义行为,但是我们可以使用 reference system 将对象存储在 registry 中,
并且维护这个 reference, 就像使用 C pointer 一样。
 
另外一种选择 key 的方式是:使用 C library 中的 static 变量的地址作为 key(的创建源)。
C 链接器可以保证这些变量的地址是唯一的, 常量的大概不行,比如:字符串常量,宏定义的常量。
不过直接用 static 变量的地址作wei index 是错的,得玩点黑魔法:
lua_pushlightuserdata();或 lua_rawsetp(), lua_rawgetp();

继续阅读

为什么使用 do {} while(0)

有些宏定义含有多行代码,一般使用 do {} while(0) 把代码放在 'do' 和 'while' 之间的 '{}' 中。

#define foorbar(msg, callback) do {\
         struct Task __task = msg_to_task((msg)); \
           if (__task != NULL) { \
              process(__task, (callback)); \
              char *__msg = task_to_msg(__task); \
              if (__msg != NULL) \ {
                 send_msg(__msg); \
                 free(__msg); \
              } \
              destroy_task(__task); \
           } while (0)

这样用的原因是:
1.符合 C/C++ 语法习惯。
每条语句后面都一个';', 而 do {} while 语句后面也要加一个 ';'.

2.避免出现语法错误。
不用 do {} while(0) 把多行代码包起来,在 if else 语句中就会有语法错误,例如:

#define foorbar(a, b) foor((a)); bar((b))
if (something)
  /* 以下有语法错误 */
  foorbar(3, 2);
else
  // do something

仅仅使用 '{}' 把多行代码包起来,如果在调用宏之后加 ';', 也会有语法错误。

#define foorbar(a, b) {\
           foor((a)); bar((b));\
       }
foorbar(3, 2); // 此处有语法错误

/* 编译器提示:
 * error: expected ‘;’ before ‘}’ token
 * 如果不加 ';', 不会有语法错误但是这样不符合 C/C++ 的语法习惯
 */

3.do {} while(0) 可以根据条件跳出执行。

#define foorbar() do {\
        struct condition __cond; \
        if (__cond.wait_cond()) \
           break; // 条件发生退出执行 \

        // 条件没有发生 do something 
      } while(0)

 

4.私以为 do {} while(0) 可以保证代码执行并且只执行一次。

5.需要注意的地方。
(a)宏定义时用 '\' 连接多行语句;
(b)宏定义中定义变量,注意与外部变量名字冲突,不然原本希望用外面的变量,却用了新定义的变量。
(c)有些编译器会优化掉 do {} while(0); 直接展开 '{}' 内的代码, 如(b)所描述,此时会出现语法错误。
   FIXME: 如果内部有 'break' 并且 'break' 的执行依赖运行时条件,编译器就不会优化掉 do {} while(0); 了。
举例:

#define foorbar() do {\
        struct condition cond; \
        if (cond.wait_cond()) \
           break; // 条件发生退出执行 \

        // 条件没有发生 do something 
      } while(0)

struct condition cond;
// do something
foobar(); // 到底用的是哪一个 cond?
#define foorbar(a, b) do {\
         const char *something = get_something(a, b); \
      } while(0)

const char *something;
// do something

foorbar(3, 9); // 如果编译器优化掉了 do {} while(0); 这里有语法错误。

 

感谢 老猫,mike2,MovableType@源赖朝 三位网友。

参考:
do { … } while (0) — what is it good for?
do{}while(0) 的作用



 

Linux 系统中使用 inotify 监视文件或目录的改变

0.注意事项

int inotify_init();
int inotify_add_watch(int fd, const char *name);
int inotify_rm_watch(int fd, int wd);
int inotify_init1(int mask);
 
这些是一系列的 linux 系统调用;
前三个是 linux 2.6.13 引入的, 最后一个是 2.6.27 引入的。

但是一些 C libray(C 语言实现库), 并没有定义这些系统调用的封装。
可以用 syscall(__NR_inotify_init); 这样的形式调用。
__NR_inotify_init 是系统调用号,可以在 unistd.h 中看到。

有些 SDK 中的内核配置没有默认的选定对 inotify 的支持。
可以在 linux 配置中的 kernel setup:
fs-->
   [] support inotify for user space
选上对些系统调用的支持。

如果内核没有对这些系统调用的支持,
int fd = syscall(inotify_init) 总是返回 89,
read(fd, buff, sizeof(buff)) 会返回 -1, errno 被设置为 "Bad file descriptor"。

inotify 会监视目录下所有文件。
inotify 并不自动的递归监视目录下的子目录,需要程序员自己完成这样的工作。

 

1.简介

使用这些 API 可以监视文件系统的 events.
当文件或者目录有改变时,内核产生 inotify events, 用户使用这些 API 获取关注的 events.
不同于陈旧的 dnotify API, inotify API 既可以监视 files 也可以监视 directories.
监视一个目录,不仅可以获取目录自身改变的 event(e.g. 从目录移除文件),也可以监视目录内文件内容改变产生的 event.
另外:称这些函数为 API 是因为它封装了 system call。每一个函数对应一个 system call. 

继续阅读

读取 /dev/urandom or /dev/random 生成随机数

< /dev/urandom tr -dc A-NP-Za-kmnp-z2-9 | head -c 8
 获取一个 8 位的随机数,除了 0, 1, o,O, l 之外.
 
tr -dc a-z < /dev/urandom 从 /dev/urandom 读入数据并且把所有的小写字母输出。
-d, delete characters in SET1 (a-z);
-c, use the complement of SET1
一个删除,一个补全,最终的效果就是获得读入数据中的所有小写字母。
tr 是从标准输入读入数据,直到遇到 EOF 才会停止,但是 /dev/urandom 是没有 EOF 的。
head -c 8 的作用很明显,就是获得 tr 标准输出中的前 8 个字符,这样这条命令/程序,才可以退出。
 
节省熵池的方法, 生成了长度为 12 的随机数:
dd if=/dev/urandom bs=1 count=6 2> /dev/null | od -t x1 | tee test | sed '2d;s/^0\+ //;s/ //g'
 
生成一个随机 MAC 地址, 节省熵池的方法:
dd if=/dev/urandom bs=1 count=6 2> /dev/null | od -t x1 | sed '2d;s/^0\+ //;s/ /:/g'
 
不建议使用的方法:
< /dev/urandom tr -dC a-f0-9 | head -c 12 | sed s/../\&:/g | head -c 17
 
/dev/urandom:u 是 unlocked 的意思.
/dev/urandom 是 /dev/random 的非阻塞副本。
/dev/random 是 Linux/Unix  操作系统中的一个设备文件,用来作为随机数发生器/伪随机数发生器。
它允许程序访问来自设备驱动程序或其它来源的背景噪声,Linux是第一个以背景噪声产生真正的随机的实现。
 

继续阅读

为 Android 程序创建 CA keystore 以及 self-signed keystore 的方法

为什么 Android 程序需要 CA KeyStore?
1. 在 Android 程序中建立一条 SSL/TLS 连接时,受信任 CA (Trusted CAs) 用来验证 server。
    Public-Key Infrastructure (PKI) 中有 trust certs 概念,许多网络工具实现了 trusted CA 的使用。
    比如: curl, Android URLConnection.
    Android 系统中有一个 trusted CAs list, 包含 100 多个 trusted CAs.
  
2. 建立 SSL/TLS 连接时现有的 trusted CAs 不能验证服务器证书,会引发一个 security exception:
       javax.net.ssl.SSLHandshakeException: ... : Trust anchor for certification path not found.
   具体而言,以下这些情况会引发这个问题:
      (a) The CA that issued the server certificate was unknown;
      (b) The server certificate wasn't signed by a CA, but was self signed;
      (c) The server configuration is missing an intermediate CA;
 
3. 针对(a) 和 (b) 这两种情况,解决方法是创建 SSL/TLS 连接时使用 Android TurstManager 工具。
    在 Android 程序中 TrustManager 用 KeyStore instance 初始化,而 KeyStore instance 读取/解析
    BKS/JKS 格式的 KeyStore 文件,获得证书信息。
 

继续阅读

vim 中使用 cscope 的方法。

0. cscope 依赖 $EDITOR, $CSCOPE_DB 环境变量。
    使用 vim 打开 cscope 的搜索结果,需要设置 $EDITOR 环境变量为 vim.
    $ vim ~/.bashrc
        # add: export EDITOR=vim
    $ source ~/.bashrc
    NOTE: 很多工具都依赖 EDITOR 这个环境变量,修改这个环境变量的值时需考虑其影响范围。
 
    vim 启动时默认加载当前工作目录下的 cscope database 文件,并且与 database 建立连接。
    在 ~/.bashrc 或者类似的文件中设置 $CSCOPE_DB 环境变量,vim 启动时也会自动加载 $CSCOPE_DB
    指定路径的 database 文件,并与 database 建立连接。
    $ vim ~/.basrch
       # add: export CSCOPE_DB=/path/to/cscope_db_file
    $ source ~/.bashrc
   
    如果这个 database 文件用在系统中的所有工作目录,其中存储的文件路径应该是绝对路径,
    不然只能用在特定的工作目录了。
    如何做到这一点呢?下面 1.2 给出方法。
    
    
 1. cscope 的一些用法与 ctags 相同。
     1.0 cscope 也支持 -R 参数,在不加 -b 参数时,cscope 运行一个curses-based GUI.
         CTRL-d 退出该 GUI, 实际上也退出了 cscope.
           Find this C symbol:
           Find this global definition:
           Find functions called by this function:
           Find functions calling this function:
           Find this text string:
           Change this text string:
           Find this egrep pattern:
           Find this file:
           Find files #including this file:
           Find assignments to this symbol:
        debug 一些问题时,经常出现(a)找到某个 error message 是在哪里打印的;
        (b)某个打印 error message 并且退出程序的函数是如何实现/定义的;
        (c)某个宏定义的值是什么; (d) 包含某个头文件的其他文件;
        (e)函数的定义以及调用的地方; 诸如此类的需求,却无需关注全部代码,用这个工具非常方便。
         cscope 还支持: (f) 使用 egrep 格式搜索字符串; (g) 根据文件名 find 文件; 实在方便。
 

继续阅读




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee