by shigemk2

当面は技術的なことしか書かない

hexdump

xオプションをつけると2バイト単位の16進数4桁で出力するけども、リトルエンディアンです。

$ hexdump -x a.out
0000000    5653    4852    89c2    bbc3    bec3    c303    03be    bec3
0000010    c303    03be    bec3    c303    03be    bec3    c303    03be
0000020    bec3    c203    3b80    0f00    84c2    001d    0000    c348
0000030    c3bf    c383    03be    bec3    c303    03be    bec3    c303
0000040    03be    bec3    c303    03be    bec3    4803    bfc3    8bc3
0000050    bec3    c30b    c3a9    c39a    c3bf    c3bf    48bf    bfc3
0000060    83c3    c20f    3bb6    bfc3    2454    4808    83c2    84c3
0000070    5b10    83c3                                                
0000074

ndisasmとは

Solaris でハンドアセンブル (やっぱり Sun がスキ!)

ndisasm はディスアセンブラです。ディスアセンブラは、バイナリの機械語命令を読み込んで、人間に理解し易いニーモニックに変換してくれます。ディスアセンブラを使用する事で、ハンドアセンブルが正しく出来ているかを確認する事が出来ます。 

Fedoraでインストール

sudo yum install -y nasm

klopen

前提

KL-11が何枚か刺せる DL-11も何枚か刺せる

本体---KL-11---端末 (出力先は画面ではなく紙テープ)

この端末のことをテレタイプ(teletype)端末という。端末は複数刺さるが、物理スロットが少ないからつなげるのには限界がある

マルチプレクサで複数つなげることも可

原理的にはレジスタのベースアドレスをKLADDRにマッピング

klopen

KL11端末のオープン処理を行うデバイスドライバ

/dev/kl0 /dev/kl1 /dev/kl2

デバイスファイルとttyが一対かどうかは不明

デバイス→4つのレジスタ

メモリの特定の番地にKL-11が割り当てられていて、その中に4つのレジスタがある

メモリマップドIO メモリマップドI/O - Wikipedia

レジスタのアドレスは微妙に歯抜けである

値がハードコーディングされているのでKL-11の増設のたびにコンパイルしてリビルドしている

dmr/kl.c

klopen(dev, flag)
{
    register char *addr;
    register struct tty *tp;

    if(dev.d_minor >= NKL11+NDL11) {
        u.u_error = ENXIO;
        return;
    }
    tp = &kl11[dev.d_minor];
    if (u.u_procp->p_ttyp == 0) {
        u.u_procp->p_ttyp = tp;
        tp->t_dev = dev;
    }
    /*
    * set up minor 0 to address KLADDR
    * set up minor 1 thru NKL11-1 to address from KLBASE
    * set up minor NKL11 on to address from DLBASE
    */
    /* 割り当てアドレスの計算 */
    /* このあたりの計算は地味に複雑 */
    addr = KLADDR + 8*dev.d_minor;
    if(dev.d_minor)
        addr =+ KLBASE-KLADDR-8;
    if(dev.d_minor >= NKL11)
        addr =+ DLBASE-KLBASE-8*NKL11+8;
    tp->t_addr = addr;
    /* フラグ管理とモード変換 */
    if ((tp->t_state&ISOPEN) == 0) {
        tp->t_state = ISOPEN|CARR_ON;
        tp->t_flags = XTABS|LCASE|ECHO|CRMOD;
        tp->t_erase = CERASE;
        tp->t_kill = CKILL;
    }
    /* レジスタの初期設定(実際の処理はここだけ) */
    addr->klrcsr =| IENABLE|DSRDY|RDRENB;
    addr->kltcsr =| IENABLE;
}

CR ポインタを行頭に戻す LF 改行

unixだとLFはCRも同時にやってくれる

誤字の場合は#に書き換える 行ごと間違えた場合は@をつける

gttyとstty

P399

カーネルランドにある端末の情報をユーザランドに書き込むのがgtty

