Archive

Archive for March, 2014

daemon平滑升级

March 15th, 2014 No comments

对于socket server来说,平滑升级对于用户体验是一个很好的加分。这样可以不对用户操作造成任何影响。而平滑升级的最直接最核心的应该就是不在升级的过程中关掉listen端口,也不断掉当前的连接,同时还可以接受新的连接,所有的已被接受的请求必须要完整的执行完毕,不能因为升级步骤而终止.

为了到达以上几点,一般来说可以如下来达到平滑升级的目的。
1. 分区域升级。 首先在总控部分,禁掉某些部分server接受新请求的能力。然后等待这些server的当前请求全部执行完毕以后开始升级这些server上的服务。采用这种方法逐步升级所有的server
2. 采用类似nginx的平滑升级的方法。

第一种方法对于那些直接面对客户端的server是行不通的,因为一旦停掉一会儿,客户端就无法正常连接了。还有就是即便是分布式的后台加上一个proxy, 也可能会因为某些特定的请求很难在短时间内完成而无法升级(例如,一个广播流的频道请求),对于这些特定的就需要在指定的维护窗口时间强行升级服务了。
第二种方法看起来比较的容易接受。google了一下nginx的平滑升级的办法。其主要使用到了如下几点技术:

1. 未设置FD_CLOEXEC 属性的文件描述符在fork以后会被child process继承,一般来说除了listen socket和log以外的fd都会设置这个属性
2. 把需要在child process里面使用的文件描述符通过env或者命令行参数在调用execv的时候传入新的process
3. 在启动child process之前,停止accept新的请求。
4. parent process一直服务到所有的请求执行完毕以后正常退出。
5. child process在收到传入的文件描述符(通常是用于listen的那个socket的fd)以后使用这个fd来做accept操作。

这个方法解决了socket连接的平滑升级,但是对于log的问题,我还不是特别肯定。如果parent process和child process都是用同一个log文件的描述符,那么必然会造成打印出的日志是混乱的。当然如果你的日志写入是严格按照一个信息块的方式写入的话(也就是不是几个字节几个自己的写入),而vfs_write又是对inode加锁的,那么虽然会有parent process和child process的日志混杂在一起,但是每一条日志内容本身应该是有完整信息的。(这个还需要核实)。

在这里有相关的测试代码

 

Written with StackEdit.

Categories: programming Tags:

program killed by signal 4

March 4th, 2014 No comments

一个跑在linux上的程序crash了,coredump的文件名显示是被signal 4干掉的。哎呀,第一次见到signal 4. 赶紧kill -l看了一下,原来4是指SIGILL,就是invalid instruction. 一般来说我会认为这个东西是stack buffer overflow。 对我们自己的程序来说最可能出错的地方就是打印日志的时候参数类型和format里面指定的类型不一致。特别是fmt=”%s”但是传入了一个sd::string。但是我记得以前这种问题貌似都不会是SIGILL,要么SIGABORT要么SIGSEGV。那么这次是咋回事呢。

上gdb,同时把symbol文件放上去了。看了一下callstack。



(gdb) bt

0x00000000004ab9cc in waitForResponse (this=0x7f12e80804d8, cseq=2)

0x0000000000525920 in SsServiceImpl::doCommit (ss=..., ctx=..., )

0x0000000000570ecd in SsStreamCommitRequest::run (this=0x7f125c001330)

0x00007f12f51318ca in SlaveThread::run (this=0x7f12e80260d0)

0x00007f12f5120fc2 in NativeThread::_execute (thread=0x7f12e80260d0)

0x00007f12f2dba851 in start_thread () from /lib64/libpthread.so.0

0x00007f12f30b767d in clone () from /lib64/libc.so.6

其中waitForResponse里面主要是等待一个信号量。检查其代码没有发现啥问题,而且该函数内部没有任何日志打印的调用。

在老半天没有进展的境况下,我尝试着看了一下反汇编的结果



(gdb) set disassembly-flavor intel

(gdb) disas

...

   0x00000000004ab9ca <+256>:   leave

   0x00000000004ab9cb <+257>:   ret

=> 0x00000000004ab9cc <+258>:   ud2a

   0x00000000004ab9ce <+260>:   mov    ebx,edx

   0x00000000004ab9d0 <+262>:   mov    r12,rax

   0x00000000004ab9d3 <+265>:   lea    rax,[rbp-0x40]

箭头所指的地方就是crash的点,但是,但是ud2a是个神马玩意儿,对汇编不甚了解啊。

还是求助于google大神吧。

一阵瞎找以后看到了这篇文章里面解释了gcc在遇到format里面的参数类型和传入参数类型不匹配的时候报了warning,但是同时可能会产生出ud2a之类的代码,让程序在运行时挂掉。

例如:



struct A {

    int a,b,c,d;

};

A a;

log.info("What a stupid error [%p]",a);

呵呵,我们的代码中就犯了这样的错误。

修改以后让同事们多加注意代码问题以及build时候的warning.



t.c:9:2: warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘struct AA’ [-Wformat=]

  log.info("What a stupid error [%p]\n",a);

  ^

Written with StackEdit.

Categories: Uncategorized Tags:

go的测试程序,与预期不符合。不知道是哪里理解不正确了

March 1st, 2014 No comments

在学习go的时候,按照[go语言编程]第94页的例子写了一个测试程序,如下

package main
import  (
    "fmt"
)
func Count( ch chan int, value int ) {
    ch <- value
    fmt.Println("Counting")
}
func main( ) {
    chs :=make( []chan int, 10 )
    for i := 0; i < 10; i++ {
        chs[i] = make(chan int) //请注意这一行
        go Count(chs[i],i)
    }
    var value int
    for _, ch := range(chs) {
        value = <-ch
        fmt.Printf("got value %d\n",value)
    }
}


但是运行的时候发现结果与我预期的不一样。本来我认为这个程序会打印10行Counting, 但实际上只有1行Counting出现。
于是我把标示那一行改成

chs[i] = make(chan int,1)

就这样,运行的时候就可以打印出10行Counting来了。是在不明白这个到底是咋回事啊。
继续修改有标示的那一行发现,如果make( chan int, 0 )或者make( chan int)那么就只会打印一行Counting.如果make的第二个参数>= 1,那么就会如预期一般打印10行Counting.
这让我更加糊涂了,按照书上所讲。make( chan int, x)的方式是建立一个有缓冲区且大小为x的channel。而且测试程序始终能够输出如下:

got value 0
got value 1
got value 2
got value 3
got value 4
got value 5
got value 6
got value 7
got value 8
got value 9

 

那么至少代表建立的10个 go routine是开始执行了的。看起来当make( chan int, 0)的时候只有一个Count的go routine在向channel写入数据以后被切换回去继续执行了,而剩下的Count的go routine都没有得到执行机会。不过这个不能解释为何make( chan int, x) x >=1 的时候所有的go routine就都机会执行完毕了。

嗯,我的go的版本是:

$ go version
go version go1.2 linux/amd64

Written with StackEdit.

Categories: programming Tags: ,