Skip to content
hannkim edited this page Jan 10, 2023 · 5 revisions

Blackhole-Shell

1. Shell & Terminal

1-1. Terminal

  • ํ…์ŠคํŠธ ์ž…์ถœ๋ ฅ ํ™˜๊ฒฝ, CLI (Command Line Interface)

  • ๋ช…๋ น์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์—†๋Š” ๊ทธ๋ƒฅ ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

  • ํ„ฐ๋ฏธ๋„์˜ ํ˜•ํƒœ๋กœ ํ•˜๋“œ์›จ์–ด์™€ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์žˆ๋‹ค. minishell ์˜ ๊ฒฝ์šฐ ์†Œํ”„ํŠธ์›จ์–ด๊ฐœ๋…์˜ ํ„ฐ๋ฏธ๋„์ด๋‹ค.

  • ํ„ฐ๋ฏธ๋„์€ ๊ทธ๋ž˜ํ”ฝ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ‘œ์‹œํ•˜๊ณ  ์‰˜๊ณผ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด๋‹ค.

  • ํ„ฐ๋ฏธ๋„์€ ํ…์ŠคํŠธ ์ž…๋ ฅ ๋ฐ ์ถœ๋ ฅ ํ™˜๊ฒฝ์ด๋‹ค.

    ๐Ÿ’ก ํ•ต์‹ฌ์€ ๊ทธ๋ƒฅ ํ™˜๊ฒฝ์ด๋‹ค! ์ปดํ“จํ„ฐ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ  ๋ช…๋ น์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์‰˜์˜ ์—ญํ• ์ด๋‹ค.

1-2. Shell

  • ์ปดํ“จํ„ฐ์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ
  • ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ์„ ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ์˜ค๋„๋ก redirection
  • ์ปค๋งจ๋“œ๋ผ์ธ ์ธํ„ฐํŽ˜์ด์Šค(CLI)๋กœ ๊ตฌํ˜„๋œ ํ”„๋กœ๊ทธ๋žจ
  • ์ปดํ“จํ„ฐ์— ๋ช…๋ น์„ ๋‚ด๋ฆฌ๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์—ญํ• 

2. Parsing

2-1. Parsing Structure

minishell command_line Execute Flow

  • command_line โ†’ tokenize โ†’ syntax check โ†’ AST parsing tree โ†’ execute_command_line(AST)

  • Execute Flow Image

    1

  • option1 ) Parsing Linked List
    • Parsing Linked List
  • option2 ) AST Parsing Tree (์ถ”์ƒ๊ตฌ๋ฌธํŠธ๋ฆฌ)
    • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ์ฝ๊ณ  ํ•ด์„ํ• ๋•Œ ์ฃผ๋กœ ์“ฐ์ž„
    • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด ๋ฌธ๋ฒ•์— ๋”ฐ๋ผ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํŠธ๋ฆฌ ์ž๋ฃŒ๊ตฌ์กฐ ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌ
    • ์ถ”์ƒ๊ตฌ๋ฌธํŠธ๋ฆฌ๋กœ ํŒŒ์‹ฑ์„ ํ•˜๋ฉด ๋‚˜์ค‘์— ์†Œ์Šค ์ฝ”๋“œ์˜ ์ˆ˜์ • ๋ฐ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํ•  ๋•Œ ๋” ํŽธ๋ฆฌํ•  ๊ฒƒ์ด๋ผ ํŒ๋‹จํ•˜์—ฌ ์„ ํƒ
    • ๊ฐ€๋…์„ฑ์ด ์ข‹์€ ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Œ
    • But, ์ •์˜ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฌธ๋ฒ•์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์„ ๋•Œ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Œ

2-2. AST Structure

2-2-1. AST ๋ž€?

๐Ÿ’ก ์ถ”์ƒ ๊ตฌ๋ฌธ ํŠธ๋ฆฌ๋ž€ ์ž…๋ ฅ ์ŠคํŠธ๋ง์˜ ๊ตฌ๋ฌธ ๊ตฌ์กฐ๋ฅผ ์ถ”์ƒ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ํŠธ๋ฆฌ์ด๋‹ค. ์œ ๋„ํŠธ๋ฆฌ๋ณด๋‹ค ๋” ์ถ”์ƒ์ ์ธ ํŠธ๋ฆฌ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.
  • ์—ฐ์‚ฐ์„ ์ค‘์‹ฌ์œผ๋กœ ์š”์•ฝํ•ด์„œ ํ‘œํ˜„ํ•œ๋‹ค
  • AST ๊ตฌ์กฐ ์ƒ๊ฐํ•œ ํ”์ .. ( โœ๏ธ ๋„์ ..) b2

2-2-2. Blackhole-Shell EBNF

What is EBNF?

