简体   繁体   中英

how to write a cp function (in linux shell ) in c++ that runs in background?

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 :(

      }
  }
};

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM