前景信号tcsetpgrp c

时间:2016-12-12 13:56:07

标签: c unix

我正在制作自己的外壳原因为什么不是。 当您运行命令并以&结束时,该过程将在后台运行,因此我想制作一个fg命令,您可以使用该命令将后台进程置于前台。

我在制作fg功能时遇到了一些麻烦。 如果我理解正确,将si​​gnal()放在子进程中将让子进程接收信号。 Signal接收两个参数,signum和处理函数。 我们将使用tcsetpgrp()将给定的后台进程设置为前台。所以在lsh_fg中我调用tcsetpgrp(STDIN_FILENO,pid)。

所以signum应该是sigttou所以它可以从tcsetpgrp()接收信号。

我不知道应该在处理程序中放什么,因为tcsetpgrp()应该像man页面描述的那样: “ 函数tcsetpgrp()使进程组具有进程组ID        pgrp与fd关联的终端上的前台进程组 “ 据我了解,tcsetpgrp()正在向具有信号(sigttou,handler)的进程发送信号,该信号在收到信号时被置于前台。但我明显误解了这一点,因为它不起作用。

我的问题:我应该如何理解tcsetpgrp()和signal(sigttou,handler)一起工作的方式?我的处理程序包括什么? 我非常感谢你的回答,因为我真的被困在这里:-) 请参阅下面的代码: Ps:我是C和系统编程的新手,这是我的第一篇文章,所以对我的代码的任何建设性批评都受到热烈欢迎 感谢很多:D

#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>

pid_t pid;

int toke_c;
//function declaration for the function pointers
int lsh_cd(char **args);
int lsh_pwd(char **args);
int lsh_exit(char **args);
int lsh_fg(char **args);

//An array of functions:
int (*builtin_func[]) (char **) = {
  &lsh_cd,
  &lsh_pwd,
  &lsh_exit,
  &lsh_fg
};

//An array of the given strings:
char *builtin_str[] = {
  "cd",
  "pwd",
  "exit",
  "fg"
};

///built in functions cd and pwd

int lsh_fg(char **args){
tcsetpgrp(STDIN_FILENO, pid);
  return 1;
}

void fg_handler()
{
//What to put here???
}

///built in functions cd and pwd
int lsh_cd(char **args)
{
  if (args[1] == NULL) {
    fprintf(stderr, "lsh: cd: no arguments given\n");
  } else {
    if (chdir(args[1]) != 0) {
      perror("lsh");
    }
  }
  return 1;
}

int lsh_pwd(char **args)
{
char * cwd;
    cwd=getcwd (NULL,0);
    printf ("%s\n ", cwd);
  return 1;
}

int lsh_exit(char **args)
{
  return 0;
}

/* Handlers Here*/
void killer()
{
  if (pid == 0)
    exit(0);
}

void handler()
{
  //I DON'T KNOW WHAT TO PUT HERE
}

int lsh_launch(char **args)
{

  int status=0;
  pid = fork();
  if (pid == 0) {
    // child process
    signal(SIGINT, killer);
    if (execvp(args[0], args) == -1) {
      fprintf(stderr,"Command not found in $PATH\n");
    }
    return 1;
  } else if (pid < 0) {
    //error
    perror("lsh");
  } else {
    // parent
    signal(SIGINT, killer);
    waitpid(pid, &status, WUNTRACED);
  }
  return 1;
}

int lsh_background(char **args)
{

  pid_t pid;
  int status=0;
  pid = fork();
  if (pid == 0) {
    // child process
    setpgid(0, 0);
    signal(SIGINT, killer);
    signal(SIGTTOU, fg_handler);
    if (execvp(args[0], args) == -1) {
      fprintf(stderr,"Command not found in $PATH\n");
    }
    return 1;
  } else if (pid < 0) {
    //error
    perror("lsh");
  } else {
    // parent
    signal(SIGTTOU, fg_handler);
    signal(SIGINT, killer);
  }
  return 1;
}

