2023年01月20日
情報科学類 オペレーティングシステム II
                                       筑波大学 システム情報系 
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.coins.tsukuba.ac.jp/~yas/coins/os2-2022/2023-01-20
あるいは、次のページから手繰っていくこともできます。
	http://www.coins.tsukuba.ac.jp/~yas/
	http://www.cs.tsukuba.ac.jp/~yas/
/dev 以下のファイルをアクセスする。
$ df / ![[←]](../icons/screen-return.gif) Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda2            232431456  13088380 207345736   6% /
$ ls -l /dev/sda2
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda2            232431456  13088380 207345736   6% /
$ ls -l /dev/sda2 ![[←]](../icons/screen-return.gif) brw-r----- 1 root disk 8, 2 Jan 24 12:00 /dev/sda2
$ ls -l /dev/console
brw-r----- 1 root disk 8, 2 Jan 24 12:00 /dev/sda2
$ ls -l /dev/console ![[←]](../icons/screen-return.gif) crw------- 1 root root 5, 1 Feb  6 15:22 /dev/console
$
crw------- 1 root root 5, 1 Feb  6 15:22 /dev/console
$ ![[]](../icons/screen-cursor.gif) 
ls -l で見ると、ブロック型は、b、文字型は、c で始まる。メ
ジャー番号は、デバイスの種類、マイナー番号は、同じ種類で、細かい違い
(上の例では、パーティション)等を意味する。
メジャー番号は、静的に決めうちにすることもあるが、
alloc_chrdev_region() を呼び、動的に割り当てられることもできる。
使われているメジャー番号は、/proc/devices に現れる。
/dev/ の下にあるブロック型と文字型のファイルは、mknod コマンド (make
node) で作ることができる。
# mknod b /dev/ファイル名 メジャー番号 マイナー番号 ![[←]](../icons/screen-return.gif) # mknod c /dev/ファイル名 メジャー番号 マイナー番号
# mknod c /dev/ファイル名 メジャー番号 マイナー番号 ![[←]](../icons/screen-return.gif) 
最近の
Linux では、起動時に自動的に mknod が行われるので、手で mknod コマンド
を打つ必要性はあまりない。
struct file_operations my_fops = { .... };
struct cdev *my_cdevp = cdev_alloc();
my_cdev->ops = &my_fops; 
my_cdev->owner = THIS_MODULE; 
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);
cdev_device_add() でも良い。device_add() すると、/sys の sysfs からもアクセス可能になる。
linux-6.1.2/fs/char_dev.c
 537:	int cdev_device_add(struct cdev *cdev, struct device *dev)
 538:	{
...
 544:	                rc = cdev_add(cdev, dev->devt, 1);
...
 549:	        rc = device_add(dev);
..
 553:	        return rc;
 554:	}
linux-6.1.2/include/linux/fs.h
2103:	struct file_operations {
2104:	        struct module *owner;
2105:	        loff_t (*llseek) (struct file *, loff_t, int);
2106:	        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
2107:	        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
2108:	        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
2109:	        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
2110:	        int (*iopoll)(struct kiocb *kiocb, struct io_comp_batch *,
2111:	                        unsigned int flags);
2112:	        int (*iterate) (struct file *, struct dir_context *);
2113:	        int (*iterate_shared) (struct file *, struct dir_context *);
2114:	        __poll_t (*poll) (struct file *, struct poll_table_struct *);
2115:	        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
2116:	        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
2117:	        int (*mmap) (struct file *, struct vm_area_struct *);
2118:	        unsigned long mmap_supported_flags;
2119:	        int (*open) (struct inode *, struct file *);
2120:	        int (*flush) (struct file *, fl_owner_t id);
2121:	        int (*release) (struct inode *, struct file *);
2122:	        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
2123:	        int (*fasync) (int, struct file *, int);
2124:	        int (*lock) (struct file *, int, struct file_lock *);
2125:	        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
2126:	        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
2127:	        int (*check_flags)(int);
2128:	        int (*flock) (struct file *, int, struct file_lock *);
2129:	        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
2130:	        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
2131:	        int (*setlease)(struct file *, long, struct file_lock **, void **);
2132:	        long (*fallocate)(struct file *file, int mode, loff_t offset,
2133:	                          loff_t len);
2134:	        void (*show_fdinfo)(struct seq_file *m, struct file *f);
2135:	#ifndef CONFIG_MMU
2136:	        unsigned (*mmap_capabilities)(struct file *);
2137:	#endif
2138:	        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
2139:	                        loff_t, size_t, unsigned int);
2140:	        loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
2141:	                                   struct file *file_out, loff_t pos_out,
2142:	                                   loff_t len, unsigned int remap_flags);
2143:	        int (*fadvise)(struct file *, loff_t, loff_t, int);
2144:	        int (*uring_cmd)(struct io_uring_cmd *ioucmd, unsigned int issue_flags);
2145:	        int (*uring_cmd_iopoll)(struct io_uring_cmd *, struct io_comp_batch *,
2146:	                                unsigned int poll_flags);
2147:	} __randomize_layout;
主な手続きの意味
デバイス・ファイルでは、open() 等で自分のメジャー番号とマイナー番号を取 り出すために使われることがある。
    int fd1 = open("file1");
    int fd2 = open("file1");
