3

I write a shm struct as following:

  class ShmWorker {
   public:
    ShmWorker() = default;
    virtual ~ShmWorker() {
      shmdt(m_data);
      shmctl(shmid, IPC_RMID, 0); // here is the problem, if ctrl-c, the shared memory
      // won't be deleted
    }   

   protected:
    key_t get_keyid(const std::string & name) {
      const std::string & s = "./" + name;
      if (access(s.c_str(), F_OK) == -1) { const std::string & s1 = "touch " + s; system(s1.c_str()); }
      key_t semkey = ftok(name.c_str(), 1); 
      CE(semkey == -1, "shm_file:%s not existed\n", s.c_str());
      return semkey;
    }   

    template <typename T>
    void init(const std::string& name, int size) {
      key_t m_key = get_keyid(name);
      shmid = shmget(m_key, 0, 0);  // TODO: check m_key existed
      char * p;
      if (shmid == -1) {
        CE(errno != ENOENT && errno != EINVAL, "errno is %s\n", strerror(errno));
        if (std::is_same_v<T, std::string>) shmid = shmget(m_key, sizeof(Header) + STRING_SIZE * size, 0666 | IPC_CREAT | O_EXCL);
        else shmid = shmget(m_key, sizeof(Header) + sizeof(T) * size, 0666 | IPC_CREAT | O_EXCL);
        CE(shmid == -1, "both connet and create are failed for shm\n")
        printf("creating new shm %s %p\n", name.c_str(), p);
        p = (char*)shmat(shmid, NULL, 0);
        Header h(size, 0);
        memcpy(p, &h, sizeof(Header));
      } else {
        p = (char*)shmat(shmid, 0, 0);
        printf("existed m_data = %p\n", p);
      }
    }
 }

}

as you can see, the deconstructor will detach this shared memory and delete it.

but sometimes i need to ctrl-c to end the process. in that case, the shared memory will not be deleted.

is there any method can delete it safely if no live process linked the shared memory?

or delete it in the next time link?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
nick
  • 832
  • 3
  • 12
  • 3
    Install a signal handler to catch ctrl-c to record the need for clean-up before exit, or register a clean-up function with atexit(). – Allan Wind Mar 12 '23 at 03:18
  • Does ctrl-c execute the functions registered with `atexit()`? – Paul Sanders Mar 12 '23 at 04:20
  • 1
    @PaulSanders tested, no – nick Mar 12 '23 at 04:20
  • 2
    Then installing a signal handler is your only option. Please note that only certain library functions are 'signal-safe', as listed [here](https://man7.org/linux/man-pages/man7/signal-safety.7.html), so you might just need to set a 'get out quick' flag and otherwise ignore the signal. – Paul Sanders Mar 12 '23 at 04:23
  • 1
    @PaulSanders - That can be further handled by regularly checking the "get out quick" flag, and triggering a timely and orderly shutdown when the flag is set. – Peter Mar 12 '23 at 05:11
  • @Peter Sure, I left that to the OP :) – Paul Sanders Mar 12 '23 at 05:36
  • @Peters huh, apparently I came up with that solution some decade ago without asking anyone. To be honest POSIX _needs_ some guides how to deal with typical scenarios. So far the guides that ARE up there, like the socket one, are greatly misleading because they're using "simple" scenarios. Thir approach of "simple implementation, detailed use" results in coding nightmare for big projects which are started from scratch by new teams. – Swift - Friday Pie Mar 13 '23 at 08:48

1 Answers1

2

Ctrl-C and similar keystrokes in some environments are processed by terminal and terminal invokes appropriate actions upon them, e.g. by calling kill. In other cases it's handled by run-time library. You have to handle interrupts.

E.g. in POSIX-compatible (which also includes console MinGW applications on Windows) Ctrl-C can be intercepted as SIGINT signal:

#include <iostream>
#include <thread>
#include <atomic>
#include <signal.h>

std::atomic<int> keepRunning;

void interruptHandler(int signum) {
    // if intercepting several signals, we should do checks here 
    // setting some flag to signal main program to quit running.
    keepRunning = 0;
}

// The "main worker" thread.
void threadFunction() {
    std::cout << "Hello, world!" << std::endl;
    std::cout << "Press Ctrl-C to exit." << std::endl;
    while(keepRunning);

    // calling exit() here would result in terminate call but atexit handlers will be invoked
    std::cout << "Interrupt detected." << std::endl;
}

std::thread *infiniteThread;

void exitHandler() {
    std::cout << "Bye, cruel world." << std::endl;
    delete infiniteThread;
}

int main()
{
     signal(SIGINT, interruptHandler);

     std::atexit(exitHandler);

     keepRunning = 1;
     infiniteThread = new std::thread(threadFunction);
     infiniteThread->join();
}

interruptHandler should do something to cause process end gracefully. Which may include closing files, sockets, freeing other OS-related resources which aren't under normal circumstances by OS specs, calling atexit-handlers. Not all signals can be caught, when OS actually kills the process, it doesn't have that grace time. interruptHandler is called from what is known as signal trampoline, so provoking exceptions there is not recommended. See manpages.

What can be used in handler is limited. In POSIX environment it's not safe to call exit() from signal handler according to the list of safe functions. But _exit() or _Exit() wouldn't call any atexit functions, it would do only init-related cleanup.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42