- หน้าแรก
- Documentation
- เนทีฟ
- โครงสร้างภายในของ Shell, PTY, กระบวนการ และคีย์ในระดับ Native
โครงสร้างภายในของ Shell, PTY, กระบวนการ และคีย์ในระดับ Native
เอกสารนี้ครอบคลุม primitives สำหรับการรัน/กระบวนการ/เทอร์มินัล ใน @f5-sales-demo/pi-natives ได้แก่ shell, pty, ps และ keys โดยใช้คำศัพท์สถาปัตยกรรมจาก docs/natives-architecture.md
ไฟล์ที่เกี่ยวข้องกับการ Implement
หัวข้อที่มีชื่อว่า “ไฟล์ที่เกี่ยวข้องกับการ Implement”crates/pi-natives/src/shell.rscrates/pi-natives/src/shell/windows.rs(เฉพาะ Windows)crates/pi-natives/src/pty.rscrates/pi-natives/src/ps.rscrates/pi-natives/src/keys.rscrates/pi-natives/src/task.rs(พฤติกรรมการยกเลิกที่ใช้ร่วมกันโดย shell/pty)packages/natives/src/shell/index.tspackages/natives/src/shell/types.tspackages/natives/src/pty/index.tspackages/natives/src/pty/types.tspackages/natives/src/ps/index.tspackages/natives/src/ps/types.tspackages/natives/src/keys/index.tspackages/natives/src/keys/types.tspackages/natives/src/bindings.ts
การเป็นเจ้าของในแต่ละชั้น
หัวข้อที่มีชื่อว่า “การเป็นเจ้าของในแต่ละชั้น”- ชั้น TS wrapper/API (
packages/natives/src/*): จุดเข้าถึงแบบ typed, พื้นผิวการยกเลิก (timeoutMs,AbortSignal) และ ergonomics ของ JS - ชั้น Rust N-API module (
crates/pi-natives/src/*): การรันกระบวนการ shell/PTY, การสำรวจ/ยุติ process-tree และการแยกวิเคราะห์ key-sequence - Validation gate (
native.ts, ระดับสถาปัตยกรรม): ตรวจสอบว่า export ที่จำเป็น (Shell,executeShell,PtySession,killTree,listDescendants, ตัวช่วย key) มีอยู่ก่อนที่จะใช้งาน wrapper
ระบบย่อย Shell (shell)
หัวข้อที่มีชื่อว่า “ระบบย่อย Shell (shell)”โมเดล API
หัวข้อที่มีชื่อว่า “โมเดล API”มีสองโหมดการรัน:
- แบบครั้งเดียว ผ่าน
executeShell(options, onChunk?) - เซสชันถาวร ผ่าน
new Shell(options?)แล้วตามด้วยshell.run(...)ซ้ำๆ
ทั้งสองโหมดส่งออกผลลัพธ์ผ่าน threadsafe callback และคืนค่า { exitCode?, cancelled, timedOut }
การสร้างเซสชันและโมเดลสภาพแวดล้อม
หัวข้อที่มีชื่อว่า “การสร้างเซสชันและโมเดลสภาพแวดล้อม”Rust สร้าง brush_core::Shell ด้วย:
- โหมดไม่โต้ตอบ,
do_not_inherit_env: true,- การสร้างสภาพแวดล้อมใหม่อย่างชัดเจนจาก env ของ host,
- รายการ skip สำหรับตัวแปรที่ sensitive ต่อ shell (
PS1,PWD,SHLVL, การ export ฟังก์ชัน bash เป็นต้น)
พฤติกรรมของ env ในเซสชัน:
ShellOptions.sessionEnvถูกใช้งานเพียงครั้งเดียวเมื่อสร้างเซสชันShellRunOptions.envมีขอบเขตต่อคำสั่ง (EnvironmentScope::Command) และถูกนำออกหลังจากการรันแต่ละครั้งPATHถูก merge เป็นพิเศษบน Windows โดยมีการ dedupe แบบ case-insensitive
การเพิ่มประสิทธิภาพ path เฉพาะ Windows (shell/windows.rs): path ของ Git-for-Windows ที่ค้นพบ (cmd, bin, usr/bin) จะถูกต่อท้ายหากมีอยู่และยังไม่ได้รวมอยู่
วงจรชีวิตขณะรันและการเปลี่ยนสถานะ
หัวข้อที่มีชื่อว่า “วงจรชีวิตขณะรันและการเปลี่ยนสถานะ”Shell แบบถาวร (Shell.run) ใช้ state machine นี้:
- Idle/Uninitialized:
session: None - Running: การเรียก
run()ครั้งแรกสร้างเซสชันแบบ lazy, เก็บ tokencurrent_abort, และรันคำสั่ง - Completed + keepalive: หากการควบคุม execution flow เป็น
Normal,current_abortจะถูกล้างและเซสชันจะถูกนำกลับมาใช้ - Completed + teardown: หาก control flow เกี่ยวข้องกับ loop/script/shell-exit (
BreakLoop,ContinueLoop,ReturnFromFunctionOrScript,ExitShell) เซสชันจะถูก drop (session: None) - Cancelled/Timed out: task การรันถูกยกเลิก, รอแบบ grace (2 วินาที), แล้วบังคับ abort; เซสชันจะถูก drop
- Error: เซสชันจะถูก drop
Shell แบบครั้งเดียว (executeShell) จะสร้างและ drop เซสชันใหม่ทุกครั้งที่เรียก
พฤติกรรม Streaming/Output
หัวข้อที่มีชื่อว่า “พฤติกรรม Streaming/Output”- Stdout/stderr ถูกส่งเข้าไปยัง pipe ที่ใช้ร่วมกันและอ่านพร้อมกัน
- Reader ถอดรหัส UTF-8 แบบเพิ่มทีละน้อย; ลำดับ byte ที่ไม่ถูกต้องจะปล่อย chunk การแทนที่
U+FFFD - หลังจากกระบวนการเสร็จสิ้น การระบาย output จะมีการป้องกัน idle/max (
250msidle,2sสูงสุด) เพื่อหลีกเลี่ยงการค้างกับ background jobs ที่เปิด descriptor ไว้
การยกเลิก, timeout และ background jobs
หัวข้อที่มีชื่อว่า “การยกเลิก, timeout และ background jobs”CancelTokenสร้างจากtimeoutMsและAbortSignalที่เป็น optional- เมื่อยกเลิก/timeout token การยกเลิก shell จะถูก trigger แล้ว task จะได้รับเวลา grace 2 วินาทีก่อนการบังคับ abort
- หากเกิดการยกเลิก background jobs จะถูกยุติ (
TERMแล้วตามด้วยKILLที่มีความล่าช้า) โดยใช้ข้อมูล job ของ brush
พฤติกรรม Shell.abort():
- abort เฉพาะคำสั่งที่กำลังรันอยู่สำหรับ instance
Shellนั้น, - เป็น no-op success เมื่อไม่มีอะไรกำลังรันอยู่
พฤติกรรมเมื่อเกิดความล้มเหลว
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดความล้มเหลว”ข้อผิดพลาดที่พบบ่อย ได้แก่:
- ความล้มเหลวในการ init เซสชัน (
Failed to initialize shell), - ข้อผิดพลาด cwd (
Failed to set cwd), - ความล้มเหลวในการ set/pop env,
- ความล้มเหลวในการรับ snapshot source,
- ความล้มเหลวในการสร้าง/clone pipe,
- ความล้มเหลวในการรัน (
Shell execution failed: ...), - ความล้มเหลวของ task wrapper (
Shell execution task failed: ...)
Flag การยกเลิกในระดับ Result:
- timeout ->
exitCode: undefined,timedOut: true - abort signal ->
exitCode: undefined,cancelled: true
ระบบย่อย PTY (pty)
หัวข้อที่มีชื่อว่า “ระบบย่อย PTY (pty)”โมเดล API
หัวข้อที่มีชื่อว่า “โมเดล API”new PtySession() เปิดเผย:
start(options, onChunk?) -> Promise<{ exitCode?, cancelled, timedOut }>write(data)resize(cols, rows)kill()
วงจรชีวิตขณะรันและการเปลี่ยนสถานะ
หัวข้อที่มีชื่อว่า “วงจรชีวิตขณะรันและการเปลี่ยนสถานะ”State machine ของ PtySession:
- Idle:
core: None - Reserved:
start()ติดตั้ง control channel แบบ synchronous (core: Some) ก่อนที่งาน async จะเริ่มต้น ดังนั้นwrite/resize/killจึงพร้อมใช้งานทันที - Running: loop การบล็อก PTY จัดการสถานะ child, เหตุการณ์ reader, heartbeat การยกเลิก และข้อความ control
- Terminal closed: การออกของ child + การเสร็จสิ้นของ reader
- Finalized:
coreจะถูก reset เป็นNoneเสมอหลังจาก task start เสร็จสิ้น (ทั้งสำเร็จและเกิดข้อผิดพลาด)
การป้องกัน Concurrency:
- การเริ่มต้นในขณะที่กำลังรันอยู่จะคืนค่า
PTY session already running
รูปแบบ Spawn/Attach/Write/Read/Terminate
หัวข้อที่มีชื่อว่า “รูปแบบ Spawn/Attach/Write/Read/Terminate”- PTY เปิดผ่าน
portable_pty::native_pty_system().openpty(...) - คำสั่งปัจจุบันรันเป็น
sh -lc <command>พร้อมcwdและการ override env ที่เป็น optional write()ส่ง raw bytes ไปยัง stdin ของ PTYresize()จำกัดขนาด (cols 20..400,rows 5..200) และเรียก master resizekill()ทำเครื่องหมายการรันว่าถูกยกเลิกและ kill child process
เส้นทาง Output:
- thread reader เฉพาะอ่าน master stream,
- ถอดรหัส UTF-8 แบบเพิ่มทีละน้อยพร้อมการแทนที่
U+FFFDสำหรับ byte ที่ไม่ถูกต้อง, - chunk ส่งต่อผ่าน N-API threadsafe callback
ความหมายของการยกเลิกและ timeout
หัวข้อที่มีชื่อว่า “ความหมายของการยกเลิกและ timeout”timeoutMsและAbortSignalป้อนเข้าCancelToken- loop เรียก
ct.heartbeat()เป็นระยะ; การ abort จะ trigger การ kill child - การจำแนก timeout อาศัย string (
"Timeout"substring ในข้อผิดพลาด heartbeat)
พฤติกรรมเมื่อเกิดความล้มเหลว
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดความล้มเหลว”พื้นผิวของ Error ได้แก่:
- ความล้มเหลวในการ allocate/open PTY,
- ความล้มเหลวในการ spawn PTY,
- ความล้มเหลวในการรับ writer/reader,
- ความล้มเหลวในสถานะ/การรอของ child,
- lock poisoning,
- การตัดการเชื่อมต่อของ control-channel (
PTY session is no longer available)
ความล้มเหลวของการเรียก control เมื่อไม่ได้รันอยู่:
write/resize/killคืนค่าPTY session is not running
ระบบย่อย Process-tree (ps)
หัวข้อที่มีชื่อว่า “ระบบย่อย Process-tree (ps)”โมเดล API
หัวข้อที่มีชื่อว่า “โมเดล API”killTree(pid, signal) -> numberlistDescendants(pid) -> number[]
TS wrapper ยังลงทะเบียน native kill-tree integration เข้าใน shared utils ผ่าน setNativeKillTree(native.killTree)
การ Implement เฉพาะแพลตฟอร์ม
หัวข้อที่มีชื่อว่า “การ Implement เฉพาะแพลตฟอร์ม”- Linux: อ่าน
/proc/<pid>/task/<pid>/childrenแบบ recursive - macOS: ใช้
libprocproc_listchildpids - Windows: สร้าง snapshot ตาราง process ด้วย
CreateToolhelp32Snapshot, สร้าง map parent->children, ยุติด้วยOpenProcess(PROCESS_TERMINATE)+TerminateProcess
พฤติกรรม Kill-tree
หัวข้อที่มีชื่อว่า “พฤติกรรม Kill-tree”- Descendants ถูกเก็บรวบรวมแบบ recursive
- ลำดับการ Kill เป็นแบบ bottom-up (descendants ที่ลึกที่สุดก่อน) เพื่อลด orphan re-parenting
- Root pid ถูก kill เป็นลำดับสุดท้าย
- ค่าที่คืนมาคือจำนวนการยุติที่สำเร็จ
พฤติกรรม Signal:
- POSIX:
signalที่ให้มาจะถูกส่งไปยังkill - Windows:
signalจะถูกเพิกเฉย; การยุติเป็นแบบ unconditional process terminate
พฤติกรรมเมื่อเกิดความล้มเหลว
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดความล้มเหลว”โมดูลนี้ออกแบบมาโดยเจตนาให้ไม่ throw ที่พื้นผิว API:
- สาขา process tree ที่หายไป/เข้าถึงไม่ได้จะถูกข้าม,
- ความล้มเหลวในการ kill ต่อ pid จะนับเป็นไม่สำเร็จ (ไม่ใช่ข้อผิดพลาด),
- การค้นหาที่พลาดมักให้ผล
[]จากlistDescendantsและ0จากkillTree
ระบบย่อยการแยกวิเคราะห์คีย์ (keys)
หัวข้อที่มีชื่อว่า “ระบบย่อยการแยกวิเคราะห์คีย์ (keys)”โมเดล API
หัวข้อที่มีชื่อว่า “โมเดล API”ตัวช่วยที่เปิดเผย:
parseKey(data, kittyProtocolActive)matchesKey(data, keyId, kittyProtocolActive)parseKittySequence(data)matchesKittySequence(data, expectedCodepoint, expectedModifier)matchesLegacySequence(data, keyName)
โมเดลการแยกวิเคราะห์
หัวข้อที่มีชื่อว่า “โมเดลการแยกวิเคราะห์”Parser รวม:
- การ mapping แบบ single-byte โดยตรง (
enter,tab,ctrl+<letter>, ASCII ที่พิมพ์ได้), - การค้นหา escape-sequence แบบ legacy แบบ O(1) (PHF map),
- การแยกวิเคราะห์
modifyOtherKeysของ xterm, - การแยกวิเคราะห์ Kitty protocol (
CSI u,CSI ~,CSI 1;...<letter>), - การ normalize เป็น key IDs (
ctrl+c,shift+tab,pageUp,f5เป็นต้น)
การจัดการ Modifier:
- เปรียบเทียบเฉพาะ bit shift/alt/ctrl สำหรับการจับคู่คีย์,
- bit lock ถูก mask ออกก่อนการเปรียบเทียบ
พฤติกรรม Layout:
- การ fallback ของ base-layout ถูกจำกัดโดยเจตนาเพื่อให้ layout ที่ถูก remap ไม่สร้าง false match สำหรับตัวอักษร/สัญลักษณ์ ASCII
พฤติกรรมเมื่อเกิดความล้มเหลว
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดความล้มเหลว”- ลำดับที่ไม่รู้จักหรือไม่ถูกต้องจะให้ผล
nullจากฟังก์ชัน parse - ฟังก์ชัน match คืนค่า
falseเมื่อ parse ล้มเหลวหรือไม่ตรงกัน - ไม่มีพื้นผิวของ error ที่ throw สำหรับ key input ที่ไม่ถูกต้อง
การ mapping ระหว่าง JS wrapper API และ Rust export
หัวข้อที่มีชื่อว่า “การ mapping ระหว่าง JS wrapper API และ Rust export”Shell + PTY + กระบวนการ
หัวข้อที่มีชื่อว่า “Shell + PTY + กระบวนการ”| TS wrapper API | Rust N-API export | หมายเหตุ |
|---|---|---|
executeShell(options, onChunk?) | executeShell (execute_shell) | การรัน shell แบบครั้งเดียว |
new Shell(options?) | Shell class | เซสชัน shell แบบถาวร |
shell.run(options, onChunk?) | Shell::run | นำเซสชันกลับมาใช้เมื่อ keepalive control flow |
shell.abort() | Shell::abort | Abort การรันที่ active สำหรับ shell instance นั้น |
new PtySession() | PtySession class | เซสชัน PTY แบบ stateful |
pty.start(options, onChunk?) | PtySession::start | การรัน PTY แบบ interactive |
pty.write(data) | PtySession::write | Raw stdin passthrough |
pty.resize(cols, rows) | PtySession::resize | ขนาดเทอร์มินัลแบบ clamped |
pty.kill() | PtySession::kill | Force-kill child PTY ที่ active |
killTree(pid, signal) | killTree (kill_tree) | การยุติ process tree แบบ children-first |
listDescendants(pid) | listDescendants (list_descendants) | รายการ descendants แบบ recursive |
| TS wrapper API | Rust N-API export | หมายเหตุ |
|---|---|---|
matchesKittySequence(data, cp, mod) | matchesKittySequence (matches_kitty_sequence) | การจับคู่ Kitty codepoint+modifier |
parseKey(data, kittyProtocolActive) | parseKey (parse_key) | Parser key-id แบบ normalized |
matchesLegacySequence(data, keyName) | matchesLegacySequence (matches_legacy_sequence) | การตรวจสอบ legacy sequence map แบบตรงทั้งหมด |
parseKittySequence(data) | parseKittySequence (parse_kitty_sequence) | ผลการ parse Kitty แบบ structured |
matchesKey(data, keyId, kittyProtocolActive) | matchesKey (matches_key) | ตัวจับคู่คีย์ระดับสูง |
หมายเหตุเกี่ยวกับการทำความสะอาดเซสชันที่ถูกทิ้งและการ Finalize
หัวข้อที่มีชื่อว่า “หมายเหตุเกี่ยวกับการทำความสะอาดเซสชันที่ถูกทิ้งและการ Finalize”- เซสชัน Shell แบบถาวร: หากการรันถูกยกเลิก/timeout/เกิดข้อผิดพลาด/non-keepalive control flow Rust จะ drop สถานะเซสชันภายในอย่างชัดเจน การรันปกติที่สำเร็จจะเก็บเซสชันไว้เพื่อนำกลับมาใช้
- เซสชัน PTY:
coreจะถูกล้างเสมอหลังจากstart()เสร็จสิ้น รวมถึงเส้นทางความล้มเหลว - ไม่มี contract การ kill ที่ขับเคลื่อนด้วย JS finalizer อย่างชัดเจน ที่เปิดเผยโดย wrapper; การทำความสะอาดเชื่อมโยงกับเส้นทางการเสร็จสิ้น/ยกเลิกการรันเป็นหลัก ผู้เรียกควรใช้
timeoutMs,AbortSignal,shell.abort()หรือpty.kill()เพื่อการ teardown ที่กำหนดได้