2012-02-20 11 views
21

Yükseltme ASIO kullanarak bir C++ kütüphanesi oluşturdum. Kütüphane hem diş güvenli hem de çatal güvenli olmalıdır. io_service::run()'u arayarak hizmet zamanlayıcısı iş parçacığına sahiptir. Çatal emniyetini desteklemek için pre_fork, post_fork_parent ve post_fork_child işleyicileri kaydettim. pre_fork() işleyicisi, _io_service.notify_fork(boost::io_service:fork_prepare() numaralı telefonu arayarak, post_fork_parent işleyicisi _io_service.notify_fork(boost::asio::io_service::fork_parent) numaralı telefonu ve post_fork_child çağrılarını _io_service.notify_fork(boost::asio::io_service::fork_child) çağırır.Asio çatalını nasıl güvenli hale getirebilir

Karşılaştığım sorun, fork() gerçekleştiği zaman, hizmet zamanlayıcısı iş parçacığı bazı işlemlerin ortasında olabilir ve io_service nesnesinin veri üyeleri üzerinde kilitlenebilir. Bu nedenle, çocuk işlemi, aynı durumda ve _io_service.notify_fork(boost::asio::io_service::fork_child) numaralı telefonu aradığımızda post_fork_child() 'da görür, aynı nesne üzerindeki kilidi almayı dener ve bu nedenle süresiz olarak engellenir (kilidin açılması için çocukta bir iplik olmadığı için).

Ben engellenir çocuk süreç, gördüğünüz yığın izlemesi -

fffffd7ffed07577 lwp_park (0, 0, 0) 
fffffd7ffecffc18 mutex_lock_internal() + 378 
fffffd7ffecfffb2 mutex_lock_impl() + 112 
fffffd7ffed0007b mutex_lock() + b 
fffffd7fff26419d __1cFboostEasioGdetailLscoped_lock4n0CLposix_mutex__2t5B6Mrn0D__v_() + 1d 
fffffd7fff2866a2 __1cFboostEasioGdetailQdev_poll_reactorMfork_service6Mn0BKio_serviceKfork_event__v_() + 32 
fffffd7fff278527 __1cFboostEasioGdetailQservice_registryLnotify_fork6Mn0BKio_serviceKfork_event__v_() + 107 
fffffd7fff27531c __1cDdesGtunnelQServiceSchedulerPpost_fork_child6M_v_() + 1c 
fffffd7fff29de24 post_fork_child() + 84 
fffffd7ffec92188 _postfork_child_handler() + 38 
fffffd7ffecf917d fork() + 12d 
fffffd7ffec172d5 fork() + 45 
fffffd7ffef94309 fork() + 9 
000000000043299d main() + 67d 
0000000000424b2c ????????() 

Anlaşılan "dev_poll_reactor" hizmet zamanlayıcısı dizisindeki (bazı bekleyen olayları göndermesiyle gibi görünüyor çünkü) kilitli çatal ne zaman meydana geldiğini soruna neden oluyor.

Sorunu çözmeyi düşünüyorum, hizmet zamanlayıcısı iş parçacığının, fork oluştuğunda herhangi bir işlemin ortasında olmamasını ve pre_fork() işleyicisinde io_service.stop() numaralı telefonu aramak için bir yol olduğunu garanti etmem gerekiyor. iyi bir çözüm gibi gelmiyor. Kütüphane çatalını güvenli hale getirmek için doğru yaklaşımın ne olduğunu bana bildirir misiniz?

Kod parçacıkları böyle bir şeye benziyor.

/** 
* Combines Boost.ASIO with a thread for scheduling. 
*/ 
class ServiceScheduler : private boost::noncopyable 
{ 
public : 
    /// The actual thread used to perform work. 
    boost::shared_ptr<boost::thread>    _service_thread; 

    /// Service used to manage async I/O events 
    boost::asio::io_service      _io_service; 

    /// Work object to block the ioservice thread. 
    std::auto_ptr<boost::asio::io_service::work> _work; 
    ... 
}; 

/** 
* CTOR 
*/ 
ServiceScheduler::ServiceScheduler() 
    : _io_service(), 
     _work(std::auto_ptr<boost::asio::io_service::work>( 
       new boost::asio::io_service::work(_io_service))), 
     _is_running(false) 
{ 
} 

/** 
* Starts a thread to run async I/O service to process the scheduled work. 
*/ 
void ServiceScheduler::start() 
{ 
    ScopedLock scheduler_lock(_mutex); 
    if (!_is_running) { 
     _is_running = true; 
     _service_thread = boost::shared_ptr<boost::thread>( 
       new boost::thread(boost::bind( 
         &ServiceScheduler::processServiceWork, this))); 
    } 
} 

/** 
* Processes work passed to the ASIO service and handles uncaught 
* exceptions 
*/ 
void ServiceScheduler::processServiceWork() 
{ 
    try { 
     _io_service.run(); 
    } 
    catch (...) { 
    } 
} 

/** 
* Pre-fork handler 
*/ 
void ServiceScheduler::pre_fork() 
{ 
    _io_service.notify_fork(boost::asio::io_service::fork_prepare); 
} 

/** 
* Post-fork parent handler 
*/ 
void ServiceScheduler::post_fork_parent() 
{ 
    _io_service.notify_fork(boost::asio::io_service::fork_parent); 
} 

/** 
* Post-fork child handler 
*/ 
void ServiceScheduler::post_fork_child() 
{ 
    _io_service.notify_fork(boost::asio::io_service::fork_child); 
} 

