SlideShare a Scribd company logo
ttwrite()
        スタート低レイヤー #6  #start_printf
                   @kusabanachi
ttwrite()
全体像
ttwrite()
引数
int
ttwrite(struct tty *tp, struct uio *uio, int flag)
tp: 対象の端末、出力先
uio: writeするデータのアドレス、サイズなど
flag: writeに関するフラグ
(追記、ブロック無し、同期、バッファリング無しなど)
接続が無い場合の処理
接続が無い場合の処理
loop:
    mutex_spin_enter(&tty_lock);
    if (!CONNECTED(tp)) {
        if (ISSET(tp­>t_state, TS_ISOPEN)) {
            mutex_spin_exit(&tty_lock);
            return (EIO);
        } else if (flag & IO_NDELAY) {
            mutex_spin_exit(&tty_lock);
            error = EWOULDBLOCK;
            goto out;
        } else {
            /* Sleep awaiting carrier. */
            error = ttysleep(tp, &tp­>t_rawcv, true, 0);
            mutex_spin_exit(&tty_lock);
            if (error)
                goto out;
            goto loop;
        }
    }
接続が無い場合の処理
TERMIOS(4) (全般的な端末の回線規則) より
端末デバイスファイルのオープン
端末ファイルがオープンされると、プロセスは通信の確立を待つ
ほとんどのハードウェアではCARRIERDETECT(CD)線で接続が分かる
接続が無い場合の処理
CONNECTED()
/*
 * まだ接続しているか判断するために使われる. いずれかの場合にtrueになる
 * 1) キャリアを持っている
 * 2) ローカルにアタッチされた端末なので、キャリアを無視する
 * 3) キャリア信号をオーバーロードするフロー制御機構を使っている
 */
#define CONNECTED(tp) (ISSET(tp­>t_state, TS_CARR_ON) || 
             ISSET(tp­>t_cflag, CLOCAL | MDMBUF))
#define ISSET(t, f) ((t) & (f))
 
