Intelligence without ambition is a bird without wings.

2016-04-20
服务器优化

tcp

  • 根据协议特点,设置套接字接受缓冲区低水位标志(SO_RCVLOWAT,默认值为1)为对应的协议头大小
阅读此文

2016-04-18
mysql-c-connector

要点

  1. C里空指针 => mysqlNULL

参考

  1. http://dev.mysql.com/doc/refman/5.7/en/mysql-fetch-row.html
阅读此文

2016-04-18
select

API

1
2
3
4
5
6
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

select

  • nfds 3个描述符集合中最大的描述符+1,用来减少select遍历次数

  • readfds 监听对应描述符集合的读就绪(包括end-of-file即tcp断开)

  • writefds 监听对应描述符集合的写就绪

  • exceptfds 监听对应描述符集合的异常

对比epoll

  • 通过不同的set来监听对应事件,增加了数据拷贝量

  • 通过修改传入的set,来返回对应IO就绪套接字(所以对于应用层代码来说,还需要额外保存下原set)

  • 通过4个辅助宏来管理描述符,和判断哪个描述符上IO就绪

BUGS:

  • 在linux中,select可能返回对应的描述符读就绪,但实际读失败。可能原因有,数据到达但校验失败被丢弃或者被惊醒

优缺点

优点

  • 跨平台,普遍支持

缺点

  • 描述符大小有限制(也就限制了数量),因为内部采用bitmap(固定数组)实现,只能预分配固定大小的

    描述符必须小于FD_SETSIZE

  • 效率较低

    • 从api上看,监听套接字的多个事件时,需要更多的拷贝,因为select是通过set来区分事件的

    • 每次调用都需要从应用层拷贝描述符集合到内核

    • 每次调用返回,都需要遍历整个描述符集合比较

要点

fd_set实现(/usr/include/sys/select.h)

  • 描述符采用整数数组实现,通过bitmap来存储对应描述符

  • FD_SETSIZE默认为1024

    /usr/include/bits/typesizes.h
    #define __FD_SETSIZE        1024
    

源码分析

/usr/include/sys/select.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* The fd_set member is required to be an array of longs.  */
typedef long int __fd_mask;

/* Some versions of <linux/posix_types.h> define this macros. */
#undef __NFDBITS
/* It's easier to assume 8-bit bytes than to get CHAR_BIT. */
#define __NFDBITS (8 * (int) sizeof (__fd_mask))
#define __FD_ELT(d) ((d) / __NFDBITS)
#define __FD_MASK(d) ((__fd_mask) 1 << ((d) % __NFDBITS))

/* fd_set for select and pselect. */
typedef struct
{
/* XPG4.2 requires this member name. Otherwise avoid the name
from the global namespace. */

#ifdef __USE_XOPEN
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
} fd_set;

/* Maximum number of file descriptors in `fd_set'. */
#define FD_SETSIZE __FD_SETSIZE

#ifdef __USE_MISC
/* Sometimes the fd_set member is assumed to have this type. */
typedef __fd_mask fd_mask;

/* Number of bits per word of `fd_set' (some code assumes this is 32). */
# define NFDBITS __NFDBITS
#endif


/* Access macros for `fd_set'. */
#define FD_SET(fd, fdsetp) __FD_SET (fd, fdsetp)
#define FD_CLR(fd, fdsetp) __FD_CLR (fd, fdsetp)
#define FD_ISSET(fd, fdsetp) __FD_ISSET (fd, fdsetp)

/usr/include/bits/select.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#if defined __GNUC__ && __GNUC__ >= 2

# if __WORDSIZE == 64
# define __FD_ZERO_STOS "stosq"
# else
# define __FD_ZERO_STOS "stosl"
# endif

# define __FD_ZERO(fdsp) \
do { \
int __d0, __d1; \
__asm__ __volatile__ ("cld; rep; " __FD_ZERO_STOS \
: "=c" (__d0), "=D" (__d1) \
: "a" (0), "0" (sizeof (fd_set) \
/ sizeof (__fd_mask)), \
"1" (&__FDS_BITS (fdsp)[0]) \
: "memory"); \
} while (0)


#else /* ! GNU CC */

/* We don't use `memset' because this would require a prototype and
the array isn't too big. */

# define __FD_ZERO(set) \
do { \
unsigned int __i; \
fd_set *__arr = (set); \
for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i) \
__FDS_BITS (__arr)[__i] = 0; \
} while (0)


#endif /* GNU CC */

#define __FD_SET(d, set) \
((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))

#define __FD_CLR(d, set) \
((void) (__FDS_BITS (set)[__FD_ELT (d)] &= ~__FD_MASK (d)))

#define __FD_ISSET(d, set) \
((__FDS_BITS (set)[__FD_ELT (d)] & __FD_MASK (d)) != 0)

阅读此文

