分类目录归档:操作系统

UNIX 是我的最爱

升级手记:通通最新版,php-5.5.0/nginx-1.4.1/mysql-5.6.12

俗话说“稳定压倒一切”,不出问题或没有特殊需求的话,哪怕他们如火如荼的发布新版,吹嘘着新功能新特性,大部分人仍会选择不升级。

手上的机器大多也是停留在超旧的版本上,比如:php-5.2.x,nginx-0.8.44,mysql-5.0.89 …

相比之下确实太老太老了,都好几年前发布的甚至有的已经逐步停止支持(即使有BUG也没人修复),总归得向前看。这次激进一点,全部拿最新版本开刀。

我个人有一个习惯,使用新东西前会比较全面的去了解一下。像这些重要软件都会采用源码方式编译,仔细核对手册上的配置选项级含义,然后做一套基础模板出来。这么多年过去了,会有新的选项支持或废弃了旧选项。

1. nginx 这是足以取代 apache 的新生 web 服务器软件,目前我们已经全面推广在使用。特点就是高性能,低开销。从 0.8.44 升级来还算顺利,配置文件也基本没变。我的配置指令如下:

CFLAGS=-O2 ./configure --prefix=/path/to/nginx \
--user=nobody --group=nogroup \
--without-select_module --without-poll_module \
--with-http_ssl_module --with-http_gzip_static_module \
--with-http_stub_status_module \
--with-pcre \
--without-http_ssi_module --without-http_userid_module \
--without-http_geo_module --without-http_map_module \
--without-http_uwsgi_module --without-http_scgi_module \
--without-http_memcached_module

make && make install

一路顺利,然后还测试了最近很火红和被热追的 ngx_lua 模块,安装比较复杂,这里不细说了。

下面再分享一下我这儿关于 PHP 的配置部分,完美支持 PATH_INFO(甚至你的目录名取为 *.php 也支持):

## PHP/FCGI support
error_page 434 = @fastcgi;
location ~ \.php$ {
  if (-d $request_filename) {
    rewrite ^ $uri/ redirect;
  }
  if (!-f $request_filename) {
    return 434;
  }
  include fastcgi.conf;
}

## PHP with pathinfo
location ~ \.php/ {
  try_files $uri $uri/ @fastcgi;
}

## FastCGI handler
location @fastcgi {
  fastcgi_split_path_info ^(.+?\.php)(/.*)$;
  if (!-f $document_root$fastcgi_script_name) {
    return 404;
  }
  include fastcgi.conf;
}

2. PHP-5.5,编译安装也很顺利,内置了 ZendOpcache,因此配合 apcu(取代apc) 来做 user cache。
但面临的大量 error log,主要是以前不规范的 E_NOTICE,还有很多 E_STRICT, E_DEPRECATED 这种是随着 PHP 变迁
而造成的,怨不得人。最无比蛋疼的是:preg_replace 中的 /e 修正符,mysql 扩展(mysql_connect)deprecated 了。

🙁 为确保新代码的规范性,我们决定在 php.ini 里采用 E_ALL,而旧代码则在公共头文件修改 error_reporting 来临时屏蔽。

3. MySQL 5.6.12,这个是最纠结的。编译时间相当长,下面是我的 cmake 配置:

cmake -DCMAKE_INSTALL_PREFIX=/home/soft/mysql56 \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS=all \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_MEMORY_STORAGE_ENGINE=1 \
-DWITH_READLINE=1

make && make install

漫长编译过后。从网上下载了一份比较新的中文版的 my.cnf 对照 changeslog 改了改做模板(不少选项失效了,如:query-slow-log)。
就头疼那个数据库转换,拿几个库简单测试一下都不太好,用 mysql_upgrade 均失败,大概版本跨度太大了。

于是手动修改新的 my.cnf 将服务绑在别的端口和 socket 上先,然后新旧2个 MYSQL 都开起来。
采用 mysqldump 将旧版数据导出来(7G多),再导入新版 mysql 中,再在新版处运行 bin/mysql_upgrade 完成升级。

注:需要手动删除旧版导出数据中的 `mysql.user` 表的 root 等相关帐号,避免冲突导不进。

激动,终于找到 xunsearch-1.4.x 以来的潜伏 BUG

BUG 是这样的,不断有用户反馈说 xunsearch 会出现 signal 11 等各种意外的挂掉。虽然内部的保护和重生机制能保持进程自动重启,但还是非常影响用户体验,甚至偶尔还会出现内存泄露。

再多次 debug 无效后,导出 core 文件分析发现 11 信号来自 malloc 调用,这说明前面的堆内存用法错误或越界破坏了 malloc 内部维护的数据才会导致。

于是根据少得可怜的 backtrace 信息搜寻,很快定位到了问题函数 get_queryparser ,代码是这样的,大家别看后面先自己找找问题在哪?

/**
 * Get a queryparser object from cached chain
 */