๐Ÿ’ก ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ๊ตฌ๋ฌธ์„ ๊ธฐ์ˆ ํ•˜๋Š” ๋ฐ ๋งค์šฐ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ‘œ๊ธฐ๋ฐฉ๋ฒ• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด ๋ฌธ๋ฒ• ์ •์˜ ๋ฐฉ๋ฒ•๋ก 
  • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์˜ ๊ตฌ๋ฌธ์„ ๊ธฐ์ˆ ํ•˜๋Š”๋ฐ ๋งค์šฐ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ‘œ๊ธฐ๋ฒ•
  • ๊ธฐ์กด์˜ BNF(Backus-Naur Form)์—์„œ ๋” ๋ฐœ์ „๋œ ํ‘œ๊ธฐ๋ฒ•
  • ๊ธฐ์กด์˜ ์žฌ๊ท€์  ์ •์˜์—์„œ ๋ฐ˜๋ณต์  ์ •์˜๋กœ ๋” ๊ฐ„ํŽธํ•˜๊ฒŒ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ํ•จ
  • {} : 0๋ฒˆ ์ด์ƒ ๋ฐ˜๋ณต
  • [ ]: 0๋ฒˆ ํ˜น์€ 1๋ฒˆ ๋ฐ˜๋ณต (option)
  • ๋งˆ์ง€๋ง‰์ด ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์€ ๊ฒƒ์ด๋‹ค

Blackhole-Shell EBNF

<program>        -> { <pipe_line> }
<pipe_line>      -> <command> { '|' <pipe_line> }
<command>        -> [ <simple_command> ][  <redirects> ]
<simple_command> -> <exec_path> { <argv> }
<redirects>      -> <io_redirect> { <redirects> }
<io_redirect>    -> <redirect_op> <file_path>

<redirect_op>    -> '>' | '>>' | '<' | '<<'
<exec_path>      -> word | "word" | 'word'
<file_path>      -> word | "word" | 'word'
<argv>           -> word | "word" | 'word'

2-3. Blackhole-Shell Parser

b3

blackhole-shell parse-execute flow diagram

2-3-1. Token

typedef enum e_type
{
	T_NULL,
	T_WORD,
	T_REDIRECT,
	T_PIPE
}				t_type;

typedef struct s_token
{
	t_type		type;
	char		*value;
}				t_token;
  • T_NULL T_WORD T_REDIRECT T_PIPE ์œผ๋กœ ๊ตฌ๋ถ„๋˜๊ณ  value ์— ๋‹ด์•„์„œ ์‚ฌ์šฉ
  • ํ† ํฐ์˜ ๋ชฉ์  ๋ณ„๋กœ ๊ตฌ๋ถ„ํ•จ

2-3-2. Lexical Analayzer

์ž…๋ ฅ ์ŠคํŠธ๋ง์„ ์ฝ์–ด์„œ ํ† ํฐ ํ˜•ํƒœ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ฐ˜ํ™˜ (๊ตฌ๋ฌธ ๋ถ„ํ•ด ๋ฐ ์˜ฌ๋ฐ”๋ฅธ ๊ตฌ๋ฌธ ํ˜•ํƒœ(ํ† ํฐ์ด) ์ผ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์—ญํ• )

  • get_token ํ•จ์ˆ˜๋กœ AST ํŒŒ์‹ฑ์„ ํ•˜๋ฉด์„œ Syntax Check ์™€ ํ•จ๊ป˜ value ๋ฅผ ๋ฐ˜ํ™˜
  • match โ†’ fetch_token โ†’ get_token โ†’ lexer

match(token)

char	*match(t_type type)
{
	t_token	token;

	token = fetch_token(UPDATE);
	if (token.type == type)
		return (token.value);
	else
		throw_error_syntax(token);
	return (NULL);
}

fetch_token(fetch_type)

  • GET : ํ˜„์žฌ ํ† ํฐ value ๋ฐ˜ํ™˜
  • UPDATE : ํ† ํฐ ์—…๋ฐ์ดํŠธ ๋ฐ ์ด์ „ ํ† ํฐ value ๋ฐ˜ํ™˜
t_token	fetch_token(t_fetch_type type)
{
	static t_token	token;
	t_token			prev_token;

	prev_token = token;
	if (type == GET)
		return (token);
	else if (type == UPDATE)
		token = get_token();
	return (prev_token);
}

get_token

  • ๋‹ค์Œ ํ† ํฐ ๋ฐ˜ํ™˜
t_token	get_token(void)
{
	char	*begin;
	char	*end;
	t_token	token;
	int		flag;

	token.type = T_NULL;
	token.value = NULL;
	flag = lexical_analyzer(&token, &begin, &end);
	if (g_manager.rc >= (int)ft_strlen(g_manager.command_line))
		return (token);
	if (flag == ERROR_FLAG)
		g_manager.exit_code = EXIT_SYNTAXERR;
	token.value = bs_calloc(end - begin + 1, sizeof(char));
	if (!token.value)
		return (token);
	if (!ft_strlcpy(token.value, begin, end - begin + 1))
		return (token);
	g_manager.rc += end - begin;
	return (token);
}

