Alternative to kill() function on Windows, for sending signals to other processes

On POSIX systems, C (or C++) has the kill() function defined in <signal.h>.

The function is called as:
int kill(pid_t pid, int sig);

This sends the signal (sig) to the process which has process ID (pid).

I am porting a POSIX C program to Windows (which I had previously asked about here:https://www.cplusplus.com/forum/windows/275908/). However, it is large program containing several C and fortran modules. So, I would prefer to modify as little as I can.

So I need an alternative to kill() on windows that can send a signal to another process on Windows. Windows has a TerminateProcess() from <processthreadsapi.h> but if I understand correctly it just terminates the process, it does not send a signal, so it is not useful in my case.

The program basically has one master thread and several child threads. When the C program receives a signal (SIGTERM or SIGSEGV) it sends the signal to the child processes (in C). Those processes then call Fortran subroutines to stop the processing, delete temporary files, save data etc.

If it's not possible to get one function instead of kill() I am willing to write C code to emulate that type of behaviour. The C part of the code is very short, so I can change it, however, the fortran part is large and I cannot modify it.

Thanks!
Well if you're only going to use Windows or POSIX, you could try something with the system() command and see what that does. You'll probably have to use a pipe to redirect it to your "other processes" though.

I don't use Windows a lot, so I can't help you much beyond that, sorry.
Windows doesn't do posix signals as standard. It has it's own methods using events. If you want to keep your posix program under Windows then you might want to consider using cygwin which I believe supports posix - although I've not used it.

See https://docs.microsoft.com/en-us/windows/win32/api/synchapi/ for the available Windows event functions.

The main process creates the event and then uses SetEvent() as appropriate to signal to other processes that that event has occurred.

The other process(s) open the created event and use WaitForSingleObject() to check if a particular event has been signalled. This API has a time parameter which can be 0 in which case it acts like an if statement. If the event has been signalled then appropriate routines can be called etc.
Last edited on
@agent max How do I use the system command? The windows native taskkill.exe does not seem to be sending signals, it just shuts down the process. I have tried using signal handlers with taskkill but it does not work. The idea to use pipe seems to be interesting, could you perhaps direct me to an example of an easy implementation of pipes? I am not a C programmer, so all of this is new to me.

@seeplus Thanks for the link. I looked at WaitForSingleObject() but it seems like it just waits for the event to occur. But I need something like a passive wait mechanism, that just sits there, and allows the rest of the program to move on.

My C code is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


#ifdef __cplusplus
extern "C" {
#endif

#ifdef INT64
void mrccend_(long*);
void dmrccend_(long*);
long ERROR_SIGTERM = -1;
long ERROR_SIGSEGV = -2;
#else
void mrccend_(int*);
void dmrccend_(int*);
int ERROR_SIGTERM = -1;
int ERROR_SIGSEGV = -2;
#endif

void parent_sigterm_(int, siginfo_t *, void *);
void child_sigterm_(int, siginfo_t *, void *);
void child_sigsegv_(int, siginfo_t *, void *);
void sendSignalToChildren(int);

static struct sigaction old_act;

void signalinit_() {
// initialise dmrcc's responses to signals
   struct sigaction act_term;
   memset(&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &parent_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);
}

void parent_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise response to signal SIGTERM
   pid_t pid_parent;
   char pidchar[10];
   char command[40];

   pid_parent = getpid();
   sprintf(pidchar, "%d", pid_parent);

   printf("\n Program dmrcc recieved SIGTERM\n"); fflush(stdout);

   sendSignalToChildren(SIGTERM);
   sleep(5);
   sendSignalToChildren(SIGKILL);

   printf("\n Program dmrcc terminating\n"); fflush(stdout);
   dmrccend_(&ERROR_SIGTERM);
}

void sendSignalToChildren(int sig) {
   int ownPid = getpid();
   int pid, numPids = 0;
   int *pids;
   FILE *pidfile = fopen("pids", "r");
   if (pidfile == NULL) {
      printf("Error: Could not open pids file\n");
      return;
   }

   // number of running processes other than the current process
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         numPids++;
      }
   }
   rewind(pidfile);

   // read other process' PIDs
   pids = (int *)malloc(numPids * sizeof(int));
   int i = -1;
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         pids[++i] = pid;
      }
   }

   // send signal sig to processes
   printf("\n Sending signal %2d to child processes\n", sig); fflush(stdout);
   for (i = 0; i < numPids; i++) {
      kill((pid_t)pids[i], sig);
   }
   
   fclose(pidfile);
   free(pids);
}

void signalinitchild_() {
// initialise child's responses to signals
   struct sigaction act_term;
   memset (&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &child_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);

   struct sigaction act_segv;
   memset (&act_segv, '\0', sizeof(act_segv));
   act_segv.sa_sigaction = &child_sigsegv_;
   act_segv.sa_flags = SA_SIGINFO;
   sigaction(SIGSEGV, &act_segv, &old_act);
}

void child_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGTERM
   mrccend_(&ERROR_SIGTERM);
}

