日期:2014-05-16  浏览次数:20731 次

linux多线程下载工具 axel 下载中止的问题
linux多线程下载工具 axel 下载中止的问题

1 axel是什么
axel是一个linux下的多线程下载工具, 官网 http://axel.alioth.debian.org/

2 我遇到的问题
    $> axel -a -n 10 -s 409600 "myurl"
    下载一段时间再无进度. 而且这种现象很难重现.
3 axel 源码的逻辑
  
  main()
    {
        axel_new()
        {
            /* 发送HTTP GET 请求 1个字节,获取下载文件字节数 */
        }
        axel_open()
        {
            /* 分配每个连接下载文件数据的起止字节数, 并打开用于保存下载数据的文件 */
            axel->outfd = open();
        }
        axel_start()
        {
            /* 创建线程池 */
            setup_thread()
            {
                /* 线程处理函数 仅仅是创建连接 之后就返回了.而且 connect() 未设置超时 */
                gethostbyname();
                connect();
                return;
            }
        }
        /* 注册 SIGINT 和 SIGTERM 信号  */
        while (下载未完成 且 未接收到信号)
        {
            axel_do()
            {
                /*
                   保存下载状态 
                   注册 select() 
                   如果当前线程池未创建有效连接,即所有描述符(axel->conn[0-n].fd <= 0), 则回收线程或重新创建线程 
                   如果某线程 创建连接成功或未成功返回,则回收线程,并再次创建线程,执行连接操作. 
                   如果某线程 在设定时间内未返回,则 pthread_cancel() 掉该线程. 但是 pthread_cancel() 并不回立马生效 
                   select()
                   如果某连接可读, 则读取数据,并写入文件对应位置.
                   如果某连接不可读 且 超时 45秒, 则关闭连接.下次循环时会创建线程重新连接.
                 */


            }
        }
    }




4 分析过程
    使用strace跟踪当前 axel 进程:
    strace -f -tt -p PID 
    然后看单个线程的执行过程:

16457 14:28:54.194584 clone(child_stack=0xb5e35494, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb5e35bd8, {entry_number:6, base_addr:0xb5e35b70, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb5e35bd8) = 23423
23423 14:28:54.194938 set_robust_list(0xb5e35be0, 0xc <unfinished ...>
23423 14:28:54.195125 <... set_robust_list resumed> ) = 0
23423 14:28:54.195300 futex(0xb77bcde0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
16457 14:29:14.077986 tgkill(16457, 23423, SIGRTMIN <unfinished ...>
23423 14:29:14.078204 <... futex resumed> ) = ? ERESTARTSYS (To be restarted)
23423 14:29:14.078424 --- SIGRTMIN (Unknown signal 32) @ 0 (0) ---
23423 14:29:14.078676 madvise(0xb5635000, 8372224, MADV_DONTNEED <unfinished ...>
23423 14:29:14.078761 <... madvise resumed> ) = 0
23423 14:29:14.078916 _exit(0)          = ?


16457 14:28:54.195374 <... clone resumed> child_stack=0xb5634494, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb5634bd8, {entry_number:6, base_addr:0xb5634b70, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb5634bd8) = 23424
23424 14:28:54.195524 set_robust_list(0xb5634be0, 0xc <unfinished ...>
23424 14:28:54.195666 <... set_robust_list resumed> ) = 0
23424 14:28:54.195877 futex(0xb77bcde0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
16457 14:29:14.078985 tgkill(16457, 23424, SIGRTMIN) = 0
23424 14:29:14.079124 <... futex resumed> ) = ? ERESTARTSYS (To be restarted)
23424 14:29:14.079353 --- SIGRTMIN (Unknown signal 32) @ 0 (0) ---
23424 14:29:14.079510 madvise(0xb4e34000, 8372224, MADV_DONTNEED <unfinished ...>
23424 14:29:14.079719 <... madvise resumed> ) = 0
23424 14:29:14.079944 _exit(0)          = ?


...


发现:

    -> 新分配的线程都停在了 futex(0xb77bcde0, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
    |
    v
    -> 显然是死锁了.
    |  为什么死锁?
    v
    -> 查看源码, 并没有使用锁机制, 整个代码中调用的线程函数有:
    |   pthre