Soru Qt: Tek örnek uygulama koruması için en iyi uygulama


QSingleApplication? QMutex? QSharedMemory? Windows, OSX ve Linux'ta (Ubuntu) sorunsuzca çalışacak bir şey arıyorum. Qt 4.7.1 Kullanımı


32
2018-02-15 16:40


Menşei


stackoverflow.com/questions/783212/... - Martin Beckett
Buradaki bağlantılar kamera, dolaşım vb. işlevler içeren bir "mobilite" API'sine gider. QtSingleApplication'ı nereden aldığınızdan emin misiniz? - JasonGenX
@ Cevaptaki bağlantı doğru doc.trolltech.com/solutions/4/qtsingleapplication/...Sorudaki QtSingleApplication bağlantısı yanlıştır. - Martin Beckett


Cevaplar:


Basit çözüm, istediğini yapar. Ağ bağımlılığı olmadan (as QtSingleApplication) ve herhangi bir ek yük olmadan.

Kullanımı:

int main()
{
    RunGuard guard( "some_random_key" );
    if ( !guard.tryToRun() )
        return 0;

    QAppplication a(/*...*/);
    // ...
}

RunGuard.h

#ifndef RUNGUARD_H
#define RUNGUARD_H

#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>


class RunGuard
{

public:
    RunGuard( const QString& key );
    ~RunGuard();

    bool isAnotherRunning();
    bool tryToRun();
    void release();

private:
    const QString key;
    const QString memLockKey;
    const QString sharedmemKey;

    QSharedMemory sharedMem;
    QSystemSemaphore memLock;

    Q_DISABLE_COPY( RunGuard )
};


#endif // RUNGUARD_H

RunGuard.cpp

#include "RunGuard.h"

#include <QCryptographicHash>


namespace
{

QString generateKeyHash( const QString& key, const QString& salt )
{
    QByteArray data;

    data.append( key.toUtf8() );
    data.append( salt.toUtf8() );
    data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();

    return data;
}

}


RunGuard::RunGuard( const QString& key )
    : key( key )
    , memLockKey( generateKeyHash( key, "_memLockKey" ) )
    , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
    , sharedMem( sharedmemKey )
    , memLock( memLockKey, 1 )
{
    memLock.acquire();
    {
        QSharedMemory fix( sharedmemKey );    // Fix for *nix: http://habrahabr.ru/post/173281/
        fix.attach();
    }
    memLock.release();
}

RunGuard::~RunGuard()
{
    release();
}

bool RunGuard::isAnotherRunning()
{
    if ( sharedMem.isAttached() )
        return false;

    memLock.acquire();
    const bool isRunning = sharedMem.attach();
    if ( isRunning )
        sharedMem.detach();
    memLock.release();

    return isRunning;
}

bool RunGuard::tryToRun()
{
    if ( isAnotherRunning() )   // Extra check
        return false;

    memLock.acquire();
    const bool result = sharedMem.create( sizeof( quint64 ) );
    memLock.release();
    if ( !result )
    {
        release();
        return false;
    }

    return true;
}

void RunGuard::release()
{
    memLock.acquire();
    if ( sharedMem.isAttached() )
        sharedMem.detach();
    memLock.release();
}

51
2018-01-27 13:53



Bu benim için çalışır, uygulama çökerse, yeniden başlatmaya izin verir (bu benim problemim), teşekkürler @SaZ - Haris
@SaZ Bu kodu yayınladığınız için teşekkürler - sadece uygulamamda denedim - ilk kez çalışıyor :) - Michael Vincent
Vay. Mükemmel çalıştı! Çok teşekkürler :) Sadece RunGuard.h ana dahil etmek zorunda kaldı. - mrg95
Başka bir soru: sadece kullanamazsın QSharedMemory::lock() ayrı yerine QSystemSemaphore? - bweber
@AoeAoe bir kod örneğidir, herhangi bir kısıtlama yoktur. Herhangi bir açıklama yapmadan bile kullanmaktan çekinmeyin. - Dmitry Sazonov


Kullanabilirsiniz QSharedMemory belirli bir anahtarla ve o anahtara sahip paylaşılan belleğin oluşturulup oluşturulamayacağını kontrol edin. Eğer oluşturulamazsa, bir örnek zaten çalıştırılır:

