2011年02月01日
情報科学類 オペレーティングシステム II
筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2010/2011-02-01
あるいは、次のページから手繰っていくこともできます。
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 に現れる。
$ cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
...
226 drm
254 pcmcia
Block devices:
1 ramdisk
8 sd
9 md
11 sr
65 sd
...
135 sd
253 device-mapper
254 mdp
$
/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);
include/linux/fs.h
1488: struct file_operations {
1489: struct module *owner;
1490: loff_t (*llseek) (struct file *, loff_t, int);
1491: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
1492: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
1493: ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1494: ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
1495: int (*readdir) (struct file *, void *, filldir_t);
1496: unsigned int (*poll) (struct file *, struct poll_table_struct *);
1497: long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
1498: long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
1499: int (*mmap) (struct file *, struct vm_area_struct *);
1500: int (*open) (struct inode *, struct file *);
1501: int (*flush) (struct file *, fl_owner_t id);
1502: int (*release) (struct inode *, struct file *);
1503: int (*fsync) (struct file *, int datasync);
1504: int (*aio_fsync) (struct kiocb *, int datasync);
1505: int (*fasync) (int, struct file *, int);
1506: int (*lock) (struct file *, int, struct file_lock *);
1507: ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
1508: unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
1509: int (*check_flags)(int);
1510: int (*flock) (struct file *, int, struct file_lock *);
1511: ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
1512: ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
1513: int (*setlease)(struct file *, long, struct file_lock **);
1514: };
主な手続きの意味
デバイス・ファイルでは、open() 等で自分のメジャー番号とマイナー番号を取 り出すために使われることがある。
int fd1 = open("file1");
int fd2 = open("file1");
ファイル名 "file1" で表現されるファイルの inode 構造体は、1 個でも、
file 構造体は、2 個割り当てられる。
例
int ioctl(int d, int request, ...);
$ date
Fri Jan 28 17:33:21 JST 2011
$ hwclock --show
Fri Jan 28 17:33:22 2011 -0.000369 seconds
$
1:
2: /*
3: 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: }
$ make rtc-read-time
cc rtc-read-time.c -o rtc-read-time
$ ls -l /dev/rtc
crw-r--r-- 1 root root 10, 135 Jan 24 20:59 /dev/rtc
$ ./rtc-read-time
2011-01-31 16:01:40
$ date
Mon Jan 31 16:01:44 JST 2011
$ ./rtc-read-time ; date
2011-01-31 16:01:54
Mon Jan 31 16:01:54 JST 2011
$
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 (起点となる年月日) を読む/書く |
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)) {
...
1064: rtc_release_region();
1065: return -ENODEV;
1066: }
...
1132: (void) init_sysctl();
1133:
1134: printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n");
1135:
1136: return 0;
1137: }
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: }
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() を使ったり、直接ポインタを操 作してもユーザ空間がアクセスできてしまうが、それは誤りである。
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);
1340:
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;
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() ) もある。
$ 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
$
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: /* Routines for accessing the CMOS RAM/RTC. */
147: unsigned char rtc_cmos_read(unsigned char addr)
148: {
149: unsigned char val;
150:
151: lock_cmos_prefix(addr);
152: outb(addr, RTC_PORT(0));
153: val = inb(RTC_PORT(1));
154: lock_cmos_suffix(addr);
155:
156: return val;
157: }
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
...
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 */
asm ( アセンブラの命令列 : 出力オペランド(省略可) : 入力オペランド(省略可) : 破壊するレジスタ(省略可) )
"制約"(Cの値)
= があると、書き込み専用
arch/x86/include/asm/io.h
287: #define BUILDIO(bwl, bw, type) \
288: static inline void out##bwl(unsigned type value, int port) \
289: { \
290: asm volatile("out" #bwl " %" #bw "0, %w1" \
291: : : "a"(value), "Nd"(port)); \
292: } \
293: \
294: static inline unsigned type in##bwl(int port) \
295: { \
296: unsigned type value; \
297: asm volatile("in" #bwl " %w1, %" #bw "0" \
298: : "=a"(value) : "Nd"(port)); \
299: return value; \
300: } \
...
327: BUILDIO(b, b, char)
328: BUILDIO(w, w, short)
329: 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));
}
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);
unsigned char hh;
outb( /*空欄(a)*/, 0x70 );
hh = inb( /*空欄(b)*/ );