逆にユーザランドにある端末の情報をカーネルランドに書き込むのがstty

書き込み先の指定はsgtty関数で行う(処理は丸投げ)

suwordはカーネル→ユーザ fuwordはユーザ→カーネル

dmr/tty.c

gtty

/*
 * The routine implementing the gtty system call.
 * Just call lower level routine and pass back values.
 */
gtty()
{
    /* gttyシステムコールで読み出すデータを指定する */
    /* ユーザランドのポインタだからupはわかるけど、vpのvってなんだ。vectorか */
    int v[3];
    register *up, *vp;

    vp = v;
    sgtty(vp);
    if (u.u_error)
        return;
    /* 端末情報を読み出すユーザ空間中のアドレス */
    up = u.u_arg[0];
    suword(up, *vp++);
    suword(++up, *vp++);
    suword(++up, *vp++);
}

dmr/tty.c

stty

/*
 * The routine implementing the stty system call.
 * Read in values and call lower level.
 */
stty()
{
    register int *up;

    /* 端末情報を読み出すユーザ空間中のアドレス */
    up = u.u_arg[0];
    u.u_arg[0] = fuword(up);
    u.u_arg[1] = fuword(++up);
    u.u_arg[2] = fuword(++up);
    sgtty(0);
}

uとは

sys/user.h

uはグロ変

/*
 * The user structure.
 * One allocated per process.
 * Contains all per process data
 * that doesn't need to be referenced
 * while the process is swapped.
 * The user block is USIZE*64 bytes
 * long; resides at virtual kernel
 * loc 140000; contains the system
 * stack per user; is cross referenced
 * with the proc structure for the
 * same process.
 */
struct user
{
    int    u_rsav[2];     /* save r5,r6 when exchanging stacks */
    int    u_fsav[25];        /* save fp registers */
                    /* rsav and fsav must be first in structure */
    char   u_segflg;       /* flag for IO; user or kernel space */
    char   u_error;        /* return error code */
    char   u_uid;          /* effective user id */
    char   u_gid;          /* effective group id */
    char   u_ruid;         /* real user id */
    char   u_rgid;         /* real group id */
    int    u_procp;        /* pointer to proc structure */
    char   *u_base;        /* base address for IO */
    char   *u_count;       /* bytes remaining for IO */
    char   *u_offset[2];      /* offset in file for IO */
    int    *u_cdir;        /* pointer to inode of current directory */
    char   u_dbuf[DIRSIZ];     /* current pathname component */
    char   *u_dirp;        /* current pointer to inode */
    struct {           /* current directory entry */
        int    u_ino;
        char   u_name[DIRSIZ];
    } u_dent;
    int    *u_pdir;        /* inode of parent directory of dirp */
    int    u_uisa[16];        /* prototype of segmentation addresses */
    int    u_uisd[16];        /* prototype of segmentation descriptors */
    int    u_ofile[NOFILE];    /* pointers to file structures of open files */
    int    u_arg[5];      /* arguments to current system call */
    int    u_tsize;        /* text size (*64) */
    int    u_dsize;        /* data size (*64) */
    int    u_ssize;        /* stack size (*64) */
    int    u_sep;          /* flag for I and D separation */
    int    u_qsav[2];     /* label variable for quits and interrupts */
    int    u_ssav[2];     /* label variable for swapping */
    int    u_signal[NSIG];     /* disposition of signals */
    int    u_utime;        /* this process user time */
    int    u_stime;        /* this process system time */
    int    u_cutime[2];       /* sum of childs' utimes */
    int    u_cstime[2];       /* sum of childs' stimes */
    int    *u_ar0;         /* address of users saved R0 */
    int    u_prof[4];     /* profile arguments */
    char   u_intflg;       /* catch intr from sys */
                    /* kernel stack per user
                    * extends from u + USIZE*64
                    * backward not to reach here
                    */
} u;

klclose

P396

KL11のクローズ処理を行う(wflushtty)

