0

I am trying to write a my own small linux shell , and i want to write the function cp , the function format is like the following :

cp <old-file-path> <new-file-path>

It copies the first file into the second file (overwriting it) , and if the second file doesn't exist it will create a new one. If the files didn't open or any system call did not succeed it will print an error message. However, sometimes I want to copy large files so I want to run this cp command in the background (using fork without waiting for it to finish).

My problem is: how can I use fork and not wait for the process to finish? Currently, the child process becomes a zombie process.

Here is my code :

// num_args contains the number of arguments sent to cp
class CopyCommand : public BuiltInCommand {
 public:
  CopyCommand(const char* cmd_line) : BuiltInCommand(cmd_line){}
  virtual ~CopyCommand() {}
  void execute() override{
      if(this->num_args < 1){  // if no arguments were send to cp
          perror("invalid arguments");
          return;
      }
      char* buff;
      int fd1 = open(args[1], O_RDONLY);   
      if(fd1 == -1){
            perror("open failed");
            return;
      }
      if(this->num_args==2){          // copy file1 into file2 (overrite file 1)
          int fd2 = open(args[2], O_TRUNC);
            if (fd2 == -1) {                // if we couldn't open the file then create a new one (not sure if we supposed to this ?)
                fd2 = open(args[2], O_CREAT, 0666);
                if (fd2 == -1) {
                    perror("open failed");
                    return;
                }
            }
          pid_t PID = fork();
          if(PID == -1){
              perror("fork failed");
              return;
          }
          else if(PID == 0){

             // i need to use fork here :( before i start to write
            int read_res = read(fd1, &buff, 1);  /// read from the file fd1 into fd2
            while (read_res != -1) {
                if (!read_res) {
                    break;
                }
                if (write(fd2, buff, 1) == -1) {
                    perror("write failed");
                }
                read_res = read(fd1, buff, 1);
            }
            if (read_res == -1) {
                perror("read failed");
            }     
}   
      }
      else if(this->num_args==1){               // create file2 and copy file1 into file2

          // don't know how to do this yet 
          // i need to use fork here :(

      }
  }
};
anna
  • 111
  • 8
  • Does this answer your question? [Zombie process and fork](https://stackoverflow.com/questions/4873092/zombie-process-and-fork) – Botje May 05 '20 at 15:29
  • i don't want to wait for the process , i want cp to run in the background , unlike the person who asked – anna May 05 '20 at 15:32
  • tl;dr of dupe: either set `SIGCHLD` to `SIG_IGN` or handle it with a function that `wait`s on it. (The latter won't block, since once you get the signal, you know the child is already dead, so the `wait` will return immediately.) – Joseph Sible-Reinstate Monica May 05 '20 at 15:32
  • @anna The accepted answer starts with "If you have no intention to wait for your child processes ...", which is your situation. The other answers explain how you would solve it if you _are_ interested in waiting for the child. (to get exit status, for example) – Botje May 05 '20 at 15:37
  • how can i do this ? do i call fork() before i start the copying process ? and how to send this signal ? does the os understand by itself to make int process wait for this process? – anna May 05 '20 at 15:38
  • Botje when do i call this system call ? after fork immediatly ? – anna May 05 '20 at 15:40
  • Can you in Linux, append the `&` to the command to make it run in the background? I remember doing this on Sun Sparcstation and a BSD Unix system. – Thomas Matthews May 05 '20 at 15:53
  • yes i wrote a function like this that when i call a command and append & to it then it will run in the background , but here i want cp to always run in background beacause i will sometimes copy large files – anna May 05 '20 at 15:59

1 Answers1

0

For starters, I rewrote your code a bit. In particular, note that the child branch (PID == 0) exits when it is done. The parent closes the passed-down file descriptors after forking and in case of error.

if (this->num_args == 2) {
    int fd1 = open(args[1], O_RDONLY);
    if (fd1 == -1) {
        perror("open failed");
        return;
    }

  int fd2 = open(args[2], O_TRUNC);
  if (fd2 == -1) {
    fd2 = open(args[2], O_CREAT, 0666);
    if (fd2 == -1) {
      perror("open failed");
      close(fd1);
      return;
    }
  }

  pid_t PID = fork();
  if (PID == -1) {
    perror("fork failed");
  } else if (PID == 0) {
    char buff[1024];
    int read_res = read(fd1, &buff, 1024); /// read from the file fd1 into fd2
    while (read_res != -1) {
      if (!read_res) {
        break;
      }
      if (write(fd2, buff, read_res) == -1) {
        perror("write failed");
      }
      read_res = read(fd1, buff, 1024);
    }
    if (read_res == -1) {
      perror("read failed");
    }
    exit(0);
  } else {
    printf("Copy running in background (pid: %d)\n", PID);
  }

  close(fd1);
  close(fd2);
  return
}

When the child process calls exit, the process will stick around in "Zombie" state. This state allows the parent process (you) to call wait or waitpid to retrieve the exit code.

As a secondary effect of the process ending, the kernel will send a SIGCHLD to your process, to let you know you can actually call wait without blocking. In your situation, you do not care about the exit code, so you can set up a "don't care" signal handler at the start of your program and let the kernel clean up the process:

signal(SIGCHLD, SIG_IGN);

This is documented in signal(2):

If a process explicitly specifies SIG_IGN as the action for the signal SIGCHLD, the system will not create zombie processes when children of the calling process exit. As a consequence, the system will discard the exit status from the child processes.

Botje
  • 26,269
  • 3
  • 31
  • 41