2-3-2. Syntax Analyzer

์ž…๋ ฅ ์ŠคํŠธ๋ง์„ (์žฌ๊ท€ ํ•˜๊ฐ•) ํŒŒ์‹ฑ, ํ•ด๋‹น ์ž…๋ ฅ์˜ AST ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜

  • ์žฌ๊ท€์ ์œผ๋กœ token ์„ Tree ๊ตฌ์กฐ๋กœ ํŒŒ์‹ฑํ•˜๋ฉด์„œ AST ์ƒ์„ฑ
  • token ์„ ์ฝ๊ณ , ํ•ด๋‹น ๋ฌธ๋ฒ•์— ๋งž๋Š”์ง€ Syntax Error ํ™•์ธ

2-3-3. Execute

AST ๋ฅผ ์ฝ์œผ๋ฉด์„œ ํ•ด์„ ๋ฐ ์‹คํ–‰

  • Heredoc ์‹คํ–‰
    • ํŒŒ์‹ฑ๋œ AST ์— ๋”ฐ๋ผ heredoc ๋จผ์ € ์‹คํ–‰
  • Redirection ํ•ด์„ ๋ฐ ์‹คํ–‰ํ•ด์„
    • redirect_op ์ฒดํฌ
    • in out heredoc append ์‹คํ–‰
  • Pipe ํ•ด์„ ๋ฐ ์‹คํ–‰
    • U_PIPE ๊ฐฏ์ˆ˜์— ๋”ฐ๋ผ ์ž์‹ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
  • Command ๊ฐฏ์ˆ˜
    • single_command ์ผ ๋•Œ
      • single_command - builtin
      • single_command - general
    • multi_command ์ผ ๋•Œ
      • fork() ๋กœ ์ž์‹ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰

3. Redirection

  • < infile : ์กด์žฌํ•˜๋Š” infile์„ ์ž…๋ ฅ
  • > outfile : ๊ฒฐ๊ณผ๋ฅผ outfile๋กœ ์ถœ๋ ฅ
  • << heredoc : ์ž…๋ ฅ๋ฐ›์€ ๋ฌธ์ž์—ด์„ infile๋กœ ๋ฐ›๊ณ  end_text ๋˜๋Š” EOF๋กœ ์ž…๋ ฅ ์ค‘๋‹จ
  • >> append : ๊ฒฐ๊ณผ๋ฅผ outfile์— ์ด์–ด์“ฐ๊ธฐ

4. Signal

4-1. Signal์ด๋ž€

  • ํ”„๋กœ์„ธ์Šค๊ฐ„์˜ ๋น„๋™๊ธฐ์ ์ธ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•
  • Software Interrupt
  • standard signals
    • (2) SIGINT : ctrl-c
      • Action : Term
    • (3) SIGQUIT : ctrl-\
      • Action : Core
      • ์ฝ”์–ด ๋คํ”„ : ํŠน์ • ์‹œ์ ์— ์ฝ”์–ด ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์ข…๋ฃŒํ•จ. ์ฝ”์–ด ํŒŒ์ผ์ด๋ž€ ๋น„์ •์ƒ์ ์œผ๋กœ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋˜๋Š” ๊ฒฝ์šฐ, ํ”„๋กœ์„ธ์Šค์˜ ์˜์—ญ์„ ์ฝ”์–ด ํŒŒ์ผ์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ ์œ„์น˜์— ์ €์žฅํ•˜๋Š” ํŒŒ์ผ
  • bash์—์„œ์˜ signal ๋™์ž‘
    • SIGINT : print a new propmt on a newline
    • SIGQUIT : do nothing
  • ์‹œ๊ทธ๋„์€ ์•„๋‹ˆ์ง€๋งŒ ์ถ”๊ฐ€๋กœ, EOF ctrl-d ๋ฅผ ๋งŒ๋‚˜๋ฉด exit the shell

4-2. signal ํ•จ์ˆ˜

#include <signal.h>

void (*signal(int signo, void (*func)(int)))(int);
  • signo : ์‹œ๊ทธ๋„์˜ ์ด๋ฆ„
  • func(int signo) : signal handler ํ•จ์ˆ˜
    • SIG_IGN : ์‹œ๊ทธ๋„ ๋ฌด์‹œ
    • SIG_DFL : ๋””ํดํŠธ ์‹œ๊ทธ๋„ ์‚ฌ์šฉ
    • ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜

4-3. Process์™€ Signal