QSharedMemory sharedMemory;
sharedMemory.setKey("MyApplicationKey");

if (!sharedMemory.create(1))
{
    QMessageBox::warning(this, tr("Warning!"), tr("An instance of this application is running!") );

    exit(0); // Exit already a process running
}

4
2017-11-22 06:34



Çözüm tamamlanmadı. Bir sürü olası hata var. Uygulama kilitlendiğinde durumları kontrol etmiyorsunuz. Cevabımı eksiksiz olarak kontrol edebilirsin. - Dmitry Sazonov
Bu çözüm gerçekten çok eksik. -1 - TimZaman


Gibi QtSingleApplication göreceli olarak eskimiş ve artık sürdürülmüyor, yerine yeni bir isim yazdım SingleApplication.

Dayanmaktadır QSharedMemory ve kullanır QLocalServer yumurtlama yeni örneğinin ana sürecini bildirmek için. Tüm platformlarda çalışır ve Qt 5 ile uyumludur.

Tam kod ve dokümantasyon mevcuttur İşte.


2
2018-01-14 20:56



Bunu gönderdiğiniz için teşekkürler. Bağlantıyla birlikte gönderilecek bir "komut" ekleyen çekme isteğine açık olacak mısınız? İsteğe bağlı olarak "açık dosya" gibi bir komutu çalıştırmak istiyorum. - cheez
signal( SIGILL, SingleApplicationPrivate::terminate ); // 4  LOL, bunu denedin mi? Sanırım hayır. Bu şey işe yaramıyor. - LtWorf
@LtWorf Burada sorun tam olarak nedir? - Itay Grudev
Üzgünüm, SIGKILL olarak yanlış anladım. Her neyse kırılmış, çünkü sigkill durumunda herhangi bir temizlik yapmıyor. - LtWorf
Ben idare edemediğimi biliyorum SIGKILLama ben bu davayı ele almak için başka bir hack kullandım. Başlatma ve sonra açıkça silerek QSharedMemory Örneğin çekirdek, ona bağlı herhangi bir aktif işlem yoksa bloğu temizler. - Itay Grudev


pencereler için:

HANDLE g_app_mutex = NULL;

bool check_one_app_instance () {     g_app_mutex = :: CreateMutex (NULL, FALSE, L "8BD290769B404A7816985M9E505CF9AD64"); // bu dize gibi farklı bir anahtar     eğer (GetLastError () == ERROR_ALREADY_EXISTS)     {         CloseHandle (g_app_mutex);         yanlış dönüş;     }

return true;

}


0
2018-02-28 12:51



Soru çapraz platform çözümü ile ilgilidir. - Dmitry Sazonov


Bu çözümü şu an için kullanıyorum.

Ancak, aynı anda birden fazla yerden giriş yapsalar bile, programın kullanıcı tarafından sadece bir kez çalıştırılabileceği dezavantajına sahiptir.

singleinstance.h

#ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H

typedef enum {
    SYSTEM,
    SESSION,
} scope_t;

class SingleInstance
{
public:
    static bool unique(QString key, scope_t scope);
};

#endif // SINGLEINSTANCE_H

singleinstance.cpp

#include <QLockFile>
#include <QProcessEnvironment>

#include "singleinstance.h"

/**
 * @brief filename
 * @param key
 * @param scope
 * @return a fully qualified filename
 *
 * Generates an appropriate filename for the lock
 */
static QString filename(QString key, scope_t scope) {

    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString tmp = env.value("TEMP", "/tmp") + "/";
    QString user = env.value("USER", "alfio");


    QString r;                                                                                                                                                                         
    switch (scope) {                                                                                                                                                                   
        case SYSTEM:                                                                                                                                                                   
            r = tmp;                                                                                                                                                                   
            break;
        case SESSION:
            //FIXME this will prevent trabucco to run in multiple X11 sessions
            r = env.value("XDG_RUNTIME_DIR", tmp + user) + "/";
            break;
    }
    return r + key + ".lock";
}

/**
 * @brief SingleInstance::unique
 * @param key the unique name of the program
 * @param scope wether it needs to be system-wide or session-wide
 * @return true if this is the only instance
 *
 * Make sure that this instance is unique.
 */
