メモリ管理、ページ・フォールト、入出力、inb()とoutb()

					2010年02月09日
情報科学類 オペレーティングシステム II

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2009/2010-02-09
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■今日の大事な話

■前回資料

◆1段のページ・テーブル

印刷資料は、先週配布済み。

■ページ・フォールト

アドレス空間は割り当てられているが、メモリが割り当てられていない場所を プロセスがアクセスした時には、do_page_fault() が呼ばれる。

◆x86 do_page_fault()

arch/x86/mm/fault.c:
do_page_fault(struct pt_regs *regs, unsigned long error_code)
do_page_fault() は、CPUアーキテクチャによって違う。 x86 の ハードウェアの割り込み 14 により呼ばれる。次の条件で生じる。 x86 は、次のような情報を残す。

ページ・フォールトのエラーコード
Bit 2 Bit 1 Bit 0
0 カーネル 読み込み ページがない
1 ユーザ 書き込み 保護違反

arch/x86/mm/fault.c
 944: do_page_fault(struct pt_regs *regs, unsigned long error_code)
 945: {
 946:         struct vm_area_struct *vma;
 947:         struct task_struct *tsk;
 948:         unsigned long address;
 949:         struct mm_struct *mm;
 950:         int write;
 951:         int fault;
 952: 
 953:         tsk = current;
 954:         mm = tsk->mm;
...
 957:         address = read_cr2();
...
1072:         vma = find_vma(mm, address);
1073:         if (unlikely(!vma)) {
1074:                 bad_area(regs, error_code, address);
1075:                 return;
1076:         }
1077:         if (likely(vma->vm_start <= address))
1078:                 goto good_area;
1079:         if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
1080:                 bad_area(regs, error_code, address);
1081:                 return;
1082:         }
...
1095:         if (unlikely(expand_stack(vma, address))) {
1096:                 bad_area(regs, error_code, address);
1097:                 return;
1098:         }
...
1104: good_area:
1105:         write = error_code & PF_WRITE;
1106: 
1107:         if (unlikely(access_error(error_code, write, vma))) {
1108:                 bad_area_access_error(regs, error_code, address);
1109:                 return;
1110:         }
..
1117:         fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
...
1137: }

◆handle_mm_fault()

mm/memory.c
2963: int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
2964:                 unsqigned long address, unsigned int flags)
2965: {
2966:         pgd_t *pgd;
2967:         pud_t *pud;
2968:         pmd_t *pmd;
2969:         pte_t *pte;
...
2978:         pgd = pgd_offset(mm, address);
2979:         pud = pud_alloc(mm, pgd, address);
2980:         if (!pud)
2981:                 return VM_FAULT_OOM;
2982:         pmd = pmd_alloc(mm, pud, address);
2983:         if (!pmd)
2984:                 return VM_FAULT_OOM;
2985:         pte = pte_alloc_map(mm, pmd, address);
2986:         if (!pte)
2987:                 return VM_FAULT_OOM;
2988: 
2989:         return handle_pte_fault(mm, vma, address, pte, pmd, flags);
2990: }

◆handle_pte_fault()

2907: static inline int handle_pte_fault(struct mm_struct *mm,
2908:                 struct vm_area_struct *vma, unsigned long address,
2909:                 pte_t *pte, pmd_t *pmd, unsigned int flags)
2910: {
2911:         pte_t entry;
2912:         spinlock_t *ptl;
2913: 
2914:         entry = *pte;
...
2916:                 if (pte_none(entry)) {
2917:                         if (vma->vm_ops) {
2918:                                 if (likely(vma->vm_ops->fault))
2919:                                         return do_linear_fault(mm, vma, address,
2920:                                                 pte, pmd, flags, entry);
2921:                         }
2922:                         return do_anonymous_page(mm, vma, address,
2923:                                                  pte, pmd, flags);
2924:                 }
...
2928:                 return do_swap_page(mm, vma, address,
2929:                                         pte, pmd, flags, entry);
...
2958: }

■入出力

◆x86 CMOS Real-Time Clock

CMOS や Real Time という名前が紛らわしい。固有名詞と思う。次の2つの機能 がある。
TOD (time of day) clock
時刻を、year/month/day hour:minute:second という形式で持つ。 秒以下は読めない。
定期的な割込み用
2Hz から 8192Hz の範囲で、2 の冪乗の周期で割込みを起こせる。
Linux は、起動時に TOD clock を読んで、の時計を初期化する。 以後、普段はこのハードウェアを参照することはしない。 hwclock コマンドで参照できる。
% date [←]
Mon Feb  8 23:33:36 JST 2010
% hwclock --show [←]
Mon Feb  8 23:33:40 2010  -0.748647 seconds
% []

◆rtc_get_rtc_time()