ファイル名 "file1" で表現されるファイルの inode 構造体は、1 個でも、
file 構造体は、2 個割り当てられる。
例
int ioctl(int d, int request, ...);
# hwclock --show ![[←]](../icons/screen-return.gif) Tue Jan 17 13:56:19 2023  -0.662156 seconds
# date
Tue Jan 17 13:56:19 2023  -0.662156 seconds
# date ![[←]](../icons/screen-return.gif) Tue Jan 17 13:56:20 JST 2023
#
Tue Jan 17 13:56:20 JST 2023
# ![[]](../icons/screen-cursor.gif) 
   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:	}
$ ls -l /dev/rtc ![[←]](../icons/screen-return.gif) lrwxrwxrwx 1 root root 4 Oct 26 09:53 /dev/rtc -> rtc0
$ ls -l /dev/rtc0
lrwxrwxrwx 1 root root 4 Oct 26 09:53 /dev/rtc -> rtc0
$ ls -l /dev/rtc0 ![[←]](../icons/screen-return.gif) crw------- 1 root root 252, 0 Oct 26 09:53 /dev/rtc0
$ ls -lL /dev/rtc
crw------- 1 root root 252, 0 Oct 26 09:53 /dev/rtc0
$ ls -lL /dev/rtc ![[←]](../icons/screen-return.gif) crw------- 1 root root 252, 0 Oct 26 09:53 /dev/rtc
$
crw------- 1 root root 252, 0 Oct 26 09:53 /dev/rtc
$ ![[]](../icons/screen-cursor.gif) 
$ make rtc-read-time ![[←]](../icons/screen-return.gif) cc     rtc-read-time.c   -o rtc-read-time
$ su
cc     rtc-read-time.c   -o rtc-read-time
$ su ![[←]](../icons/screen-return.gif) Password: 
# ./rtc-read-time
Password: 
# ./rtc-read-time ![[←]](../icons/screen-return.gif) 2023-01-17 04:59:05
# ./rtc-read-time; hwclock --show; date
2023-01-17 04:59:05
# ./rtc-read-time; hwclock --show; date ![[←]](../icons/screen-return.gif) 2023-01-17 04:59:22
Tue Jan 17 13:59:23 2023  -0.349547 seconds
Tue Jan 17 13:59:23 JST 2023
# ./rtc-read-time; hwclock --show; date
2023-01-17 04:59:22
Tue Jan 17 13:59:23 2023  -0.349547 seconds
Tue Jan 17 13:59:23 JST 2023
# ./rtc-read-time; hwclock --show; date ![[←]](../icons/screen-return.gif) 2023-01-17 04:59:29
Tue Jan 17 13:59:30 2023  -0.704207 seconds
Tue Jan 17 13:59:30 JST 2023
#
2023-01-17 04:59:29
Tue Jan 17 13:59:30 2023  -0.704207 seconds
Tue Jan 17 13:59:30 JST 2023
# ![[]](../icons/screen-cursor.gif) 
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-6.1.2/include/linux/types.h
  13:	typedef u32 __kernel_dev_t;
...
  16:	typedef __kernel_dev_t          dev_t;