Yükseltme 1.47'yi kullanıyorum ve uygulamayı Solaris i386 üzerinde çalıştırıyorum. Kütüphane ve uygulama stüdyo-12.0 kullanılarak inşa edilmiştir.

+0

Çatal aradıktan sonra çocuğa başka bir şey exec() veya _exit() çağırmayı mı bekliyorsunuz? Eğer öyleyse, yeniden düşünmelisiniz. Değilse, problemi göremiyorum. – janm

+0

Sadece iş parçacığı, komut arabirimi görevleri ve üst-alt işlem için ana iş parçacığı ayırtabilirsiniz. Çataldan sonra, çocukta sadece ana iplik bulunmaktadır. Geri yükleme için iç konfigürasyon verilerini tutabilir ve çocuk süreçlerinde gerekli iplikleri oluşturabilirsiniz. Bu şekilde temiz bir kapsülleme sağlar ve kilitleme ihtiyaçlarını önler. –

+3

İki proje için boost :: asio kullanmayı denedikten sonra, artırma kullanmamanın daha iyi olduğuna inanıyorum. Basit örneklerde bile parçalara ayırır. Karmaşık şablon yapısının, anlaşılması zor ve muhtemel bir sebebi bile anlamlı bir şekilde anlamak ve anlamak imkansızdır. – wallyk

cevap

2

Asio kodu, io_service kodunda herhangi bir kod olduğunda notify_fork()'un çalışmadığını belirtir.

Bu fonksiyon, başka bir iplik olarak adlandırılan ediliyor, io_service ile bağlantılı bir I/O nesne üzerinde herhangi bir fonksiyonu ise başka io_service fonksiyonu olarak adlandırılır veya edilmemelidir. Bununla birlikte, io_service'ye erişen başka bir iş parçacığı olmaması koşuluyla, bir tamamlama işleyicisinde 'dan bu işlevi çağırmak güvenlidir.

Bu, run veya kitaplıkla ilişkili IO'lardan herhangi birini içerdiği anlaşılıyor. Pre_fork işleminizin bir iş öğesini sıfırladığını düşünüyorum.

örn. boost documentation den

boost::asio::io_service io_service; 
auto_ptr<boost::asio::io_service::work> work(
    new boost::asio::io_service::work(io_service)); 
... 
pre_fork() { 
    work.reset(); // Allow run() to exit. 
    // check run has finished... 
    io_service.notify_fork(...); 
} 

Bakım hala post_fork() tamamladı önce run() çağrılmaz olun

  1. alınması gerekir.
  2. yeni work nesne fark edilir run fesih için önümüzdeki run
  3. Uygun senkronizasyon için yaratılmış olduğundan emin olun.
0

Bir çatalın programlanıp programlanmadığını kontrol etmek için io_service :: run_one'u kullanabilirsin/io_service hala çalışıyor olmalıdır. Çatal bir şey olduğunda, ipliğin uyanmasını sağlamak için io_service'ye bazı çalışmalar eklenebilir. İplik çalışma koşulunu kontrol eder ve derhal durur. Çatapat sonra ya ebeveyn ya da çocuk çalışan bir iş parçacığı yeniden başlatabilir.

/** 
* Combines Boost.ASIO with a thread for scheduling. 
*/ 
class ServiceScheduler : private boost::noncopyable 
{ 
public : 
    /// The actual thread used to perform work. 
    boost::shared_ptr<boost::thread>    _service_thread; 

    /// Service used to manage async I/O events 
    boost::asio::io_service      _io_service; 

    /// Work object to block the ioservice thread. 
    std::auto_ptr<boost::asio::io_service::work> _work; 
    ServiceScheduler(); 
    void start(); 
    void pre_fork(); 
private: 
    void processServiceWork(); 
    void post_fork_parent(); 
    void post_fork_child(); 
    std::atomic<bool> _is_running; 
}; 

/** 
* CTOR 
*/ 
ServiceScheduler::ServiceScheduler() 
    : _io_service(), 
     _work(std::auto_ptr<boost::asio::io_service::work>(
       new boost::asio::io_service::work(_io_service))), 
     _is_running(false) 
{ 
} 

/** 
* Starts a thread to run async I/O service to process the scheduled work. 
*/ 
void ServiceScheduler::start() 
{ 
    if(!_is_running) { 
     _service_thread = boost::shared_ptr<boost::thread>(
       new boost::thread(boost::bind(
         &ServiceScheduler::processServiceWork, this))); 
    } 
} 

/** 
* Processes work passed to the ASIO service and handles uncaught 
* exceptions 
*/ 
void ServiceScheduler::processServiceWork() 
{ 
    try { 
     while(_is_running) { 
      _io_service.run_one(); 
     } 
    } 
    catch (...) { 
    } 
    _is_running = false; 
} 

/** 
* Pre-fork handler 
*/ 
void ServiceScheduler::pre_fork() 
{ 
    _is_running = false; 
    _io_service.post([](){ /*no_op*/}); 
    _service_thread->join(); 
    _service_thread.reset(); 
    _io_service.notify_fork(boost::asio::io_service::fork_prepare); 
} 

/** 
* Post-fork parent handler 
*/ 
void ServiceScheduler::post_fork_parent() 
{ 
    start(); 
    _io_service.notify_fork(boost::asio::io_service::fork_parent); 
} 

/** 
* Post-fork child handler 
*/ 
void ServiceScheduler::post_fork_child() 
{ 
    _io_service.notify_fork(boost::asio::io_service::fork_child); 
}