システムコンソールからログインする際の流れ
例として、システムコンソールに「login:」と表示され、実際に bash が起動するまでの流れは以下のようになっている。
- login: プロンプトを表示し、ユーザー名の入力を受け付ける処理を mingetty プロセス が行う
- ユーザー名を受け付けた migetty プロセスは login プロセスを exec() する
- login プロセスがパスワードによりユーザーを認証したのち、プロセスを fork() する
- fork () された子プロセスは bash プロセスを exec() する
図に表すとこんな感じ。
要するに、
exec(): PID は変えずにプロセスのみを変える
fork(): 異なる PID で同一のプロセスを作成する
実際に、mingetty.c を見てみる。
mingetty.c
while ((logname = get_logname ()) == 0);
execl (_PATH_LOGIN, _PATH_LOGIN, "--", logname, NULL);
error ("%s: can't exec " _PATH_LOGIN ": %s", tty, sys_errlist[errno]);
exit (0);
execl() で exec() システムコールを用いて、PATHLOGIN_ で指定されるプログラムを実行している。 次は、login.c を見てみよう。
login.c
static void fork_session(struct login_context *cxt) {
//...中略...
child_pid = fork();
//親プロセスの child_pid には子プロセスの PID が入る
//子プロセスの child_pid には 0 が入る
if (child_pid < 0) { warn(_("fork failed")); pam_setcred(cxt->pamh, PAM_DELETE_CRED);
pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
sleepexit(EXIT_FAILURE);
}
if (child_pid) {
/*
* parent - wait for child to finish, then clean up session
*/
close(0);
close(1);
close(2);
sa.sa_handler = SIG_IGN;
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
/* wait as long as any child is there */
//子プロセスが終了するのを待って、自分も終了する
while (wait(NULL) == -1 && errno == EINTR) ;
openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
pam_setcred(cxt->pamh, PAM_DELETE_CRED);
pam_end(cxt->pamh, pam_close_session(cxt->pamh, 0));
exit(EXIT_SUCCESS);
}
//子プロセスの実行
/*
* child
*/
sigaction(SIGHUP, &oldsa_hup, NULL); /* restore old state */
sigaction(SIGTERM, &oldsa_term, NULL);
if (got_sig)
exit(EXIT_FAILURE);
/*
* Problem: if the user's shell is a shell like ash that doesn't do
* setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
* process in the pgrp, will kill us.
*/
/* start new session */
setsid();
/* make sure we have a controlling tty */
open_tty(cxt->tty_path);
openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */
/*
* TIOCSCTTY: steal tty from other process group.
*/
if (ioctl(0, TIOCSCTTY, 1))
syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
signal(SIGINT, SIG_DFL);
}
//...中略...
int main(int argc, char **argv) {
//...中略...
fork_session(&cxt);
//...中略...
//子プロセスは exec で bash に変化する
execvp(childArgv[0], childArgv + 1);
}
fork() でプロセスを複製し、子プロセスを exec() で bash に変化させている。