linux-6.1.2/include/linux/kdev_t.h
   7:	#define MINORBITS       20
   8:	#define MINORMASK       ((1U << MINORBITS) - 1)
   9:	
  10:	#define MAJOR(dev)      ((unsigned int) ((dev) >> MINORBITS))
  11:	#define MINOR(dev)      ((unsigned int) ((dev) & MINORMASK))
  12:	#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))
linux-6.1.2/include/linux/rtc.h
  87:	struct rtc_device {
...
  91:	        int id;
...
  96:	        struct cdev char_dev;
...
 163:	};
linux-6.1.2/drivers/rtc/dev.c
 524:	static const struct file_operations rtc_dev_fops = {
 525:	        .owner          = THIS_MODULE,
 526:	        .llseek         = no_llseek,
 527:	        .read           = rtc_dev_read,
 528:	        .poll           = rtc_dev_poll,
 529:	        .unlocked_ioctl = rtc_dev_ioctl,
 530:	#ifdef CONFIG_COMPAT
 531:	        .compat_ioctl   = rtc_dev_compat_ioctl,
 532:	#endif
 533:	        .open           = rtc_dev_open,
 534:	        .release        = rtc_dev_release,
 535:	        .fasync         = rtc_dev_fasync,
 536:	};
  19:	static dev_t rtc_devt;
 561:	void __init rtc_dev_init(void)
 562:	{
...
 565:	        err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
...
 568:	}
 540:	void rtc_dev_prepare(struct rtc_device *rtc)
 541:	{
...
 550:	        rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
...
 557:	        cdev_init(&rtc->char_dev, &rtc_dev_fops);
 558:	        rtc->char_dev.owner = rtc->owner;
 559:	}
linux-6.1.2/drivers/rtc/class.c
 389:	int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc)
 390:	{
...
 413:	        rtc_dev_prepare(rtc);
 414:	
 415:	        err = cdev_device_add(&rtc->char_dev, &rtc->dev);
...
 437:	}
linux-6.1.2/drivers/rtc/dev.c
  23:	static int rtc_dev_open(struct inode *inode, struct file *file)
  24:	{
  25:	        struct rtc_device *rtc = container_of(inode->i_cdev,
  26:	                                        struct rtc_device, char_dev);
  27:	
  28:	        if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))
  29:	                return -EBUSY;
  30:	
  31:	        file->private_data = rtc;
...
  37:	        return 0;
  38:	}
&rtc->flagsの
RTC_DEV_BUSY
ビットをテストする。
それがセットされていれば、
どれかのプロセスにより open("/dev/rtc",) がされていて、
close() はされていない状態である。
その場合は、EBUSY というエラーにして open() が失敗する。
&rtc->flagsの
RTC_DEV_BUSY
ビットは、
test_and_set の結果、自動的にセットされる。
linux-6.1.2/drivers/rtc/dev.c
 502:	static int rtc_dev_release(struct inode *inode, struct file *file)
 503:	{
 504:	        struct rtc_device *rtc = file->private_data;
...
 520:	        clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
 521:	        return 0;
 522:	}
rtc_dev_open() でセットした変数 &rtc->flagsの
RTC_DEV_BUSY ビットは、close() システム・コールで呼ばれる
rtc_dev_release() ( struct file_operations rtc_fops の.release) で、ク
リアされる。
linux-6.1.2/drivers/rtc/dev.c
 203:	static long rtc_dev_ioctl(struct file *file,
 204:	                          unsigned int cmd, unsigned long arg)
 205:	{
 206:	        int err = 0;
 207:	        struct rtc_device *rtc = file->private_data;
 208:	        const struct rtc_class_ops *ops = rtc->ops;
 209:	        struct rtc_time tm;
...
 212:	        void __user *uarg = (void __user *)arg;
...
 256:	        switch (cmd) {
 257:	        case RTC_ALM_READ:
...
 268:	        case RTC_ALM_SET:
...
 320:	        case RTC_RD_TIME:
...
 323:	                err = rtc_read_time(rtc, &tm);
 324:	                if (err < 0)
 325:	                        return err;
 326:	
 327:	                if (copy_to_user(uarg, &tm, sizeof(tm)))
 328:	                        err = -EFAULT;
 329:	                return err;
 330:	
 331:	        case RTC_SET_TIME:
...
 334:	                if (copy_from_user(&tm, uarg, sizeof(tm)))
 335:	                        return -EFAULT;
 336:	
 337:	                return rtc_set_time(rtc, &tm);
...
 460:	        }
...
 465:	}