klclose(dev)
{
    register struct tty *tp;

    tp = &kl11[dev.d_minor];
    /* 実際の処理 */
    wflushtty(tp);
    /* 状態を保存する */
    tp->t_state = 0;
}

dmr/tty.c

他の処理をスリープしつつ、キューを空にする

/*
 * Wait for output to drain, then flush input waiting.
 */
wflushtty(atp)
struct tty *atp;
{
    register struct tty *tp;

    tp = atp;
    /* 他の処理を止める */
    spl5();
    while (tp->t_outq.c_cc) {
        tp->t_state =| ASLEEP;
        sleep(&tp->t_outq, TTOPRI);
    }
    /* 実際の処理 */
    flushtty(tp);
    spl0();
}

wflushttyの実際の処理

/*
 * flush all TTY queues
 */
flushtty(atp)
struct tty *atp;
{
    register struct tty *tp;
    register int sps;

    tp = atp;
    while (getc(&tp->t_canq) >= 0);
    while (getc(&tp->t_outq) >= 0);
    wakeup(&tp->t_rawq);
    wakeup(&tp->t_outq);
    sps = PS->integ;
    spl5();
    while (getc(&tp->t_rawq) >= 0);
    tp->t_delct = 0;
    PS->integ = sps;
}

sgtty

sgtty

dmr/tty.c

gttyとsttyの実際の処理

/*
 * Stuff common to stty and gtty.
 * Check legality and switch out to individual
 * device routine.
 * v  is 0 for stty; the parameters are taken from u.u_arg[].
 * c  is non-zero for gtty and is the place in which the device
 * routines place their information.
 */
sgtty(v)
int *v;
{
    register struct file *fp;
    register struct inode *ip;

    if ((fp = getf(u.u_ar0[R0])) == NULL)
        return;
    ip = fp->f_inode;
    if ((ip->i_mode&IFMT) != IFCHR) {
        u.u_error = ENOTTY;
        return;
    }
    /* 端末設定デバイスドライバを呼び出す */
    (*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v);
}

klsgtty

dmr/kl.c

klsgtty(dev, v)
int *v;
{
    register struct tty *tp;

    tp = &kl11[dev.d_minor];
    ttystty(tp, v);
}

ttystty

実際にtty構造体への書き込みと読み出しを行う dmr/tty.c

引数avでモードの切り替えを行っている

/*
 * Common code for gtty and stty functions on typewriters.
 * If v is non-zero then gtty is being done and information is
 * passed back therein;
 * if it is zero stty is being done and the input information is in the
 * u_arg array.
 */
ttystty(atp, av)
int *atp, *av;
{
    register  *tp, *v;

    tp = atp;
    if(v = av) {
        /* 重要な処理はココ */
        *v++ = tp->t_speeds;
        v->lobyte = tp->t_erase;
        v->hibyte = tp->t_kill;
        v[1] = tp->t_flags;
        /* 戻り値にあまり意味は無い */
        return(1);
    }
    wflushtty(tp);
    v = u.u_arg;
    /* 重要な処理はココ */
    tp->t_speeds = *v++;
    tp->t_erase = v->lobyte;
    tp->t_kill = v->hibyte;
    tp->t_flags = v[1];
    return(0);
}

端末からの文字入力

概要

P403

  1. 1文字入力したら割り込みが発生
  2. 変換した値をtty.t_rawqに追加

割り込みのハンドラ

マイナー番号に対応するtty構造体をkl11[]から取得し、kl11の入力レジスタから入力された値を取得する

受信可能状態にしたあとにttyinputを実行

dmr/kl.c

klrint(dev)
{
    register int c, *addr;
    register struct tty *tp;

    tp = &kl11[dev.d_minor];
    addr = tp->t_addr;
    c = addr->klrbuf;
    addr->klrcsr =| RDRENB;
    /* 受信したデータが空だったら、送信用データレジスタに受信結果を格納 */
    if ((c&0177)==0)
        addr->kltbuf = c;    /* hardware botch */
    ttyinput(c, tp);
}

