多线程同时操作一个epoll_fd

为什么选择多线程?而不是多进程?

比起多进程来说,线程间通信简单(全局变量就可以了),而多进程之间的通信相对而言更繁琐一些,呵呵...

 

我们的问题如何产生的?问题的根本原因是什么?

事情是这样的,模块之间需要通信,我们用了openwrt的开源代码ubus做消息转发

在我们的每个需要通信的模块中创建了一个线程(ubus thread)循环接收ubusd转发而来的消息(用的是libubox提供的API  uloop_run)

在模块需要发送时,主线程调用ubus的消息发送接口(ubus_send_event)

大多数情况是这样的:

1. 模块(作为一个后台进程)启动时装载了libubox.so(注意,这个库的载入时加入了一个全局变量 int epoll_fd)

2. 随即就创建线程ubus thread循环等待接收消息,ubus thread的动作大致如下:

    a. 建立和ubusd通信的socket(记为fd1)

    b. 随即调用epoll_ctl将fd1加入到epoll_fd中

    c. 然后调用epoll_wait....

3. 主线程执行,在有需要的时候,调用ubus_send_event发送

    实际上这个库接口做的动作是这样的:

    a. 建立和ubusd通信的socket(记为fd2)

    b. 将fd2加入到全局变量epoll_fd,然后执行epoll_wait(epoll_fd没有创建的话,先调用epoll_create)

那么问题来了,一般都是ubus thread先执行,然后阻塞在epoll_wait中,主线程在ubus thread阻塞时将fd2加入到同一个epoll_fd中....

也就是两线程在操作同一个epoll_fd

结果fd2状态发生变化时,ubus thread被唤醒执行了,然后就乱套了......

 

尝试了一下,在主线程发送的时候去fork子进程,子进程完成发送的动作,反而更悲剧了,整个进程崩掉了....

为啥呢,因为fork出来的子进程没有执行exec切换进程执行上下文,完全是一个父进程的拷贝,那个出问题的epoll_fd也被它拿着了

然后子进程新建了一个和ubusd通信的fd2,加入到这个epoll_fd中,然后执行epoll_wait

结果fd2发生状态变化时,内核唤醒了ubus thread,尼玛,ubus thread哪里知道fd2的存在,结果非法地址访问,Segmentfault....呵呵...


最后,最后,因为我们这群小蜜蜂实在是飞得太低,所以,我们借助了ubus源码附带提供的ubus命令行工具

在主线程发送时 system("ubus send %s ", message)

这就相当于在主线程发送时fork子进程,然后子进程的执行环境切换到ubus工具,ubus工具会调用ubus_send_event

 

另一种解决方案:

还是在主线程发送的时候去fork子进程,子进程一上来直接去循环关闭从0-1024的fd,哈哈哈哈...(不知道会不会有什么其他的影响,不过我觉得应该可以)

for (i = 0; i < FDSET; i++)

  close(i)

这样做是强迫子进程再次去调用epoll_create,而不是复用从父进程那里继承而来的epoll_fd。

 

所以,用了开源库,加上我们独特的线程设计,给自己整了个大坑....

 

posted @ 2015-09-09 14:25  xiaokuang  阅读(4056)  评论(1编辑  收藏  举报