カーネル空間とユーザ空間でデータをコピーする時には、次のような特殊な関 数を使う必要がある。
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-6.1.2/drivers/rtc/interface.c
 110:	int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
 111:	{
...
 118:	        err = __rtc_read_time(rtc, tm);
...
 123:	}
  84:	static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
  85:	{
  86:	        int err;
...
  93:	                memset(tm, 0, sizeof(struct rtc_time));
  94:	                err = rtc->ops->read_time(rtc->dev.parent, tm);
...
 101:	                rtc_add_offset(rtc, tm);
...
 103:	                err = rtc_valid_tm(tm);
 107:	        return err;
 108:	}
linux-6.1.2/drivers/rtc/rtc-cmos.c
 623:	static const struct rtc_class_ops cmos_rtc_ops = {
 624:	        .read_time              = cmos_read_time,
 625:	        .set_time               = cmos_set_time,
 626:	        .read_alarm             = cmos_read_alarm,
 627:	        .set_alarm              = cmos_set_alarm,
 628:	        .proc                   = cmos_procfs,
 629:	        .alarm_irq_enable       = cmos_alarm_irq_enable,
 630:	};
 223:	static int cmos_read_time(struct device *dev, struct rtc_time *t)
 224:	{
...
 234:	        ret = mc146818_get_time(t);
...
 240:	        return 0;
 241:	}
linux-6.1.2/drivers/rtc/rtc-mc146818-lib.c
 133:	int mc146818_get_time(struct rtc_time *time)
 134:	{
 135:	        struct mc146818_get_time_callback_param p = {
 136:	                .time = time
 137:	        };
 138:	
 139:	        if (!mc146818_avoid_UIP(mc146818_get_time_callback, &p)) {
...
 142:	        }
...
 170:	        if (time->tm_year <= 69)
 171:	                time->tm_year += 100;
 172:	
 173:	        time->tm_mon--;
 174:	
 175:	        return 0;
 176:	}
  17:	bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
  18:	                        void *param)
  19:	{
...
  25:	                spin_lock_irqsave(&rtc_lock, flags);
...
  36:	                seconds = CMOS_READ(RTC_SECONDS);
...
  45:	                if (seconds != CMOS_READ(RTC_SECONDS)) {
...
  48:	                }
  49:	
  50:	                if (callback)
  51:	                        callback(seconds, param);
...
  73:	                spin_unlock_irqrestore(&rtc_lock, flags);
...
  78:	}
 102:	static void mc146818_get_time_callback(unsigned char seconds, void *param_in)
 103:	{
 104:	        struct mc146818_get_time_callback_param *p = param_in;
 105:	
 106:	        /*
 107:	         * Only the values that we read from the RTC are set. We leave
 108:	         * tm_wday, tm_yday and tm_isdst untouched. Even though the
 109:	         * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
 110:	         * by the RTC when initially set to a non-zero value.
 111:	         */
 112:	        p->time->tm_sec = seconds;
 113:	        p->time->tm_min = CMOS_READ(RTC_MINUTES);
 114:	        p->time->tm_hour = CMOS_READ(RTC_HOURS);
 115:	        p->time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
 116:	        p->time->tm_mon = CMOS_READ(RTC_MONTH);
 117:	        p->time->tm_year = CMOS_READ(RTC_YEAR);
...
 131:	}
void outb(unsigned char value, unsigned short port) ポート番号 port に 1 バイトの value を出力する unsigned char inb(unsigned short port) ポート番号 port から 1 バイトの value を入力してその値を返す1 バイト 8 ビットではなくて 2 バイト 16 ビット 単位のもの (inw(), outw()) や4 バイト 32 ビット単位のもの( inl(), outl() ) もある。
linux-6.1.2/arch/x86/include/asm/mc146818rtc.h
  11:	#ifndef RTC_PORT
  12:	#define RTC_PORT(x)     (0x70 + (x))
