Я пытаюсь создать свою собственную оболочку.Идея состоит в том, что, когда я делаю многоступенчатую конвейерную команду, такую как ps -j | more
, первый процесс ps -j
будет лидером группы процессов, а все последующие процессы будут использовать один и тот же идентификатор группы процессов.
Для некоторыхпричина, это работает для команд как ls | grep pipes
, но зависает на ps -j | more
.
Вот изображение, чтобы продемонстрировать:
Вещи, которые я сделал:
- Закрытие всех каналов в секунду, когда я совершаю любой
dup2()
вызов как для детей, так и для родителей - Вызов
setpgid()
перед любым execvp()
вызовом ив и дочерний и родительский
Код ниже.Краткое объяснение того, как это работает: каждый процесс вызывает fork_process()
, который принимает строку позиции: FIRST | MIDDLE | FINAL | ONLY
, которая указывает, какой процесс находится в конвейере.
Например, ps-j
= FIRST и more
= FINAL.
fork_process()
затем создает каналы, разветвления и выполняет команду в дочернем процессе.Это также, где setpgid()
называется.Если разветвленный процесс является FIRST или ONLY, то он сохраняет свой pid в cmd_group->pgid
, поэтому другой процесс может setpgid()
для этого pgid.
execute_commands :
int execute_commands(Command_Group* cmd_group) {
int cmd_count = cmd_group->cmd_count;
int fd_count = cmd_group->fd_count;
int pipe_count = cmd_group->pipe_count;
for (int i = 0; i < cmd_count; i++) {
if (i == 0 && pipe_count == 0 && cmd_count == 1) {
if (fork_process(cmd_group, i, NULL, NULL, ONLY) < 0) {
perror("invalid error executing command");
return -1;
}
}
else if (i == 0 && pipe_count > 0) {
int* fd = cmd_group->pipe_fds[fd_count++];
if (fork_process(cmd_group, i, fd, NULL, FIRST) < 0) {
perror("invalid error executing command");
return -1;
}
}
else if (i == (cmd_count - 1) && pipe_count > 0) {
int* fd = cmd_group->pipe_fds[fd_count - 1];
if (fork_process(cmd_group, i, fd, NULL, FINAL) < 0) {
perror("invalid error executing command");
return -1;
}
}
else {
int* previous_fd = cmd_group->pipe_fds[fd_count - 1];
int* fd = cmd_group->pipe_fds[fd_count++];
if (fork_process(cmd_group, i, previous_fd, fd, MIDDLE) < 0) {
perror("invalid error executing command");
return -1;
}
}
}
close_pipes(cmd_group->pipe_fds, pipe_count);
printf("PARENT pid: %d, pgid: %d, ppid: %d\n", getpid(), getpgid(0), getppid());
for (int i = 0; i < cmd_group->cmd_count; i++) {
int pid = cmd_group->cmd_list[i]->pid;
char* cmd = cmd_group->cmd_list[i]->cmd;
printf("CHILD cmd: %s, pid: %d, pgid: %d\n", cmd, pid, getpgid(pid));
}
int status;
int corpse;
while ((corpse = waitpid(-1, &status, 0)) > 0) {
fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
(int)getpid(), corpse, (unsigned)status);
}
return 0;
}
fork_process :
int fork_process(Command_Group* cmd_group, int cmd_idx, int fd[2], int second_fd[2], char* position) {
if (cmd_group == NULL) {
perror("invalid null command passed to fork_process");
return -1;
}
Command* cmd = cmd_group->cmd_list[cmd_idx];
pid_t pid;
if (strcmp(position, FIRST) == 0) {
pipe(fd);
} else if (strcmp(position, MIDDLE) == 0) {
pipe(second_fd);
}
if ((pid = fork()) < 0) {
perror("invalid fork command\n");
exit(EXIT_FAILURE);
} else if (pid == 0) { // Child
if (strcmp(position, FIRST) == 0) {
if (dup2(fd[OUT], STDOUT_FILENO) < 0) {
perror("invalid piping: dup2 failure");
return -1;
}
if (setpgid(pid, pid) < 0) {
perror("invalid setpgid failure");
}
}
else if (strcmp(position, MIDDLE) == 0) {
if (dup2(fd[IN], STDIN_FILENO) < 0 || dup2(second_fd[OUT], STDOUT_FILENO) < 0) {
perror("invalid piping: dup2 failure");
return -1;
}
if (setpgid(pid, cmd_group->pgid) < 0) {
perror("invalid setpgid failure");
}
}
else if (strcmp(position, FINAL) == 0) {
if (dup2(fd[IN], STDIN_FILENO) < 0) {
perror("invalid piping: dup2 failure");
return -1;
}
if (setpgid(pid, cmd_group->pgid) < 0) {
perror("invalid setpgid failure");
}
}
close_pipes(cmd_group->pipe_fds, cmd_group->pipe_count);
if (create_redirects(cmd) < 0) {
perror("invalid redirection error");
return -1;
}
if (execvp(cmd->cmd, cmd->args) < 0) {
perror("invalid: execvp failure");
return -1;
}
}
// Parent
if (strcmp(position, FIRST) == 0 || strcmp(position, ONLY) == 0) {
setpgid(pid, pid);
cmd_group->pgid = pid;
} else {
setpgid(pid, cmd_group->pgid);
}
cmd->pid = pid;
return 0;
}