static Xapian::QueryParser *get_queryparser()
{
	static struct cache_qp *head;

	pthread_mutex_lock(&qp_mutex);
	for (head = qp_base; head != NULL; head = head->next)
	{
		if (head->in_use == false)
		{
			log_debug("reuse qp (ADDR:%p)", head);
			break;
		}
	}
	if (head == NULL) /* alloc new one */
	{
		debug_malloc(head, sizeof(struct cache_qp), struct cache_qp);
		if (head == NULL)
		{
			pthread_mutex_unlock(&qp_mutex);
			throw new Xapian::InternalError("not enough memory to create cache_qp");
		}
		log_debug("create qp (ADDR:%p)", head);
		head->qp = new Xapian::QueryParser();
		log_debug("new (Xapian::QueryParser *) %p", head->qp);
		head->next = qp_base;
		qp_base = head;
	}
	head->in_use = true;
	pthread_mutex_unlock(&qp_mutex);

	head->qp->clear();
	return head->qp;
}

各位看到这可能还是并没感觉出问题,最大的问题就是 “static”,完全不应该出现在这,由于前面复制代码的时候把全局定义的宣告直接 COPY 过来于是保留了这个  static 关键字。

这还不打紧,后面不是有互斥锁吗?可是多线程调用和调度完全是不可确认的,在锁之外做了 return,在高并发压力下 return 的 head->qp 或许已经不是当年的那个 head 了。。。

于是就出现了内存混乱!!!这问题牵挂了我整整三天,这三天连发三个 xunsearch 版本,实在抱歉啊!!!

malloc 调用时产生 SIGSEGV

xunsearch searchd 在大压力下仍然很容易出现 sigsegv(11信号,内存非法访问),导出 core 文件进行分析调试发现,这个信号经常发生在 malloc() 系统调用上。

查了一些相关资料发现以下注解:

malloc can segfault for example when the heap is corrupted. Check that you are not writing anything beyond the bounds of any previous allocation.

大概哪儿非法溢出写入内存了,因此会导致这个结局。这下依赖库有点多,调查有点难,先看看!

自定义信号处理,同时又想生成 core dumped 文件

在 UNIX 环境编程时,通常会在收到异常信号时定义自己的处理函数做一些善后工作。

常见的如 SIGABRT,SIGSEGV 信号的默认行为是生成 core 文件然后终止进程,但是当您自定义处理函数后将不再生成 core 文件,但这又非常不利于调试寻找问题。

这里有个婉转办法可以两者兼得,就是在自定义信号处理函数中,恢复默认的处理行为,然后再给自己发送一个信号。示范代码

#include <stdio.h>
#include <signal.h>

static void sig_abrt(int sig)
{
   // put your codes HERE ... 
   printf("run sig_abrt()\n");
   // 还原信号默认处理行为并给自己发送同一个信号
   signal(sig, SIG_DFL);
   raise(sig);
}

int main()
{
  signal(SIGABRT, sig_abrt);
  printf("My PID: %d\n", getpid());
  raise(SIGABRT);
  sleep(100);
}

UNIX 环境多线程编程的几点注意

这个标题写得有点大,其实只是这几天在追踪修复 xunsearch 搜索进程(多线程)死锁时碰到的一系列可能问题的总结和记录。

1. 线程取消时一定要注意“取消点”,特别容易导致死锁
2. 多线程处理信号时谨防在信号处理时,其它线程又产生了别的信号从而引发不可预测的后果,该屏蔽就暂时屏蔽掉,或者加入相关的变量来判断。
3. 多线程交叉判断的全局变量,最好加上 volatile 关键字
4. 结束进程时如无特殊要求建议直接用 _exit() 而不是 exit()
5. … 慢慢补充 …

Linux 指令简单将攻击IP列入iptables 限制范围

今天部分服务器收到真IP的非SYN攻击,郁闷.

netstat -an | grep -v LISTEN | awk ‘{print $5}’ | awk ‘BEGIN { FS=”:” } { Num[$1]++ } END { for(i in Num) if(Num[b][i[/b]]>[color=red][b]8[/b][/color]) { print i} }’ | xargs -i[] iptables -I INPUT -s [] -j DROP

红色部分为设定值,这条句子会自动将netstat -an 中查到的来自同一IP的超过一定量的连接的列入禁止范围,红色部分为阀值.

VI,VIM 使用备忘图

[size=2]简单的说一下[color=#cc0033]vi[/color]编辑器──unix的通用编辑器, 读作vee-eye (visual editor的简写)。 (这里的[color=#cc0033]vim[/color],是改进的[color=#cc0033]vi[/color], [color=#cc0033]Vi[/color] Improved简写,[color=#cc0033]Vim[/color] 由Bram Moolenaar编写) [color=#cc0033]vi[/color]的工作模式分为命令模式和插入模式。[/size]
[size=2][/size]
[size=2]很多命令粗用时感觉很多很难记,用熟了就快了。现发一张常用的VIM命令组成的图表。[/size]
[size=2][/size]