...
  93:	#define CMOS_READ(addr) rtc_cmos_read(addr)
  94:	#define CMOS_WRITE(val, addr) rtc_cmos_write(val, addr)
linux-6.1.2/arch/x86/kernel/rtc.c
  81:	unsigned char rtc_cmos_read(unsigned char addr)
  82:	{
  83:	        unsigned char val;
  84:	
  85:	        lock_cmos_prefix(addr);
  86:	        outb(addr, RTC_PORT(0));
  87:	        val = inb(RTC_PORT(1));
  88:	        lock_cmos_suffix(addr);
  89:	
  90:	        return val;
  91:	}
...
  94:	void rtc_cmos_write(unsigned char val, unsigned char addr)
  95:	{
  96:	        lock_cmos_prefix(addr);
  97:	        outb(addr, RTC_PORT(0));
  98:	        outb(val, RTC_PORT(1));
  99:	        lock_cmos_suffix(addr);
 100:	}
linux-6.1.2/include/linux/mc146818rtc.h
  50:	#define RTC_SECONDS             0
  51:	#define RTC_SECONDS_ALARM       1
  52:	#define RTC_MINUTES             2
  53:	#define RTC_MINUTES_ALARM       3
  54:	#define RTC_HOURS               4
  55:	#define RTC_HOURS_ALARM         5
...
  59:	#define RTC_DAY_OF_WEEK         6
  60:	#define RTC_DAY_OF_MONTH        7
  61:	#define RTC_MONTH               8
  62:	#define RTC_YEAR                9
linux-5.15.12/arch/x86/boot/boot.h
  39:	static inline void outb(u8 v, u16 port)
  40:	{
  41:	        asm volatile("outb %0,%1" : : "a" (v), "dN" (port));
  42:	}
  43:	static inline u8 inb(u16 port)
  44:	{
  45:	        u8 v;
  46:	        asm volatile("inb %1,%0" : "=a" (v) : "dN" (port));
  47:	        return v;
  48:	}
asm ( "アセンブラの命令列" : 出力オペランド(省略可) : 入力オペランド(省略可) : 破壊するレジスタ(省略可) )
= があると、書き込み専用になる。
linux-6.1.2/arch/x86/include/asm/shared/io.h
   7:	#define BUILDIO(bwl, bw, type)                                          \
   8:	static inline void __out##bwl(type value, u16 port)                     \
   9:	{                                                                       \
  10:	        asm volatile("out" #bwl " %" #bw "0, %w1"                       \
  11:	                     : : "a"(value), "Nd"(port));                       \
  12:	}                                                                       \
  13:	                                                                        \
  14:	static inline type __in##bwl(u16 port)                                  \
  15:	{                                                                       \
  16:	        type value;                                                     \
  17:	        asm volatile("in" #bwl " %w1, %" #bw "0"                        \
  18:	                     : "=a"(value) : "Nd"(port));                       \
  19:	        return value;                                                   \
  20:	}
  21:	
  22:	BUILDIO(b, b, u8)
  23:	BUILDIO(w, w, u16)
  24:	BUILDIO(l,  , u32)
...
  27:	#define inb __inb
  28:	#define inw __inw
  29:	#define inl __inl
  30:	#define outb __outb
  31:	#define outw __outw
  32:	#define outl __outl
