多線程同時操作一個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。

 

所以,用了開源庫,加上我們獨特的線程設計,給自己整了個大坑....

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM