8

I have 3 files — SwimMill.c, Fish.c, and Pellets.c — each of which is compiled into an executable. When SwimMill is run, it uses fork() and exec() to run Fish and Pellets. However, for some reason, when I use the terminal, compile the programs with make, and run SwimMill, the file Fish runs first. Can anybody help me?

Makefile

all: SwimMill Fish Pellets

SwimMill: SwimMill.c
    gcc -o SwimMill SwimMill.c

Fish: Fish.c
    gcc -o Fish Fish.c -lm

Pellets: Pellets.c
    gcc -o Pellets Pellets.c

SwimMill.c

// Uses both fish and pellets, 30 seconds, then print it out
// Create pellets at random intervals, from 0x80
// Eating --> Get rid of most significant bit
// Use shared memory for fish and pellet position only
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define SHM_SIZE 1000

/*
TODO: SwimMIll FIRST, draw and get everything working
*/

/* 1. Create share memory using shmget
2. Attach to shared memory using shmat
3. Do operations
4. Detach using shmdt
*/

void printGrid(int*);
void handler(int);

int main(int argc, char* argv[]) {

    printf("Hello");
    signal(SIGINT, handler);

    key_t key;
    int shmid;
    int *shm;
    int timer = 0;
    int fish = 0;
    int pellet[20];

    key = ftok("SwimMill.c", 'b'); //generate random ke
    shmid = shmget(key, SHM_SIZE, IPC_CREAT|0666);
    shm = shmat(shmid, NULL, 0); // Attach

    // Initializing the shared memory to prevent segmentation fault
    for (int i = 0; i < SHM_SIZE; i++){
        shm[i] = -1;
    }

    int index = 0;
    while(timer <= 30){
        sleep(1); // Slow process down
        fish = fork();
        execv("Fish", argv);
        pellet[index] = fork();
        execv("Pellets", argv);
        printGrid(shm);
        printf("\n");
        timer++;
        index++;
    }

    shmdt(shm);
    shmctl(shmid, IPC_RMID, NULL);
    printf("Program finished! \n");
    getchar(); // Pause consol
    return 0;
}

void printGrid(int* shm) {
    int row = 10;
    int column = 10;
    char stream[row][column]; //2D Dimensional array, fish can only move last row of 2d


    //Initializing grid first
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < column; j++) {
            stream[i][j] = '~';
        }
    }

    //Printing out grid with fish and pellet
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < column; j++) {
            stream[i][j] = '~'; // water
            for (int k = 0; k < 20; k++) {
                stream[shm[k]/10][shm[k]%10] = 'O'; // pellets
                stream[shm[0]/10][shm[0]%10] = 'Y'; // Fish
            }
            printf("%c ", stream[i][j]   );
        }
        printf("\n");
    }

}

void handler(int num) {
    perror(" Interrupt signal is pressed!! \n");
    exit(1);
}

Fish.c

// 1 fish
// Scan the entire array, and focus on one pellet
// Arrange itself

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <math.h>
#include <unistd.h>

int findClosestPellet(int*);
void moveLeft(int, int*);
void moveRight(int, int*);

int main() {
  printf("printing from fish");
  key_t key = ftok("SwimMill.c", 'b');
  int shmid = shmget(key, 1024, IPC_CREAT|0666);
  int *shm = (int*) shmat(shmid, NULL, 0);

  int fish = 94; // Middle position
  shm[0] = fish; // Store first shared memor space to fish
  int columnToMoveTo = 0;
  while(1) {
    int closestPellet = shm[findClosestPellet(shm)];
    if ((closestPellet % 10) > (fish % 10) ) {
      moveRight(fish, shm);
    }
    else if ((closestPellet % 10) < (fish % 10)) {
      moveLeft(fish, shm);
    }
    sleep(1);
  }
  shmdt(shm);
  return 0;
}

int findClosestPellet(int* shm) {
  // Using distance formula to find closest pellet
  // (x2 - x1)^2 + (y2 - y1)^2
  int closestPellet = 0;
  int distance[20] = {0}; // Distance of all 20 pellets
  int minimumDistance = 0;
  // shm[1] = 11;
  // shm[2] = 14;
  // shm[3] = 10;
  // shm[4] = 55;
  int x2 =  shm[0] % 10;
  int y2 = shm[0] / 10;
  for (int i = 1; i < 20; i++) {
    int x1 = shm[i] % 10;
    int y1 = shm[i] / 10;
    distance[i] = pow(x2-x1,2) + pow(y2-y1,2); // Storing them
  }
  minimumDistance = distance[1];

  //Finding smallest distance
  for (int i = 2; i < 20; i++) {
    if (distance[i] <= minimumDistance) {
      closestPellet = i;
    }
  }
  printf("Closest pellet %d \n", closestPellet);
  return shm[closestPellet];
}

void moveLeft(int fish, int* shm) {
  if (shm[0] <= 90) {
  }
  else{
    fish--;
    shm[0]--;
  }
}

void moveRight(int fish, int* shm) {
  if (shm[0] >= 99){
  }
  else{
    fish++;
    shm[0]++;
  }
}

Pellets.c

// Multiple pellets
//Process ID, position, eaten/misse
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>

