In general, mutable memory location will not be shared between applications unless explicitly asked for. Thus, when
libcnt writes to
cnt_loads, the page on which
cnt_loads resides on will be duplicated. This behaviour is known as copy-on-write (COW). The same thing happens if you duplicated a process with
fork(): the child process will not share writeable memory with the parent, if any write occurs to "forked" page, the page will be duplicated.
If you want to use shared memory for interprocess communication, you should use SystemV shared memory, POSIX shared memory or mmap instead. Note that these methods are somewhat persistent, i.e. you will need to remove the shared memory object after use.
You can approximate the behaviour you originally wanted using shared memory by defining
__attribute__((destructor)) functions for your shared library. The
constructor function is run every time the library is opened, so you can use it to initialize/open the shared memory and increment the the load count. If you also maintain a reference count (how many times the shared library is open in the system right now) with the
destructor---in addition to the load count---, your can properly remove the shared memory once the reference count falls to 0.
Note that using shared memory for inter-process communication absolutely requires some form of mutual exclusion, for example a semaphore or mutex. Failing to synchronize properly will lead to race conditions (for example, what happens when two processes open your library at precisely same time?). You may avoid mutual exclusion if you can increment/decrement the counters in an atomic way. I recommend you to use OS provided inter-process semaphores instead of atomics, because atomic operations are tricky to utilize properly, and your problem is not performance critial at all (thus no need for lock-free operations).