ttyinput

tty.t_rawqにデータを追加する

データの変換が発生するので、生データは送り込まない

データの変換ルール

  • CRの書き換え
  • FS DELが入力されたらシグナルを送る
  • 大文字を小文字に変換

などなど。

ttyinput実際の処理

/*
 * Place a character on raw TTY input queue, putting in delimiters
 * and waking up top half as needed.
 * Also echo if required.
 * The arguments are the character and the appropriate
 * tty structure.
 */
ttyinput(ac, atp)
struct tty *atp;
{
    register int t_flags, c;
    register struct tty *tp;

    tp = atp;
    c = ac;
    t_flags = tp->t_flags;
    if ((c =& 0177) == '\r' && t_flags&CRMOD)
        c = '\n';
    /* シグナルの送信 */
    if ((t_flags&RAW)==0 && (c==CQUIT || c==CINTR)) {
        signal(tp, c==CINTR? SIGINT:SIGQIT);
        flushtty(tp);
        return;
    }
    /* キューがたまりすぎてたらキューを空にして処理を終了 */
    if (tp->t_rawq.c_cc>=TTYHOG) {
        flushtty(tp);
        return;
    }
    /* 大文字→小文字変換 */
    if (t_flags&LCASE && c>='A' && c<='Z')
        c =+ 'a'-'A';
    /* メイン処理 t_rawqへのデータ追加 */
    /* なお、putcはアセンブラ */
    /* putcのアセンブラは長いので省略 */
    putc(c, &tp->t_rawq);
    /* デリミタ処理 */
    if (t_flags&RAW || c=='\n' || c==004) {
        wakeup(&tp->t_rawq);
        if (putc(0377, &tp->t_rawq)==0)
            tp->t_delct++;
    }
    /* ECHOモードの場合の処理 */
    if (t_flags&ECHO) {
        ttyoutput(c, tp);
        ttstart(tp);
    }
}

割り込みが終わったらスケジューラ

入力されたデータの読み込み

入力されたデータは以下の流れでユーザ空間に読み込まれる

  1. ユーザプログラムが端末に対してreadシステムコールを発行
  2. tty.t_rawqからデータを読み出し、tty.t_canqへデータを移す
  3. tty.t_canqのデータをユーザ空間に呼び出す

klread(データをユーザ空間に読み込む)→ttread(tty.t_canqからユーザ空間にデータを送り込む)

/*
 * Called from device's read routine after it has
 * calculated the tty-structure given as argument.
 * The pc is backed up for the duration of this call.
 * In case of a caught interrupt, an RTI will re-execute.
 */
ttread(atp)
struct tty *atp;
{
    register struct tty *tp;

    tp = atp;
    if ((tp->t_state&CARR_ON)==0)
        return;
    /* passcを使ってtty.t_canqのデータをユーザ空間に送り込む */
    if (tp->t_canq.c_cc || canon(tp))
        while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0);
}

canon

tty.t_rawqからtty.t_canqへデータを送り込む tty.t_rawqにデータが追加されたら1が変える

/*
 * transfer raw input list to canonical list,
 * doing erase-kill processing and handling escapes.
 * It waits until a full line has been typed in cooked mode,
 * or until any character has been typed in raw mode.
 */
canon(atp)
struct tty *atp;
{
    register char *bp;
    char *bp1;
    register struct tty *tp;
    register int c;

