多线程同时操做一个epoll_fd

为何选择多线程?而不是多进程?

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

 

咱们的问题如何产生的?问题的根本缘由是什么?

事情是这样的,模块之间须要通讯,咱们用了openwrt的开源代码ubus作消息转发socket

在咱们的每一个须要通讯的模块中建立了一个线程(ubus thread)循环接收ubusd转发而来的消息(用的是libubox提供的API  uloop_run)工具

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

大多数状况是这样的:spa

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。

 

因此,用了开源库,加上咱们独特的线程设计,给本身整了个大坑....

相关文章
相关标签/搜索