我正在制作自己的外壳原因为什么不是。 当您运行命令并以&结束时,该过程将在后台运行,因此我想制作一个fg命令,您可以使用该命令将后台进程置于前台。
我在制作fg功能时遇到了一些麻烦。 如果我理解正确,将signal()放在子进程中将让子进程接收信号。 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;
}
答案 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
命令,您不希望或不想处理SIGTTOU
或SIGTTIN
, ,但这并不意味着您不能使用&#39 ; t需要发信号 。相反,调用tcsetpgrp()
的(最初的前景)流程应该在新的forground pgroup中发送 SIGCONT
,以防部分或全部这些进程停止,很可能是这样的。此信号的默认处理程序会在程序停止时恢复该过程,这正是您想要的。
简而言之,您可能根本不需要编写任何自定义信号处理程序。