void child_sigsegv_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGSEGV
   mrccend_(&ERROR_SIGSEGV);
   if(old_act.sa_flags & SA_SIGINFO)
   {
      (*old_act.sa_sigaction)(signum, siginfo, context);
   } 
   else 
   { 
      (*old_act.sa_handler)(signum);
   }
}

#ifdef __cplusplus
}
#endif  


Multiple processes are launched by MPI. Then from the fortran code, the master process calls signalinit_() and all the child processes call signalinitchild_(). I don't even need to send signals to the child processes, I just need some way for the parent process to communicate with all the child processes so that the child processes know which error has occurred, and then I can directly call childsigterm_() and childsigsegv_.
Last edited on
@sm9,
The system command sends a command directly to the operating system. You would use it like:
 
system ("cd Documents/Downloaded\ Folders/");

or something like that. That particular command, in Unix, tells the program to change directories to the "Documents/Downloaded Folders" directory.

It takes the same commands you would normally enter into the command line, basically.
@agent max, Yes I understand that system() sends a command to the command shell, but there is no command in Windows shell that sends signals.
I looked at WaitForSingleObject() but it seems like it just waits for the event to occur. But I need something like a passive wait mechanism, that just sits there, and allows the rest of the program to move on.


You can specify a time-out period. The function returns either after the event is signalled or if the time-out occurs. The return value indicates which. If the timeout is 0 then it returns immediately indicating either event has occurred or not.
Oh ah, sorry I thought you were asking how it worked. Well I don't use windows except at work when I have to, so sorry.
@seeplus I don't understand how it works. Does it stop the flow of the program until it receives the event or does it allow the program to execute as usual?

@agent max, That's ok, can I use C pipes to do this (OS independent?)
@seeplus I don't understand how it works. Does it stop the flow of the program until it receives the event or does it allow the program to execute as usual?


It will stop the program execution until either the event is received or the specified timeout (which can be 0) is reached.

Once the function returns, the return value specifies why the function returned - event occurred or time out reached.
Last edited on
@sm9,
Not sure. Meaning, "I have no idea." This is getting out of the realm of my [limited] expertise, probably time someone else took my spot.
@sm9, check out this example code.

Maybe this isn't the best way; I never was very familiar with this part of the win32 API.
Obviously you'll need to add some error checking.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#define UNICODE 1
#define _UNICODE 1

#include <windows.h>

int wmain(int argc, wchar_t** argv)
{
  // `event_name` is an identifier for the process synchronization primitive 
  // The `Global\` prefix means the event is globally accessible
  LPCWSTR event_name = L"Global\\MYEVENT";
  HANDLE  event = NULL;
  
  if (argc == 2) // parent process 
  { 
    STARTUPINFO         startup_info;
    PROCESS_INFORMATION process_info;
    
    // Create an event, promiscuously accessible to everybody.
    // Set up the access control structures to fix the security problem
    event = CreateEvent(NULL, FALSE, FALSE, event_name);

    // I guess initialization with `= {0}` is not supported by the C compiler
    ZeroMemory(&startup_info, sizeof startup_info);
    ZeroMemory(&process_info, sizeof process_info);
    startup_info.cb = sizeof startup_info;
    CreateProcess(argv[0], NULL, NULL, NULL, FALSE, DETACHED_PROCESS,
                  NULL, NULL, &startup_info, &process_info);
    
    // Signal the child process and wait for the child process to complete
    // Separately, you can call SetEvent and WaitForSingleObject 
    SignalObjectAndWait(event, process_info.hProcess, INFINITE, FALSE);
  } else // child process
  {
    DWORD wait_result;
    // Open the globally accessible event by name
    event = OpenEvent(SYNCHRONIZE, FALSE, event_name);
    
    do wait_result = WaitForSingleObject(event, 100); while (wait_result == WAIT_TIMEOUT);

    if (wait_result == WAIT_OBJECT_0)
      MessageBoxW(NULL, L"Event signalled, worker can terminate", L"Worker", MB_OK);
    else
      __debugbreak();
  }
}
Last edited on
@mbozzi Thanks! That is quite helpful even though I don't understand a lot of the code :-(
 
do wait_result = WaitForSingleObject(event, 100); while (wait_result == WAIT_TIMEOUT);


If I understand correctly, this loop busy-waits for the event i.e. the loop carries on executing until WAIT_OBJECT_0 is returned?

The C code in the program I am using, is called by a fortran code. If the function containing the busy-wait is called, I don't know if the fortran call will return. Is there anything I can do about it? Because the fortran call must proceed, otherwise the program won't run at all.
Last edited on
The idea behind the loop was that you could do units of work before or after calling WaitForSingleObject. The loop is nonessential.

As is, I should have written
wait_result = WaitForSingleObject(event, INFINITE);
Which blocks, but does not busy-wait.

To use this approach you'll have to arrange to periodically wait on the event, whether it's by calling
WaitForSingleObject(event, 0)
after every unit of work, or waiting forever in another thread.
Last edited on
Topic archived. No new replies allowed.