void eatPellet();
void missPellet();

int main() {
  key_t key = ftok("SwimMill.c", 'b');
  int shmid = shmget(key, 1024, IPC_CREAT|0666);
  int *shm = (int*) shmat(shmid, NULL, 0);

  int i = 1; // 1 - 19 are pellets
  for (; i < 20; i++) {
    int pelletPosition = rand() % 9 + 0; // random number from 0 - 9
    shm[i] = pelletPosition;
    break;
  }
  while(1) {
    if (shm[i] < 90) {
      shm[i] += 10;
    }
    else if (shm[i] == shm[0]) {
      eatPellet();
      printf("Position: %d\n", shm[i] );
      break;
      // EATEN and KILL
    }
    else {
      // KIll process, terminate
      missPellet();
      printf("Position: %d\n", shm[i] );
      break;
    }
    // printf("%d\n",shm[i] );
    i++;
    sleep(1);
  }
  shmdt(shm);
  return 0;
}

void eatPellet() {
  printf("Pellet eaten!");
  printf("PID: %d \n", getpid());

}

void missPellet() {
  printf("Pellet missed");
  printf("PID: %d \n", getpid());
}

For the makefile, I run "make". Then I run ./SwimMill. However, it runs Fish for some reason.

dbush
  • 205,898
  • 23
  • 218
  • 273
Slay
  • 101
  • 6
  • Possible duplicate of [Two 'main' functions in C/C++](https://stackoverflow.com/questions/1990932/two-main-functions-in-c-c) – erik258 Oct 30 '18 at 20:24
  • Even though it's a duplicate, this is the highest quality question I've seen in a while. I commend you for including all the relevant materials. Without them, I never would have thought you might be defining `main` function multiple times. – erik258 Oct 30 '18 at 20:26
  • @DanFarrell Thanks for the reply. However, my program has one main function in each file. The link you showed me has problems with having multiple main functions in one file. Is this relevant? – Slay Oct 30 '18 at 20:29
  • 1
    @DanFarrell Huh? He's not linking them together. That's not a problem. – Rafael Oct 30 '18 at 20:30
  • Yeah, I'll redact that , sorry. – erik258 Oct 30 '18 at 20:31
  • Not the problem you're asking about, but some advice: `shm` should be declared as `volatile int *shm` in all three programs. Without the `volatile` qualifier, the compiler will assume that the contents of `shm` will not be modified externally, which may cause incorrect behavior. –  Oct 30 '18 at 21:07
  • Note that you don't "run `makefile`" or "run `SwimMill.c`"; you run `make` (which processes the `makefile`) and you run `SwimMill` (which was compiled from `SwimMill.c`). – Jonathan Leffler Oct 30 '18 at 21:15

2 Answers2

9

You're not using fork / exec correctly:

while(timer <= 30){
    sleep(1); // Slow process down
    fish = fork();
    execv("Fish", argv);
    pellet[index] = fork();
    execv("Pellets", argv);
    printGrid(shm);
    printf("\n");
    timer++;
    index++;
}

Recall that the fork function returns twice: once to the parent process where it returns the child's pid, and once to the child process where it returns 0.

You're forking a new process, but not checking the return value. So both the child and the parent call execv to start the "Fish" program, so you have two copies of "Fish" running and no copies of "SwimMill" running.

You need to check the return value of fork to see if the process is the parent or the child and act accordingly.

while(timer <= 30){
    sleep(1); // Slow process down
    fish = fork();
    if (fish == -1) {
        perror("fork failed");
        exit(1);
    } else if (fish == 0) {
        execv("Fish", argv);
        perror("exec failed");
        exit(1);
    }

    pellet[index] = fork();
    if (pellet[index]== -1) {
        perror("fork failed");
        exit(1);
    } else if (pellet[index] == 0) {
        execv("Pellets", argv);
        perror("exec failed");
        exit(1);
    }

    printGrid(shm);
    printf("\n");
    timer++;
    index++;
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Extremely appreciate the help with forks! The segmentation fault error stopped occurring. However, I still can't run SwimMill.c! Fish.c is being run first. Is this a problem with the program or with my VM Linux? – Slay Oct 30 '18 at 20:48
  • @Slay It is being run first because you started it first. Because you have multiple processes running at once in the same terminal their output is intermingled. – dbush Oct 30 '18 at 20:50
  • I sort of understand, but not really. So you're saying I need to send some signal to kill the process in order to run the correct process? – Slay Oct 30 '18 at 20:59
  • @Slay What I'm saying is that you **are** running the correct process. It just doesn't look like it because both are running concurrently and writing to the terminal at the same time. – dbush Oct 30 '18 at 21:00
2

What makes you think Fish runs before SwimMill?

If you are relying on the order of the output to stdout, you will be misled by the buffering of "hello" which will not be immediately output until either '\n' is written, fflush(stdout) called, or the process terminates.

That is to say SwimMill does run first, but Fish displays output first.

Change:

printf("Hello");

to

printf("Hello\n");

And similarly:

printf("printing from fish");

to

printf("printing from fish\n");
Clifford
  • 88,407
  • 13
  • 85
  • 165