4-3-1. fork()

  • create a child process
  • pid๊ฐ€ ๋‹ค๋ฅธ ์ƒˆ๋กœ์šด ํ”„๋กœ์„ธ์Šค๋ฅผ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹น
  • ํ˜ธ์ถœํ•œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒˆ๋กœ์šด ๊ณต๊ฐ„์œผ๋กœ ๋ชจ๋‘ ๋ณต์‚ฌํ•˜๋ฉฐ ๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค์™€ ์ž์‹ ํ”„๋กœ์„ธ์Šค๋Š” ๊ฐ์ž ์ง„ํ–‰

4-3-2. execve()

  • execute program
  • ํ˜„์žฌ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์ƒˆ๋กœ ์ดˆ๊ธฐํ™”๋œ stack, heap, data segments์™€ ํ•จ๊ป˜ ์ƒˆ๋กœ์šด ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๋Œ€์ฒด๋จ

4-3-3. fork์™€ execve์—์„œ signal ๋™์ž‘

  • fork๋Š” ๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค์˜ signal handler๋ฅผ ๋ณต์‚ฌํ•ด์„œ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
  • execve๋Š” signal handler๊ฐ€ default๋กœ reset ๋จ. ํ•˜์ง€๋งŒ Ignored ์ƒํƒœ์˜€๋‹ค๋ฉด ๋ณ€ํ•˜์ง€ ์•Š์Œ.

A child created via fork(2) inherits a copy of its parentโ€™s signal dispositions. During an execve(2), the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged.

The dispositions of any signals that are being caught are reset to the default (signal(7)).

POSIX.1 specifies that the dispositions of any signals that are ignored or set to the default are left unchanged.

signal

signal(7) - Linux manual page

execve(2) - Linux manual page

4-4. Bash์—์„œ signal ๋™์ž‘

4-4-1. ๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค์—์„œ์˜ Signal

  • SIGINT (ctrl+c)
    • ๊ฐœํ–‰(\n) ์ถœ๋ ฅ (์ƒˆ prompt๋ฅผ ์•„๋ž˜ ์ค„์— ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•จ)
    • ๋ฒ„ํผ ๋น„์šฐ๊ธฐ โ†’ rl_replace_line()
    • ์ƒˆ prompt ๋„์šฐ๊ธฐ โ†’ rl_on_new_line(), rl_redisplay()
  • SIGQUIT (ctrl+) : do nothing โ†’ SIG_IGN ์‚ฌ์šฉ
  • init_signal.c ์ฐธ๊ณ 

4-4-2. ์ž์‹ ํ”„๋กœ์„ธ์Šค์—์„œ์˜ Signal

  • SIGINT (ctrl+c)
    • execve ์—์„œ SIG_DFL default ๋™์ž‘
    • ์ฃผ์˜! ๋Œ€๊ธฐ์ค‘์ธ ๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค๋Š” ์ข…๋ฃŒ๋˜๋ฉด ์•ˆ ๋จ
  • SIGQUIT (ctrl+)
    • execve์—์„œ SIG_DFL default ๋™์ž‘
    • ์ฃผ์˜! ๋Œ€๊ธฐ์ค‘์ธ ๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค๋Š” ์ข…๋ฃŒ๋˜๋ฉด ์•ˆ๋จ

5. Bash Built-in commands

  • bash์— ๋‚ด์žฅ๋œ ๋ช…๋ น์–ด๋กœ ์‹คํ–‰ํŒŒ์ผ์ด ๋”ฐ๋กœ ์—†์„ ์ˆ˜ ์žˆ๋‹ค

  • type ์œผ๋กœ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค

    • builtin์ธ ๊ฒฝ์šฐ [command] is a shell builtin
  • simple command์˜ ์ฒซ๋ฒˆ์งธ word๋กœ builtin์„ ์‚ฌ์šฉํ•˜๋ฉด, ์‰˜์€ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์„ invokingํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์‹คํ–‰ํ•จ

    Builtin commands are contained within the shell itself. When the name of a builtin command is used as the first word of a simple command (seeย Simple Commands), the shell executes the command directly, without invoking another program. Builtin commands are necessary to implement functionality impossible or inconvenient to obtain with separate utilities.

    (GNU bash ๊ณต์‹๋ฌธ์„œ)

    • simple commands โ†’ Blackhole-shell EBNF ์ฐธ๊ณ 
  • builtin command๊ฐ€ simple command์ผ ๊ฒฝ์šฐ, builtin command๊ฐ€ simple command๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, general command์ธ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ํ”„๋กœ์„ธ์Šค fork ์œ ๋ฌด๊ฐ€ ๋‹ฌ๋ผ์ง€๊ณ , ๊ทธ์— ๋”ฐ๋ฅธ signal ์ฒ˜๋ฆฌ๋„ ๋‹ฌ๋ผ์ง„๋‹ค.

  • ์ง์ ‘ ๊ตฌํ˜„ํ•œ builtin commands

    => echo, cd, pwd, export, unset, env, exit