wdrivers/char/rtc.c
1299: static void rtc_get_rtc_time(struct rtc_time *rtc_tm)
1300: {
1301:         unsigned long uip_watchdog = jiffies, flags;
1302:         unsigned char ctrl;
...
1327:         spin_lock_irqsave(&rtc_lock, flags);
1328:         rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
1329:         rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
1330:         rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
1331:         rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
1332:         rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
1333:         rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
1334:         /* Only set from 2.6.16 onwards */
1335:         rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK);
...
1340:         ctrl = CMOS_READ(RTC_CONTROL);
1341:         spin_unlock_irqrestore(&rtc_lock, flags);
1342: 
1343:         if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
1344:                 rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
1345:                 rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
1346:                 rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
1347:                 rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
1348:                 rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
1349:                 rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
1350:                 rtc_tm->tm_wday = bcd2bin(rtc_tm->tm_wday);
1351:         }

1361:         rtc_tm->tm_year += epoch - 1900;
1362:         if (rtc_tm->tm_year <= 69)
1363:                 rtc_tm->tm_year += 100;
1364: 
1365:         rtc_tm->tm_mon--;
1366: }
BCD では、10進数1桁を4ビットで表現する。12(10進2桁) は、16ビットの数 0x12 で表現する。

◆rtc_cmos_read()

arch/x86/include/asm/mc146818rtc.h
  94: #define CMOS_READ(addr) rtc_cmos_read(addr)
  95: #define CMOS_WRITE(val, addr) rtc_cmos_write(val, addr)

arch/x86/kernel/rtc.c
 146: unsigned char rtc_cmos_read(unsigned char addr)
 147: {
 148:         unsigned char val;
 149: 
 150:         lock_cmos_prefix(addr);
 151:         outb(addr, RTC_PORT(0));
 152:         val = inb(RTC_PORT(1));
 153:         lock_cmos_suffix(addr);
 154: 
 155:         return val;
 156: }

include/linux/mc146818rtc.h
  43: #define RTC_SECONDS             0
  45: #define RTC_MINUTES             2
  47: #define RTC_HOURS               4
...
  52: #define RTC_DAY_OF_WEEK         6
  53: #define RTC_DAY_OF_MONTH        7
  54: #define RTC_MONTH               8
  55: #define RTC_YEAR                9

  86: #define RTC_CONTROL     RTC_REG_B
  92: # define RTC_DM_BINARY 0x04     /* all time/date values are BCD if clear */

arch/x86/include/asm/mc146818rtc.h
  13: #define RTC_PORT(x)     (0x70 + (x))
  14: #define RTC_ALWAYS_BCD  1       /* RTC operates in binary mode */

◆outb()とinb()

outb(unsigned char value, int port)
  ポート番号 port に 1 バイトの value を出力する

unsigned char inb(int port)
  ポート番号 port から 1 バイトの value を入力してその値を返す

◆asm()文

C言語のプログラムの中にアセンブリ言語のプログラムを混ぜて書く方法。
asm ( アセンブラの命令列
 : 出力オペランド(省略可)
 : 入力オペランド(省略可)
 : 破壊するレジスタ(省略可) )

volatile をつけると、コンパイラによる最適化(命令の順番の入れ替え等)を 抑止できる。

◆outb()とinb()

arch/x86/include/asm/io_32.h

 126: #define __BUILDIO(bwl, bw, type)                                \
 127: static inline void out##bwl(unsigned type value, int port)      \
 128: {                                                               \
 129:         out##bwl##_local(value, port);                          \
 130: }                                                               \
 131:                                                                 \
 132: static inline unsigned type in##bwl(int port)                   \
 133: {                                                               \
 134:         return in##bwl##_local(port);                           \
 135: }
 136: 
 137: #define BUILDIO(bwl, bw, type)                                          \
 138: static inline void out##bwl##_local(unsigned type value, int port)      \
 139: {                                                                       \
 140:         asm volatile("out" #bwl " %" #bw "0, %w1"               \
 141:                      : : "a"(value), "Nd"(port));                       \
 142: }                                                                       \
 143:                                                                         \
 144: static inline unsigned type in##bwl##_local(int port)                   \
 145: {                                                                       \
 146:         unsigned type value;                                            \
 147:         asm volatile("in" #bwl " %w1, %" #bw "0"                \
 148:                      : "=a"(value) : "Nd"(port));                       \
 149:         return value;                                                   \
 150: }                                                                       \
 151:                                                                         \
...

 192: BUILDIO(b, b, char)
 193: BUILDIO(w, w, short)
 194: BUILDIO(l, , int)

■クイズ8 メモリ管理、ページ・フォールト、入出力、inb()とoutb()

★問題(801) ページフォールト

Linux のユーザ・プロセスがページアウトされているページをアクセスすると ページフォールトが発生する。この時実行される関数を1つ選び、その関数の 役割を簡単に説明しなさい。

★問題(802) x86 CMOS RTCからの時間データの入力

次のプログラムは、x86 CMOS RTC ハードウェアから時間(hours)データを読み 出し変数 hh (m) に入れるプログラムの一部である。空欄/*(a)*/と空欄/*(b)*/を埋めて完成さ せなさい。
    unsigned char hh;
    outb( /*(a)*/, 0x70 );
    hh = inb( /*(b)*/ );

Last updated: 2010/02/09 13:34:38
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>