id1##id2」 は、
識別子(関数名、変数名等)の結合を意味する。
たとえば、マクロ定義の引数 bwl が値 b を持っていれば、out##bwl は、outb となる。
(「#」がなければ、「out bwl」 は、「out b」と間に空白が残る)。
#var」 は、文字列化。
たとえば、マクロ定義の引数 bwl が値 b を持っていれば、#bwl は、"b" となる。
(「#」がなければ、bwl は、b )。
static inline void __outb(u8 value, u16 port) {
	asm volatile("outb %b0, %w1" : : "a"(value), "Nd"(port));
}
static inline u8 __inb(u16 port) {
	u8 value;
	asm volatile("inb %w1, %b0" : "=a"(value) : "Nd"(port));
	return value;
}
$ cat /proc/ioports ![[←]](../icons/screen-return.gif) 0000-0cf7 : PCI Bus 0000:00
  0000-001f : dma1
  0020-0021 : PNP0001:00
    0020-0021 : pic1
  0040-0043 : timer0
  0050-0053 : timer1
  0060-0060 : keyboard
  0061-0061 : PNP0800:00
  0064-0064 : keyboard
  0070-0073 : rtc0
  0080-008f : dma page reg
...
0d00-feff : PCI Bus 0000:00
...
  e000-efff : PCI Bus 0000:19
$
0000-0cf7 : PCI Bus 0000:00
  0000-001f : dma1
  0020-0021 : PNP0001:00
    0020-0021 : pic1
  0040-0043 : timer0
  0050-0053 : timer1
  0060-0060 : keyboard
  0061-0061 : PNP0800:00
  0064-0064 : keyboard
  0070-0073 : rtc0
  0080-008f : dma page reg
...
0d00-feff : PCI Bus 0000:00
...
  e000-efff : PCI Bus 0000:19
$ ![[]](../icons/screen-cursor.gif) 

図? x86 の Intel 8259

図? x86 の APIC
例:
例:

図? 割り込み記述子テーブルと割り込みハンドラ
typedef void (*funcp_t)(void); funcp_t idt[256];
old_pc = pc; old_flags = flags;
n = 割り込みベクタ; handler = idt[n];
push old_pc; push old_flags; pc = handler; // (*handler)();ただし、単純な call 命令とは違い、スタック上にプログラムカウンタの他に、 プロセッサの状態を示すフラグ等も積む。

図? PICの線が不足した時の対応
include/linux/interrupt.h
typedef irqreturn_t (*irq_handler_t)(int, void *);
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
            const char *name, void *dev)
void free_irq(unsigned int, void *dev)
irqreturn_t handler(int irq, void *dev)
$ cat /proc/interrupts  ![[←]](../icons/screen-return.gif) CPU0       CPU1       
  0:    4208761      38584    IO-APIC-edge  timer
  1:          0          3    IO-APIC-edge  i8042
  7:          0          0    IO-APIC-edge  parport0
  8:          1          2    IO-APIC-edge  rtc
  9:          0          0   IO-APIC-level  acpi
 12:          3          1    IO-APIC-edge  i8042
 50:       5380      86508         PCI-MSI  ahci
 74:        346          0         PCI-MSI  HDA Intel
 98:        294      28232         PCI-MSI  eth1
169:        130      57006   IO-APIC-level  uhci_hcd:usb3
177:          0          0   IO-APIC-level  uhci_hcd:usb4, uhci_hcd:usb7
217:        358     149530   IO-APIC-level  ehci_hcd:usb1, uhci_hcd:usb5
225:          0          0   IO-APIC-level  ehci_hcd:usb2, uhci_hcd:usb6
233:          0          0   IO-APIC-level  uhci_hcd:usb8
NMI:          0          0 
LOC:    4246864    4246863 
ERR:          0
MIS:          0
$
           CPU0       CPU1       
  0:    4208761      38584    IO-APIC-edge  timer
  1:          0          3    IO-APIC-edge  i8042
  7:          0          0    IO-APIC-edge  parport0
  8:          1          2    IO-APIC-edge  rtc
  9:          0          0   IO-APIC-level  acpi
 12:          3          1    IO-APIC-edge  i8042
 50:       5380      86508         PCI-MSI  ahci
 74:        346          0         PCI-MSI  HDA Intel
 98:        294      28232         PCI-MSI  eth1
169:        130      57006   IO-APIC-level  uhci_hcd:usb3
177:          0          0   IO-APIC-level  uhci_hcd:usb4, uhci_hcd:usb7
217:        358     149530   IO-APIC-level  ehci_hcd:usb1, uhci_hcd:usb5
225:          0          0   IO-APIC-level  ehci_hcd:usb2, uhci_hcd:usb6
233:          0          0   IO-APIC-level  uhci_hcd:usb8
NMI:          0          0 
LOC:    4246864    4246863 
ERR:          0
MIS:          0
$ ![[]](../icons/screen-cursor.gif) 
linux-6.1.2/arch/x86/include/asm/mc146818rtc.h
 101:	#define RTC_IRQ 8
linux-6.1.2/drivers/rtc/rtc-cmos.c
  73:	struct cmos_rtc {
  74:	        struct rtc_device       *rtc;
  75:	        struct device           *dev;
  76:	        int                     irq;
....
  92:	};
 697:	static struct cmos_rtc  cmos_rtc;
 916:	static int INITSECTION
 917:	cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 918:	{
...
1016:	        cmos_rtc.rtc = devm_rtc_allocate_device(dev);
...
1063:	                irq_handler_t rtc_cmos_int_handler;
...
1075:	                        rtc_cmos_int_handler = cmos_interrupt;
1076:	
1077:	                retval = request_irq(rtc_irq, rtc_cmos_int_handler,
1078:	                                0, dev_name(&cmos_rtc.rtc->dev),
1079:	                                cmos_rtc.rtc);
...
1090:	        retval = devm_rtc_register_device(cmos_rtc.rtc);
...
1130:	}
 699:	static irqreturn_t cmos_interrupt(int irq, void *p)
 700:	{
 701:	        u8              irqstat;
...
 713:	        irqstat = CMOS_READ(RTC_INTR_FLAGS);
...
 740:	        if (is_intr(irqstat)) {
 741:	                rtc_update_irq(p, 1, irqstat);
 742:	                return IRQ_HANDLED;
 743:	        } else
 744:	                return IRQ_NONE;
 745:	}
linux-6.1.2/arch/x86/kernel/traps.c
1454:	void __init trap_init(void)
1455:	{
...
1465:	        idt_setup_traps();
...
1467:	}
linux-6.1.2/arch/x86/include/asm/trapnr.h
   7:	#define X86_TRAP_DE              0      /* Divide-by-zero */
   8:	#define X86_TRAP_DB              1      /* Debug */
   9:	#define X86_TRAP_NMI             2      /* Non-maskable Interrupt */
  10:	#define X86_TRAP_BP              3      /* Breakpoint */
  11:	#define X86_TRAP_OF              4      /* Overflow */
  12:	#define X86_TRAP_BR              5      /* Bound Range Exceeded */
  13:	#define X86_TRAP_UD              6      /* Invalid Opcode */
  14:	#define X86_TRAP_NM              7      /* Device Not Available */
linux-6.1.2/arch/x86/include/asm/irq_vectors.h
  45:	#define IA32_SYSCALL_VECTOR             0x80
...
 109:	#define NR_VECTORS                       256
linux-6.1.2/arch/x86/kernel/idt.c
 167:	static gate_desc idt_table[IDT_ENTRIES] __page_aligned_bss;
  83:	static const __initconst struct idt_data def_idts[] = {
  84:	        INTG(X86_TRAP_DE,               asm_exc_divide_error),
  85:	        ISTG(X86_TRAP_NMI,              asm_exc_nmi, IST_INDEX_NMI),
  86:	        INTG(X86_TRAP_BR,               asm_exc_bounds),
  87:	        INTG(X86_TRAP_UD,               asm_exc_invalid_op),
  88:	        INTG(X86_TRAP_NM,               asm_exc_device_not_available),
...
 124:	};
 226:	void __init idt_setup_traps(void)
 227:	{
 228:	        idt_setup_from_table(idt_table, def_idts, ARRAY_SIZE(def_idts), true);
 229:	}
linux-6.1.2/arch/x86/include/asm/idtentry.h
 429:	#define DECLARE_IDTENTRY(vector, func)                                  \
 430:	        idtentry vector asm_##func func has_error_code=0
 548:	DECLARE_IDTENTRY(X86_TRAP_DE,           exc_divide_error);
 549:	DECLARE_IDTENTRY(X86_TRAP_OF,           exc_overflow);
 550:	DECLARE_IDTENTRY(X86_TRAP_BR,           exc_bounds);
 551:	DECLARE_IDTENTRY(X86_TRAP_NM,           exc_device_not_available);
linux-6.1.2/arch/x86/entry/entry_64.S
 607:	#include <asm/idtentry.h>
 383:	.macro idtentry vector asmsym cfunc has_error_code:req
 384:	SYM_CODE_START(\asmsym)
...
 408:	        idtentry_body \cfunc \has_error_code
 412:	.endm
 342:	.macro idtentry_body cfunc has_error_code:req
...
 365:	        call    \cfunc
...
 370:	        jmp     error_return
 371:	.endm
1127:	SYM_CODE_START_LOCAL(error_return)
...
1131:	        jz      restore_regs_and_return_to_kernel
 662:	SYM_INNER_LABEL(restore_regs_and_return_to_kernel, SYM_L_GLOBAL)
...
 702:	        iretq
linux-6.1.2/arch/x86/kernel/traps.c
 205:	DEFINE_IDTENTRY(exc_divide_error)
 206:	{
 207:	        do_error_trap(regs, 0, "divide error", X86_TRAP_DE, SIGFPE,
 208:	                      FPE_INTDIV, error_get_trap_addr(regs));
 209:	}
linux-6.1.2/arch/x86/entry/entry_64.S
  87:	SYM_CODE_START(entry_SYSCALL_64)
...
 120:	        call    do_syscall_64           /* returns with IRQs disabled */
...
 226:	        sysretq
linux-6.1.2/arch/x86/kernel/cpu/common.c
2029:	void syscall_init(void)
2030:	{
2031:	        wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
2032:	        wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
...
2063:	}
linux-6.1.2/kernel/irq/handle.c
 139:	irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc)
 140:	{
 141:	        irqreturn_t retval = IRQ_NONE;
 142:	        unsigned int irq = desc->irq_data.irq;
 143:	        struct irqaction *action;
...
 147:	        for_each_action_of_desc(desc, action) {
 148:	                irqreturn_t res;
...
 158:	                res = action->handler(irq, action->dev_id);
...
 183:	                retval |= res;
 184:	        }
 185:	
 186:	        return retval;
 187:	}