bool SingleInstance::unique(QString key, scope_t scope) {
    QLockFile* lock = new QLockFile(filename(key, scope));
    bool r = lock->tryLock();
    if (!r)
        delete lock;
    return r;
}

0
2017-08-24 07:31



Geçici klasöre yazma erişiminiz yoksa ne yapacaksınız? Ayrıca performans için daha da kötüdür. - Dmitry Sazonov
Düzgün çalışan bir makinede buna erişebilirsiniz. / Tmp’niz yoksa, grafik girişi yapamazsınız. Program başladığında bir kez yapılan bir kontrol olduğundan performanslar pek de önemli değil. - LtWorf
Platformuna bağlı. Çok kısıtlı erişime sahip Windows XP için bir uygulama oluşturdum. Çalışma zamanında bu klasöre uygulama ihtiyaçları ve yolu için yalnızca bir yazılabilir klasör sağlanmıştır. Bu yüzden kilitli dosyaları kontrol etmek çok pahalı bir işlemdi. - Dmitry Sazonov


linux için:

// ----------------------------------

QProcess *m_prSystemCall;
m_prSystemCall = new QProcess();

QString Commnd = "pgrep  " + qApp->applicationDisplayName();
m_prSystemCall->start(Commnd);
m_prSystemCall->waitForFinished(8000);
QString output(m_prSystemCall->readAllStandardOutput());
QStringList AppList = output.split("\n", QString::SkipEmptyParts);
qDebug() <<"pgrep out:"<<AppList;
for(int i=0;i<AppList.size()-1;i++)
{
    Commnd = "kill " + AppList.at(i);
    m_prSystemCall->start(Commnd);
    m_prSystemCall->waitForFinished(8000);
}

// ------------------------------------------------ -------

ve Windows için:

#include <tlhelp32.h>
#include <comdef.h>

QString pName = qApp->applicationDisplayName();
pName += ".exe";
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

if (Process32First(snapshot, &entry) == TRUE)
{
    DWORD myPID =  GetCurrentProcessId();
    while (Process32Next(snapshot, &entry) == TRUE)
    {
        const WCHAR* wc = entry.szExeFile ;
        _bstr_t b(wc);
        const char* c = b;

        if (stricmp(c, pName.toStdString().c_str()) == 0)
        {
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);

            qDebug() <<"myPID: "<< myPID << "entry.th32ProcessID" << entry.th32ProcessID;
            if(myPID != entry.th32ProcessID)
                TerminateProcess(hProcess,0);
            QThread::msleep(10);
            CloseHandle(hProcess);
        }
    }

}

CloseHandle(snapshot);

0
2017-09-25 05:09





Qt'ın doktoruna göre, QSystemSemaphore İşlem, Unix benzeri işletim sistemleri altında yıkıcısını çağırmadan çökerse otomatik olarak yayınlanmayacaktır. Aynı semaforu elde etmeye çalışan başka bir süreçte bir çıkmazın sebebi de olabilirdi. Programınızın çökmeleri doğru şekilde uyguladığı ve Qt'yi kullanmayacağınız konusunda% 100 emin olmak istiyorsanız, Süreçler öldüğünde işletim sistemlerinin otomatik olarak serbest bıraktığı diğer kilitleme mekanizmalarını kullanmak isteyebilirsiniz - örneğin, lockf() ve O_EXLOCK bayrak geçti open() içinde bahsedilen Sıfır çökmelere neden olan süreç sıfırlandığında bir semaforu nasıl kurtarabilirim? veya flock(). Aslında, paylaşılan belleğin oluşturulması artık gerekli değildir. flock() kullanıldı. Sadece kullanarak flock() tek örnek uygulama koruması yapmak için yeterlidir.