    tp = atp;
    spl5();
    while (tp->t_delct==0) {
        if ((tp->t_state&CARR_ON)==0)
            return(0);
        sleep(&tp->t_rawq, TTIPRI);
    }
    spl0();
loop:
    bp = &canonb[2];
    while ((c=getc(&tp->t_rawq)) >= 0) {
        if (c==0377) {
            tp->t_delct--;
            break;
        }
        if ((tp->t_flags&RAW)==0) {
            if (bp[-1]!='\\') {
                if (c==tp->t_erase) {
                    if (bp > &canonb[2])
                        bp--;
                    continue;
                }
                if (c==tp->t_kill)
                    goto loop;
                if (c==CEOT)
                    continue;
            } else
            if (maptab[c] && (maptab[c]==c || (tp->t_flags&LCASE))) {
                if (bp[-2] != '\\')
                    c = maptab[c];
                bp--;
            }
        }
        *bp++ = c;
        if (bp>=canonb+CANBSIZ)
            break;
    }
    bp1 = bp;
    bp = &canonb[2];
    c = &tp->t_canq;
    while (bp<bp1)
        putc(*bp++, c);
    return(1);
}

端末への出力

  1. ユーザプログラムが端末にwriteシステムコールを発行
  2. ユーザ空間のデータがtty.t_outqに追加
  3. tty.t_outqの先頭データをKL11レジスタに設定して端末の出力処理を開始
  4. 出力端末の動作が完了するとKL11が割り込みをかけて、割り込みハンドラはtty.t_outqにデータが残っていれば出力処理を継続

最上位ビットが1になっていたときはtimeoutを実行する

TTHIWAとTTLOWATの間でキューのデータ数を確認する

klwrite

ttwrite呼び出し

klwrite(dev)
{
    ttwrite(&kl11[dev.d_minor]);
}

ttwrite

端末への出力時にデバイスドライバから呼び出される端末共通処理関数 (データをttyoutputに丸投げする)

/*
 * Called from the device's write routine after it has
 * calculated the tty-structure given as argument.
 */
ttwrite(atp)
struct tty *atp;
{
    register struct tty *tp;
    register int c;

    tp = atp;
    if ((tp->t_state&CARR_ON)==0)
        return;
    while ((c=cpass())>=0) {
        spl5();
        /* 処理能力がレッドゾーンならすでに入っているものを出力しつつスリープ */
        while (tp->t_outq.c_cc > TTHIWAT) {
            ttstart(tp);
            tp->t_state =| ASLEEP;
            sleep(&tp->t_outq, TTOPRI);
        }
        spl0();
        /* 処理能力の範囲内ならデータをtty.t_outqに放り込む */
        ttyoutput(c, tp);
    }
    /* 端末への出力処理を開始する */
    ttstart(tp);
}

ttstart

端末への出力処理を行う

/*
 * Start output on the typewriter. It is used from the top half
 * after some characters have been put on the output queue,
 * from the interrupt routine to transmit the next
 * character, and after a timeout has finished.
 * If the SSTART bit is off for the tty the work is done here,
 * using the protocol of the single-line interfaces (KL, DL, DC);
 * otherwise the address word of the tty structure is
 * taken to be the name of the device-dependent startup routine.
 */
ttstart(atp)
struct tty *atp;
{
    register int *addr, c;
    register struct tty *tp;
    struct { int (*func)(); };

    tp = atp;
    addr = tp->t_addr;
    if (tp->t_state&SSTART) {
        (*addr.func)(tp);
        return;
        }
    /* 端末が送信処理中または送信遅延処理中なら終了 tttcsrは端末インターフェイスの送信ステータスレジスタを操作するために定義された構造体の要素 */
    if ((addr->tttcsr&DONE)==0 || tp->t_state&TIMEOUT)
        return;
    if ((c=getc(&tp->t_outq)) >= 0) {
        /* 文字を出力する処理 */
        if (c<=0177)
            addr->tttbuf = c | (partab[c]&0200);
        /* タイムアウト */
        else {
            timeout(ttrstrt, tp, c&0177);
            tp->t_state =| TIMEOUT;
        }
    }
}
/*
 * Restart typewriter output following a delay
 * timeout.
 * The name of the routine is passed to the timeout
 * subroutine and it is called during a clock interrupt.
 */
ttrstrt(atp)
{
    register struct tty *tp;

    tp = atp;
    tp->t_state =& ~TIMEOUT;
    ttstart(tp);
}

klxint

出力処理が完了したときに発生する割り込みハンドラ

