System-wide global variable / semaphore / mutex in C++/Linux?

Deadly 提交于 2019-12-12 10:37:32

问题


Is it possible to create a system-wide global variable / semaphore / mutex in C++ on Linux?

Here's the reason: I've got a system that often runs multiple copies of the same software on unrelated data. It's common to have 4 jobs, each running the same software. The software has a small section where it creates a huge graph that takes a lot of memory; outside that section memory usage is moderate.

It so happens sometimes that 2 jobs simultaneously hit the same memory-hungry section and the whole system starts swapping. Thus we want to prevent that by creating something like a critical section mutex between different jobs so that no more than one of them would allocate a lot of memory at a time.

If these were thread of the same job pthread locks would do the job.

What would be a good way to implement such mutex between different jobs?


回答1:


You can use a named semaphore if you can get all the processes to agree on a common name.

A named semaphore is identified by a name of the form /somename; that is, a null-terminated string of up to NAME_MAX-4 (i.e., 251) characters consisting of an initial slash, followed by one or more characters, none of which are slashes. Two processes can operate on the same named semaphore by passing the same name to sem_open(3).




回答2:


For interprocess mutual exclusion, you can use file locking. With linux, the code is as simple as protecting the critical section with a call to flock.

int fd_lock = open(LOCK_FILE, O_CREAT);

flock(fd_lock, LOCK_EX);

// do stuff

flock(fd_lock, LOCK_UN);

If you need POSIX compatibility, you can use fcntl.




回答3:


You can make C++ mutexes work across process boundaries on Linux. However, there's some black magic involved which makes it less appropriate for production code.

Explanation:

The standard library's std::mutex and std::shared_mutex use pthread's struct pthread_mutex_s and pthread_rwlock_t under the hood. The native_handle() method returns a pointer to one of these structures.

The drawback is that certain details are abstracted out of the standard library and defaulted in the implementation. For example, std::shared_mutex creates its underlying pthread_rwlock_t structure by passing NULL as the second parameter to pthread_rwlock_init(). This is supposed to be a pointer to a pthread_rwlockattr_t structure containing an attribute which determines sharing policy.

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

In theory, it should receive default attributes. According to the man pages for pthread_rwlockattr_getpshared():

The default value of the process-shared attribute is PTHREAD_PROCESS_PRIVATE.

That said, both std::shared_mutex and std::mutex work across processes anyway. I'm using Clang 6.0.1 (x86_64-unknown-linux-gnu / POSIX thread model). Here's a description of what I did to check:

  • Create a shared memory region with shm_open.

  • Check the size of the region with fstat to determine ownership. If .st_size is zero, then ftruncate() it and the caller knows that it is the region's creating process.

  • Call mmap on it.

    • The creator process uses placement-new to construct a std::mutex or std::shared_mutex object within the shared region.
    • Later processes use reinterpret_cast<>() to obtain a typed pointer to the same object.
  • The processes now loop on calling trylock() and unlock() at intervals. You can see them blocking one another using printf() before and after trylock() and before unlock().

Extra detail: I was interested in whether the c++ headers or the pthreads implementation were at fault, so I dug into pthread_rwlock_arch_t. You'll find a __shared attribute which is zero and a __flags attribute which is also zero for the field denoted by __PTHREAD_RWLOCK_INT_FLAGS_SHARED. So it seems that by default this structure is not intended to be shared, though it seems to provide this facility anyway (as of July 2019).

Summary

It seems to work, though somewhat by chance. I would advise caution in writing production software that works contrary to documentation.




回答4:


Mutual exclusion locks (mutexes) prevent multiple threads from simultaneously executing critical sections of code that access shared data (that is, mutexes are used to serialize the execution of threads). All mutexes must be global. A successful call for a mutex lock by way of mutex_lock() will cause another thread that is also trying to lock the same mutex to block until the owner thread unlocks it by way of mutex_unlock(). Threads within the same process or within other processes can share mutexes.

Mutexes can synchronize threads within the same process or in other processes. Mutexes can be used to synchronize threads between processes if the mutexes are allocated in writable memory and shared among the cooperating processes (see mmap(2)), and have been initialized for this task.

For inter-process synchronization, a mutex needs to be allocated in memory shared between these processes. Since the memory for such a mutex must be allocated dynamically, the mutex needs to be explicitly initialized using mutex_init(). also, for inter-process synchronization, besides the requirement to be allocated in shared memory, the mutexes must also use the attribute PTHREAD_PROCESS_SHARED, otherwise accessing the mutex from another process than its creator results in undefined behaviour (see this: linux.die.net/man/3/pthread_mutexattr_setpshared): "The process-shared attribute is set to PTHREAD_PROCESS_SHARED to permit a mutex to be operated upon by any thread that has access to the memory where the mutex is allocated, even if the mutex is allocated in memory that is shared by multiple processes."



来源:https://stackoverflow.com/questions/32338732/system-wide-global-variable-semaphore-mutex-in-c-linux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!