linux-6.1.2/kernel/irq/internals.h
 165:	#define for_each_action_of_desc(desc, act)                      \
 166:	        for (act = desc->action; act; act = act->next)
linux-6.1.2/include/linux/irqreturn.h
  12:	        IRQ_NONE                = (0 << 0),
  13:	        IRQ_HANDLED             = (1 << 0),
struct irqaction *action は、
action->next でリスト構造を作っている。
プロセス・コンテキストでできること。
割り込みコンテキストでは、このうようなことはできない。 速やかに終了すべきである。busy loop はできるが、あまり やらない方がよい。他の割り込みは、実行される可能性もある。
unsigned long flags; local_irq_save(flags); /* 割り込み禁止。マクロ。 */ ... local_irq_restore(flags); /* 割り込み許可 (save の時の状態にもどる) */単一CPUの x86 では、cli() と sti() で割り込みの禁止と許可を設定する方法 があった。それそれ同名の CPU の命令を実行して、全ての割り込みを禁止/許 可する。マルチプロセッサ(マルチコア含む)では、1つのCPU で割り込みを禁止 しても、他の CPU では許可されていることがあるので、cli()/sti() の方法は 使えない。
特定の割り込み番号の割り込みを禁止する方法もある。
void disable_irq(unsigned ing irq);
    // 全CPUの割り込みを禁止する