klxint(dev)
{
    register struct tty *tp;

    tp = &kl11[dev.d_minor];
    ttstart(tp);
    if (tp->t_outq.c_cc == 0 || tp->t_outq.c_cc == TTLOWAT)
        wakeup(&tp->t_outq);
}

まとめ

  • カーネルはテレタイプ端末の処理をサポートする
  • システムのユーザはテレタイプ端末を使ってシステムとやりとりが出来る
  • システムコンソール用の端末が必ず1つ必要
  • 複数のユーザが複数の端末により同時にシステムを使うことが出来る
  • 各端末は3つのバッファキューを持ち、システムと端末間で適切に値を変更してデータをやりとりする

起動の流れ

P424

  1. ブートストラップローダプログラムがルートディスクのブロック番号0にあるブートストラッププログラムをメモリのアドレス0に読み込んで実行する
  2. ブートストラッププログラムはルートディスクのファイルシステムから/unixや/rkunixといったカーネルプログラム本体をメモリのアドレス0に読み込んで実行する
  3. カーネルがシステムの初期化を行う

だんだん実行対象が大きくなっているのは今と同じだが、同じ場所でブートを行っているので若干複雑

あと、全部アセンブリでごにょごにょしているので、結構複雑

start

MMUはまだ有効化されていなくて物理アドレスを直接扱っている状態

MMU

startが動いていろいろやる

まずやるのはカーネル空間の設定

APRに領域が割り当てられている

SR0を設定してMMUを有効化

startの処理はアセンブラ

.globl start, _end, _edata, _main
start:
    bit $1,SSR0
    bne start         / MMUが有効になっていたら処理を継続させない
    reset

/ initialize systems segments

    mov $KISA0,r0 / KISA0とKISD0はそれぞれカーネルAPR0のPARとPDRのアドレスをあらわす
    mov $KISD0,r1
    mov $200,r4
    clr r2
    mov $6,r3
1:
    mov r2,(r0)+
    mov $77406,(r1)+        / 4k rw
    add r4,r2
    sob r3,1b

/ カーネルAPR6の初期化

    mov $_end+63.,r2
    ash $-6,r2
    bic $!1777,r2
    mov r2,(r0)+        / ksr6 = sysu
    mov $usize-1\<8|6,(r1)+

/ カーネルAPR7の初期化
    mov $IO,(r0)+
    mov $77406,(r1)+        / rw 4k

/ スタックポインタの初期化とMMUの有効化
    mov $_u+[usize*64.],sp
    inc SSR0

/ BSS領域のクリア
    mov $_edata,r0
1:
    clr (r0)+
    cmp r0,$_end
    blo 1b

/ proc[0]のuser カーネルスタック領域を0クリア
    mov $_u,r0
1:
    clr (r0)+
    cmp r0,$_u+[usize*64.]
    blo 1b

/ main()の呼び出し
    mov $30000,PS
    jsr pc,_main
    mov $170000,-(sp)
    clr -(sp)
    rtt
APR PAR PDR
0 0 077406
1 0200
2 0400
3 0600
4 01000
5 01200
6 カーネルサイズによる 007406
7 07600 077406

各領域はがっちり振り分けられているわけではなく、 5と6で領域がラップしている(だぶっている)

main()

読んでもあまりおもしろくない、らしい

P430

ken/main.c

/*
 * Initialization code.
 * Called from m40.s or m45.s as
 * soon as a stack and segmentation
 * have been established.
 * Functions:
 * clear and free user core
 * find which clock is configured
 * hand craft 0th process
 * call all initialization routines
 * fork - process 0 to schedule
 *      - process 1 execute bootstrap
 *
 * panic: no clock -- neither clock responds
 * loop at loc 6 in user mode -- /etc/init
 * cannot be executed.
 */