#define TS_CARR_ON  0x00008     /* キャリアがある. */          <­­ 1)のケース
#define CLOCAL      0x00008000  /* モデムの状態線を無視する. */   <­­  2)
#define MDMBUF      0x00100000  /* DTR/DCD hardwareフロー制御 */ <­­  3)
接続が無い場合の処理
if (!CONNECTED(tp)) {
    if (ISSET(tp­>t_state, TS_ISOPEN)) {
        mutex_spin_exit(&tty_lock);
        return (EIO);
    } else if (...
#define TS_ISOPEN   0x00040     /* Openが完了している. */
 
#define EIO     5       /* 入出力エラー */
接続していないのに、オープン済みの場合はEIOのエラーで戻る
接続が無い場合の処理
if (!CONNECTED(tp)) {
...
    } else if (flag & IO_NDELAY) {
        mutex_spin_exit(&tty_lock);
        error = EWOULDBLOCK;
        goto out;
    } else ...
#define IO_NDELAY   0x00100     /* FNDELAYがファイルテーブルでセットされた */
#define FNDELAY     O_NONBLOCK  /* compat(互換用) */
#define O_NONBLOCK  0x00000004  /* no delay */
 
#define EWOULDBLOCK EAGAIN      /* 処理はブロックする */
接続していなくて、ディレイ無し指定のwriteの場合は、
(接続を待つ必要があるため)
EWOULDBLOCKのエラーでoutラベル(終了処理)へ。
接続が無い場合の処理
loop:
mutex_spin_enter(&tty_lock);
if (!CONNECTED(tp)) {
...
    } else {
        /* キャリアを待ってsleepする. */
        error = ttysleep(tp, &tp­>t_rawcv, true, 0);
        mutex_spin_exit(&tty_lock);
        if (error)
            goto out;
        goto loop;
    }
}
ttysleepでキャリアの検出を待つ
--> errorであった場合は、終了処理へ飛ぶ。
--> errorが無ければ、最初に戻って接続状態を調べる
struct tty {
...
    kcondvar_t t_rawcv;    /* 通知 */
接続が無い場合の処理
/*
 * チャンネル上でsleepし、寝ている間にttyが変化した場合はERESTARTを返す。また、
 * cv_timedwait(_sig)で報告されたエラーのいずれか(EINTR/EWOULDBLOCK)を返す。
 * ttyが取り消された場合は、保留中の呼び出しを再開すると、
 * 呼び出しの開始時に行われた検証をやり直す。
 *
 * tty lockを保持した状態で、呼び出すこと
 */
int
ttysleep(struct tty *tp, kcondvar_t *cv, bool catch, int timo)
{
    int error;
    short   gen;
 
    KASSERT(mutex_owned(&tty_lock)); /// <­ tty lockを保持していること
 
    gen = tp­>t_gen;                /// <­ 寝る前の世代を取得しておく
...
...                                 /// <­ sleep処理
...
    return (tp­>t_gen == gen ? 0 : ERESTART);
                                    /// <­ 世代が変わっていたら、ERESTART
}
接続が無い場合の処理
int
ttysleep(struct tty *tp, kcondvar_t *cv, bool catch, int timo)
{
...
    gen = tp­>t_gen;
    if (cv == NULL)
        error = kpause("ttypause", catch, timo, &tty_lock);
            /// condition variableがNULLの場合はkpauseで待つ
    else if (catch)
        error = cv_timedwait_sig(cv, &tty_lock, timo);
            /// catchがtrueの場合、signal付きのwait
    else
        error = cv_timedwait(cv, &tty_lock, timo);
            /// catchがfalseの場合、signal無しのwait
    if (error != 0)
        return (error);
            /// エラーがあればエラーで戻る
    return (tp­>t_gen == gen ? 0 : ERESTART);
            /// エラー無しでttyも変わってなければ、0で戻る
}
CONDVAR(9) より
ConditionVariables(CVs)はカーネル内でリソースへのアクセスを
同期するため、I/O操作の完了を待つために使われる。
kcondvar_t型はCVオブジェクトのストレージを提供する。
不透明なオブジェクトとして扱い、使用者に直接検査されるべきでない。
CONDVAR(9) より
cv_wait(cv, mtx)
LWPに割り込み不可なwaitをさせる。
LWPは他のスレッドのcv_signal()またはcv_broadcast()で実行再開する。
cv_wait()に入る時にmutexを持っていなければならない。
LWPがsleepの準備をした時点でmutexはリリースされ、cv_wait()から戻る前
に再取得される。
リソースが利用可能かのテストとcv_wait()でリソースを待つ間に、
リソースが利用可能にならないことをmutexは保証する。
割り込み不可のwaitはシステムをデッドロックする可能性があるので、
短い間(通常は、1秒未満)にしなければならない
CONDVAR(9) より
cv_wait_sig(cv, mtx)
cv_wait()と同様だが、LWPに割り込み可能なwaitをさせる。
signalの受信、またはLWPを含むプロセスの終了のようなの割り込みで
waitは早期に終了し、エラーコードを返す。
cv_timedwait(cv, mtx, ticks)
cv_wait()と同様だが、ticks引数で指定されたタイムアウト付き
ticksは秒間のクロック割り込みの数に関連する、アーキテクチャとシステ
ム依存値。
ticksがゼロの場合、cv_timedwait()はcv_wait()のように動作する。
cv_timedwait_sig(cv, mtx, ticks)
cv_wait_sig()と同様だが、タイムアウト付き
KPAUSE(9) より
kpause(const char *wmesg, bool intr, int timo, kmutex_t *mtx)
LWPをsleepさせる。
対応するcv_signal(9)の無いcv_timedwait_sig(9)に似ている。
kpause()は自分で起きる可能性があるので、
呼び出し元は対処しなければならない。
wmesg:  kpause()に関連するリソースや条件を表す文字列
intr:  trueなら、シグナル / 割り込み可能にsleepする
timeo: タイムアウト
mtx:  mutex
接続が無い場合の処理
/* キャリアを待ってsleepする. */
error = ttysleep(tp, &tp­>t_rawcv, true, 0);
int
ttysleep(struct tty *tp, kcondvar_t *cv, bool catch, int timo)
{
...
    gen = tp­>t_gen;
    if (cv == NULL)
        error = kpause("ttypause", catch, timo, &tty_lock);
            /// condition variableがNULLの場合はkpauseで待つ
    else if (catch)
        error = cv_timedwait_sig(cv, &tty_lock, timo);
            /// catchがtrueの場合、signal付きのwait
    else
        error = cv_timedwait(cv, &tty_lock, timo);
            /// catchがfalseの場合、signal無しのwait
...
ttwrite()からは
Condition Variable有り、割り込み可能、タイムアウト無しで
cv_timedwait_sig(&tp->t_rawcv, &tty_lock, 0) が呼ばれるだろう
接続が無い場合の処理 まとめ
loop:
   mutex_spin_enter(&tty_lock);
   if (!CONNECTED(tp)) {                 /// 接続しているかチェック
       if (ISSET(tp­>t_state, TS_ISOPEN)) {
           mutex_spin_exit(&tty_lock);
           return (EIO);                 /// オープン済みはエラー
       } else if (flag & IO_NDELAY) {
           mutex_spin_exit(&tty_lock);
           error = EWOULDBLOCK;
           goto out;                     /// ディレイ無し指定はエラー
       } else {
           /* キャリアを待ってsleepする. */
           error = ttysleep(tp, &tp­>t_rawcv, true, 0);
           mutex_spin_exit(&tty_lock);
           if (error)
               goto out;                 /// sleepの間にエラー
           goto loop;                    /// エラー0の場合、最初からリスタート
       }
   }
バックグラウンドの場合の処理
バックグラウンドの場合の処理
/*
 * プロセスがバックグラウンドの場合、ハングアップさせる
 */
p = curproc;
if (isbackground(p, tp) &&
    ISSET(tp­>t_lflag, TOSTOP) && (p­>p_lflag & PL_PPWAIT) == 0 &&
    !sigismasked(curlwp, SIGTTOU)) {
    if (p­>p_pgrp­>pg_jobc == 0) {
        error = EIO;
        mutex_spin_exit(&tty_lock);
        goto out;
    }
    mutex_spin_exit(&tty_lock);
 
    mutex_enter(proc_lock);
    pgsignal(p­>p_pgrp, SIGTTOU, 1);
    mutex_exit(proc_lock);
 
    mutex_spin_enter(&tty_lock);
    error = ttypause(tp, hz);
    mutex_spin_exit(&tty_lock);
    if (error)
        goto out;
    goto loop;
}
mutex_spin_exit(&tty_lock);
TERMIOS(4) より
Session
PGID xxx0 (bg)
login
PGID xxx2 (bg)
emacs
PGID xxx3 (bg)
emacs
PGID xxx4 (fg)
ps
PGID xxx1 (bg)
tail grep
pipe
tty
foreground
ログインシェルから生成されたプロセスは同じセッション
関連するプロセスは同じプロセスグループに置かれる
端末にはフォアグラウンドのプロセスグループIDが割り当てられている
特殊なケースを除いて、フォアグラウンドのプロセスグループだけが端
末のread/writeを許可される
TERMIOS(4) より
孤児になるプロセスグループ
同じセッション内で違うプロセスグループに親がいる、
というプロセスが1つもない
概念的に、プロセスグループ終了時に世話してくれる親がいない意味
キーボードからのストップシグナルやジョブ制御シグナルの影響を受け
ない
孤児になるプロセスグループ
同じセッション内で違うプロセスグループに親がいる、
というプロセスが1つもない
バックグラウンドの場合の処理
/* 端末tpはプロセスpの制御端末か? */
#define isctty(p, tp)                           
    ((p)­>p_session == (tp)­>t_session && (p)­>p_lflag & PL_CONTROLT)
    /// pとtpのセッションが同じ && pは制御端末を持つ
 
 
/* プロセスpは端末tpのバックグラウンドか? */
#define isbackground(p, tp)                     
    (isctty((p), (tp)) && (p)­>p_pgrp != (tp)­>t_pgrp)
    /// tpがpの制御端末 &&
    /// pのプロセスグループがtのフォアグラウンド プロセスグループではない
struct tty {
...
    struct  pgrp *t_pgrp;       /* フォアグラウンド プロセスグループ */
#define PL_CONTROLT 0x00000002 /* 制御端末を持つ */
バックグラウンドの場合の処理
p = curproc;
if (isbackground(p, tp) &&
    ISSET(tp­>t_lflag, TOSTOP) && (p­>p_lflag & PL_PPWAIT) == 0 &&
    !sigismasked(curlwp, SIGTTOU)) {
   /// pはバックグラウンド プロセスグループ &&
   /// TOSTOPがセットされている && 親が子のexec/exitを待っていない &&
   /// SIGTTOUシグナルをマスクしていない
TERMIOS(4) より
端末のアクセスコントロール
バックグラウンド プロセスグループのプロセスから制御端末へのwriteは
以下の特殊ケース以外はSIGTTOUシグナルが送信される
TOSTOPがセットされていない
プロセスがSIGTTOUシグナルを無視かブロックしている
writeするプロセスのプロセスグループが孤児の場合、
write(2)はEIOのerrnoと供に-1を返してシグナルは送信されない
#define PL_PPWAIT   0x00000010 /* 親は子のexec/exitを待っている */
バックグラウンドの場合の処理
p = curproc;
if (isbackground(p, tp) &&
    ISSET(tp­>t_lflag, TOSTOP) && (p­>p_lflag & PL_PPWAIT) == 0 &&
    !sigismasked(curlwp, SIGTTOU)) {
    if (p­>p_pgrp­>pg_jobc == 0) {
...
    }
...
struct pgrp {
...
    int     pg_jobc;    /* プロセスグループをジョブ制御の<br>
                             適任とするプロセスの数 */
のチェックは孤児のプロセスグループかのチェックのようだ
バックグラウンドの場合の処理
pg_jobcについて、カウントされている場所
fixjobc(struct proc *p, struct pgrp *pgrp, int entering)
{
...
    /*
     * pが自身のプロセスグループを適任とするか確認するため、pの親をチェックする
     * もしそうなら、pのプロセスグループのカウントを調整する
     */
    hispgrp = p­>p_pptr­>p_pgrp;     /// hispgrpは親のプロセスグループ
    if (hispgrp != pgrp && hispgrp­>pg_session == mysession) {
        /// 親と子のプロセスグループは異なり、セッションは同じ場合
        if (entering) {
        /// 対象プロセスが、プロセスグループに加わる場合
            pgrp­>pg_jobc++;               /// pg_jobcをインクリメント
            p­>p_lflag &= ~PL_ORPHANPG;
        } else if (­­pgrp­>pg_jobc == 0)  /// 出る場合はデクリメント
            orphanpg(pgrp);         /// 0に達したなら孤児のプロセスグループ
    }
pg_jobc == 0
バックグラウンドの場合の処理
p = curproc;
if (isbackground(p, tp) &&
    ISSET(tp­>t_lflag, TOSTOP) && (p­>p_lflag & PL_PPWAIT) == 0 &&
    !sigismasked(curlwp, SIGTTOU)) {
    if (p­>p_pgrp­>pg_jobc == 0) {
        error = EIO;
        mutex_spin_exit(&tty_lock);
        goto out;
    }
...
孤児のプロセスグループの場合はエラー番号EIOで
シグナルを送信せずに終わる
--> TERMIOS(4)の記述と一致
バックグラウンドの場合の処理
/*
 * プロセスがバックグラウンドの場合、ハングアップさせる
 */
p = curproc;
if (isbackground(p, tp) &&     /// バックグラウンド且つ、特殊ケースではない
    ISSET(tp­>t_lflag, TOSTOP) && (p­>p_lflag & PL_PPWAIT) == 0 &&
    !sigismasked(curlwp, SIGTTOU)) {
    if (p­>p_pgrp­>pg_jobc == 0) {  /// 孤児のプロセスグループか
        error = EIO;
        mutex_spin_exit(&tty_lock);
        goto out;
    }
    mutex_spin_exit(&tty_lock);     /// tty_lockのmutexを一旦解放して
 
    mutex_enter(proc_lock);         /// proc_lockのmutexを取得
    pgsignal(p­>p_pgrp, SIGTTOU, 1);/// SIGTTOUシグナルを送信
    mutex_exit(proc_lock);
 
    mutex_spin_enter(&tty_lock);    /// tty_lockのmutexを再取得
    error = ttypause(tp, hz);       /// 1hz待つ
    mutex_spin_exit(&tty_lock);
    if (error)
        goto out;                    /// pauseの間にエラー
    goto loop;                       /// エラー0の場合、最初からリスタート
}
mutex_spin_exit(&tty_lock);
データの処理
データの処理
/*
 * 最大でOBUFSIZのチャンク単位でユーザーのデータを処理する。
 * 任意の出力変換を行う。
 * ハイウォーターマークの記録を付け、オーバーフロー時は
 * 新しいスペースを獲得を目的にデバイスを待ってsleepする。
 */
while (uio­>uio_resid > 0 || cc > 0) {
    if (ISSET(tp­>t_lflag, FLUSHO)) {
        uio­>uio_resid = 0;
        return (0);
    }
    if (tp­>t_outq.c_cc > hiwat)
        goto ovhiwat;
    /*
     * 前回からの残り物が無ければ、ユーザーからのデータの塊を掴む
     */
    if (cc == 0) {
        cc = min(uio­>uio_resid, OBUFSIZ);
        cp = obuf;
        error = uiomove(cp, cc, uio);
        if (error) {
            cc = 0;
            goto out;
        }
    }
...
データの処理
while (uio­>uio_resid > 0 || cc > 0) {  /// まだ転送するデータがある
    if (ISSET(tp­>t_lflag, FLUSHO)) {   /// フラッシュ済みなら
        uio­>uio_resid = 0;
        return (0);                     /// 転送は正常に終了、関数から戻る
    }
    if (tp­>t_outq.c_cc > hiwat)     /// 出力キューの文字数がハイウォーター
        goto ovhiwat;                /// を越えていたら、エラー処理へ
...
uio->uio_residはttwrite()全体での残り転送量。
ccはチャンク単位の転送に分けた、残りの転送量。
#define FLUSHO      0x00800000  /* 出力はフラッシュ済み (状態) */
 
struct tty {
...
    struct  clist t_outq;       /* デバイス出力キュー */
 
struct clist {
..
    int c_cc;                   /* キューの中の文字数 */
データの処理
/*
 * 前回からの残り物が無ければ、ユーザーからのデータの塊を掴む
 */
if (cc == 0) {                          /// 残り物の量は、cc
    cc = min(uio­>uio_resid, OBUFSIZ);  /// 最大でOBUFSIZのチャンク量を設定
    cp = obuf;                          /// u_char  obuf[OBUFSIZ];
    error = uiomove(cp, cc, uio);       /// uioからcpにccの量だけコピー
    if (error) {
        cc = 0;
        goto out;                       /// エラーがあれば終了処理へ
    }
}
cpに次の転送のデータ、ccに転送量が入る
uiomove()内で、uio->uio_residの値は更新されている
    /*
     * 手が込んだことが行われる必要がないなら、ttyoutputの処理以外で扱える
     * それらの文字を掴み、出力キューにただ転送する。
     * 特別な処理を要求する文字(char_typeの中でbitで示されている)は、
     * ttyoutputを呼ぶ。
     * データの塊を処理した後で、^O's がすぐに反映されるようにFLUSHOを探す。
     */
    mutex_spin_enter(&tty_lock);
    while (cc > 0) {
        if (!ISSET(tp­>t_oflag, OPOST))
            ce = cc;
        else {
            ce = cc ­ scanc((u_int)cc, cp, char_type,
                CCLASSMASK);
            /* ceがゼロなら、ttyoutputで特殊文字を処理する */
            if (ce == 0) {
                tp­>t_rocount = 0;
                if (ttyoutput(*cp, tp) >= 0) {
                    /* 容量不足 */
...
                }
                cp++;
                cc­­;
                if (ISSET(tp­>t_lflag, FLUSHO) ||
                    tp­>t_outq.c_cc > hiwat) {
                    mutex_spin_exit(&tty_lock);
                    goto ovhiwat;
                }
                continue;
            }
データの処理
    mutex_spin_enter(&tty_lock);
    while (cc > 0) {                    /// ccが無くなるまで繰り返す
        if (!ISSET(tp­>t_oflag, OPOST)) /// OPOSTが設定されていなければ
            ce = cc;                    /// 全ての文字を変更無く送信する
        else {
...
TERMIOS(4) より
出力モード
OPOST /* 続く出力処理を有効にする */
ONLCR/* NL を CR-NL に変換する (ala CRMOD) */
OCRNL /* CRを NL に変換する */
OXTABS /* タブをスペースに置き換える */
ONOEOT /* 出力上の EOT (^D) を捨てる */
ONOCR/* 最初の列で CRを送信しない */
ONLRET /* 端末上で NL は CRの機能をする */
OPOSTがセットされると、残りのフラグが解釈される。
そうでなければ文字は変更せずに送信される
ceは特殊文字を含まない、送信される文字数
データの処理
    mutex_spin_enter(&tty_lock);
    while (cc > 0) {                    /// ccが無くなるまで繰り返す
        if (!ISSET(tp­>t_oflag, OPOST))
            ce = cc;
        else {                          /// OPOSTは設定されている
            ce = cc ­ scanc((u_int)cc, cp, char_type,
                CCLASSMASK);            /// char_typeのテーブルでスキャン
...
SCANC(9) より
int scanc(size_t size, const u_char *cp,
const u_char table[], int mask);
scanc() 関数は、長さが sizeのバイト文字列 cp をスキャンする
文字列中の文字は 256byteの tableの index として使われる
テーブルの値と mask のAND(論理積)がゼロでないか、
文字列が無くなった時に、スキャンは止まる
scanc() 関数はスキップされた文字数を返す
データの処理
ce = cc ­ scanc((u_int)cc, cp, char_type, CCLASSMASK);
#define E   0x00    /* Even parity. */        /// mask外
#define O   0x80    /* Odd parity. */         /// mask外
...
#define CCLASSMASK  0x3f           /// maskは6bit
...
#define BS  BACKSPACE              /// 2
#define CC  CONTROL                /// 1
#define CR  RETURN                 /// 2
#define NA  ORDINARY | ALPHA       /// 0 | 0x40   mask外
#define NL  NEWLINE                /// 3
#define NO  ORDINARY               /// 0          mask外
#define TB  TAB                    /// 4
#define VT  VTAB                   /// 5
....
unsigned char const char_type[] = {
    E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul ­ bel */
    O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs ­ si */
...
    E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( ­ / */   mask外
    E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 ­ 7 */   mask外
...
    O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p ­ w */   mask外
    E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x ­ del */ mask外
...
データの処理
    while (cc > 0) {
...
            ce = cc ­ scanc((u_int)cc, cp, char_type,
                CCLASSMASK);
            /* ceがゼロなら、ttyoutputで特殊文字を処理する */
            if (ce == 0) {
...
scanc()はcpの先頭からスキャンし、特殊文字が見つかった時点で返る
scanc()はスキップされた文字数を返すので、
ccから減算すると、cp先頭からの特殊文字でない文字数を得られる
ceがゼロなら、cpの先頭に特殊文字がある
データの処理
    /* ceがゼロなら、ttyoutputで特殊文字を処理する */
    if (ce == 0) {                      /// 先頭が特殊文字
        tp­>t_rocount = 0;              /// 行countをリセット
        if (ttyoutput(*cp, tp) >= 0) {  /// 1文字分ttyoutput
            /* 容量不足 */
            mutex_spin_exit(&tty_lock);
            goto overfull;              /// 失敗した場合、エラー処理へ
        }
        cp++;                           /// バッファのポインタを進める
        cc­­;                           /// 残り文字数を減らす
        if (ISSET(tp­>t_lflag, FLUSHO) ||  /// フラッシュ済み、または
            tp­>t_outq.c_cc > hiwat) {     /// キューがハイウォーター
            mutex_spin_exit(&tty_lock);    /// を越えていたら、エラー処理へ
            goto ovhiwat;
        }
        continue;              /// 特殊文字を1文字処理したらスキャンから再開
    }
}
/*
 * 必要に応じて出力処理(タブの展開、改行処理)をしつつ、端末に1文字出力する。
 * 成功すれば0より小さい値を返し、そうでなければ再送すべき文字を返す。
 * 再帰的でなければならない。
 */
int ttyoutput(int c, struct tty *tp)
データの処理
    while (cc > 0) {
        if (!ISSET(tp­>t_oflag, OPOST))  /// OPOSTフラグ無しなら
            ce = cc;                     /// 文字の変更無し
        else {
            ce = cc ­ scanc((u_int)cc, cp, char_type,
                CCLASSMASK);             /// 特殊文字をスキャン
            if (ce == 0) {
                ...                      /// ceがゼロなら特殊文字を処理する
                continue;
            }
        }
        /*
         * (処理するチャンクには)一束の通常の文字が見つかっている。
         * 出力キューに一斉に転送して、ループの先頭に戻って処理を継続する。
         * このOBUFSIZ以下のチャンク内に更なる文字がある場合には、
         * 最初の物はttyouputによる特別な扱いを必要とする文字であるはずだ。
         */
         ... /// 出力キューへの転送処理
    }
    ttstart(tp);
    mutex_spin_exit(&tty_lock);
}
データの処理
        /*
         * (処理するチャンクには)一束の通常の文字が見つかっている。
         * 出力キューに一斉に転送して、ループの先頭に戻って処理を継続する。
         * このOBUFSIZ以下のチャンク内に更なる文字がある場合には、
         * 最初の物はttyouputによる特別な扱いを必要とする文字であるはずだ。
         */
        tp­>t_rocount = 0;                /// 行countをリセット
        i = b_to_q(cp, ce, &tp­>t_outq);  /// 出力キューにバッファの通常文字を転送
        ce ­= i;                          /// ceは転送できたバイト数
        tp­>t_column += ce;               /// 行カウントを進める
        cp += ce, cc ­= ce, tk_nout += ce;
                                  /// バッファを進め、残り文字数を減算、
                                  /// tk_noutは統計ログ用の端末出力カウント
        tp­>t_outcc += ce;                /// 出力キューの統計情報を記録
...
/*
 * バッファをclist(リングバッファのキュー)にコピーする
 * 転送できなかったバイト数を返す
 */
int b_to_q(const u_char *cp, int count, struct clist *clp)
struct tty {
...
    long    t_outcc;        /* 出力キューの統計情報 */
データの処理
    while (cc > 0) {                    /// ccが無くなるまで繰り返す
      ...
        i = b_to_q(cp, ce, &tp­>t_outq);  /// 出力キューにバッファの通常文字を転送
         ...
        if (i > 0) {                      /// 転送できなかった文字がある
            /* 容量不足 */
            mutex_spin_exit(&tty_lock);
            goto overfull;                  /// エラー処理へ
        }
        if (ISSET(tp­>t_lflag, FLUSHO) || /// フラッシュ済み、または
            tp­>t_outq.c_cc > hiwat)      /// キューがハイウォーター
            break;                        /// を越えていたら、ループを抜ける
    }
    ttstart(tp);          /// ccが無くなるか、breakで抜けて来る
                          /// ttstartでキューに入れたデータの転送をはじめる
    mutex_spin_exit(&tty_lock);
}
終了・エラー処理
終了・エラー処理
out:
    /* ccがゼロでない場合、offsetとiovポインタが前方に移動してしまったので、
     * 私たちはuio構造体を一貫性の無いものにする、しかしそれは問題ない。
     * (呼び出しは不足を返すか、新しいuioで再スタートするかのいずれかだろう。)
     */
    uio­>uio_resid += cc;     /// uioの残り転送量にccを加える。
                              /// (カウンタだけ増やしてデータを巻き戻さない)
    return (error);           /// errorに入っている値を返す
 
 overfull:
    /*
     * 私達はリングバッファを使用しているので、出力キューに
     * これ以上挿入できないならば、リングはいっぱいで誰かが
     * ハイウォーターマークを正しくセットし忘れたと推測できる。
     * これをセットして、通常通り進める。
    hiwat = tp­>t_outq.c_cc ­ 1;    /// hiwatにキューの文字数­1を入れる
                                    /// 続けてovhiwat処理へ
 ovhiwat:
...
終了・エラー処理
ovhiwat:
    mutex_spin_enter(&tty_lock);
    ttstart(tp);                     /// ttstartでデータの転送をはじめる
                                     /// (hiwatを越えたのでキューを空けたい)
    /*
     * これは、FLUSHOがt_lflagにセットされたか、
     * ttstart/oprocが同時に起こる(またはとても速い)場合のみ発生する。
     */
    if (tp­>t_outq.c_cc <= hiwat) {  /// hiwatより低くなった
        mutex_spin_exit(&tty_lock);
        goto loop;                   /// 最初に戻ってやり直し
    }
    if (flag & IO_NDELAY) {          /// ディレイ無し指定はエラーで抜ける
        mutex_spin_exit(&tty_lock);  /// (この後、sleepがあるので)
        error = EWOULDBLOCK;
        goto out;
    }
    error = ttysleep(tp, &tp­>t_outcv, true, 0);
                                     /// 出力のCondition Variableでsleep
    mutex_spin_exit(&tty_lock);
    if (error)
        goto out;
    goto loop;                       /// 最初に戻ってやり直し
}

More Related Content

What's hot

コルーチンの使い方
コルーチンの使い方コルーチンの使い方
コルーチンの使い方
Naohiro Yoshikawa
 
effective modern c++ chapeter36
effective modern c++ chapeter36effective modern c++ chapeter36
effective modern c++ chapeter36
Tatsuki SHIMIZU
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.ContextAkira Takahashi
 
Effective modern-c++#9
Effective modern-c++#9Effective modern-c++#9
Effective modern-c++#9
Tatsuki SHIMIZU
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutinemelpon
 
emc++ chapter32
emc++ chapter32emc++ chapter32
emc++ chapter32
Tatsuki SHIMIZU
 
trueコマンドに0以外の終了コードをはかせる方法
trueコマンドに0以外の終了コードをはかせる方法trueコマンドに0以外の終了コードをはかせる方法
trueコマンドに0以外の終了コードをはかせる方法
mutz0623
 
Sharing Deep Dive
Sharing Deep DiveSharing Deep Dive
Sharing Deep Dive
Takaaki Suzuki
 
Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本
Kota Mizushima
 
Swift 2.0 の Error Handling #yhios
Swift 2.0 の Error Handling #yhiosSwift 2.0 の Error Handling #yhios
Swift 2.0 の Error Handling #yhios
Tomohiro Kumagai
 
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35
Keisuke Fukuda
 
What is Metasepi?
What is Metasepi?What is Metasepi?
What is Metasepi?
Kiwamu Okabe
 
Visual C++コード分析を支えるSAL
Visual C++コード分析を支えるSALVisual C++コード分析を支えるSAL
Visual C++コード分析を支えるSAL
egtra
 
Php in ruby
Php in rubyPhp in ruby
Php in rubydo_aki
 
Inside FastEnum
Inside FastEnumInside FastEnum
Inside FastEnum
Takaaki Suzuki
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
yohhoy
 
ジェネリック関数の呼び出され方 #cocoa_kansai
ジェネリック関数の呼び出され方 #cocoa_kansaiジェネリック関数の呼び出され方 #cocoa_kansai
ジェネリック関数の呼び出され方 #cocoa_kansai
Tomohiro Kumagai
 

What's hot (19)

コルーチンの使い方
コルーチンの使い方コルーチンの使い方
コルーチンの使い方
 
llvm入門
llvm入門llvm入門
llvm入門
 
effective modern c++ chapeter36
effective modern c++ chapeter36effective modern c++ chapeter36
effective modern c++ chapeter36
 
Continuation with Boost.Context
Continuation with Boost.ContextContinuation with Boost.Context
Continuation with Boost.Context
 
Effective modern-c++#9
Effective modern-c++#9Effective modern-c++#9
Effective modern-c++#9
 
Boost.Coroutine
Boost.CoroutineBoost.Coroutine
Boost.Coroutine
 
emc++ chapter32
emc++ chapter32emc++ chapter32
emc++ chapter32
 
trueコマンドに0以外の終了コードをはかせる方法
trueコマンドに0以外の終了コードをはかせる方法trueコマンドに0以外の終了コードをはかせる方法
trueコマンドに0以外の終了コードをはかせる方法
 
Sharing Deep Dive
Sharing Deep DiveSharing Deep Dive
Sharing Deep Dive
 
Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本
 
Swift 2.0 の Error Handling #yhios
Swift 2.0 の Error Handling #yhiosSwift 2.0 の Error Handling #yhios
Swift 2.0 の Error Handling #yhios
 
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35
 
What is Metasepi?
What is Metasepi?What is Metasepi?
What is Metasepi?
 
Visual C++コード分析を支えるSAL
Visual C++コード分析を支えるSALVisual C++コード分析を支えるSAL
Visual C++コード分析を支えるSAL
 
Php in ruby
Php in rubyPhp in ruby
Php in ruby
 
Inside winnyp
Inside winnypInside winnyp
Inside winnyp
 
Inside FastEnum
Inside FastEnumInside FastEnum
Inside FastEnum
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
 
ジェネリック関数の呼び出され方 #cocoa_kansai
ジェネリック関数の呼び出され方 #cocoa_kansaiジェネリック関数の呼び出され方 #cocoa_kansai
ジェネリック関数の呼び出され方 #cocoa_kansai
 

Recently uploaded

CS集会#13_なるほどわからん通信技術 発表資料
CS集会#13_なるほどわからん通信技術 発表資料CS集会#13_なるほどわからん通信技術 発表資料
CS集会#13_なるほどわからん通信技術 発表資料
Yuuitirou528 default
 
ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---
ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---
ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---
Matsushita Laboratory
 
TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024
TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024
TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024
Matsushita Laboratory
 
JSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさ
JSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさJSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさ
JSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさ
0207sukipio
 
論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...
論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...
論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...
Toru Tamaki
 
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアルLoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
CRI Japan, Inc.
 
遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化
遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化
遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化
t m
 
This is the company presentation material of RIZAP Technologies, Inc.
This is the company presentation material of RIZAP Technologies, Inc.This is the company presentation material of RIZAP Technologies, Inc.
This is the company presentation material of RIZAP Technologies, Inc.
chiefujita1
 

Recently uploaded (8)

CS集会#13_なるほどわからん通信技術 発表資料
CS集会#13_なるほどわからん通信技術 発表資料CS集会#13_なるほどわからん通信技術 発表資料
CS集会#13_なるほどわからん通信技術 発表資料
 
ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---
ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---
ReonHata_便利の副作用に気づかせるための発想支援手法の評価---行為の増減の提示による気づきへの影響---
 
TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024
TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024
TaketoFujikawa_物語のコンセプトに基づく情報アクセス手法の基礎検討_JSAI2024
 
JSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさ
JSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさJSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさ
JSAI_類似画像マッチングによる器への印象付与手法の妥当性検証_ver.3_高橋りさ
 
論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...
論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...
論文紹介:When Visual Prompt Tuning Meets Source-Free Domain Adaptive Semantic Seg...
 
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアルLoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
 
遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化
遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化
遺伝的アルゴリズムと知識蒸留による大規模言語モデル(LLM)の学習とハイパーパラメータ最適化
 
This is the company presentation material of RIZAP Technologies, Inc.
This is the company presentation material of RIZAP Technologies, Inc.This is the company presentation material of RIZAP Technologies, Inc.
This is the company presentation material of RIZAP Technologies, Inc.
 

ttwrite