Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18292] ファイルの概要

このコミットは、Go言語のsyscallパッケージにNetBSD向けのTermios構造体を追加するものです。これにより、GoプログラムからNetBSDシステム上でターミナル設定を操作するための低レベルなインターフェースが提供されます。

コミット

commit 081e2d01535f648d28813e81fe6e1ce74eb6b579
Author: Michael Gehring <mg@ebfe.org>
Date:   Sun Jan 19 09:57:02 2014 -0800

    syscall: add syscall.Termios on netbsd
    
    R=golang-codereviews, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/54290043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/081e2d01535f648d28813e81fe6e1ce74eb6b579

元コミット内容

syscall: add syscall.Termios on netbsd

R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/54290043

変更の背景

Go言語のsyscallパッケージは、オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。これにより、GoプログラムはOSの機能に直接アクセスできます。このコミットが行われた2014年当時、Goは様々なOSへの対応を進めており、特にUnix系OSにおけるターミナル制御は重要な機能の一つでした。

termios構造体は、POSIXシステム(Unix系OS)において、シリアルポートやターミナルデバイスの属性(ボーレート、文字サイズ、パリティ、フロー制御、特殊文字など)を制御するために使用されます。Go言語でNetBSDシステム上でターミナル関連の操作(例: sttyコマンドのような機能の実装、インタラクティブなCLIアプリケーションの作成)を行うためには、このtermios構造体とそれに関連するシステムコール(tcgetattr, tcsetattrなど)へのアクセスが必要不可欠でした。

このコミットは、NetBSD環境におけるGoプログラムのターミナル制御機能を強化し、より幅広いシステムプログラミングのユースケースに対応できるようにすることを目的としています。

前提知識の解説

1. システムコール (System Call)

システムコールは、ユーザー空間で動作するプログラムが、カーネル空間で動作するOSの機能(ファイルI/O、プロセス管理、メモリ管理、ネットワーク通信など)を利用するためのインターフェースです。Go言語のsyscallパッケージは、これらのシステムコールをGoの関数としてラップし、GoプログラムからOSの低レベル機能にアクセスできるようにします。

2. termios (Terminal I/O)

termiosは、Unix系オペレーティングシステムにおけるターミナルI/O(入出力)の制御メカニズムです。これは、termios.hヘッダーファイルで定義される構造体と、それに関連する関数群(tcgetattr, tcsetattr, cfsetispeed, cfsetospeedなど)から構成されます。

termios構造体には、以下のようなターミナル設定が含まれます。

  • c_iflag (Input flags): 入力モードフラグ。入力処理(例: エコー、改行変換、特殊文字の処理)を制御します。
  • c_oflag (Output flags): 出力モードフラグ。出力処理(例: 改行変換、タブ展開)を制御します。
  • c_cflag (Control flags): 制御モードフラグ。ハードウェア制御(例: ボーレート、文字サイズ、パリティ、フロー制御)を制御します。
  • c_lflag (Local flags): ローカルモードフラグ。ローカル処理(例: カノニカルモード、エコー、シグナル生成)を制御します。
  • c_cc (Control characters): 制御文字配列。EOF、EOL、INTRなどの特殊文字の定義が含まれます。
  • c_ispeed (Input speed): 入力ボーレート。
  • c_ospeed (Output speed): 出力ボーレート。

これらのフラグや設定を操作することで、ターミナルの挙動を細かく制御できます。例えば、パスワード入力時に入力文字をエコーしないようにしたり、行バッファリングを無効にして文字単位の入力を受け付けたりすることが可能です。

3. NetBSD

NetBSDは、BSD系Unixライクなオープンソースのオペレーティングシステムです。移植性が非常に高く、様々なハードウェアアーキテクチャで動作することで知られています。Go言語は、このような多様なOS環境をサポートすることを目指しており、各OS固有のシステムコールやデータ構造への対応が不可欠です。

4. Cgo

Go言語は、C言語のコードをGoプログラムから呼び出すためのcgoというメカニズムを提供します。syscallパッケージの多くの部分は、C言語で定義されたOSのデータ構造や関数をGoの型や関数にマッピングするためにcgoを利用しています。このコミットでも、C言語のstruct termiosをGoのsyscall.Termios型にマッピングするためにcgoの機能が間接的に利用されています。具体的には、#include <termios.h>というCヘッダーのインクルードがその証拠です。

技術的詳細

このコミットの主要な目的は、NetBSDシステムにおけるtermios構造体をGoのsyscallパッケージで利用可能にすることです。これは以下のステップで実現されています。

  1. termios.hのインクルード: src/pkg/syscall/types_netbsd.goファイルに#include <termios.h>が追加されています。これは、cgoがC言語のヘッダーファイルを読み込み、その中で定義されている型や定数をGoのコードから利用できるようにするための指示です。これにより、C言語のstruct termiosの定義がGoのビルドプロセスに認識されます。

  2. Termios型の定義: src/pkg/syscall/types_netbsd.goにおいて、type Termios C.struct_termiosというエイリアスが追加されています。これは、C言語のstruct termiosをGoのsyscall.Termios型として扱うことを宣言しています。これにより、Goプログラム内でsyscall.Termiosという型名を使ってターミナル設定を表現できるようになります。

  3. アーキテクチャ固有のTermios構造体の定義: NetBSDは複数のCPUアーキテクチャ(386, amd64, armなど)をサポートしています。各アーキテクチャにおいて、C言語のstruct termiosのメモリレイアウトやフィールドのサイズが異なる場合があります。このため、src/pkg/syscall/ztypes_netbsd_386.go, src/pkg/syscall/ztypes_netbsd_amd64.go, src/pkg/syscall/ztypes_netbsd_arm.goの各ファイルに、それぞれのアーキテクチャに対応するTermios構造体が明示的に定義されています。

    これらの構造体は、C言語のstruct termiosのGoにおけるミラーリングであり、Goの型システムに適合するようにGoのプリミティブ型(uint32, int32, uint8など)でフィールドが定義されています。例えば、Ccフィールドは制御文字を格納するための配列であり、NetBSDでは20バイトのuint8配列として定義されています。

    type Termios struct {
        Iflag  uint32
        Oflag  uint32
        Cflag  uint32
        Lflag  uint32
        Cc     [20]uint8 // Control characters
        Ispeed int32
        Ospeed int32
    }
    

    この定義により、Goプログラムはsyscall.Termios型のインスタンスを作成し、そのフィールドを読み書きすることで、NetBSDのターミナル設定を操作できるようになります。

