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 文件; 实在方便。
?

继续阅读