2012年01月31日 情報科学類 オペレーティングシステム II 筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2011/2012-01-31
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/
/dev
以下のファイルをアクセスする。
$ df /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 232431456 13088380 207345736 6% /
$ ls -l /dev/sda2
brw-r----- 1 root disk 8, 2 Jan 24 12:00 /dev/sda2
$
ls -l で見ると、ブロック型は、b
、文字型は、c
で始まる。メ
ジャー番号は、デバイスの種類、マイナー番号は、同じ種類で、細かい違い
(上の例では、パーティション)等を意味する。
メジャー番号は、静的に決めうちにすることもあるが、
alloc_chrdev_region() を呼び、動的に割り当てられることもできる。
使われているメジャー番号は、/proc/devices
に現れる。
/dev/ の下にあるブロック型と文字型のファイルは、mknod コマンド (make
node) で作ることができる。
# mknod b /dev/ファイル名 メジャー番号 マイナー番号
# mknod c /dev/ファイル名 メジャー番号 マイナー番号
最近の
Linux では、起動時に自動的に mknod が行われるので、手で mknod コマンド
を打つ必要性はあまりない。
struct file_operations my_fops = { .... }; struct cdev *my_cdevp = cdev_alloc(); my_cdev->ops = &my_fops; my_cdev->owner = &my_fops;
struct file_operations my_fops = { .... }; struct cdev my_cdev ; cdev_init(&my_cdev,&my_fops); cdev_add(&my_cdev,num, count)register_chrdev() という関数で登録することもできる。以前はこの方法が主。
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
linux-3.1.3/include/linux/fs.h 1563: struct file_operations { 1564: struct module *owner; 1565: loff_t (*llseek) (struct file *, loff_t, int); 1566: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 1567: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 1568: ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 1569: ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 1570: int (*readdir) (struct file *, void *, filldir_t); 1571: unsigned int (*poll) (struct file *, struct poll_table_struct *); 1572: long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); 1573: long (*compat_ioctl) (struct file *, unsigned int, unsigned long); 1574: int (*mmap) (struct file *, struct vm_area_struct *); 1575: int (*open) (struct inode *, struct file *); 1576: int (*flush) (struct file *, fl_owner_t id); 1577: int (*release) (struct inode *, struct file *); 1578: int (*fsync) (struct file *, loff_t, loff_t, int datasync); 1579: int (*aio_fsync) (struct kiocb *, int datasync); 1580: int (*fasync) (int, struct file *, int); 1581: int (*lock) (struct file *, int, struct file_lock *); 1582: ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); 1583: unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); 1584: int (*check_flags)(int); 1585: int (*flock) (struct file *, int, struct file_lock *); 1586: ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); 1587: ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); 1588: int (*setlease)(struct file *, long, struct file_lock **); 1589: long (*fallocate)(struct file *file, int mode, loff_t offset, 1590: loff_t len); 1591: };主な手続きの意味
デバイス・ファイルでは、open() 等で自分のメジャー番号とマイナー番号を取 り出すために使われることがある。
int fd1 = open("file1"); int fd2 = open("file1");ファイル名 "file1" で表現されるファイルの inode 構造体は、1 個でも、 file 構造体は、2 個割り当てられる。
例
int ioctl(int d, int request, ...);
$ date
Mon Jan 30 14:24:03 JST 2012
$ hwclock --show
Mon Jan 30 14:24:05 2012 -0.605407 seconds
$
1: 2: /* 3: ~yas/syspro/time/rtc-read-time.c -- Read CMOS Realtime Clock in Linux 4: Created on: 2011/01/28 17:12:36 5: */ 6: 7: 8: #include <sys/types.h> /* open() */ 9: #include <sys/stat.h> /* open() */ 10: #include <fcntl.h> /* open() */ 11: #include <sys/ioctl.h> /* ioctl() */ 12: #include <unistd.h> /* close() */ 13: #include <stdio.h> /* printf() */ 14: #include <stdlib.h> /* exit() */ 15: #include <linux/rtc.h> /* RTC_RD_TIME */ 16: 17: #define RTC_DEVICE_FILE "/dev/rtc" 18: 19: main() 20: { 21: int fd; 22: struct rtc_time t1 ; 23: if( (fd = open( RTC_DEVICE_FILE, O_RDONLY ))< 0 ) 24: { 25: perror("open"); 26: exit( 1 ); 27: } 28: if( ioctl( fd, RTC_RD_TIME, &t1 ) < 0 ) 29: { 30: perror("ioctl(RTC_RD_TIME)"); 31: exit( 2 ); 32: } 33: printf("%04d-%02d-%02d %02d:%02d:%02d\n", 34: t1.tm_year+1900, t1.tm_mon+1, t1.tm_mday, 35: t1.tm_hour, t1.tm_min, t1.tm_sec ); 36: close( fd ); 37: }
$ rm rtc-read-time
$ make rtc-read-time
cc rtc-read-time.c -o rtc-read-time
$ ./rtc-read-time
2012-01-30 14:28:16
$ date
Mon Jan 30 14:28:18 JST 2012
$ ./rtc-read-time ; date
2012-01-30 14:28:23
Mon Jan 30 14:28:23 JST 2012
$
RTC_RD_TIME を含めて、/dev/rtc に対する ioctl() では、次のようなコマン
ドが使える。詳しくは、man rtc を参照。
コマンド | 説明 |
---|---|
RTC_RD_TIME | RTCのTODを読む(read) |
RTC_SET_TIME | RTCのTODに値をセットする |
RTC_ALM_READ,RTC_ALM_SET | RTCのalarmを読む/セットする |
RTC_IRQP_READ | alarmによる定期的な割り込みの(periodic interrupt)の周波数を読む/セットする |
RTC_AIE_ON, RTC_AIE_OFF | alarmの割り込みを許可する/禁止する |
RTC_UIE_ON, RTC_UIE_OFF | clockの更新後との割り込みを許可する/禁止する |
RTC_PIE_ON, RTC_PIE_OFF | 定期的な割り込みを許可する/禁止する |
RTC_EPOCH_READ, RTC_EPOCH_SET | RTCのepoch (起点となる年月日) を読む/書く |
linux-3.1.3/drivers/char/rtc.c 899: static const struct file_operations rtc_fops = { 900: .owner = THIS_MODULE, 901: .llseek = no_llseek, 902: .read = rtc_read, 903: #ifdef RTC_IRQ 904: .poll = rtc_poll, 905: #endif 906: .unlocked_ioctl = rtc_ioctl, 907: .open = rtc_open, 908: .release = rtc_release, 909: .fasync = rtc_fasync, 910: }; 911: 912: static struct miscdevice rtc_dev = { 913: .minor = RTC_MINOR, 914: .name = "rtc", 915: .fops = &rtc_fops, 916: }; ... 953: static int __init rtc_init(void) 954: { ... 1058: if (misc_register(&rtc_dev)) { ... 1066: } ... 1132: (void) init_sysctl(); 1133: 1134: printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n"); 1135: 1136: return 0; 1137: } linux-3.1.3/include/linux/miscdevice.h 23: #define RTC_MINOR 135
$ dmesg | grep Real
Real Time Clock Driver v1.12ac
$
dmesg コマンドの古いものは、(syslogd の働きで) /var/log/messages* 等の
ファイルに保存される。
# grep Real /var/log/messages.2
...
Jan 24 12:00:40 windell50 kernel: Real Time Clock Driver v1.12ac
#
drivers/char/rtc.c 181: #define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ ... 191: static unsigned long rtc_status; /* bitmapped status byte. */ ... 730: static int rtc_open(struct inode *inode, struct file *file) 731: { 732: spin_lock_irq(&rtc_lock); 733: 734: if (rtc_status & RTC_IS_OPEN) 735: goto out_busy; 736: 737: rtc_status |= RTC_IS_OPEN; ... 740: spin_unlock_irq(&rtc_lock); 741: return 0; 742: 743: out_busy: 744: spin_unlock_irq(&rtc_lock); 745: return -EBUSY; 746: }
linux-3.1.3/drivers/char/rtc.c 718: static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 719: { 720: long ret; 721: ret = rtc_do_ioctl(cmd, arg, 0); 722: return ret; 723: } ... 398: static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) 399: { 400: struct rtc_time wtime; ... 418: switch (cmd) { ... 480: case RTC_ALM_READ: /* Read the present alarm time */ 481: { 487: memset(&wtime, 0, sizeof(struct rtc_time)); 488: get_rtc_alm_time(&wtime); 489: break; 490: } 491: case RTC_ALM_SET: /* Store a time into the alarm */ ... 540: case RTC_RD_TIME: /* Read the time/date from RTC */ 541: { 542: memset(&wtime, 0, sizeof(struct rtc_time)); 543: rtc_get_rtc_time(&wtime); 544: break; 545: } 546: case RTC_SET_TIME: /* Set the RTC */ ... 711: default: 712: return -ENOTTY; 713: } 714: return copy_to_user((void __user *)arg, 715: &wtime, sizeof wtime) ? -EFAULT : 0; 716: }
カーネル空間とユーザ空間でデータをコピーする時には、次のような特殊な関 数を使う必要がある。
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
これらの関数は、コピーの途中でページフォールトが発生した時にもうまくコ ピーできる(ページインの処理でプロセスがスリープすることがある)。また、 引数の番地が有効かどうかをチェックする。
Linux x86 アーキテクチャでは、カーネル空間とユーザ空間が一部重なってい ることがある。この場合、カーネルでmemcpy() を使ったり、直接ポインタを操 作してもユーザ空間がアクセスできてしまうが、それは誤りである。
linux-3.1.3/drivers/char/rtc.c 1297: static void rtc_get_rtc_time(struct rtc_time *rtc_tm) 1298: { 1299: unsigned long uip_watchdog = jiffies, flags; 1300: unsigned char ctrl; ... 1325: spin_lock_irqsave(&rtc_lock, flags); 1326: rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); 1327: rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); 1328: rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); 1329: rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); 1330: rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); 1331: rtc_tm->tm_year = CMOS_READ(RTC_YEAR); ... 1338: ctrl = CMOS_READ(RTC_CONTROL); 1339: spin_unlock_irqrestore(&rtc_lock, flags); ... 1341: if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 1342: rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); 1343: rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); 1344: rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); 1345: rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); 1346: rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); 1347: rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); ... 1349: } ... 1359: rtc_tm->tm_year += epoch - 1900; 1360: if (rtc_tm->tm_year <= 69) 1361: rtc_tm->tm_year += 100; 1362: 1363: rtc_tm->tm_mon--; 1364: }
outb(unsigned char value, int port) ポート番号 port に 1 バイトの value を出力する unsigned char inb(int port) ポート番号 port から 1 バイトの value を入力してその値を返す1 バイト 8 ビットではなくて 2 バイト 16 ビット 単位のもの (inw(), outw()) や4 バイト 32 ビット単位のもの( inl(), outl() ) もある。
linux-3.1.3/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) linux-3.1.3/arch/x86/kernel/rtc.c 157: /* Routines for accessing the CMOS RAM/RTC. */ 158: unsigned char rtc_cmos_read(unsigned char addr) 159: { 160: unsigned char val; 161: 162: lock_cmos_prefix(addr); 163: outb(addr, RTC_PORT(0)); 164: val = inb(RTC_PORT(1)); 165: lock_cmos_suffix(addr); 166: 167: return val; 168: } linux-3.1.3/include/linux/mc146818rtc.h 43: #define RTC_SECONDS 0 44: #define RTC_SECONDS_ALARM 1 45: #define RTC_MINUTES 2 46: #define RTC_MINUTES_ALARM 3 47: #define RTC_HOURS 4 48: #define RTC_HOURS_ALARM 5 ... 52: #define RTC_DAY_OF_WEEK 6 53: #define RTC_DAY_OF_MONTH 7 54: #define RTC_MONTH 8 55: #define RTC_YEAR 9 ... 59: #define RTC_REG_A 10 60: #define RTC_REG_B 11 ... 86: #define RTC_CONTROL RTC_REG_B ... 92: # define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */ linux-3.1.3/arch/x86/include/asm/mc146818rtc.h 13: #define RTC_PORT(x) (0x70 + (x)) 14: #define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
asm ( アセンブラの命令列 : 出力オペランド(省略可) : 入力オペランド(省略可) : 破壊するレジスタ(省略可) )
"制約"(Cの値)
=
があると、書き込み専用
linux-3.1.3/arch/x86/include/asm/io.h 268: #define BUILDIO(bwl, bw, type) \ 269: static inline void out##bwl(unsigned type value, int port) \ 270: { \ 271: asm volatile("out" #bwl " %" #bw "0, %w1" \ 272: : : "a"(value), "Nd"(port)); \ 273: } \ 274: \ 275: static inline unsigned type in##bwl(int port) \ 276: { \ 277: unsigned type value; \ 278: asm volatile("in" #bwl " %w1, %" #bw "0" \ 279: : "=a"(value) : "Nd"(port)); \ 280: return value; \ 281: } \ ... 308: BUILDIO(b, b, char) 309: BUILDIO(w, w, short) 310: BUILDIO(l, , int)
id1##id2
」 は、
識別子(関数名、変数名等)の結合を意味する。
たとえば、マクロ定義の引数 bwl
が値 b
を持っていれば、out##bwl
は、outb
となる。
(「#
」がなければ、「out bwl
」 は、「out b
」と間に空白が残る)。
#var
」 は、文字列化。
たとえば、マクロ定義の引数 bwl が値 b を持っていれば、#bwl は、"b" となる。
(「#」がなければ、bwl は、b )。
static inline unsigned char inb(int port) { unsigned char value; asm volatile("inb %w1, %b0" : "=a"(value) : "Nd"(port)); return value; } static inline void outb(unsigned char value, int port) { asm volatile("outb %b0, %w1" : : "a"(value), "Nd"(port)); }
$ cat /proc/ioports
0000-001f : dma1
0020-0021 : pic1
0040-0043 : timer0
0050-0053 : timer1
0060-0060 : keyboard
0064-0064 : keyboard
0070-0077 : rtc
0080-008f : dma page reg
...
ff80-ff9f : 0000:00:1d.0
ff80-ff9f : uhci_hcd
$
意味 | バイナリ(16進表記,%x) | 標準的なバイナリの意味(10進表記,%d) |
---|---|---|
0 | 0x00 | 0 |
1 | 0x01 | 1 |
2 | 0x02 | 2 |
3 | 0x03 | 3 |
4 | 0x04 | 4 |
5 | 0x05 | 5 |
6 | 0x06 | 6 |
7 | 0x07 | 7 |
8 | 0x08 | 8 |
9 | 0x09 | 9 |
10 | 0x10 | 16 |
11 | 0x11 | 17 |
12 | 0x12 | 18 |
13 | 0x13 | 19 |
14 | 0x14 | 20 |
15 | 0x15 | 21 |
16 | 0x16 | 22 |
17 | 0x17 | 23 |
18 | 0x18 | 24 |
19 | 0x19 | 25 |
20 | 0x20 | 32 |
30 | 0x30 | 48 |
40 | 0x40 | 64 |
50 | 0x50 | 80 |
60 | 0x60 | 96 |
70 | 0x70 | 112 |
80 | 0x80 | 128 |
90 | 0x90 | 144 |
99 | 0x99 | 153 |
100 | 0x100 | 256 |
linux-3.1.3/lib/bcd.c 1: #include <linux/bcd.h> 2: #include <linux/module.h> 3: 4: unsigned bcd2bin(unsigned char val) 5: { 6: return (val & 0x0f) + (val >> 4) * 10; 7: } 8: EXPORT_SYMBOL(bcd2bin); 9: 10: unsigned char bin2bcd(unsigned val) 11: { 12: return ((val / 10) << 4) + val % 10; 13: } 14: EXPORT_SYMBOL(bin2bcd);
memcpy( /*空欄(a)*/,/*空欄(b)*/,/*空欄(c)*/ ); return 0;なお、memcpy() のインタフェースは、次のようになっている。
void * memcpy(void *destination, const void *source, size_t len);sourceは、コピー元、destination は、コピー先、len は長さ(バイト数)であ る。結果として destination を返す。
C言語の 3 項演算子(?と:)は、次のような意味である。
条件 ? 式1 : 式2「条件」が成り立つ(非0)なら、「式1」の値、成り立たな ければ、「式2」の値になる。この課題では、「間違ったプログラム」 を書く課題であり、
return 0;
と常に 0 (成功) を返すようにしている。
unsigned char hh; outb( /*空欄(a)*/, 0x70 ); hh = inb( /*空欄(b)*/ );