2016-04-14
c++-bind

要点

  • bind的参数是值传递!意味着,如果直接传递某个值,将会复制构造!所以若不想这样,得传递指针!

参考

  1. http://stackoverflow.com/questions/16016112/stdbind-of-class-member-function
阅读此文

2016-04-14
TIME_WAIT

是什么?

为什么需要?

如何查看?

如何优化?

1
2
3
4
5
6
net.ipv4.tcp_tw_reuse = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout
#修改系統默认的 TIMEOUT 时间。
阅读此文

2016-04-13
centos-安装记录

  • 安装VBoxGuestAdditions

  • 配置sudo

    visudo
    
    wallace ALL=(ALL)       NOPASSWD: ALL
    
  • 安装epel(http://www.cyberciti.biz/faq/installing-rhel-epel-repo-on-centos-redhat-7-x/)

    yum install epel-release
    
  • 添加组

    yum install libuser
    lgroup -M wallace vboxsf
    
  • 配置环境变量

    vi /etc/profile
    
  • 配置系统语言

    echo LANG=en_US.UTF-8 >/etc/locale.conf
    
  • 配置启动时间

    vi /boot/grub2/grub.cfg
    
    set timeout=1
    
阅读此文

2016-04-12
死锁

产生的必要条件

  • 互斥
  • 请求与保持
  • 不被剥夺
  • 循环等待

    一直等待请求额外互斥资源

可能发生的场景

  • 在函数嵌套调用中,多次请求同一资源

  • 不同线程中,以不同的顺序请求多个资源,而其中某个资源不足

调试与诊断方法

  1. ps查看进程PID,状态,阻塞原因

  2. gdb attach到目标进程

  3. 保存现场,防止后续调试破环

    generate-core-file
    
  4. 查看所有线程的调用栈

    thread apply all bt
    
  5. 切换到相应线程

    thread NUM
    
  6. 查看相应互斥量,持有情况(pthread_mutex_t.__owner)

    print MUTEX
    

预防

  • 打破产生死锁的4个必要条件中的任何一个
  • 利用RAII手段,确保stack mutex离开作用域时被解锁(MutexGuard, std::lock_guard)

    • 异常结束(throw)

    • 分支跳出(break, continue, return)

    • 意外退出点(exit)

阅读此文

2016-04-08
cpp

选项

  • -dCHARS

    CHARS前面不能有空格

    • M

      处理#define指令

    • D(常用)

      Like M except in two respects: it does not include the predefined macros, and it outputs both the #define directives and the result of preprocessing.
      Both kinds of output go to the standard output file.

    • I

      Output #include directives in addition to the result of preprocessing.

参考

  1. cpp输出格式
阅读此文

2016-04-07
mysql-char-vs-varchar

区别

占用空间

  • char(M)占用空间固定,M[0,255]个字符,具体占用字节与编码相关

  • varchar(M[0,65535])占用空间不固定,因为首部占用1或2B来记录长度M+1(M<256),占用M+2(255<M<65536)

关于空格

  • 在存储字符串到char中时,会填充空格至相应长度

  • 取出char类型值时,若sql_mode没有设置PAD_CHAR_TO_FULL_LENGTH,则会去除尾部空格

    所以导致插入带空格的字符串,取出时却不带有空格!

  • varchar会存储空格和保留空格,不会转换!

参考

  1. http://dev.mysql.com/doc/refman/5.6/en/char.html
阅读此文

2016-04-05
redis

配置(/etc/redis.conf)

1
requirepass foobared # 设置密码

命令

  • 启动redis

    redis-server /etc/redis.conf
    
  • 根据pattern删除key

    redis-cli KEYS "prefix:*" | xargs redis-cli DEL
    

命令返回值

1
2
3
4
5
6
7
8
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
int len; /* Length of string */
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
  • 状态(#define REDIS_REPLY_STATUS 5), str=> value

    • SET => “OK”

    • PING => “PONG”

  • 错误(#define REDIS_REPLY_ERROR 6), str => value

  • 整数(#define REDIS_REPLY_INTEGER 3), integer => value

    一些整数操作命令,如INCR返回增加后的键值

  • 字符串(#define REDIS_REPLY_STRING 1), str => value

    当请求一个字符串类型键的键值时,若每个键不存在,则返回特殊值nil(#define REDIS_REPLY_NIL 4)

  • 字符串数组(#define REDIS_REPLY_ARRAY 2), str => value, element => next

备份与恢复

  • SAVE

    同步保存数据到磁盘文件中

    dbfilename dump.rdb
    dir /var/lib/redis/
    
  • BGSAVE

    异步保存

  • CONFIG get dir

    恢复数据(dir由/etc/redis.conf中定义)

Server端处理客户端socket

Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:

  1. 首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。
  2. 然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法
  3. 然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送
阅读此文