Unix'teki çökmelerden semaforun iyileşmesi önemli değil, Dmitry Sazonov'un cevabı RunGuard hala biraz basitleştirilmiş olabilir:

  1. Yıkıcı ~RunGuard() ve RunGuard::release() o zamandan beri alınabilir QSharedMemory Qt dosyasında olduğu gibi, otomatik olarak paylaşılan bellek segmentinden kendi imhasıyla ayrılacak QSharedMemory::~QSharedMemory(): "Yıkıcı, paylaşılan bellek nesnesini temeldeki paylaşılan bellek segmentinden ayırmaya zorlayan anahtarı temizler."

  2. RunGuard::isAnotherRunning() ayrıca da alınabilir. Amaç özel yürütme. @Nejat'ın belirttiği gibi, QT'de olduğu gibi, herhangi bir zamanda herhangi bir zamanda bir anahtar için oluşturulmuş en fazla bir paylaşımlı bellek segmentinde olabileceği gerçeğinden faydalanabiliriz. QSharedMemory::create(): "Anahtar tarafından tanımlanan bir paylaşılan bellek bölümü zaten varsa, ek işlemi gerçekleştirilmez ve yanlış döndürülür."

  3. Doğru şekilde anlarsam, "düzelt" amacını QSharedMemory Kurucudaki nesne, Qt belgesinde olduğu gibi önceki işlem çökmesi nedeniyle hayatta kalan paylaşımlı bellek bölümünü yok etmektir: "Unix: ... Son iş parçacığı veya işlemin bir örneğine sahip olduğunda QSharedMemory Belirli bir paylaşımlı bellek segmentine eklenmiş, örneğini yok ederek segmentten ayrılır QSharedMemory, Unix çekirdeği paylaşılan bellek segmentini serbest bırakır. Ancak bu son iş parçacığı veya işlemi çalıştırmadan çöküyorsa QSharedMemory yıkıcı, paylaşılan bellek segmenti kazazede hayatta kalıyor. "." düzelt "imha edildiğinde, bir örtülü detach() yıkıcı tarafından çağrılmalı ve eğer varsa, kalan paylaşımlı hafıza segmenti serbest bırakılacaktır.

  4. Olduğundan emin değilim QSharedMemory iş parçacığı güvenli / işlem güvenli veya değil. Aksi halde, ilgili kod memLock iplik güvenliği dahili olarak ele alınırsa daha fazla çıkarılabilir QSharedMemory. Diğer yandan, fix ayrıca tarafından korunmalıdır memLock güvenlik bir sorun ise:

    RunGuard::RunGuard( const QString& key )
        : key( key )
        , memLockKey( generateKeyHash( key, "_memLockKey" ) )
        , sharedMemKey( generateKeyHash( key, "_sharedMemKey" ) )
        , sharedMem( sharedMemKey )
        , memLock( memLockKey, 1 )
    {
        memLock.acquire();
        {
            QSharedMemory fix( sharedMemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
            fix.attach();
        }
        memLock.release();
    }
    

    çünkü açık attach() ve örtülü detach() etrafa çağrılır fix.

  5. Basitleştirilmiş sürümü RunGuard Şöyleki:

    Kullanımı:

    int main()
    {
        RunGuard guard( "some_random_key" );
        if ( !guard.tryToRun() )
            return 0;
    
        QAppplication a(/*...*/);
        // ...
    }
    

    runGuard.h:

    #ifndef RUNGUARD_H
    #define RUNGUARD_H
    
    #include <QObject>
    #include <QSharedMemory>
    #include <QSystemSemaphore>
    
    class RunGuard
    {
    
    public:
        RunGuard( const QString& key );
        bool tryToRun();
    
    private:
        const QString key;
        const QString memLockKey;
        const QString sharedMemKey;
    
        QSharedMemory sharedMem;
        QSystemSemaphore memLock;
    
        Q_DISABLE_COPY( RunGuard )
    };
    
    
    #endif // RUNGUARD_H
    

    runGuard.cpp:

    #include "runGuard.h"
    #include <QCryptographicHash>
    
    namespace
    {
    
        QString generateKeyHash( const QString& key, const QString& salt )
        {
            QByteArray data;
            data.append( key.toUtf8() );
            data.append( salt.toUtf8() );
            data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
            return data;
    }
    
    }
    
    RunGuard::RunGuard( const QString& key )
        : key( key )
        , memLockKey( generateKeyHash( key, "_memLockKey" ) )
        , sharedMemKey( generateKeyHash( key, "_sharedMemKey" ) )
        , sharedMem( sharedMemKey )
        , memLock( memLockKey, 1 )
    {
        QSharedMemory fix( sharedMemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
        fix.attach();
    }
    
    bool RunGuard::tryToRun()
    {
        memLock.acquire();
        const bool result = sharedMem.create( sizeof( quint64 ) );
        memLock.release();
        if ( !result )
            return false;
    
        return true;
    }
    
  6. Burada olası bir yarış durumu var:

    bool RunGuard::tryToRun()
    {
        if ( isAnotherRunning() )   // Extra check
            return false;
                                                                   // (tag1)
        memLock.acquire();
        const bool result = sharedMem.create( sizeof( quint64 ) ); // (tag2)
        memLock.release();
        if ( !result )
        {
            release();                                             // (tag3)
            return false;
        }
    
        return true;
    }
    

    Senaryoyu düşünün:

    Mevcut süreç ne zaman ProcCur çalışır (tag1) aşağıdaki olur: (unutmayın (tag1) kilit korumanın dışındadır)

    1. Başka bir süreç ProcOther kullanma RunGuard koşmaya başlar.
    2. ProcOther çalışır (tag2) ve başarıyla paylaşılan belleği oluşturur.
    3. ProcOther aramadan önce çöküyor release() en (tag3).
    4. ProcCur koşmaya devam ediyor (tag1).
    5. ProcCur çalışır (tag2) ve paylaşılan bellek oluşturmaya çalışır. Ancak, sharedMem.create() dönecek false Çünkü ProcOther oluşturulmuş bir tane bıraktı. Dokümanında görebileceğimiz gibi QSharedMemory::create(): "Anahtar tarafından tanımlanan bir paylaşılan bellek bölümü zaten varsa, ek işlemi gerçekleştirilmez ve yanlış döndürülür."
    6. En sonunda, RunGuard::tryToRun() içinde ProcCur dönecek falsebeklendiği gibi değil çünkü ProcCur kullanan tek mevcut işlem RunGuard.

-1
2017-10-25 14:01



1. Ekstra mantık yapmak istediğinizde ve RunGuard'ı doğrudan serbest bırakmanız gerektiğinde, bu durum çok basittir. 2. Bu kontrol, IPC gerekli ise, diğer durumlar için kullanılabilir. 3. Evet, bu sizin durumunuz için * nix için bir çözümdür. 4. QSystemSemaphore çapraz işlem "muteks" olarak kullanılır. Bazı yarış koşullarından korunmak gereklidir. Bu bir "garson" değil. RunGuard iplik güvenli değildir, çünkü iplik güvenliği burada gerekli değildir. Ancak RunGuard, süreç güvenli olmalıdır. - Dmitry Sazonov
QSharedMemory işlem güvenli / iplik güvenli veya değilse, hala bilinmemektedir. Bu nedenle QSharedMemory'nin QSystemSemaphore tarafından üye fonksiyon çağrılarını korumak önemsiz olabilir. QSharedMemory'nin proses güvenli / iplik-güvenli olmadığına dair herhangi bir yerde belge bulunmalıdır, ancak bu belgelerin hiçbirini bulamıyorum. Bu yüzden, "memLock" dedim MAYIS AYI daha fazla kaldırılmalıdır. - Justin
"Düzelt" korumasına katılıyorum. Ama sanırım süreç güvenliği ve iplik güvenliği nedir? RunGuard'da iplik güvenliğini önemsemek gerekli değildir. memLock bazı ırkları önlemek için kullanılır. - Dmitry Sazonov
Bu arada, QSharedMemory süreç güvenli / iplik güvenli olmasaydı, "düzeltme" de "memLock" tarafından korunmuş olmalıdır. Yukarıda 4. değiştirdiğim gibi. - Justin
Göre bu: "Ek olarak, bir sınıfın olduğu söyleniyor evresel her iş parçacığı kullandığı sürece üye işlevleri birden çok iş parçacığından güvenle çağrılabilirse farklı sınıfın örneği. Sınıf evreli Eğer tüm işlevler, tüm iş parçacıklarını kullanıyor olsa bile, birden çok iş parçacığından güvenle çağrılabilir. aynı sınıfın örneği. ”RunGuard'ın her uygulamada yalnızca bir kez kullanılması amaçlanıyorsa, QSharedMemory nesnesi diğer uygulamalardaki diğer tüm RunGuard öğelerinden farklı olan tek örnektir. - Justin