ディレクトリ構成
/ └─ usr ├─ include(stdio.h等のヘッダ) │ └─ w32api(windows.h等のwindowsヘッダ) │ (windowsのランライムライブラリ用のcファイル) │ ├─ bin(cygwin1.dll等) │ (gcc,x86_64-pc-cygwin-gcc,x86_64-w64-mingw32-gcc,i686-w64-mingw32-gcc) │ ├─ lib(crt0.o、libc.a、libcygwin.a等の静的ライブラリ) │ ├─ gcc │ │ ├─ x86_64-pc-cygwin │ │ │ └─ 5.4.0(cc1,collect2等のバイナリ、crtbegin.o,crtend.o、libgcc.a、libssp.a等の静的ライブラリ,libssp.dll.a等のインポートライブラリ) │ │ │ └─ include(stddef.h等のヘッダ) │ │ │ │ │ ├─ x86_64-w64-mingw32 │ │ │ └─ 5.4.0(cc1,collect2等のバイナリ、crtbegin.o,crtend.o、libgcc.a、libssp.a等の静的ライブラリ,libssp.dll.a等のインポートライブラリ) │ │ │ └─ include(stddef.h等のヘッダ) │ │ │ │ │ └─ i686-w64-mingw32 │ │ └─ 5.4.0(cc1,collect2等のバイナリ、crtbegin.o,crtend.o、libgcc.a、libssp.a等の静的ライブラリ,libssp.dll.a等のインポートライブラリ) │ │ └─ include(stddef.h等のヘッダ) │ │ │ └─ w32api(libuser32.a等のwindowsの静的ライブラリ) │ ├─ x86_64-w64-mingw32 │ └─ sys-root │ └─ mingw │ └─ bin(gccのDLL(libssp,dllなど)) │ └─ i686-w64-mingw32 └─ sys-root └─ mingw └─ bin(gccのDLL(libssp,dllなど))
フォント
使用するフォント
ReactOSの日本語のフォントはDroid Sans Fallbackが標準になっているようですが、svnからダウンロードしたReactOSのソースには、Droid Sans Fallbackは含まれていないようです。
そのため、Droid Sans Fallbackのフォントを入れてみたのですが、設定の方法がきちんと理解できていないこともあり、フォントをちゃんと設定することができませんでした(半角がすべて□になってしまいました。)。
そのため、Drroid Sans Fallbackの導入については、あまり追及するのをやめ、非公式ReactOS日本語版パッケージ配布所でも使われていた、梅フォントで設定することにしました。
レジストリ
FontLinkが動作していないようなので、レジストリのうち、tahoma関係を書き換えます。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes\
Helv | REG_SZ | Ume Gothic |
MS Sans Serif | REG_SZ | Ume Gothic |
MS Shell Dlg | REG_SZ | Ume UI Gothic |
MS Shell Dlg 2 | REG_SZ | Ume UI Gothic |
MS UI Gothic | REG_SZ | Ume UI Gothic |
MS UI Gothic 2 | REG_SZ | Ume UI Gothic |
Tahoma | REG_SZ | Ume Gothic |
フォントの初期設定
コントロールパネルの上級者向け設定にある、非UNICODEプログラムの言語バージョンをJapanease以外からJapaneseにすると、初期設定のままでは、使うフォントがDrid Sans FallBackとなりますが、Droid Sans FallBackをインストールしていない場合には、文字化け(文字が□になる。)が起こります。
これは、レジストリが、font.infで初期化されるためですので、fonts.inf(c:\ReactOS\inf\font.inf ソースの場合には、reactos/media/inf/font.inf)に正しい初期値を設定します。
Font.CJK.Regをもとに、「Droid Sans Fallback」を「Ume Gothic」等のお好みのフォント名に置き換えたFont.Japan.Regを作ります。
[Font.Japan.Reg] HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Arial",0x00000000,"Liberation Sans" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Courier",0x00000000,"FreeMono" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Courier New",0x00000000,"FreeMono" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Fixedsys",0x00000000,"Fixedsys Excelsior 3.01-L2" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Helv",0x00000000,"Ume Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Helvetica",0x00000000,"Liberation Sans" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Lucida Console",0x00000000,"DejaVu Sans Mono" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Sans Serif",0x00000000,"Ume Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Shell Dlg",0x00000000,"Ume UI Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS Shell Dlg 2",0x00000000,"Ume UI Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS UI Gothic",0x00000000,"Ume UI Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","MS UI Gothic 2",0x00000000,"Ume UI Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Tahoma",0x00000000,"Ume Gothic" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Terminal",0x00000000,"DejaVu Sans Mono" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Times",0x00000000,"Liberation Serif" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Times New Roman",0x00000000,"Liberation Serif" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes","Tms Rmn",0x00000000,"Liberation Serif"
cp932(Shift-JIS)の参照先を、Font.CJK.Regから上で作ったFont.Japan.Regに変更します。)
; cp932/cp932 Japanese [Font.CP932.96] AddReg = Font.Reg.96 AddReg = Font.Japan.Reg [Font.CP932.120] AddReg = Font.Reg.120 AddReg = Font.Japan.Reg
ime探検(2)タスクトレイ
タスクトレイの表示
windows2000
キーボード | ステータスアイコン | 選択表示 |
---|---|---|
英語キーボード | EN | (アイコンあり)English(United States) |
MS-IME | MS-IMEのアイコン | (アイコンあり)Microsoft IME 2000(Japanese) |
MZ-IME | MZ-IMEのアイコン | (アイコンあり)MZ-IME日本語入力 0.0 |
ReactOS
キーボード | ステータスアイコン | 選択表示 |
---|---|---|
英語キーボード | EN | (アイコンなし)米国 |
日本語キーボード | JA | (アイコンなし)日本語 |
MZ-IME | ?? | (アイコンなし)mz-ime |
英語キーボードや日本語キーボードが違うのはわかるとして、MZ-IMEの表示が全然違うのが気になる。
タスクトレイの表示は、kbswitch.exeが担当。
kbswitch.cのCreateTrayIcon(LPTSTR szLCID)では、
lId = (LANGID)_tcstoul(szLCID, NULL, 16); if (GetLocaleInfo(lId, LOCALE_SISO639LANGNAME, szBuf, ARRAYSIZE(szBuf)) == 0) { StringCchCopy(szBuf, ARRAYSIZE(szBuf), _T("??")); }
となっていて、LCIDがISO639LANGUAGENAMEで取得できなければ、"??"としてアイコンを作ることになっている。
これを、imeからアイコンを取得できるように修正すればいいのかな。
ime探検(1)調べものなど
対策など
レジストリに直接登録すると、コントロールパネルに出てくるようになり、選択できるようになる。
タスクトレイにも出てくるようになるが、windows2000とは、アイコン表示が異なる。
レジストリ
0411部分は、ロケールID(jaは0x0411)と思われる
ロケール ID (LCID) の一覧
頭4桁はなんでしょう?
windows10(x64_86)
デフォルトの日本語キーボード
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\00000411\
Layout Display Name | REG_EXPAND_SZ | @%SystemRoot%\system32\input.dll,-5061 |
Layout File | REG_SZ | KBDJPN.DLL |
Layout Text | REG_SZ | Japanese |
KBDJPN.DLLは、\Windows\system32にある。
MS-IME
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\e0200411\
Ime File | REG_SZ | imjp14.ime |
Layout Display Name | REG_SZ | Microsoft Office IME 2010 |
Layout File | REG_SZ | kbdjpn.dll |
Layout Text | REG_SZ | Microsoft Office IME 2010 |
KBDJPN.DLL、IMJP14.IMEは、\Windows\system32にある。
MZ-IME
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\E0120411\
IME file | REG_SZ | MZIMEJA.IME |
layout file | REG_SZ | kbdjpn.dll |
layout text | REG_SZ | 日本語(MZ-IME) |
KBDJPN.DLLは、\Windows\system32にある。
mzimeja.imeは、\Windows\SysWOW64にある。
windows2000
デフォルトの日本語キーボード
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\00000411\
Layout File | REG_SZ | KBDJPN.DLL |
Layout Text | REG_SZ | Japanese |
kbdjpn.dllは、\WINNT\system32にある。
MS-IME
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\E00100411\
Ime File | REG_SZ | imejp.ime |
Layout File | REG_SZ | Kbdjpn.dll |
Layout Text | REG_SZ | Japanese Input System(MS-IME2000) |
kbdjpn.dll、imejp.imeは、\WINNT\system32にある。
MZ-IME
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\E0120411\
IME file | REG_SZ | MZIMEJA.IME |
layout file | REG_SZ | kbdjpn.dll |
layout text | REG_SZ | 日本語(MZ-IME) |
kbdjpn.dll、mzimeja.imeは、\WINNT\system32にある。
ReactOS
デフォルトの日本語キーボード
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\00000411\
Layout Display Name | REG_SZ | @%SystemRoot%\system32\input.dll,-5061 |
Layout File | REG_SZ | kbdja.dll |
Layout ID | REG_SZ | 0001 |
Layout Text | REG_SZ | Japanese |
kbdja.dllは、\ReactOS\system32にある。
Layout Display NameはREG_EXPAND_SZが正しいのかも
Layout IDは、謎
ReactOS対策
windows2000を参考に、以下を作成する。
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\E0120411\
Ime File | REG_SZ | mzimeja.ime |
Layout File | REG_SZ | kbdja.dll |
Layout Text | REG_SZ | mz-ime |
kbdja.dll、mzimeja.imeは、\ReactOS\system32にある。
コントロールパネルにmz-imeが出てきて、選べるようになる。
また、タスクトレイのステータスアイコンを左クリックしても、選べるようになる。
入力は、日本語キーボードのレイアウト。ただし、半角/全角キーを押しても無反応。
また、デフォルトに設定することも可能だが、起動直後は英語キーボードの設定になってしまう。
再度、MZ-IMEに切り替えなおすと、日本語キーボードのレイアウトで入力できる。
しかし、ステータスアイコンを右クリックすると、英語キーボードの選択になってしまう。
MZ-IMEの呼び出しに失敗している模様
kalloc.c
// Physical memory allocator, intended to allocate // memory for user processes, kernel stacks, page table pages, // and pipe buffers. Allocates 4096-byte pages. #include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "spinlock.h" void freerange(void *vstart, void *vend); extern char end[]; // first address after kernel loaded from ELF file struct run { struct run *next; }; struct { struct spinlock lock; int use_lock; struct run *freelist; } kmem; // Initialization happens in two phases. // 1. main() calls kinit1() while still using entrypgdir to place just // the pages mapped by entrypgdir on free list. // 2. main() calls kinit2() with the rest of the physical pages // after installing a full page table that maps them on all cores. void kinit1(void *vstart, void *vend) { initlock(&kmem.lock, "kmem"); kmem.use_lock = 0; freerange(vstart, vend); } void kinit2(void *vstart, void *vend) { freerange(vstart, vend); kmem.use_lock = 1; } void freerange(void *vstart, void *vend) { char *p; p = (char*)PGROUNDUP((uint)vstart); for(; p + PGSIZE <= (char*)vend; p += PGSIZE) kfree(p); } //PAGEBREAK: 21 // Free the page of physical memory pointed at by v, // which normally should have been returned by a // call to kalloc(). (The exception is when // initializing the allocator; see kinit above.) void kfree(char *v) { struct run *r; if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP) panic("kfree"); // Fill with junk to catch dangling refs. memset(v, 1, PGSIZE); if(kmem.use_lock) acquire(&kmem.lock); r = (struct run*)v; r->next = kmem.freelist; kmem.freelist = r; if(kmem.use_lock) release(&kmem.lock); } // Allocate one 4096-byte page of physical memory. // Returns a pointer that the kernel can use. // Returns 0 if the memory cannot be allocated. char* kalloc(void) { struct run *r; if(kmem.use_lock) acquire(&kmem.lock); r = kmem.freelist; if(r) kmem.freelist = r->next; if(kmem.use_lock) release(&kmem.lock); return (char*)r; }
swtch.S
# Context switch # # void swtch(struct context **old, struct context *new); # # Save current register context in old # and then load register context from new. .globl swtch swtch: movl 4(%esp), %eax movl 8(%esp), %edx # Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi # Switch stacks movl %esp, (%eax) movl %edx, %esp # Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp ret
proc.c
#include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "x86.h" #include "proc.h" #include "spinlock.h" struct { struct spinlock lock; struct proc proc[NPROC]; } ptable; static struct proc *initproc; int nextpid = 1; extern void forkret(void); extern void trapret(void); static void wakeup1(void *chan); void pinit(void) { initlock(&ptable.lock, "ptable"); } //PAGEBREAK: 32 // Look in the process table for an UNUSED proc. // If found, change state to EMBRYO and initialize // state required to run in the kernel. // Otherwise return 0. static struct proc* allocproc(void) { struct proc *p; char *sp; acquire(&ptable.lock); for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) if(p->state == UNUSED) goto found; release(&ptable.lock); return 0; found: p->state = EMBRYO; p->pid = nextpid++; release(&ptable.lock); // Allocate kernel stack. if((p->kstack = kalloc()) == 0){ p->state = UNUSED; return 0; } sp = p->kstack + KSTACKSIZE; // Leave room for trap frame. sp -= sizeof *p->tf; p->tf = (struct trapframe*)sp; // Set up new context to start executing at forkret, // which returns to trapret. sp -= 4; *(uint*)sp = (uint)trapret; sp -= sizeof *p->context; p->context = (struct context*)sp; memset(p->context, 0, sizeof *p->context); p->context->eip = (uint)forkret; return p; } //PAGEBREAK: 32 // Set up first user process. void userinit(void) { struct proc *p; extern char _binary_initcode_start[], _binary_initcode_size[]; p = allocproc(); initproc = p; if((p->pgdir = setupkvm()) == 0) panic("userinit: out of memory?"); inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); p->sz = PGSIZE; memset(p->tf, 0, sizeof(*p->tf)); p->tf->cs = (SEG_UCODE << 3) | DPL_USER; p->tf->ds = (SEG_UDATA << 3) | DPL_USER; p->tf->es = p->tf->ds; p->tf->ss = p->tf->ds; p->tf->eflags = FL_IF; p->tf->esp = PGSIZE; p->tf->eip = 0; // beginning of initcode.S safestrcpy(p->name, "initcode", sizeof(p->name)); p->cwd = namei("/"); // this assignment to p->state lets other cores // run this process. the acquire forces the above // writes to be visible, and the lock is also needed // because the assignment might not be atomic. acquire(&ptable.lock); p->state = RUNNABLE; release(&ptable.lock); } // Grow current process's memory by n bytes. // Return 0 on success, -1 on failure. int growproc(int n) { uint sz; sz = proc->sz; if(n > 0){ if((sz = allocuvm(proc->pgdir, sz, sz + n)) == 0) return -1; } else if(n < 0){ if((sz = deallocuvm(proc->pgdir, sz, sz + n)) == 0) return -1; } proc->sz = sz; switchuvm(proc); return 0; } // Create a new process copying p as the parent. // Sets up stack to return as if from system call. // Caller must set state of returned proc to RUNNABLE. int fork(void) { int i, pid; struct proc *np; // Allocate process. if((np = allocproc()) == 0){ return -1; } // Copy process state from p. if((np->pgdir = copyuvm(proc->pgdir, proc->sz)) == 0){ kfree(np->kstack); np->kstack = 0; np->state = UNUSED; return -1; } np->sz = proc->sz; np->parent = proc; *np->tf = *proc->tf; // Clear %eax so that fork returns 0 in the child. np->tf->eax = 0; for(i = 0; i < NOFILE; i++) if(proc->ofile[i]) np->ofile[i] = filedup(proc->ofile[i]); np->cwd = idup(proc->cwd); safestrcpy(np->name, proc->name, sizeof(proc->name)); pid = np->pid; acquire(&ptable.lock); np->state = RUNNABLE; release(&ptable.lock); return pid; } // Exit the current process. Does not return. // An exited process remains in the zombie state // until its parent calls wait() to find out it exited. void exit(void) { struct proc *p; int fd; if(proc == initproc) panic("init exiting"); // Close all open files. for(fd = 0; fd < NOFILE; fd++){ if(proc->ofile[fd]){ fileclose(proc->ofile[fd]); proc->ofile[fd] = 0; } } begin_op(); iput(proc->cwd); end_op(); proc->cwd = 0; acquire(&ptable.lock); // Parent might be sleeping in wait(). wakeup1(proc->parent); // Pass abandoned children to init. for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->parent == proc){ p->parent = initproc; if(p->state == ZOMBIE) wakeup1(initproc); } } // Jump into the scheduler, never to return. proc->state = ZOMBIE; sched(); panic("zombie exit"); } // Wait for a child process to exit and return its pid. // Return -1 if this process has no children. int wait(void) { struct proc *p; int havekids, pid; acquire(&ptable.lock); for(;;){ // Scan through table looking for exited children. havekids = 0; for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->parent != proc) continue; havekids = 1; if(p->state == ZOMBIE){ // Found one. pid = p->pid; kfree(p->kstack); p->kstack = 0; freevm(p->pgdir); p->pid = 0; p->parent = 0; p->name[0] = 0; p->killed = 0; p->state = UNUSED; release(&ptable.lock); return pid; } } // No point waiting if we don't have any children. if(!havekids || proc->killed){ release(&ptable.lock); return -1; } // Wait for children to exit. (See wakeup1 call in proc_exit.) sleep(proc, &ptable.lock); //DOC: wait-sleep } } //PAGEBREAK: 42 // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: // - choose a process to run // - swtch to start running that process // - eventually that process transfers control // via swtch back to the scheduler. void scheduler(void) { struct proc *p; for(;;){ // Enable interrupts on this processor. sti(); // Loop over process table looking for process to run. acquire(&ptable.lock); for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->state != RUNNABLE) continue; // Switch to chosen process. It is the process's job // to release ptable.lock and then reacquire it // before jumping back to us. proc = p; switchuvm(p); p->state = RUNNING; swtch(&cpu->scheduler, p->context); switchkvm(); // Process is done running for now. // It should have changed its p->state before coming back. proc = 0; } release(&ptable.lock); } } // Enter scheduler. Must hold only ptable.lock // and have changed proc->state. Saves and restores // intena because intena is a property of this // kernel thread, not this CPU. It should // be proc->intena and proc->ncli, but that would // break in the few places where a lock is held but // there's no process. void sched(void) { int intena; if(!holding(&ptable.lock)) panic("sched ptable.lock"); if(cpu->ncli != 1) panic("sched locks"); if(proc->state == RUNNING) panic("sched running"); if(readeflags()&FL_IF) panic("sched interruptible"); intena = cpu->intena; swtch(&proc->context, cpu->scheduler); cpu->intena = intena; } // Give up the CPU for one scheduling round. void yield(void) { acquire(&ptable.lock); //DOC: yieldlock proc->state = RUNNABLE; sched(); release(&ptable.lock); } // A fork child's very first scheduling by scheduler() // will swtch here. "Return" to user space. void forkret(void) { static int first = 1; // Still holding ptable.lock from scheduler. release(&ptable.lock); if (first) { // Some initialization functions must be run in the context // of a regular process (e.g., they call sleep), and thus cannot // be run from main(). first = 0; iinit(ROOTDEV); initlog(ROOTDEV); } // Return to "caller", actually trapret (see allocproc). } // Atomically release lock and sleep on chan. // Reacquires lock when awakened. void sleep(void *chan, struct spinlock *lk) { if(proc == 0) panic("sleep"); if(lk == 0) panic("sleep without lk"); // Must acquire ptable.lock in order to // change p->state and then call sched. // Once we hold ptable.lock, we can be // guaranteed that we won't miss any wakeup // (wakeup runs with ptable.lock locked), // so it's okay to release lk. if(lk != &ptable.lock){ //DOC: sleeplock0 acquire(&ptable.lock); //DOC: sleeplock1 release(lk); } // Go to sleep. proc->chan = chan; proc->state = SLEEPING; sched(); // Tidy up. proc->chan = 0; // Reacquire original lock. if(lk != &ptable.lock){ //DOC: sleeplock2 release(&ptable.lock); acquire(lk); } } //PAGEBREAK! // Wake up all processes sleeping on chan. // The ptable lock must be held. static void wakeup1(void *chan) { struct proc *p; for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) if(p->state == SLEEPING && p->chan == chan) p->state = RUNNABLE; } // Wake up all processes sleeping on chan. void wakeup(void *chan) { acquire(&ptable.lock); wakeup1(chan); release(&ptable.lock); } // Kill the process with the given pid. // Process won't exit until it returns // to user space (see trap in trap.c). int kill(int pid) { struct proc *p; acquire(&ptable.lock); for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->pid == pid){ p->killed = 1; // Wake process from sleep if necessary. if(p->state == SLEEPING) p->state = RUNNABLE; release(&ptable.lock); return 0; } } release(&ptable.lock); return -1; } //PAGEBREAK: 36 // Print a process listing to console. For debugging. // Runs when user types ^P on console. // No lock to avoid wedging a stuck machine further. void procdump(void) { static char *states[] = { [UNUSED] "unused", [EMBRYO] "embryo", [SLEEPING] "sleep ", [RUNNABLE] "runble", [RUNNING] "run ", [ZOMBIE] "zombie" }; int i; struct proc *p; char *state; uint pc[10]; for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->state == UNUSED) continue; if(p->state >= 0 && p->state < NELEM(states) && states[p->state]) state = states[p->state]; else state = "???"; cprintf("%d %s %s", p->pid, state, p->name); if(p->state == SLEEPING){ getcallerpcs((uint*)p->context->ebp+2, pc); for(i=0; i<10 && pc[i] != 0; i++) cprintf(" %p", pc[i]); } cprintf("\n"); } }