void disable_irq_nosync(unsigned ing irq);
    // 同上。ただし、割り込みハンドラの終了を待たない。
void enable_irq(unsigned ing irq);
    // 割り込みを許可する。
void synchronize_irq(unsigned ing irq);
    // 割り込みハンドラの終了を待つ。
if( memcpy( /*空欄(a)*/,/*空欄(b)*/,/*空欄(c)*/ ), 0 ) return -EFAULT;なお、memcpy() のインタフェースは、次のようになっている。
void * memcpy(void *destination, const void *source, size_t len);sourceは、コピー元、destination は、コピー先、len は長さ(バイト数)であ る。結果として destination を返す。
C言語の「,」演算子は、次のような意味である。
    式1 , 式2
「式1」を評価し、次に「式2」を評価し、
全体としては、「式2」の値を返す。
この課題は、常に 0 (成功) を返すようにしている。
なお、__user は、ユーザ空間のアドレスを意味し、エラー・チェックに使われ る、Cプリプロセッサで空の文字列に展開されることもある。この問題では空の 文字列に展開されると考えなさい。
    unsigned char day;
    outb( /*空欄(a)*/, 0x70 );
    day = inb( /*空欄(b)*/ );
drivers/rtc/{class.c,dev.c,rtc-cmos.c} で定義されて、このページで説明し ている関数のうち、次のものを1つ上げなさい。