この変更は、Goのsyscallパッケージが各OSの低レベルなAPIをGoの型システムに適合させるための典型的なアプローチを示しています。cgoとアーキテクチャ固有の型定義を組み合わせることで、Goは多様なプラットフォームで一貫したシステムプログラミングインターフェースを提供しています。

コアとなるコードの変更箇所

このコミットによる変更は、以下の4つのファイルにわたっています。

  1. src/pkg/syscall/types_netbsd.go:

    • #include <termios.h> の追加。
    • type Termios C.struct_termios の追加。
  2. src/pkg/syscall/ztypes_netbsd_386.go:

    • type Termios struct { ... } の定義(386アーキテクチャ向け)。
  3. src/pkg/syscall/ztypes_netbsd_amd64.go:

    • type Termios struct { ... } の定義(amd64アーキテクチャ向け)。
  4. src/pkg/syscall/ztypes_netbsd_arm.go:

    • type Termios struct { ... } の定義(ARMアーキテクチャ向け)。

コアとなるコードの解説

src/pkg/syscall/types_netbsd.go

--- a/src/pkg/syscall/types_netbsd.go
+++ b/src/pkg/syscall/types_netbsd.go
@@ -18,6 +18,7 @@ package syscall
 #include <dirent.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <termios.h> // <-- 追加
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/param.h>
@@ -222,6 +223,10 @@ type BpfHdr C.struct_bpf_hdr
 
 type BpfTimeval C.struct_bpf_timeval
 
+// Terminal handling
+//
+type Termios C.struct_termios // <-- 追加
+
 // Sysctl
 
 type Sysctlnode C.struct_sysctlnode

このファイルは、NetBSD固有のシステムコール関連の型定義やCヘッダーのインクルードを管理しています。 #include <termios.h>の追加は、C言語のtermios関連の定義をGoのビルドシステムに認識させるための重要なステップです。これにより、C.struct_termiosという形でCの構造体を参照できるようになります。 type Termios C.struct_termiosは、Goのsyscallパッケージ内でTermiosという型名を使ってC言語のstruct termiosを参照するためのエイリアスを定義しています。これにより、GoのコードからよりGoらしいシンタックスでターミナル設定を扱えるようになります。

src/pkg/syscall/ztypes_netbsd_386.go, src/pkg/syscall/ztypes_netbsd_amd64.go, src/pkg/syscall/ztypes_netbsd_arm.go

これらのファイルは、Goのビルドプロセスによって自動生成されることが多い(zプレフィックスがその慣習を示す)アーキテクチャ固有の型定義を含んでいます。各ファイルには、それぞれのCPUアーキテクチャにおけるstruct termiosのGo言語での表現が定義されています。

例として、src/pkg/syscall/ztypes_netbsd_amd64.goの変更を見てみましょう。他のアーキテクチャのファイルも同様の構造です。

--- a/src/pkg/syscall/ztypes_netbsd_amd64.go
+++ b/src/pkg/syscall/ztypes_netbsd_amd64.go
@@ -377,6 +377,16 @@ type BpfTimeval struct {
 	Usec int64
 }
 
+type Termios struct { // <-- 追加
+	Iflag  uint32
+	Oflag  uint32
+	Cflag  uint32
+	Lflag  uint32
+	Cc     [20]uint8
+	Ispeed int32
+	Ospeed int32
+}
+
 type Sysctlnode struct {
 	Flags           uint32
 	Num             int32

ここで定義されているTermios構造体は、C言語のstruct termiosのフィールドをGoの対応する型にマッピングしたものです。

  • Iflag, Oflag, Cflag, Lflag: これらはそれぞれ入力、出力、制御、ローカルモードのフラグを表し、通常はビットマスクとして使用されるためuint32型で定義されています。
  • Cc: 制御文字の配列です。NetBSDでは20個の制御文字が定義されており、それぞれが1バイトのuint8で表現されるため、[20]uint8という固定サイズの配列として定義されています。
  • Ispeed, Ospeed: 入力および出力のボーレートを表し、int32型で定義されています。

これらのアーキテクチャ固有の定義により、Goのコンパイラは各ターゲットプラットフォームでsyscall.Termios型がC言語のstruct termiosと正しくメモリ上で対応するように、適切なサイズとアライメントで構造体を扱えるようになります。これにより、Goプログラムがtcgetattrtcsetattrのようなシステムコールを呼び出す際に、Termios構造体のポインタを渡すことで、OSカーネルと正しくデータをやり取りできるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特にsrc/pkg/syscallディレクトリ)
  • NetBSDのシステムプログラミングに関するドキュメント
  • POSIX標準のtermiosに関する仕様
  • Go言語のcgoに関するドキュメント