main()
{
    extern schar;
    register i, *p;

    /*
    * メモリとスワップ領域の初期化
    */

    updlock = 0;
    i = *ka6 + USIZE;
    UISD->r[0] = 077406;
    for(;;) {
        UISA->r[0] = i;
        if(fuibyte(0) < 0)
            break;
        clearseg(i);
        /* メモリが1バイトずつ増えていくイメージ */
        maxmem++;
        /* 物理領域 */
        /* 空き領域が増えていく */
        mfree(coremap, 1, i);
        i++;
    }
    if(cputype == 70)
    /* メモリの領域がわかる */
    for(i=0; i<62; i=+2) {
        UBMAP->r[i] = i<<12;
        UBMAP->r[i+1] = 0;
    }
    printf("mem = %l\n", maxmem*5/16);
    printf("RESTRICTED RIGHTS\n\n");
    printf("Use, duplication or disclosure is subject to\n");
    printf("restrictions stated in Contract with Western\n");
    printf("Electric Company, Inc.\n");

    maxmem = min(maxmem, MAXMEM);
    /* スワップの領域確保 */
    mfree(swapmap, nswap, swplo);

    /*
    * クロック装置の初期化
    */

    UISA->r[7] = ka6[1]; /* io segment */
    UISD->r[7] = 077406;
    lks = CLOCK1;
    if(fuiword(lks) == -1) {
        lks = CLOCK2;
        if(fuiword(lks) == -1)
            panic("no clock");
    }

    /*
    * プロセス0(絶対に死なないプロセス)の生成
    */

    proc[0].p_addr = *ka6;
    proc[0].p_size = USIZE;
    proc[0].p_stat = SRUN;
    proc[0].p_flag =| SLOAD|SSYS;
    u.u_procp = &proc[0];

    /*
    * 資源の初期化
    */

    *lks = 0115;
    /* コンソールの初期化 */
    cinit();
    /* ブロックの初期化 */
    binit();
    /* スーパーブロックの初期化 */
    iinit();
    rootdir = iget(rootdev, ROOTINO);
    rootdir->i_flag =& ~ILOCK;
    u.u_cdir = iget(rootdev, ROOTINO);
    u.u_cdir->i_flag =& ~ILOCK;

    /*
    * make init process
    * enter scheduling loop
    * with system process
    */
    /* プロセス1の作成 */
    if(newproc()) {
        expand(USIZE+1);
        estabur(0, 1, 0, 0);
        copyout(icode, 0, sizeof icode);
        /*
        * Return goes to loc. 0 of user init
        * code just copied out.
        */
        return;
    }
    sched();
}

bssとスタックの間の隙間を増やしてメモリを確保する breakとexpand

$ find sys/ -type f | xargs grep "lks" -n
sys/dmr/dp.c:8: * device it talks to, which is to say most of the protocol
sys/dmr/hs.c:46:        register mblks;
sys/dmr/hs.c:49:        mblks = 1024; /* RJS03 */
sys/dmr/hs.c:51:                mblks = 2048; /* RJS04 */
sys/dmr/hs.c:52:        if(bp->b_blkno >= mblks) {
sys/ken/clock.c:35:     *lks = 0115;
sys/ken/main.c:90:      lks = CLOCK1;
sys/ken/main.c:91:      if(fuiword(lks) == -1) {
sys/ken/main.c:92:              lks = CLOCK2;
sys/ken/main.c:93:              if(fuiword(lks) == -1)
sys/ken/main.c:111:     *lks = 0115;
sys/systm.h:47:int      *lks;                   /* pointer to clock device */

クロックとは、定期的に上がってくる何か。PCの動きと同期しておらず、むしろ同期しないほうがいい

/etc/init

  1. 登録されている端末に対しプロセスを生成
  2. 各プロセスは端末のダイアルアップ接続、ユーザのログインを待つ。ログインするとシェルプログラムが起動
  3. /etc/initはその後無限ループに入り、宙に浮いたプロセスを処理し続ける

matome

  • proc[0]はシステムプロセス
  • proc[1]は/etc/initを実行する

経緯

Lions本は読みづらいから読みやすく書き直そう