GNU/Linux >> Belajar Linux >  >> Linux

Variabel global seluruh sistem/semaphore/mutex di C++/Linux?

Untuk pengecualian timbal balik antarproses, Anda dapat menggunakan penguncian file. Dengan linux, kodenya sesederhana melindungi bagian penting dengan panggilan ke flock .

int fd_lock = open(LOCK_FILE, O_CREAT);

flock(fd_lock, LOCK_EX);

// do stuff

flock(fd_lock, LOCK_UN);

Jika Anda memerlukan kompatibilitas POSIX, Anda dapat menggunakan fcntl .


Anda dapat menggunakan semaphore bernama jika Anda bisa membuat semua proses menyetujui nama umum.

Semaphore bernama diidentifikasi dengan nama form/somename; yaitu, string yang diakhiri null hingga NAME_MAX-4 (yaitu, 251) karakter yang terdiri dari garis miring awal, diikuti oleh satu atau beberapa karakter, tidak ada yang berupa garis miring. Dua proses dapat beroperasi pada semafor bernama sama dengan meneruskan nama yang sama ke sem_open(3) .


Saya melihat menggunakan solusi shared-pthread-mutex tetapi tidak menyukai perlombaan logika di dalamnya. Jadi saya menulis kelas untuk melakukan ini menggunakan atom builtin

#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

using std::string;

//from the command line - "ls /dev/shm" and "lsof /dev/shm/<name>" to see which process ID has access to it

template<typename PAYLOAD>
class InterprocessSharedVariable
{
protected:
    int mSharedMemHandle;
    string const mSharedMemoryName;
    bool mOpenedMemory;
    bool mHaveLock;
    pid_t mPID;

    // this is the shared memory structure
    typedef struct 
    {
        pid_t mutex;
        PAYLOAD payload;
    }
    tsSharedPayload;


    tsSharedPayload* mSharedData;


    bool openSharedMem()
    {
        mPID = getpid();

        // The following caters for the shared mem being created by root but opened by non-root,
        //  giving the shared-memory 777 permissions.
        int openFlags = O_CREAT | O_RDWR;
        int shareMode = S_IRWXU | S_IRWXG | S_IRWXO;

        // see https://stackoverflow.com/questions/11909505/posix-shared-memory-and-semaphores-permissions-set-incorrectly-by-open-calls
        // store old
        mode_t old_umask = umask(0);

        mSharedMemHandle = shm_open (mSharedMemoryName.c_str(), openFlags, shareMode);

        // restore old
        umask(old_umask);

        if (mSharedMemHandle < 0) 
        {
            std::cerr << "failed to open shared memory"  << std::endl;
            return false;
        }

        if (-1 == ftruncate(mSharedMemHandle, sizeof(tsSharedPayload)))
        {
            std::cerr <<  "failed to resize shared memory" << std::endl;
            return false;
        }

        mSharedData = (tsSharedPayload*) mmap (NULL, 
                                            sizeof(tsSharedPayload),
                                            PROT_READ | PROT_WRITE,
                                            MAP_SHARED,
                                            mSharedMemHandle,
                                            0);

        if (MAP_FAILED == mSharedData)
        {
            std::cerr << "failed to map shared memory" << std::endl;
            return false;
        }

        return true;
    }


    void closeSharedMem()
    {
        if (mSharedMemHandle > 0)
        {
            mSharedMemHandle = 0;
            shm_unlink (mSharedMemoryName.c_str());
        }
    }

public:
    InterprocessSharedVariable () = delete;

    InterprocessSharedVariable (string const&& sharedMemoryName) : mSharedMemoryName(sharedMemoryName)
    {
        mSharedMemHandle = 0;
        mOpenedMemory = false;
        mHaveLock = false;
        mPID = 0;
    }

    virtual ~InterprocessSharedVariable ()
    {
        releaseSharedVariable ();
        closeSharedMem ();
    }

    // no copying
    InterprocessSharedVariable (InterprocessSharedVariable const&) = delete;
    InterprocessSharedVariable& operator= (InterprocessSharedVariable const&) = delete;


    bool tryLockSharedVariable (pid_t& ownerProcessID)
    {
        // Double-checked locking.  See if a process has already grabbed the mutex.  Note the process could be dead
        __atomic_load (&mSharedData->mutex, &ownerProcessID, __ATOMIC_SEQ_CST);

        if (0 != ownerProcessID)
        {
            // It is possible that we have started with the same PID as a previous process that terminated abnormally
            if (ownerProcessID == mPID)
            {
                // ... in which case, we already "have ownership"
                return (true);
            }

            // Another process may have the mutex.  Check whether it is alive.
            // We are specifically looking for an error returned with ESRCH
            // Note that if the other process is owned by root, "kill 0" may return a permissions error (which indicates the process is running!)
            int processCheckResult = kill (ownerProcessID, 0);

            if ((0 == processCheckResult) || (ESRCH != errno))
            {
                // another process owns the shared memory and is running
                return (false);
            }

            // Here: The other process does not exist ((0 != processCheckResult) && (ESRCH == errno))
            // We could assume here that we can now take ownership, but be proper and fall into the compare-exchange
            ownerProcessID = 0;
        }

        // It's possible that another process has snuck in here and taken ownership of the shared memory.
        // If that has happened, the exchange will "fail" (and the existing PID is stored in ownerProcessID)

        // ownerProcessID == 0 -> representing the "expected" value
        mHaveLock = __atomic_compare_exchange_n (&mSharedData->mutex,
                                                &ownerProcessID,      //"expected"
                                                mPID,                 //"desired"
                                                false,                //"weak"
                                                __ATOMIC_SEQ_CST,     //"success-memorder"
                                                __ATOMIC_SEQ_CST);    //"fail-memorder"

        return (mHaveLock);
    }


