1

I'm writing a c++ multi-process program with ncurses.

Each process is required to display something on the screen.

My Example code:

int main() {
  initscr();
  noecho();
  curs_set(0);

  int flag = fork();
  if (flag == -1)
    exit(1);
  else if (flag == 0) {
    WINDOW *win = newwin(4, 4, 0, 0);
    int n = 0;
    while (1) {
      mvwprintw(win, 0, 0, "%d", n % 9);
      wrefresh(win);
      n = (n + 1) % 9;
      sleep(1);
    }
  }
  else {
    WINDOW *win = newwin(4, 4, 8, 8);
    int n = 0;
    while (1) {
      mvwprintw(win, 0, 0, "%d", n % 9);
      wrefresh(win);
      n = (n + 1) % 9;
      sleep(1);
    }
  }
  endwin();

  return 0;
}

But it can only display one process's information on the screen.

How can I solve it?

z7ph1r
  • 21
  • 5

2 Answers2

1

I have hacked about something ugly that roughly works but shows what the problems are. I suspect a single window manager process which other processes communicate with would be better - or some horrible set of mutexes.

#include <stdlib.h>
#include <unistd.h>
#include <curses.h>
int main() {
  initscr();
  noecho();
  curs_set(0);
    WINDOW *win0 = newwin(4, 4, 0, 0);
    WINDOW *win1 = newwin(4, 4, 8, 8);

  int flag = fork();
  if (flag == -1)
    exit(1);
  else if (flag == 0) {
    int n = 0;
    while (1) {
      mvwprintw(win0, 0, 0, "%d", n % 9);
      wrefresh(win0);
      wrefresh(win1);
      n = (n + 1) % 9;
      sleep(1);
    }
  }
  else {
    int n = 0;
    while (1) {
      mvwprintw(win1, 0, 0, "%d", n % 9);
      wrefresh(win1);
      wrefresh(win0);
      n = (n + 1) % 9;
      sleep(1);
    }
  }
  endwin();

  return 0;
}

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
1

The example creates two 4x4 windows, with the second offset to 8,8. So they have no lines in common.

Since you're using fork (rather than vfork), the two processes should have separate address spaces, and there should be no way for one process to refresh a window which is modified in the other process. In some cases, developers have chosen to equate vfork and fork. With Linux, the vfork manual page comments:

Standard Description
(From POSIX.1) The vfork() function has the same effect as fork(2), except that the behavior is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit(2) or one of the exec(3) family of functions.

but goes on to say

The requirements put on vfork() by the standards are weaker than those put on fork(2), so an implementation where the two are synonymous is compliant. In particular, the programmer cannot rely on the parent remaining blocked until the child either terminates or calls execve(2), and cannot rely on any specific behavior with respect to shared memory.

That weaker and compliant is a developer arguing that making the two functions similar doesn't really matter...

The fork manual page asserts that there are separate address spaces:

The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content. Memory writes, file mappings (mmap(2)), and unmappings (munmap(2)) performed by one of the processes do not affect the other.

but we're left with that ambiguity in the description of vfork. Your program may fail to update the window belonging to the parent process as part of this vfork behavior — and refreshing both windows in the suggested answer merely confirms that the fork function is vfork in disguise.

POSIX currently has no page for vfork. It had one here (the fork description is worth reading).

Either way, using vfork wouldn't actually improve things. If you have to work within the same address space, that's what threads are for. If you have to use separate processes, making one process update the screen and other process(es) communicate with pipes is what people actually do.

A commented suggested that fork is obsolete. POSIX has something different to say on that aspect. Quoting from the rationale for posix_spawn:

The posix_spawn() function and its close relation posix_spawnp() have been introduced to overcome the following perceived difficulties with fork(): the fork() function is difficult or impossible to implement without swapping or dynamic address translation.

  • Swapping is generally too slow for a realtime environment.

  • Dynamic address translation is not available everywhere that POSIX might be useful.

  • Processes are too useful to simply option out of POSIX whenever it must run without address translation or other MMU services.

Thus, POSIX needs process creation and file execution primitives that can be efficiently implemented without address translation or other MMU services.

The posix_spawn() function is implementable as a library routine, but both posix_spawn() and posix_spawnp() are designed as kernel operations. Also, although they may be an efficient replacement for many fork()/ exec pairs, their goal is to provide useful process creation primitives for systems that have difficulty with fork(), not to provide drop-in replacements for fork()/ exec.

Further reading:

Czipperz
  • 3,268
  • 2
  • 18
  • 25
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
  • Thank you for taking the time to explain it. – Mark Setchell Nov 05 '16 at 18:44
  • Re: POSIX and `vfork()`. `vfork()` was obsoleted by POSIX 2008, and anything that used `vfork() + execve()` should better use `posix_spawn()`. – ninjalj Mar 24 '17 at 11:51
  • I use `fork()` without `exec()` a lot. I certainly hope it is not going to become obsolete. It's extremely useful in a server. You `listen()` and on an `accept()` you `fork()` or even better, you `fork()` and each child process calls `listen()` + `accept()`. The `exec()` breaks that scheme and makes it impossible to get such an optimization (Apache uses that `fork()` + `listen()` scheme and that's one reason for its good speed.) – Alexis Wilke Jan 15 '18 at 01:18