//if a command was entered that we've been using
int lsh_exec(int argc, char **args)
{
  int i;
  if (args[0] == NULL) {return 1;}
  int tresh=4;

  char **args1=malloc(toke_c*sizeof(char *));
    int j;
    for(j=0;j<toke_c-1;j++){
      args1[j]=args[j];
    }

  if(strcmp(args[toke_c-1],"&")==0){
      return lsh_background(args1);
      }

      for (i = 0; i < tresh; i++) {
      if (strcmp(args[0], builtin_str[i]) == 0) {
        return (*builtin_func[i])(args);
      }
    }

  return lsh_launch(args);
}

#define MAX_STR 256

//reading the line
char *lsh_lread(void)
{ 
  char *str = malloc (MAX_STR);
  fgets (str, MAX_STR, stdin);
}

//tokenizer
char **lsh_tokenizer(char *line)
{
  int bufsize = 64;
  int pos_t = 0;
  char **tokens = malloc(bufsize * sizeof(char*));
  char *token;
  token = strtok(line, " \t\r\n\a");

  while (token != NULL) {
    tokens[pos_t] = token;
    pos_t++;

    token = strtok(NULL, " \t\r\n\a");
  }
  tokens[pos_t] = NULL;
  toke_c=pos_t;

  return tokens;
}


void lsh_loop(void)
{
  int argc;
  char *line;
  char **args;
  int status;

  do {
    printf(">> ");
    line = lsh_lread();
    args = lsh_tokenizer(line);
    status = lsh_exec(argc,args);
    free(line);
    free(args);
  } while (status);
}


int main(int argc, char **argv)
{
  lsh_loop();

  return 0;
}

1 个答案:

答案 0 :(得分:1)

  

我应该如何理解tcsetpgrp()和signal(sigttou,handler)一起工作的方式?

出于您的目的,他们不会。您不需要向进程发送信号以使其进程组成为前台pgroup(但请参见下文)。事实上,我不明白为什么你会故意将SIGTTOU发送给你正试图放在前台的进程组。

这里是POSIX's documentation for tcsetpgrp()的核心部分(强调添加):

  

如果进程具有控制终端,tcsetpgrp()应将与终端关联的前台进程组ID设置为pgid_id。应用程序应确保与fildes相关联的文件是调用进程的控制终端,并且控制终端当前与调用进程的会话相关联。应用程序应确保pgid_id的值与调用进程在同一会话中的进程的进程组ID匹配。

     

尝试在与其控制终端关联的tcsetpgrp()上使用来自后台进程组成员的进程中的fildes 将导致进程组被发送一个SIGTTOU信号。 [...]

您正在谈论实施fg命令。此类命令的主要用途是交互式执行,如果进程(您的shell)以交互方式接收该命令,则它必须位于前台进程组中,因为这是唯一一个从终端接收输入的进程组。然后,假设这样一个过程调用该函数,并且参数满足它们各自的要求,效果是&#34; tcsetpgrp()将与终端关联的前台进程组ID设置为pgid_id &#34;或者当然失败了。没有记录信号与此相符。

仅当SIGTTOU后台进程组中的进程调用时,

tcsetpgrp()才会显示此图片。如果我实现了一个shell,我倾向于禁用在后台运行的shell的作业控制(该命令会因错误而失败)。此信号的默认处理程序将停止该进程(与终止它不同);这适用于尝试写入其会话控制终端的后台进程。同样,SIGTTIN默认会停止进程,并传递给尝试从其会话的控制终端读取的后台进程。

对于fg命令,您不希望或不想处理SIGTTOUSIGTTIN ,但这并不意味着您不能使用&#39 ; t需要发信号 。相反,调用tcsetpgrp()的(最初的前景)流程应该在新的forground pgroup中发送 SIGCONT ,以防部分或全部这些进程停止,很可能是这样的。此信号的默认处理程序会在程序停止时恢复该过程,这正是您想要的。

简而言之,您可能根本不需要编写任何自定义信号处理程序。