    bool acquireSharedVariable (bool& failed, pid_t& ownerProcessID)
    {
        if (!mOpenedMemory)
        {
            mOpenedMemory = openSharedMem ();

            if (!mOpenedMemory)
            {
                ownerProcessID = 0;
                failed = true;
                return false;
            }
        }

        // infrastructure is working
        failed = false;

        bool gotLock = tryLockSharedVariable (ownerProcessID);
        return (gotLock);
    }

    void releaseSharedVariable ()
    {
        if (mHaveLock)
        {
            __atomic_store_n (&mSharedData->mutex, 0, __ATOMIC_SEQ_CST);
            mHaveLock = false;
        }
    }
};

Contoh penggunaan - di sini kami hanya menggunakannya untuk memastikan bahwa hanya satu contoh aplikasi yang berjalan.

int main(int argc, char *argv[])
{
    typedef struct { } tsEmpty;
    InterprocessSharedVariable<tsEmpty> programMutex ("/run-once");

    bool memOpenFailed;
    pid_t ownerProcessID;
    if (!programMutex.acquireSharedVariable (memOpenFailed, ownerProcessID))
    {
        if (memOpenFailed)
        {
            std::cerr << "Failed to open shared memory" << std::endl;
        }
        else
        {
            std::cerr << "Program already running - process ID " << ownerProcessID << std::endl;
        }
        return -1;
    }

    ... do stuff ...

    return 0;
}

Anda dapat membuat mutex C++ berfungsi melintasi batas proses di Linux. Namun, ada beberapa sihir hitam yang membuatnya kurang sesuai untuk kode produksi.

Penjelasan:

std::mutex pustaka standar dan std::shared_mutex gunakan struct pthread_mutex_s pthread dan pthread_rwlock_t Dibawah tenda. native_handle() metode mengembalikan pointer ke salah satu struktur ini.

Kelemahannya adalah detail tertentu diabstraksikan dari pustaka standar dan gagal dalam implementasinya. Misalnya, std::shared_mutex membuat pthread_rwlock_t yang mendasarinya struktur dengan meneruskan NULL sebagai parameter kedua untuk pthread_rwlock_init() . Ini seharusnya menjadi penunjuk ke pthread_rwlockattr_t struktur yang berisi atribut yang menentukan kebijakan berbagi.

public:
    __shared_mutex_pthread()
    {
        int __ret = pthread_rwlock_init(&_M_rwlock, NULL);
        ...

Secara teori, itu harus menerima atribut default. Menurut halaman manual untuk pthread_rwlockattr_getpshared() :

Nilai default atribut proses bersama adalah PTHREAD_PROCESS_PRIVATE.

Konon, keduanya std::shared_mutex dan std::mutex tetap bekerja di seluruh proses. Saya menggunakan Clang 6.0.1 (model utas x86_64-unknown-linux-gnu / POSIX). Berikut deskripsi tentang apa yang saya lakukan untuk memeriksa:

  • Buat wilayah memori bersama dengan shm_open .

  • Periksa ukuran wilayah dengan fstat untuk menentukan kepemilikan. Jika .st_size adalah nol, maka ftruncate() itu dan penelepon tahu bahwa itu adalah proses pembuatan wilayah.

  • Hubungi mmap di atasnya.

    • Proses pembuat menggunakan penempatan -new untuk membuat std::mutex atau std::shared_mutex objek dalam wilayah bersama.
    • Proses selanjutnya menggunakan reinterpret_cast<>() untuk mendapatkan penunjuk yang diketik ke objek yang sama.
  • Proses sekarang berulang kali memanggil trylock() dan unlock() pada interval. Anda dapat melihat mereka memblokir satu sama lain menggunakan printf() sebelum dan sesudah trylock() dan sebelum unlock() .

Detail ekstra:Saya tertarik pada apakah header c++ atau implementasi pthreads salah, jadi saya menggali pthread_rwlock_arch_t . Anda akan menemukan __shared atribut yang nol dan __flags atribut yang juga nol untuk bidang yang dilambangkan dengan __PTHREAD_RWLOCK_INT_FLAGS_SHARED . Jadi, tampaknya secara default struktur ini tidak dimaksudkan untuk dibagikan, meskipun tampaknya tetap menyediakan fasilitas ini (per Juli 2019).

Ringkasan

Tampaknya berhasil, meskipun agak kebetulan. Saya akan menyarankan agar berhati-hati dalam menulis perangkat lunak produksi yang bekerja bertentangan dengan dokumentasi.


Linux
  1. Cara Mengatur Variabel $Path di Linux

  2. C++/Assembly IDE di Linux

  3. Mutex seluruh sistem dengan Python di Linux

  1. Mendeteksi Windows atau Linux di C, C++

  2. Pengaruh usleep(0) di C++ di Linux

  3. aksesibilitas variabel lingkungan di Linux

  1. Cara mengatur variabel $PATH Anda di Linux

  2. Cara Menetapkan Output dari Perintah Linux ke Variabel

  3. Kiat dan trik variabel lingkungan Linux