多线程同时操作一个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工具

另一种解决方案:

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

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

  close(i)

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

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

时间: 2024-11-09 03:44:04

多线程同时操作一个epoll_fd的相关文章

iOS多线程拾贝------操作巨人编程

iOS多线程拾贝------操作巨人编程 多线程 基本 实现方案:pthread - NSThread - GCD - NSOperation Pthread 多平台,可移植 c语言,要程序员管理生命周期 创建 //这里已经开启了多线程,直接在这里调用子线程想要调用的代码 void * run(void *pramga) { NSLog(@"-------"); return NULL; } - (IBAction)btnClick:(id)sender { pthread_t pth

多线程IO操作(fork-join版)

接着上篇中没写完的(http://my.oschina.net/bluesroot/blog/223453),上篇中讲到很多,为完成对一个目录的扫描的频繁的IO操作,我们从单线程到多线程,从CountDownLatch到BlockingQueue,中间不免各种Callable和Future和ExecutorService等等,虽然纷繁,中间有些不免麻烦,但是最终仍紧紧贴着我们的需求和多线程操作这一主题. 随着jdk的版本升级,并发包也随之扩充了不少,上面博文中的API来源于jdk1.6,1.7的

多线程IO操作(扫描文件夹并计算总大小)

场景为,给到一个硬盘上文件或文件夹,(当然文件夹时,多线程的优势才能越发体现出来),得到该文件或文件夹的大小和计算该结果所需要的时间. 首先是单线程下的例子,这个可难不倒大家,代码如下: 01 public class TotalFileSizeSequential { 02   private long getTotalSizeOfFilesInDir(final File file) { 03     if (file.isFile()) return file.length(); 04  

[备忘]不用许可证 多线程直接操作界面组件比如超级列表框的实现

平时多线程来操作界面组件 同时写入或者修改数据  比如常见的把多个线程都把日志同时写入到编辑框 又或者 多个线程同时的修改一个超级列表框上的线程状态和其他信息 这样会出现一个问题 如何避免多个线程同时操作一个组件导致的组件冲突问题 我们常用的是使用许可证来给每个线程规定访问顺序来依次执行 不过这样的调整的确从效率上说很低下 大漠老师使用 发送消息 或者说是使用window消息机制来实现不加许可证的同时修改界面组件的思路非常好 511遇见老师也对这个思路进行了深度解析 已经非常的详细了http:/

C++ 多线程中的一个抛出异常

试了一下,和Java完全不同. 注意Java和C++对于多线程里面的一个线程抛出异常的影响,完全不同. Java里面,对于主线程和其他线程完全不受影响: C++里面,整个程序会退出,所有线程都会受影响. Java的多线程与异常的关系,可以看这里:http://www.cnblogs.com/charlesblc/p/6175617.html C++实验,代码如下: #include <stdio.h> #include <stdlib.h> #include <unistd.

通过一个函数,操作一个结构体,实现对应函数功能

指针结构体一直是我的盲点,所以今天有必要整“清理门户”.此种通过一个函数操作一个结构体,实现对应函数功能,用法十分巧妙,使用得当可以使得代码可移植性和易懂性大大的增加,有人说过“代码注释的最高境界是程序的自述,而不是双斜杠然后后面跟着中英文的注释”.哈哈,说远了,下面开始进入今天的加油站,补充体力了. 1 // 头文件 2 #include <stdio.h> 3 4 // 函数声明 5 typedef struct _halDeviceFuncs_t 6 { 7 void (*pfnInit

怎么设置登录名 登陆后只能看到和操作一个数据库 这个用户不能看到其他的数据库

怎样设置登录名 登陆后只能看到和操作一个数据库 这个用户不能看到其他的数据库一个服务器上有三个数据库 我想新建三个登录名 每个登录名登陆后只能看到对应的一个数据库 不能看到其余的两个 怎样进行设置呢 分享到: ------解决方案--------------------1. SQL数据库分配权限打开SQL-Server管理工具?安全性?登陆名?右键(新建登陆名) 输入相应的信息(这里要去掉强制实施密码策略,强制密码过期,用户在下次登录时必须修改密码的选择) 用户映射?选择我们要设置权限的数据库?

多线程采用不同的方法操作一个资源的例子

资源类 两个不同的方法,都是同步方法,当一个线程调用一个同步方法时,另外一个线程是不能调用另一个同步方法. 要特别注意标记的操作. notify()执行后,如果这个线程后面还是程序未执行完,要先执行完,才会让出对象的锁. public class ResDemo { private int num; private boolean flag; public ResDemo(int num, boolean flag) { super(); this.num = num; this.flag =

只开启一个窗体和进程以及多线程的操作

只开启一个窗体: 1 Form1 F1 = null; 2 public Form2(Form1 f1) 3 { 4 InitializeComponent(); 5 F1 = f1; 6 } 7 8 private void Form2_FormClosed(object sender, FormClosedEventArgs e) 9 { 10 F1.Close(); 11 } 12 13 List<Form> list = new List<Form>();//建立一个可以存