提交 a0694a30 创建 作者: 赵鹏翀's avatar 赵鹏翀

Initial commit

上级
/*.img
/bochs
/Debug
/Release
*.o
*.lst
\ No newline at end of file
用于完成Linux内核的实验
\ No newline at end of file
差异被折叠。
差异被折叠。
差异被折叠。
/*
* linux/fs/bitmap.c
*
* (C) 1991 Linus Torvalds
*/
/* bitmap.c contains the code that handles the inode and block bitmaps */
/* bitmap.c 程序含有处理 i 节点和磁盘块位图的代码 */
#include <string.h> // 字符串头文件。主要定义了一些有关字符串操作的嵌入函数。
// 主要使用了其中的memset()函数。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
//// 将指定地址(addr)处的一块内存清零。嵌入汇编程序宏。
// 输入:eax = 0,ecx = 数据块大小BLOCK_SIZE/4,edi = addr。
#define clear_block(addr) \
__asm__ __volatile__ ("cld\n\t" \
"rep\n\t" \
"stosl" \
::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)))
//// 置位指定地址开始的第nr 个位偏移处的比特位(nr 可以大于32!)。返回原比特位(0 或1)。
// 输入:%0 - eax(返回值),%1 - eax(0);%2 - nr,位偏移值;%3 - (addr),addr 的内容。
#define set_bit(nr,addr) ({\
register int res ; \
__asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
//// 复位指定地址开始的第nr 位偏移处的比特位。返回原比特位的反码(1 或0)。
// 输入:%0 - eax(返回值),%1 - eax(0);%2 - nr,位偏移值;%3 - (addr),addr 的内容。
#define clear_bit(nr,addr) ({\
register int res ; \
__asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \
"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
res;})
//// 从addr 开始寻找第1 个0 值比特位。
// 输入:%0 - ecx(返回值);%1 - ecx(0);%2 - esi(addr)。
// 在addr 指定地址开始的位图中寻找第1 个是0 的比特位,并将其距离addr 的比特位偏移值返回。
#define find_first_zero(addr) ({ \
int __res; \
__asm__ __volatile__ ("cld\n" \
"1:\tlodsl\n\t" \
"notl %%eax\n\t" \
"bsfl %%eax,%%edx\n\t" \
"je 2f\n\t" \
"addl %%edx,%%ecx\n\t" \
"jmp 3f\n" \
"2:\taddl $32,%%ecx\n\t" \
"cmpl $8192,%%ecx\n\t" \
"jl 1b\n" \
"3:" \
:"=c" (__res):"c" (0),"S" (addr)); \
__res;})
//// 释放设备dev 上数据区中的逻辑块block。
// 复位指定逻辑块block 的逻辑块位图比特位。
// 参数:dev 是设备号,block 是逻辑块号(盘块号)。
void free_block(int dev, int block)
{
struct super_block * sb;
struct buffer_head * bh;
// 取指定设备dev 的超级块,如果指定设备不存在,则出错死机。
if (!(sb = get_super(dev)))
panic("trying to free block on nonexistent device");
// 若逻辑块号小于首个逻辑块号或者大于设备上总逻辑块数,则出错,死机。
if (block < sb->s_firstdatazone || block >= sb->s_nzones)
panic("trying to free block not in datazone");
// 从hash 表中寻找该块数据。若找到了则判断其有效性,并清已修改和更新标志,释放该数据块。
// 该段代码的主要用途是如果该逻辑块当前存在于高速缓冲中,就释放对应的缓冲块。
bh = get_hash_table(dev,block);
if (bh) {
if (bh->b_count != 1) {
printk("trying to free block (%04x:%d), count=%d\n",
dev,block,bh->b_count);
return;
}
bh->b_dirt=0; // 复位脏(已修改)标志位。
bh->b_uptodate=0; // 复位更新标志。
brelse(bh);
}
// 计算block 在数据区开始算起的数据逻辑块号(从1 开始计数)。然后对逻辑块(区块)位图进行操作,
// 复位对应的比特位。若对应比特位原来即是0,则出错,死机。
block -= sb->s_firstdatazone - 1 ;
if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
panic("free_block: bit already cleared");
}
// 置相应逻辑块位图所在缓冲区已修改标志。
sb->s_zmap[block/8192]->b_dirt = 1;
}
////向设备dev 申请一个逻辑块(盘块,区块)。返回逻辑块号(盘块号)。
// 置位指定逻辑块block 的逻辑块位图比特位。
int new_block(int dev)
{
struct buffer_head * bh;
struct super_block * sb;
int i,j;
// 从设备dev 取超级块,如果指定设备不存在,则出错死机。
if (!(sb = get_super(dev)))
panic("trying to get new block from nonexistant device");
// 扫描逻辑块位图,寻找首个0 比特位,寻找空闲逻辑块,获取放置该逻辑块的块号。
j = 8192;
for (i=0 ; i<8 ; i++)
if ((bh=sb->s_zmap[i]))
if ((j=find_first_zero(bh->b_data))<8192)
break;
// 如果全部扫描完还没找到(i>=8 或j>=8192)或者位图所在的缓冲块无效(bh=NULL)则 返回0,
// 退出(没有空闲逻辑块)。
if (i>=8 || !bh || j>=8192)
return 0;
// 设置新逻辑块对应逻辑块位图中的比特位,若对应比特位已经置位,则出错,死机。
if (set_bit(j,bh->b_data))
panic("new_block: bit already set");
// 置对应缓冲区块的已修改标志。如果新逻辑块大于该设备上的总逻辑块数,则说明指定逻辑块在
// 对应设备上不存在。申请失败,返回0,退出。
bh->b_dirt = 1;
j += i*8192 + sb->s_firstdatazone-1;
if (j >= sb->s_nzones)
return 0;
// 读取设备上的该新逻辑块数据(验证)。如果失败则死机。
if (!(bh=getblk(dev,j)))
panic("new_block: cannot get block");
// 新块的引用计数应为1。否则死机。
if (bh->b_count != 1)
panic("new block: count is != 1");
// 将该新逻辑块清零,并置位更新标志和已修改标志。然后释放对应缓冲区,返回逻辑块号。
clear_block(bh->b_data);
bh->b_uptodate = 1;
bh->b_dirt = 1;
brelse(bh);
return j;
}
//// 释放指定的i 节点。
// 复位对应i 节点位图比特位。
void free_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
// 如果i 节点指针=NULL,则退出。
if (!inode)
return;
// 如果i 节点上的设备号字段为0,说明该节点无用,则用0 清空对应i 节点所占内存区,并返回。
if (!inode->i_dev) {
memset(inode,0,sizeof(*inode));
return;
}
// 如果此i 节点还有其它程序引用,则不能释放,说明内核有问题,死机。
if (inode->i_count>1) {
printk("trying to free inode with count=%d\n",inode->i_count);
panic("free_inode");
}
// 如果文件目录项连接数不为0,则表示还有其它文件目录项在使用该节点,不应释放,而应该放回等。
if (inode->i_nlinks)
panic("trying to free inode with links");
// 取i 节点所在设备的超级块,测试设备是否存在。
if (!(sb = get_super(inode->i_dev)))
panic("trying to free inode on nonexistent device");
// 如果i 节点号=0 或大于该设备上i 节点总数,则出错(0 号i 节点保留没有使用)。
if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
panic("trying to free inode 0 or nonexistant inode");
// 如果该i 节点对应的节点位图不存在,则出错。
if (!(bh=sb->s_imap[inode->i_num>>13]))
panic("nonexistent imap in superblock");
// 复位i 节点对应的节点位图中的比特位,如果该比特位已经等于0,则出错。
if (clear_bit(inode->i_num&8191,bh->b_data))
printk("free_inode: bit already cleared.\n\r");
// 置i 节点位图所在缓冲区已修改标志,并清空该i 节点结构所占内存区。
bh->b_dirt = 1;
memset(inode,0,sizeof(*inode));
}
//// 为设备dev 建立一个新i 节点。返回该新i 节点的指针。
// 在内存i 节点表中获取一个空闲i 节点表项,并从i 节点位图中找一个空闲i 节点。
struct m_inode * new_inode(int dev)
{
struct m_inode * inode;
struct super_block * sb;
struct buffer_head * bh;
int i,j;
// 从内存i 节点表(inode_table)中获取一个空闲i 节点项(inode)。
if (!(inode=get_empty_inode()))
return NULL;
// 读取指定设备的超级块结构。
if (!(sb = get_super(dev)))
panic("new_inode with unknown device");
// 扫描i 节点位图,寻找首个0 比特位,寻找空闲节点,获取放置该i 节点的节点号。
j = 8192;
for (i=0 ; i<8 ; i++)
if ((bh=sb->s_imap[i]))
if ((j=find_first_zero(bh->b_data))<8192)
break;
// 如果全部扫描完还没找到,或者位图所在的缓冲块无效(bh=NULL)则 返回0,退出(没有空闲i 节点)。
if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
iput(inode);
return NULL;
}
// 置位对应新i 节点的i 节点位图相应比特位,如果已经置位,则出错。
if (set_bit(j,bh->b_data))
panic("new_inode: bit already set");
// 置i 节点位图所在缓冲区已修改标志。
bh->b_dirt = 1;
// 初始化该i 节点结构。
inode->i_count = 1; // 引用计数。
inode->i_nlinks = 1; // 文件目录项链接数。
inode->i_dev = dev; // i 节点所在的设备号。
inode->i_uid = current->euid; // i 节点所属用户id。
inode->i_gid = current->egid; // 组id。
inode->i_dirt = 1; // 已修改标志置位。
inode->i_num = j + i * 8192; // 对应设备中的i 节点号。
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; // 设置时间。
return inode; // 返回该i 节点指针。
}
/*
* linux/fs/block_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> // 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
#include <asm/system.h> // 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。
//// 数据块写函数 - 向指定设备从给定偏移处写入指定长度字节数据。
// 参数:dev - 设备号;pos - 设备文件中偏移量指针;buf - 用户地址空间中缓冲区地址;
// count - 要传送的字节数。
// 对于内核来说,写操作是向高速缓冲区中写入数据,什么时候数据最终写入设备是由高速缓冲管理
// 程序决定并处理的。另外,因为设备是以块为单位进行读写的,因此对于写开始位置不处于块起始
// 处时,需要先将开始字节所在的整个块读出,然后将需要写的数据从写开始处填写满该块,再将完
// 整的一块数据写盘(即交由高速缓冲程序去处理)。
int block_write(int dev, long * pos, char * buf, int count)
{
// 由pos 地址换算成开始读写块的块序号block。并求出需读第1 字节在该块中的偏移位置offset。
int block = *pos >> BLOCK_SIZE_BITS; // pos所在文件数据块号
int offset = *pos & (BLOCK_SIZE-1); // pos在数据块中的偏移值
int chars;
int written = 0;
struct buffer_head * bh;
register char * p;
// 针对要写入的字节数count,循环执行以下操作,直到全部写入。
while (count > 0)
{
// 计算在该块中可写入的字节数。如果需要写入的字节数填不满一块,则只需写count 字节。
chars = BLOCK_SIZE - offset;
if (chars > count)
chars = count;
// 如果正好要写1 块数据,则直接申请1 块高速缓冲块,否则需要读入将被修改的数据块,并预读
// 下两块数据,然后将块号递增1。
if (chars == BLOCK_SIZE)
bh = getblk (dev, block); // buffer.c中实现
else
bh = breada (dev, block, block + 1, block + 2, -1);
block++;
// 如果缓冲块操作失败,则返回已写字节数,如果没有写入任何字节,则返回出错号(负数)。
if (!bh)
return written ? written : -EIO;
// p 指向读出数据块中开始写的位置。若最后写入的数据不足一块,则需从块开始填写(修改)所需
// 的字节,因此这里需置offset 为零。
p = offset + bh->b_data;
offset = 0;
// 将文件中偏移指针前移已写字节数。累加已写字节数chars。传送计数值减去此次已传送字节数。
*pos += chars;
written += chars; // 累计写入的字节数
count -= chars;
// 从用户缓冲区复制chars 字节到p 指向的高速缓冲区中开始写入的位置。
while (chars-- > 0)
*(p++) = get_fs_byte (buf++);
// 置该缓冲区块已修改标志,并释放该缓冲区(也即该缓冲区引用计数递减1)。
bh->b_dirt = 1;
brelse (bh);
}
return written;
}
//// 数据块读函数 - 从指定设备和位置读入指定字节数的数据到高速缓冲中。
int block_read(int dev, unsigned long * pos, char * buf, int count)
{
// 由pos 地址换算成开始读写块的块序号block。并求出需读第1 字节在该块中的偏移位置offset。
int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos & (BLOCK_SIZE-1);
int chars;
int read = 0;
struct buffer_head * bh;
register char * p;
// 针对要读入的字节数count,循环执行以下操作,直到全部读入。
while (count>0) {
// 计算在该块中需读入的字节数。如果需要读入的字节数不满一块,则只需读count 字节。
chars = BLOCK_SIZE-offset;
if (chars > count)
chars = count;
// 读入需要的数据块,并预读下两块数据,如果读操作出错,则返回已读字节数,如果没有读入任何
// 字节,则返回出错号。然后将块号递增1。
if (!(bh = breada(dev,block,block+1,block+2,-1)))
return read?read:-EIO;
block++;
// p 指向从设备读出数据块中需要读取的开始位置。若最后需要读取的数据不足一块,则需从块开始
// 读取所需的字节,因此这里需将offset 置零。
p = offset + bh->b_data;
offset = 0;
// 将文件中偏移指针前移已读出字节数chars。累加已读字节数。传送计数值减去此次已传送字节数。
*pos += chars;
read += chars;
count -= chars;
// 从高速缓冲区中p 指向的开始位置复制chars 字节数据到用户缓冲区,并释放该高速缓冲区。
while (chars-->0)
put_fs_byte(*(p++),buf++);
brelse(bh);
}
return read; // 返回已读取的字节数,正常退出。
}
差异被折叠。
/*
* linux/fs/char_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> // 错误号头文件。包含系统中各种出错号。
#include <sys/types.h> // 类型头文件。定义了基本的系统数据类型。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
#include <asm/io.h> // io 头文件。定义硬件端口输入/输出宏汇编语句。
extern int tty_read(unsigned minor,char * buf,int count); // 终端读。
extern int tty_write(unsigned minor,char * buf,int count); // 终端写。
// 定义字符设备读写函数指针类型。
typedef int (*crw_ptr)(int rw,unsigned minor,char * buf,int count,off_t * pos);
//// 串口终端读写操作函数。
// 参数:rw - 读写命令;minor - 终端子设备号;buf - 缓冲区;cout - 读写字节数;
// pos - 读写操作当前指针,对于终端操作,该指针无用。
// 返回:实际读写的字节数。
static int rw_ttyx(int rw,unsigned minor,char * buf,int count,off_t * pos)
{
return ((rw==READ)?tty_read(minor,buf,count):
tty_write(minor,buf,count));
}
//// 终端读写操作函数。
// 同上rw_ttyx(),只是增加了对进程是否有控制终端的检测。
static int rw_tty(int rw,unsigned minor,char * buf,int count, off_t * pos)
{
// 若进程没有对应的控制终端,则返回出错号。
if (current->tty<0)
return -EPERM;
// 否则调用终端读写函数rw_ttyx(),并返回实际读写字节数。
return rw_ttyx(rw,current->tty,buf,count,pos);
}
//// 内存数据读写。未实现。
static int rw_ram(int rw,char * buf, int count, off_t *pos)
{
return -EIO;
}
//// 内存数据读写操作函数。未实现。
static int rw_mem(int rw,char * buf, int count, off_t * pos)
{
return -EIO;
}
//// 内核数据区读写函数。未实现。
static int rw_kmem(int rw,char * buf, int count, off_t * pos)
{
return -EIO;
}
// 端口读写操作函数。
// 参数:rw - 读写命令;buf - 缓冲区;cout - 读写字节数;pos - 端口地址。
// 返回:实际读写的字节数。
static int rw_port(int rw,char * buf, int count, off_t * pos)
{
int i=*pos;
// 对于所要求读写的字节数,并且端口地址小于64k 时,循环执行单个字节的读写操作。
while (count-->0 && i<65536) {
// 若是读命令,则从端口i 中读取一字节内容并放到用户缓冲区中。
if (rw==READ)
put_fs_byte(inb(i),buf++);
// 若是写命令,则从用户数据缓冲区中取一字节输出到端口i。
else
outb(get_fs_byte(buf++),i);
// 前移一个端口。
i++;
}
// 计算读/写的字节数,并相应调整读写指针。
i -= *pos;
*pos += i;
// 返回读/写的字节数。
return i;
}
//// 内存读写操作函数。
static int rw_memory(int rw, unsigned minor, char * buf, int count, off_t * pos)
{
// 根据内存设备子设备号,分别调用不同的内存读写函数。
switch(minor) {
case 0:
return rw_ram(rw,buf,count,pos);
case 1:
return rw_mem(rw,buf,count,pos);
case 2:
return rw_kmem(rw,buf,count,pos);
case 3:
return (rw==READ)?0:count; /* rw_null */
case 4:
return rw_port(rw,buf,count,pos);
default:
return -EIO;
}
}
// 定义系统中设备种数。
#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr)))
// 字符设备读写函数指针表。
static crw_ptr crw_table[]={
NULL, /* nodev */ /* 无设备(空设备) */
rw_memory, /* /dev/mem etc */ /* /dev/mem 等 */
NULL, /* /dev/fd */ /* /dev/fd 软驱 */
NULL, /* /dev/hd */ /* /dev/hd 硬盘 */
rw_ttyx, /* /dev/ttyx */ /* /dev/ttyx 串口终端 */
rw_tty, /* /dev/tty */ /* /dev/tty 终端 */
NULL, /* /dev/lp */ /* /dev/lp 打印机 */
NULL}; /* unnamed pipes */ /* 未命名管道 */
//// 字符设备读写操作函数。
// 参数:rw - 读写命令;dev - 设备号;buf - 缓冲区;count - 读写字节数;pos -读写指针。
// 返回:实际读/写字节数。
int rw_char(int rw,int dev, char * buf, int count, off_t * pos)
{
crw_ptr call_addr;
// 如果设备号超出系统设备数,则返回出错码。
if (MAJOR(dev)>=NRDEVS)
return -ENODEV;
// 若该设备没有对应的读/写函数,则返回出错码。
if (!(call_addr=crw_table[MAJOR(dev)]))
return -ENODEV;
// 调用对应设备的读写操作函数,并返回实际读/写的字节数。
return call_addr(rw,MINOR(dev),buf,count,pos);
}
差异被折叠。
/*
* linux/fs/fcntl.c
*
* (C) 1991 Linus Torvalds
*/
/* #include <string.h> */
#include <errno.h> // 错误号头文件。包含系统中各种出错号。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
#include <fcntl.h> // 文件控制头文件。用于文件及其描述符的操作控制常数符号的定义。
#include <sys/stat.h> // 文件状态头文件。含有文件或文件系统状态结构stat{}和常量。
extern int sys_close(int fd); // 关闭文件系统调用。(fs/open.c)
//// 复制文件句柄(描述符)。
// 参数fd 是欲复制的文件句柄,arg 指定新文件句柄的最小数值。
// 返回新文件句柄或出错码
static int dupfd(unsigned int fd, unsigned int arg)
{
// 如果文件句柄值大于一个程序最多打开文件数NR_OPEN,或者该句柄的文件结构不存在,则出错,
// 返回出错码并退出。
if (fd >= NR_OPEN || !current->filp[fd])
return -EBADF;
// 如果指定的新句柄值arg 大于最多打开文件数,则出错,返回出错码并退出。
if (arg >= NR_OPEN)
return -EINVAL;
// 在当前进程的文件结构指针数组中寻找索引号大于等于arg 但还没有使用的项。
while (arg < NR_OPEN)
if (current->filp[arg])
arg++;
else
break;
// 如果找到的新句柄值arg 大于最多打开文件数,则出错,返回出错码并退出。
if (arg >= NR_OPEN)
return -EMFILE;
// 在执行时关闭标志位图中复位该句柄位。也即在运行exec()类函数时不关闭该句柄。
current->close_on_exec &= ~(1<<arg);
// 令该文件结构指针等于原句柄fd 的指针,并将文件引用计数增1。
(current->filp[arg] = current->filp[fd])->f_count++;
return arg; // 返回新的文件句柄
}
//// 复制文件句柄系统调用函数。
// 复制指定文件句柄oldfd,新句柄值等于newfd。如果newfd 已经打开,则首先关闭之。
int sys_dup2(unsigned int oldfd, unsigned int newfd)
{
sys_close(newfd); // 若句柄newfd 已经打开,则首先关闭之。
return dupfd(oldfd,newfd); // 复制并返回新句柄。
}
//// 复制文件句柄系统调用函数。
// 复制指定文件句柄oldfd,新句柄的值是当前最小的未用句柄。
int sys_dup(unsigned int fildes)
{
return dupfd(fildes,0);
}
//// 文件控制系统调用函数。
// 参数fd 是文件句柄,cmd 是操作命令(参见include/fcntl.h)。
int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
// 如果文件句柄值大于一个进程最多打开文件数NR_OPEN,或者该句柄的文件结构指针为空,则出错,
// 返回出错码并退出。
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
// 根据不同命令cmd 进行分别处理。
switch (cmd) {
case F_DUPFD: // 复制文件句柄。
return dupfd(fd,arg);
case F_GETFD: // 取文件句柄的执行时关闭标志。
return (current->close_on_exec>>fd)&1;
case F_SETFD: // 设置句柄执行时关闭标志。arg 位0 置位是设置,否则关闭。
if (arg&1)
current->close_on_exec |= (1<<fd);
else
current->close_on_exec &= ~(1<<fd);
return 0;
case F_GETFL: // 取文件状态标志和访问模式。
return filp->f_flags;
case F_SETFL: // 设置文件状态和访问模式(根据arg 设置添加、非阻塞标志)。
filp->f_flags &= ~(O_APPEND | O_NONBLOCK);
filp->f_flags |= arg & (O_APPEND | O_NONBLOCK);
return 0;
case F_GETLK: case F_SETLK: case F_SETLKW: // 为实现
return -1;
default:
return -1;
}
}
/*
* linux/fs/file_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> // 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)
#include <fcntl.h> // 文件控制头文件。用于文件及其描述符的操作控制常数符号的定义。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
#define MIN(a,b) (((a)<(b))?(a):(b)) // 取a,b 中的最小值。
#define MAX(a,b) (((a)>(b))?(a):(b)) // 取a,b 中的最大值。
//// 文件读函数 - 根据i 节点和文件结构,读设备数据。
// 由i 节点可以知道设备号,由filp 结构可以知道文件中当前读写指针位置。buf 指定用户态中
// 缓冲区的位置,count 为需要读取的字节数。返回值是实际读取的字节数,或出错号(小于0)。
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
{
int left,chars,nr;
struct buffer_head * bh;
// 若需要读取的字节计数值小于等于零,则返回。
if ((left = count) <= 0)
return 0;
// 若还需要读取的字节数不等于0,就循环执行以下操作,直到全部读出。
while (left)
{
// 根据i 节点和文件表结构信息,取数据块文件当前读写位置在设备上对应的逻辑块号nr。若nr 不
// 为0,则从i 节点指定的设备上读取该逻辑块,如果读操作失败则退出循环。若nr 为0,表示指定
// 的数据块不存在,置缓冲块指针为NULL。
if ((nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)))
{
if (!(bh = bread (inode->i_dev, nr)))
break;
}
else
bh = NULL;
// 计算文件读写指针在数据块中的偏移值nr,则该块中可读字节数为(BLOCK_SIZE-nr),然后与还需
// 读取的字节数left 作比较,其中小值即为本次需读的字节数chars。若(BLOCK_SIZE-nr)大则说明
// 该块是需要读取的最后一块数据,反之则还需要读取一块数据。
nr = filp->f_pos % BLOCK_SIZE;
chars = MIN (BLOCK_SIZE - nr, left);
// 调整读写文件指针。指针前移此次将读取的字节数chars。剩余字节计数相应减去chars。
filp->f_pos += chars;
left -= chars;
// 若从设备上读到了数据,则将p 指向读出数据块缓冲区中开始读取的位置,并且复制chars 字节
// 到用户缓冲区buf 中。否则往用户缓冲区中填入chars 个0 值字节。
if (bh)
{
char *p = nr + bh->b_data;
while (chars-- > 0)
put_fs_byte (*(p++), buf++);
brelse (bh);
}
else
{
while (chars-- > 0)
put_fs_byte (0, buf++);
}
}
// 修改该i 节点的访问时间为当前时间。返回读取的字节数,若读取字节数为0,则返回出错号。
inode->i_atime = CURRENT_TIME;
return (count-left)?(count-left):-ERROR;
}
//// 文件写函数 - 根据i 节点和文件结构信息,将用户数据写入指定设备。
// 由i 节点可以知道设备号,由filp 结构可以知道文件中当前读写指针位置。buf 指定用户态中
// 缓冲区的位置,count 为需要写入的字节数。返回值是实际写入的字节数,或出错号(小于0)。
int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
{
off_t pos;
int block,c;
struct buffer_head * bh;
char * p;
int i=0;
/*
* ok, append may not work when many processes are writing at the same time
* but so what. That way leads to madness anyway.
*/
/*
* ok,当许多进程同时写时,append 操作可能不行,但那又怎样。不管怎样那样做会
* 导致混乱一团。
*/
// 如果是要向文件后添加数据,则将文件读写指针移到文件尾部。否则就将在文件读写指针处写入。
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;
// 若已写入字节数i 小于需要写入的字节数count,则循环执行以下操作。
while (i<count) {
// 创建数据块号(pos/BLOCK_SIZE)在设备上对应的逻辑块,并返回在设备上的逻辑块号。如果逻辑
// 块号=0,则表示创建失败,退出循环。
if (!(block = create_block(inode,pos/BLOCK_SIZE)))
break;
// 根据该逻辑块号读取设备上的相应数据块,若出错则退出循环。
if (!(bh=bread(inode->i_dev,block)))
break;
// 求出文件读写指针在数据块中的偏移值c,将p 指向读出数据块缓冲区中开始读取的位置。置该
// 缓冲区已修改标志。
c = pos % BLOCK_SIZE;
p = c + bh->b_data;
bh->b_dirt = 1;
// 从开始读写位置到块末共可写入c=(BLOCK_SIZE-c)个字节。若c 大于剩余还需写入的字节数
// (count-i),则此次只需再写入c=(count-i)即可。
c = BLOCK_SIZE-c;
if (c > count-i) c = count-i;
// 文件读写指针前移此次需写入的字节数。如果当前文件读写指针位置值超过了文件的大小,则
// 修改i 节点中文件大小字段,并置i 节点已修改标志。
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
// 已写入字节计数累加此次写入的字节数c。从用户缓冲区buf 中复制c 个字节到高速缓冲区中p
// 指向开始的位置处。然后释放该缓冲区。
i += c;
while (c-->0)
*(p++) = get_fs_byte(buf++);
brelse(bh);
}
// 更改文件修改时间为当前时间。
inode->i_mtime = CURRENT_TIME;
// 如果此次操作不是在文件尾添加数据,则把文件读写指针调整到当前读写位置,并更改i 节点修改
// 时间为当前时间。
if (!(filp->f_flags & O_APPEND)) {
filp->f_pos = pos;
inode->i_ctime = CURRENT_TIME;
}
// 返回写入的字节数,若写入字节数为0,则返回出错号-1。
return (i?i:-1);
}
/*
* linux/fs/file_table.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/fs.h> // 文件系统头文件。定义文件表结构(file,buffer_head, m_inode等).
struct file file_table[NR_FILE]; // 文件表数组 (64项)
差异被折叠。
/*
* linux/fs/ioctl.c
*
* (C) 1991 Linus Torvalds
*/
/* #include <string.h>*/
#include <errno.h> // 错误号头文件。包含系统中各种出错号。
#include <sys/stat.h> // 文件状态头文件。含有文件或文件系统状态结构stat{}和常量。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
extern int tty_ioctl(int dev, int cmd, int arg); // 终端ioctl(chr_drv/tty_ioctl.c)。
// 定义输入输出控制(ioctl)函数指针。
typedef int (*ioctl_ptr)(int dev,int cmd,int arg);
// 定义系统中设备种数。
#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr)))
// ioctl 操作函数指针表。
static ioctl_ptr ioctl_table[]={
NULL, /* nodev */
NULL, /* /dev/mem */
NULL, /* /dev/fd */
NULL, /* /dev/hd */
tty_ioctl, /* /dev/ttyx */
tty_ioctl, /* /dev/tty */
NULL, /* /dev/lp */
NULL}; /* named pipes */
//// 系统调用函数 - 输入输出控制函数。
// 参数:fd - 文件描述符;cmd - 命令码;arg - 参数。
// 返回:成功则返回0,否则返回出错码。
int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
int dev,mode;
// 如果文件描述符超出可打开的文件数,或者对应描述符的文件结构指针为空,则返回出错码,退出。
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
// 取对应文件的属性。如果该文件不是字符文件,也不是块设备文件,则返回出错码,退出。
mode=filp->f_inode->i_mode;
if (!S_ISCHR(mode) && !S_ISBLK(mode))
return -EINVAL;
// 从字符或块设备文件的i 节点中取设备号。如果设备号大于系统现有的设备数,则返回出错号。
dev = filp->f_inode->i_zone[0];
if (MAJOR(dev) >= NRDEVS)
return -ENODEV;
// 如果该设备在ioctl 函数指针表中没有对应函数,则返回出错码。
if (!ioctl_table[MAJOR(dev)])
return -ENOTTY;
// 否则返回实际ioctl 函数返回码,成功则返回0,否则返回出错码。
return ioctl_table[MAJOR(dev)](dev,cmd,arg);
}
差异被折叠。
差异被折叠。
/*
* linux/fs/pipe.c
*
* (C) 1991 Linus Torvalds
*/
#include <signal.h> // 信号头文件。定义信号符号常量,信号结构以及信号操作函数原型。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/mm.h> /* for get_free_page */ /* 使用其中的get_free_page */
// 内存管理头文件。含有页面大小定义和一些页面释放函数原型。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
//// 管道读操作函数。
// 参数inode 是管道对应的i 节点,buf 是数据缓冲区指针,count 是读取的字节数。
int read_pipe(struct m_inode * inode, char * buf, int count)
{
int chars, size, read = 0;
// 若欲读取的字节计数值count 大于0,则循环执行以下操作。
while (count>0) {
// 若当前管道中没有数据(size=0),则唤醒等待该节点的进程,如果已没有写管道者,则返回已读
// 字节数,退出。否则在该i 节点上睡眠,等待信息。
while (!(size=PIPE_SIZE(*inode))) {
wake_up(&inode->i_wait);
if (inode->i_count != 2) /* are there any writers? */
return read;
sleep_on(&inode->i_wait);
}
// 取管道尾到缓冲区末端的字节数chars。如果其大于还需要读取的字节数count,则令其等于count。
// 如果chars 大于当前管道中含有数据的长度size,则令其等于size。
chars = PAGE_SIZE-PIPE_TAIL(*inode);
if (chars > count)
chars = count;
if (chars > size)
chars = size;
// 读字节计数减去此次可读的字节数chars,并累加已读字节数。
count -= chars;
read += chars;
// 令size 指向管道尾部,调整当前管道尾指针(前移chars 字节)。
size = PIPE_TAIL(*inode);
PIPE_TAIL(*inode) += chars;
PIPE_TAIL(*inode) &= (PAGE_SIZE-1);
// 将管道中的数据复制到用户缓冲区中。对于管道i 节点,其i_size 字段中是管道缓冲块指针。
while (chars-->0)
put_fs_byte(((char *)inode->i_size)[size++],buf++);
}
// 唤醒等待该管道i 节点的进程,并返回读取的字节数。
wake_up(&inode->i_wait);
return read;
}
//// 管道写操作函数。
// 参数inode 是管道对应的i 节点,buf 是数据缓冲区指针,count 是将写入管道的字节数。
int write_pipe(struct m_inode * inode, char * buf, int count)
{
int chars, size, written = 0;
// 若将写入的字节计数值count 还大于0,则循环执行以下操作。
while (count>0) {
// 若当前管道中没有已经满了(size=0),则唤醒等待该节点的进程,如果已没有读管道者,则向进程
// 发送SIGPIPE 信号,并返回已写入的字节数并退出。若写入0 字节,则返回-1。否则在该i 节点上
// 睡眠,等待管道腾出空间。
while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) {
wake_up(&inode->i_wait);
if (inode->i_count != 2) { /* no readers */
current->signal |= (1<<(SIGPIPE-1));
return written?written:-1;
}
sleep_on(&inode->i_wait);
}
// 取管道头部到缓冲区末端空间字节数chars。如果其大于还需要写入的字节数count,则令其等于
// count。如果chars 大于当前管道中空闲空间长度size,则令其等于size。
chars = PAGE_SIZE-PIPE_HEAD(*inode);
if (chars > count)
chars = count;
if (chars > size)
chars = size;
// 写入字节计数减去此次可写入的字节数chars,并累加已写字节数到written。
count -= chars;
written += chars;
// 令size 指向管道数据头部,调整当前管道数据头部指针(前移chars 字节)。
size = PIPE_HEAD(*inode);
PIPE_HEAD(*inode) += chars;
PIPE_HEAD(*inode) &= (PAGE_SIZE-1);
// 从用户缓冲区复制chars 个字节到管道中。对于管道i 节点,其i_size 字段中是管道缓冲块指针。
while (chars-->0)
((char *)inode->i_size)[size++]=get_fs_byte(buf++);
}
// 唤醒等待该i 节点的进程,返回已写入的字节数,退出。
wake_up(&inode->i_wait);
return written;
}
//// 创建管道系统调用函数。
// 在fildes 所指的数组中创建一对文件句柄(描述符)。这对文件句柄指向一管道i 节点。fildes[0]
// 用于读管道中数据,fildes[1]用于向管道中写入数据。
// 成功时返回0,出错时返回-1。
int sys_pipe(unsigned long * fildes)
{
struct m_inode * inode;
struct file * f[2];
int fd[2];
int i,j;
// 从系统文件表中取两个空闲项(引用计数字段为0 的项),并分别设置引用计数为1。
j=0;
for(i=0;j<2 && i<NR_FILE;i++)
if (!file_table[i].f_count)
(f[j++]=i+file_table)->f_count++;
// 如果只有一个空闲项,则释放该项(引用计数复位)。
if (j==1)
f[0]->f_count=0;
// 如果没有找到两个空闲项,则返回-1。
if (j<2)
return -1;
// 针对上面取得的两个文件结构项,分别分配一文件句柄,并使进程的文件结构指针分别指向这两个
// 文件结构。
j=0;
for(i=0;j<2 && i<NR_OPEN;i++)
if (!current->filp[i]) {
current->filp[ fd[j]=i ] = f[j];
j++;
}
// 如果只有一个空闲文件句柄,则释放该句柄。
if (j==1)
current->filp[fd[0]]=NULL;
// 如果没有找到两个空闲句柄,则释放上面获取的两个文件结构项(复位引用计数值),并返回-1。
if (j<2) {
f[0]->f_count=f[1]->f_count=0;
return -1;
}
// 申请管道i 节点,并为管道分配缓冲区(1 页内存)。如果不成功,则相应释放两个文件句柄和文
// 件结构项,并返回-1。
if (!(inode=get_pipe_inode())) {
current->filp[fd[0]] =
current->filp[fd[1]] = NULL;
f[0]->f_count = f[1]->f_count = 0;
return -1;
}
// 初始化两个文件结构,都指向同一个i 节点,读写指针都置零。第1 个文件结构的文件模式置为读,
// 第2 个文件结构的文件模式置为写。
f[0]->f_inode = f[1]->f_inode = inode;
f[0]->f_pos = f[1]->f_pos = 0;
f[0]->f_mode = 1; /* read */
f[1]->f_mode = 2; /* write */
// 将文件句柄数组复制到对应的用户数组中,并返回0,退出。
put_fs_long(fd[0],0+fildes);
put_fs_long(fd[1],1+fildes);
return 0;
}
/*
* linux/fs/read_write.c
*
* (C) 1991 Linus Torvalds
*/
#include <sys/stat.h> // 文件状态头文件。含有文件或文件系统状态结构stat{}和常量。
#include <errno.h> // 错误号头文件。包含系统中各种出错号。
#include <sys/types.h> // 类型头文件。定义了基本的系统数据类型。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
// 字符设备读写函数。 fs/char_dev.c
extern int rw_char (int rw, int dev, char *buf, int count, off_t * pos);
// 读管道操作函数。 fs/pipe.c
extern int read_pipe (struct m_inode *inode, char *buf, int count);
// 写管道操作函数。fs/pipe.c
extern int write_pipe (struct m_inode *inode, char *buf, int count);
// 块设备读操作函数。fs/block_dev.c
extern int block_read (int dev, off_t * pos, char *buf, int count);
// 块设备写操作函数。fs/block_dev.c
extern int block_write (int dev, off_t * pos, char *buf, int count);
// 读文件操作函数。fs/file_dev.c
extern int file_read (struct m_inode *inode, struct file *filp,
char *buf, int count);
// 写文件操作函数。fs/file_dev.c
extern int file_write (struct m_inode *inode, struct file *filp,
char *buf, int count);
//// 重定位文件读写指针系统调用函数。
// 参数fd 是文件句柄,offset 是新的文件读写指针偏移值,origin 是偏移的起始位置,是SEEK_SET
// (0,从文件开始处)、SEEK_CUR(1,从当前读写位置)、SEEK_END(2,从文件尾处)三者之一。
int sys_lseek(unsigned int fd,off_t offset, int origin)
{
struct file * file;
int tmp;
if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode)
|| !IS_SEEKABLE(MAJOR(file->f_inode->i_dev)))
return -EBADF;
// 如果文件对应的i 节点是管道节点,则返回出错码,退出。管道头尾指针不可随意移动!
if (file->f_inode->i_pipe)
return -ESPIPE;
// 根据设置的定位标志,分别重新定位文件读写指针。
switch (origin) {
// origin = SEEK_SET,要求以文件起始处作为原点设置文件读写指针。若偏移值小于零,则出错返
// 回错误码。否则设置文件读写指针等于offset。
case 0:
if (offset<0) return -EINVAL;
file->f_pos=offset;
break;
// origin = SEEK_CUR,要求以文件当前读写指针处作为原点重定位读写指针。如果文件当前指针加
// 上偏移值小于0,则返回出错码退出。否则在当前读写指针上加上偏移值。
case 1:
if (file->f_pos+offset<0) return -EINVAL;
file->f_pos += offset;
break;
// origin = SEEK_END,要求以文件末尾作为原点重定位读写指针。此时若文件大小加上偏移值小于零
// 则返回出错码退出。否则重定位读写指针为文件长度加上偏移值。
case 2:
if ((tmp=file->f_inode->i_size+offset) < 0)
return -EINVAL;
file->f_pos = tmp;
break;
// origin 设置出错,返回出错码退出。
default:
return -EINVAL;
}
return file->f_pos; // 返回重定位后的文件读写指针值。
}
//// 读文件系统调用函数。
// 参数fd 是文件句柄,buf 是缓冲区,count 是欲读字节数。
int sys_read(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
// 如果文件句柄值大于程序最多打开文件数NR_OPEN,或者需要读取的字节计数值小于0,或者该句柄
// 的文件结构指针为空,则返回出错码并退出。
if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))
return -EINVAL;
// 若需读取的字节数count 等于0,则返回0,退出
if (!count)
return 0;
// 验证存放数据的缓冲区内存限制。
verify_area(buf,count);
// 取文件对应的i 节点。若是管道文件,并且是读管道文件模式,则进行读管道操作,若成功则返回
// 读取的字节数,否则返回出错码,退出。
inode = file->f_inode;
if (inode->i_pipe)
return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
// 如果是字符型文件,则进行读字符设备操作,返回读取的字符数。
if (S_ISCHR(inode->i_mode))
return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
// 如果是块设备文件,则执行块设备读操作,并返回读取的字节数。
if (S_ISBLK(inode->i_mode))
return block_read(inode->i_zone[0],&file->f_pos,buf,count);
// 如果是目录文件或者是常规文件,则首先验证读取数count 的有效性并进行调整(若读取字节数加上
// 文件当前读写指针值大于文件大小,则重新设置读取字节数为文件长度-当前读写指针值,若读取数
// 等于0,则返回0 退出),然后执行文件读操作,返回读取的字节数并退出。
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
if (count+file->f_pos > inode->i_size)
count = inode->i_size - file->f_pos;
if (count<=0)
return 0;
return file_read(inode,file,buf,count);
}
// 否则打印节点文件属性,并返回出错码退出。
printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
//// 写文件系统调用
// 参数fd是文件句柄,buf 是用户缓冲区,count是欲写的字节数。
int sys_write(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
// 如果文件句柄值大于程序最多打开文件数NR_OPEN,或者需要写入的字节计数小于0,或者该句柄
// 的文件结构指针为空,则返回出错码并退出。
if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
return -EINVAL;
// 若需读取的字节数count 等于0,则返回0,退出
if (!count)
return 0;
// 取文件对应的i 节点。若是管道文件,并且是写管道文件模式,则进行写管道操作,若成功则返回
// 写入的字节数,否则返回出错码,退出。
inode=file->f_inode;
if (inode->i_pipe)
return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO;
// 如果是字符型文件,则进行写字符设备操作,返回写入的字符数,退出。
if (S_ISCHR(inode->i_mode))
return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
// 如果是块设备文件,则进行块设备写操作,并返回写入的字节数,退出。
if (S_ISBLK(inode->i_mode))
return block_write(inode->i_zone[0],&file->f_pos,buf,count);
// 若是常规文件,则执行文件写操作,并返回写入的字节数,退出。
if (S_ISREG(inode->i_mode))
return file_write(inode,file,buf,count);
// 否则,显示对应节点的文件模式,返回出错码,退出。
printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
/* nothing , only the stub */
/* gohigh */
#include <errno.h>
int sys_select()
{
return -ENOSYS;
}
/*
* linux/fs/stat.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> // 错误号头文件。包含系统中各种出错号。
#include <sys/stat.h> // 文件状态头文件。含有文件或文件系统状态结构stat{}和常量。
#include <linux/fs.h> // 文件系统头文件。定义文件表结构(file,buffer_head,m_inode 等)。
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
//// 复制文件状态信息。
// 参数inode 是文件对应的i 节点,statbuf 是stat 文件状态结构指针,用于存放取得的状态信息。
static void cp_stat(struct m_inode * inode, struct stat * statbuf)
{
struct stat tmp;
int i;
// 首先验证(或分配)存放数据的内存空间。
verify_area(statbuf,sizeof (* statbuf));
// 然后临时复制相应节点上的信息。
tmp.st_dev = inode->i_dev; // 文件所在的设备号。
tmp.st_ino = inode->i_num; // 文件i 节点号。
tmp.st_mode = inode->i_mode; // 文件属性。
tmp.st_nlink = inode->i_nlinks; // 文件的连接数。
tmp.st_uid = inode->i_uid; // 文件的用户id。
tmp.st_gid = inode->i_gid; // 文件的组id。
tmp.st_rdev = inode->i_zone[0]; // 设备号(如果文件是特殊的字符文件或块文件)。
tmp.st_size = inode->i_size; // 文件大小(字节数)(如果文件是常规文件)。
tmp.st_atime = inode->i_atime; // 最后访问时间。
tmp.st_mtime = inode->i_mtime; // 最后修改时间。
tmp.st_ctime = inode->i_ctime; // 最后节点修改时间。
// 最后将这些状态信息复制到用户缓冲区中。
for (i=0 ; i<sizeof (tmp) ; i++)
put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]);
}
//// 文件状态系统调用函数 - 根据文件名获取文件状态信息。
// 参数filename 是指定的文件名,statbuf 是存放状态信息的缓冲区指针。
// 返回0,若出错则返回出错码。
int sys_stat(char * filename, struct stat * statbuf)
{
struct m_inode * inode;
// 首先根据文件名找出对应的 i节点。若出错,则返回出错码.
if (!(inode=namei(filename)))
return -ENOENT;
// 然后将i节点上的文件状态信息复制到用户缓冲区中,并放回该i节点.
cp_stat(inode,statbuf);
iput(inode);
return 0;
}
//// 文件状态系统调用 - 根据文件句柄获取文件状态信息。
// 参数fd 是指定文件的句柄(描述符),statbuf 是存放状态信息的缓冲区指针。
// 返回0,若出错则返回出错码。
int sys_fstat(unsigned int fd, struct stat * statbuf)
{
struct file * f;
struct m_inode * inode;
// 如果文件句柄值大于一个程序最多打开文件数NR_OPEN,或者该句柄的文件结构指针为空,或者
// 对应文件结构的i 节点字段为空,则出错,返回出错码并退出。
if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode))
return -EBADF;
// 将i 节点上的文件状态信息复制到用户缓冲区中。
cp_stat(inode,statbuf);
return 0;
}
int sys_lstat(char * filename, struct stat * statbuf)
{
return sys_stat(filename, statbuf);
}
int sys_readlink()
{
return -ENOSYS;
}
差异被折叠。
/*
* linux/fs/truncate.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <sys/stat.h> // 文件状态头文件。含有文件或文件系统状态结构stat{}和常量。
//// 释放一次间接块。
static void free_ind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
// 如果逻辑块号为0,则返回。
if (!block)
return;
// 读取一次间接块,并释放其上表明使用的所有逻辑块,然后释放该一次间接块的缓冲区。
if ((bh=bread(dev,block))) {
p = (unsigned short *) bh->b_data; // 指向数据缓冲区。
for (i=0;i<512;i++,p++) // 每个逻辑块上可有512 个块号。
if (*p)
free_block(dev,*p); // 释放指定的逻辑块。
brelse(bh); // 释放缓冲区。
}
// 最后释放设备上的一次间接块
free_block(dev,block);
}
//// 释放二次间接块。
static void free_dind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
// 如果逻辑块号为0,则返回。
if (!block)
return;
// 读取二次间接块的一级块,并释放其上表明使用的所有逻辑块,然后释放该一级块的缓冲区。
if ((bh=bread(dev,block))) {
p = (unsigned short *) bh->b_data; // 指向数据缓冲区。
for (i=0;i<512;i++,p++) // 每个逻辑块上可连接512 个二级块。
if (*p)
free_ind(dev,*p); // 释放所有一次间接块。
brelse(bh); // 释放缓冲区。
}
// 最后释放设备上的二次间接块。
free_block(dev,block);
}
//// 将节点对应的文件长度截为0,并释放占用的设备空间。
void truncate(struct m_inode * inode)
{
int i;
// 如果不是常规文件或者是目录文件,则返回。
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return;
// 释放i 节点的7 个直接逻辑块,并将这7 个逻辑块项全置零。
for (i=0;i<7;i++)
if (inode->i_zone[i]) {
// 如果块号不为0,则释放之。
free_block(inode->i_dev,inode->i_zone[i]);
inode->i_zone[i]=0;
}
free_ind(inode->i_dev,inode->i_zone[7]); // 释放一次间接块。
free_dind(inode->i_dev,inode->i_zone[8]); // 释放二次间接块。
inode->i_zone[7] = inode->i_zone[8] = 0; // 逻辑块项7、8 置零。
inode->i_size = 0; // 文件大小置零。
inode->i_dirt = 1; // 置节点已修改标志。
inode->i_mtime = inode->i_ctime = CURRENT_TIME; // 重置文件和节点修改时间为当前时间。
}
差异被折叠。
//// 硬件端口字节输出函数。
// 参数:value - 欲输出字节;port - 端口。
#define outb(value,port) \
__asm__ ("outb %%al,%%dx"::"a" (value),"d" (port))
//// 硬件端口字节输入函数。
// 参数:port - 端口。返回读取的字节。
#define inb(port) ({ \
unsigned char _v; \
__asm__ volatile ("inb %%dx,%%al":"=a" (_v):"d" (port)); \
_v; \
})
//// 带延迟的硬件端口字节输出函数。
// 参数:value - 欲输出字节;port - 端口。
#define outb_p(value,port) \
__asm__ ("outb %%al,%%dx\n" \
"\tjmp 1f\n" \
"1:\tjmp 1f\n" \
"1:"::"a" (value),"d" (port))
//// 带延迟的硬件端口字节输入函数。
// 参数:port - 端口。返回读取的字节。
#define inb_p(port) ({ \
unsigned char _v; \
__asm__ volatile ("inb %%dx,%%al\n" \
"\tjmp 1f\n" \
"1:\tjmp 1f\n" \
"1:":"=a" (_v):"d" (port)); \
_v; \
})
/*
* NOTE!!! memcpy(dest,src,n) assumes ds=es=normal data segment. This
* goes for all kernel functions (ds=es=kernel space, fs=local data,
* gs=null), as well as for all well-behaving user programs (ds=es=
* user data space). This is NOT a bug, as any user program that changes
* es deserves to die if it isn't careful.
*/
/*
* 注意!!!memcpy(dest,src,n)假设段寄存器ds=es=通常数据段。在内核中使用的
* 所有函数都基于该假设(ds=es=内核空间,fs=局部数据空间,gs=null),具有良好
* 行为的应用程序也是这样(ds=es=用户数据空间)。如果任何用户程序随意改动了
* es 寄存器而出错,则并不是由于系统程序错误造成的。
*/
//// 内存块复制。从源地址src 处开始复制n 个字节到目的地址dest 处。
// 参数:dest - 复制的目的地址,src - 复制的源地址,n - 复制字节数。
// %0 - edi(目的地址dest),%1 - esi(源地址src),%2 - ecx(字节数n),
#define memcpy(dest,src,n) ({ \
void * _res = dest; \
__asm__ ("cld;rep;movsb" \
::"D" ((long)(_res)),"S" ((long)(src)),"c" ((long) (n)) \
); \
_res; \
})
//// 读取fs 段中指定地址处的字节。
// 参数:addr - 指定的内存地址。
// %0 - (返回的字节_v);%1 - (内存地址addr)。
// 返回:返回内存fs:[addr]处的字节。
static inline unsigned char get_fs_byte(const char * addr)
{
unsigned register char _v;
__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));
return _v;
}
//// 读取fs 段中指定地址处的字。
// 参数:addr - 指定的内存地址。
// %0 - (返回的字_v);%1 - (内存地址addr)。
// 返回:返回内存fs:[addr]处的字。
static inline unsigned short get_fs_word(const unsigned short *addr)
{
unsigned short _v;
__asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr));
return _v;
}
//// 读取fs 段中指定地址处的长字(4 字节)。
// 参数:addr - 指定的内存地址。
// %0 - (返回的长字_v);%1 - (内存地址addr)。
// 返回:返回内存fs:[addr]处的长字。
static inline unsigned long get_fs_long(const unsigned long *addr)
{
unsigned long _v;
__asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \
return _v;
}
//// 将一字节存放在fs 段中指定内存地址处。
// 参数:val - 字节值;addr - 内存地址。
// %0 - 寄存器(字节值val);%1 - (内存地址addr)。
static inline void put_fs_byte(char val,char *addr)
{
__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr));
}
//// 将一字存放在fs 段中指定内存地址处。
// 参数:val - 字值;addr - 内存地址。
// %0 - 寄存器(字值val);%1 - (内存地址addr)。
static inline void put_fs_word(short val,short * addr)
{
__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr));
}
//// 将一长字存放在fs 段中指定内存地址处。
// 参数:val - 长字值;addr - 内存地址。
// %0 - 寄存器(长字值val);%1 - (内存地址addr)。
static inline void put_fs_long(unsigned long val,unsigned long * addr)
{
__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr));
}
/*
* Someone who knows GNU asm better than I should double check the followig.
* It seems to work, but I don't know if I'm doing something subtly wrong.
* --- TYT, 11/24/91
* [ nothing wrong here, Linus ]
*/
/*
* 比我更懂GNU 汇编的人应该仔细检查下面的代码。这些代码能使用,但我不知道是否
* 含有一些小错误。
* --- TYT,1991 年11 月24 日
* [ 这些代码没有错误,Linus ]
*/
//// 取fs 段寄存器值(选择符)。
// 返回:fs 段寄存器值。
static inline unsigned long get_fs()
{
unsigned short _v;
__asm__("mov %%fs,%%ax":"=a" (_v):);
return _v;
}
//// 取ds 段寄存器值。
// 返回:ds 段寄存器值。
static inline unsigned long get_ds()
{
unsigned short _v;
__asm__("mov %%ds,%%ax":"=a" (_v):);
return _v;
}
//// 设置fs 段寄存器。
// 参数:val - 段值(选择符)。
static inline void set_fs(unsigned long val)
{
__asm__("mov %0,%%fs"::"a" ((unsigned short) val));
}
//// 切换到用户模式运行。
// 该函数利用iret 指令实现从内核模式切换到用户模式(初始任务0)。
#define move_to_user_mode() \
__asm__ ("movl %%esp,%%eax\n\t" \
"pushl $0x17\n\t" \
"pushl %%eax\n\t" \
"pushfl\n\t" \
"pushl $0x0f\n\t" \
"pushl $1f\n\t" \
"iret\n" \
"1:\tmovl $0x17,%%eax\n\t" \
"movw %%ax,%%ds\n\t" \
"movw %%ax,%%es\n\t" \
"movw %%ax,%%fs\n\t" \
"movw %%ax,%%gs" \
:::"ax")
#define sti() __asm__ ("sti"::) // 开中断嵌入汇编宏函数。
#define cli() __asm__ ("cli"::) // 关中断。
#define nop() __asm__ ("nop"::) // 空操作。
#define iret() __asm__ ("iret"::) // 中断返回。
//// 设置门描述符宏函数。
// 参数:gate_addr -描述符地址;type -描述符中类型域值;dpl -描述符特权层值;addr -偏移地址。
// %0 - (由dpl,type 组合成的类型标志字);%1 - (描述符低4 字节地址);
// %2 - (描述符高4 字节地址);%3 - edx(程序偏移地址addr);%4 - eax(高字中含有段选择符)。
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
"movw %0,%%dx\n\t" \
"movl %%eax,%1\n\t" \
"movl %%edx,%2" \
: \
: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"o" (*((char *) (gate_addr))), \
"o" (*(4+(char *) (gate_addr))), \
"d" ((char *) (addr)),"a" (0x00080000))
//// 设置中断门函数。
// 参数:n - 中断号;addr - 中断程序偏移地址。
// &idt[n]对应中断号在中断描述符表中的偏移值;中断描述符的类型是14,特权级是0。
#define set_intr_gate(n,addr) \
_set_gate(&idt[n],14,0,addr)
//// 设置陷阱门函数。
// 参数:n - 中断号;addr - 中断程序偏移地址。
// &idt[n]对应中断号在中断描述符表中的偏移值;中断描述符的类型是15,特权级是0。
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
//// 设置系统调用门函数。
// 参数:n - 中断号;addr - 中断程序偏移地址。
// &idt[n]对应中断号在中断描述符表中的偏移值;中断描述符的类型是15,特权级是3。
#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr)
//// 设置段描述符函数。
// 参数:gate_addr -描述符地址;type -描述符中类型域值;dpl -描述符特权层值;
// base - 段的基地址;limit - 段限长。(参见段描述符的格式)
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
*(gate_addr) = ((base) & 0xff000000) | \
(((base) & 0x00ff0000)>>16) | \
((limit) & 0xf0000) | \
((dpl)<<13) | \
(0x00408000) | \
((type)<<8); \
*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \
((limit) & 0x0ffff); }
//// 在全局表中设置任务状态段/局部表描述符。
// 参数:n - 在全局表中描述符项n 所对应的地址;addr - 状态段/局部表所在内存的基地址。
// type - 描述符中的标志类型字节。
// %0 - eax(地址addr);%1 - (描述符项n 的地址);%2 - (描述符项n 的地址偏移2 处);
// %3 - (描述符项n 的地址偏移4 处);%4 - (描述符项n 的地址偏移5 处);
// %5 - (描述符项n 的地址偏移6 处);%6 - (描述符项n 的地址偏移7 处);
#define _set_tssldt_desc(n,addr,type) \
__asm__ ("movw $104,%1\n\t" \
"movw %%ax,%2\n\t" \
"rorl $16,%%eax\n\t" \
"movb %%al,%3\n\t" \
"movb $" type ",%4\n\t" \
"movb $0x00,%5\n\t" \
"movb %%ah,%6\n\t" \
"rorl $16,%%eax" \
::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
"m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
)
//// 在全局表中设置任务状态段描述符。
// n - 是该描述符的指针;addr - 是描述符中的基地址值。任务状态段描述符的类型是0x89。
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x89")
//// 在全局表中设置局部表描述符。
// n - 是该描述符的指针;addr - 是描述符中的基地址值。局部表描述符的类型是0x82。
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),"0x82")
#ifndef _CONST_H
#define _CONST_H
#define BUFFER_END 0x200000 // 定义缓冲使用内存的末端(代码中没有使用该常量)
// i节点数据结构中 i_mode 字段的个标志位
#define I_TYPE 0170000 // 指明i节点类型(类型屏蔽码)
#define I_DIRECTORY 0040000 // 是目录文件。
#define I_REGULAR 0100000 // 是常规文件,不是目录文件或特殊文件
#define I_BLOCK_SPECIAL 0060000 // 是块设备特殊文件
#define I_CHAR_SPECIAL 0020000 // 是字符设备特殊文件
#define I_NAMED_PIPE 0010000 // 是命名管道节点
#define I_SET_UID_BIT 0004000 // 在执行时设置有效用户ID类型
#define I_SET_GID_BIT 0002000 // 在执行时设置有效组 ID类型
#endif
#ifndef _CTYPE_H
#define _CTYPE_H
#define _U 0x01 /* upper */ // 该比特位用于大写字符[A-Z]。
#define _L 0x02 /* lower */ // 该比特位用于小写字符[a-z]。
#define _D 0x04 /* digit */ // 该比特位用于数值[0-9]。
#define _C 0x08 /* cntrl */ // 该比特位用于控制字符。
#define _P 0x10 /* punct */ // 该比特位用于标点字符
#define _S 0x20 /* white space (space/lf/tab) */ // 空白字符,如空格、\t、\n等。
#define _X 0x40 /* hex digit */ // 该比特位用于十六进制数字
#define _SP 0x80 /* hard space (0x20) */ // 该比特位用于空格字符(0x20)
extern unsigned char _ctype[]; // 字符特征数组(表),定义各个字符对应上面的属性。
extern char _ctmp; // 一个临时字符变量(在定义lib/cype.c中)
// 下面是一些确定字符类型的宏
#define isalnum(c) ((_ctype+1)[(int)c]&(_U|_L|_D)) // 是字符或数字 [A-Z]、 [a-z]、[0-9]。
#define isalpha(c) ((_ctype+1)[(int)c]&(_U|_L)) // 是字符。
#define iscntrl(c) ((_ctype+1)[(int)c]&(_C)) // 是控制字符。
#define isdigit(c) ((_ctype+1)[(int)c]&(_D)) // 是数字。
#define isgraph(c) ((_ctype+1)[(int)c]&(_P|_U|_L|_D)) // 是图形字符。
#define islower(c) ((_ctype+1)[(int)c]&(_L)) // 是小写字符。
#define isprint(c) ((_ctype+1)[(int)c]&(_P|_U|_L|_D|_SP)) // 是可打印字符。
#define ispunct(c) ((_ctype+1)[(int)c]&(_P)) // 是标点符号。
#define isspace(c) ((_ctype+1)[(int)c]&(_S)) // 是空白字符如空格,\f,\n,\r,\t,\v。
#define isupper(c) ((_ctype+1)[(int)c]&(_U)) // 是大写字符。
#define isxdigit(c) ((_ctype+1)[(int)c]&(_D|_X)) // 是十六进制数字。
// 在下面两个定义中,宏参数前使用了前缀(unsigned),因此c应该加括号,即表示成(c)。
// 因为在程序中C可能是一个复杂的表达式。
#define isascii(c) (((unsigned) c)<=0x7f) // 是ASCII字符。
#define toascii(c) (((unsigned) c)&0x7f) // 转换成 ASCII字符。
// 以下两个红定义中使用一个临时变量_ctmp的原因是:在宏定义中,宏的参数只能被使用一次。
// 但对于多线程来说这是不安全的,因为两个或多个线程可能在同一时刻使用这个公共临时变量。
// 因此从Linux2.2.x版本开始更改为使用两个函数来取代这两个宏定义.
#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp-('A'-'a'):_ctmp) // 转换成小写字符
#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp-('a'-'A'):_ctmp) // 转换成大写字符
#endif
#ifndef _ERRNO_H
#define _ERRNO_H
/*
* ok, as I hadn't got any other source of information about
* possible error numbers, I was forced to use the same numbers
* as minix.
* Hopefully these are posix or something. I wouldn't know (and posix
* isn't telling me - they want $$$ for their f***ing standard).
*
* We don't use the _SIGN cludge of minix, so kernel returns must
* see to the sign by themselves.
*
* NOTE! Remember to change strerror() if you change this file!
*/
/*
* ok,由于我没有得到任何其它有关出错号的资料,我只能使用与minix 系统
* 相同的出错号了。
* 希望这些是POSIX 兼容的或者在一定程度上是这样的,我不知道(而且POSIX
* 没有告诉我 - 要获得他们的混蛋标准需要出钱)。
*
* 我们没有使用minix 那样的_SIGN 簇,所以内核的返回值必须自己辨别正负号。
*
* 注意!如果你改变该文件的话,记着也要修改strerror()函数。
*/
// 系统调用以及很多库函数返回一个特殊的值以表示操作失败。这个值通常选择-1
// 或者其他一些特定的值来表示。但是这个返回值仅说明错误发生了。如果需要知道
// 出错类型,就需要查看表示系统出错号的变量errno。该变量即在errno.h文件中
// 声明。在程序开始执行时该变量值就被初始化为0.
extern int errno;
// 在出错时,系统调用会把出错号放在变量errno中(负值),然后返回-1.因此程序
// 若需要知道具体的出错号,就需要查看errno的值。
#define ERROR 99 // 一般错误。
#define EPERM 1 // 操作没有许可。
#define ENOENT 2 // 文件或目录不存在。
#define ESRCH 3 // 指定的进程不存在。
#define EINTR 4 // 中断的函数调用。
#define EIO 5 // 输入/输出错。
#define ENXIO 6 // 指定设备或地址不存在。
#define E2BIG 7 // 参数列表太长。
#define ENOEXEC 8 // 执行程序格式错误。
#define EBADF 9 // 文件句柄(描述符)错误。
#define ECHILD 10 // 子进程不存在。
#define EAGAIN 11 // 资源暂时不可用。
#define ENOMEM 12 // 内存不足。
#define EACCES 13 // 没有许可权限。
#define EFAULT 14 // 地址错。
#define ENOTBLK 15 // 不是块设备文件。
#define EBUSY 16 // 资源正忙。
#define EEXIST 17 // 文件已存在。
#define EXDEV 18 // 非法连接。
#define ENODEV 19 // 设备不存在。
#define ENOTDIR 20 // 不是目录文件。
#define EISDIR 21 // 是目录文件。
#define EINVAL 22 // 参数无效。
#define ENFILE 23 // 系统打开文件数太多。
#define EMFILE 24 // 打开文件数太多。
#define ENOTTY 25 // 不恰当的IO 控制操作(没有tty 终端)。
#define ETXTBSY 26 // 不再使用。
#define EFBIG 27 // 文件太大。
#define ENOSPC 28 // 设备已满(设备已经没有空间)。
#define ESPIPE 29 // 无效的文件指针重定位。
#define EROFS 30 // 文件系统只读。
#define EMLINK 31 // 连接太多。
#define EPIPE 32 // 管道错。
#define EDOM 33 // 域(domain)出错。
#define ERANGE 34 // 结果太大。
#define EDEADLK 35 // 避免资源死锁。
#define ENAMETOOLONG 36 // 文件名太长。
#define ENOLCK 37 // 没有锁定可用。
#define ENOSYS 38 // 功能还没有实现。
#define ENOTEMPTY 39 // 目录不空。
#endif
#ifndef _FCNTL_H
#define _FCNTL_H
#include <sys/types.h> // 类型头文件。定义了基本的系统数据类型。
/* open/fcntl - NOCTTY, NDELAY isn't implemented yet */
/* open/fcntl - NOCTTY 和NDELAY 现在还没有实现 */
#define O_ACCMODE 00003 // 文件访问模式屏蔽码。
// 打开文件open()和文件控制fcntl()函数使用的文件访问模式。同时只能使用三者之一。
#define O_RDONLY 00 // 以只读方式打开文件。
#define O_WRONLY 01 // 以只写方式打开文件。
#define O_RDWR 02 // 以读写方式打开文件。
// 下面是文件创建标志,用于open()。可与上面访问模式用'位或'的方式一起使用。
#define O_CREAT 00100 /* not fcntl */ // 如果文件不存在就创建。
#define O_EXCL 00200 /* not fcntl */ // 独占使用文件标志。
#define O_NOCTTY 00400 /* not fcntl */ // 不分配控制终端。
#define O_TRUNC 01000 /* not fcntl */ // 若文件已存在且是写操作,则长度截为0。
#define O_APPEND 02000 // 以添加方式打开,文件指针置为文件尾。
#define O_NONBLOCK 04000 /* not fcntl */ // 非阻塞方式打开和操作文件。
#define O_NDELAY O_NONBLOCK // 非阻塞方式打开和操作文件。
/*
* Defines for fcntl-commands. Note that currently
* locking isn't supported, and other things aren't really
* tested.
*/
/*
* 下面定义了fcntl 的命令。注意目前锁定命令还没有支持,而其它
* 命令实际上还没有测试过。
*/
// 文件句柄(描述符)操作函数fcntl()的命令。
#define F_DUPFD 0 /* dup */ // 拷贝文件句柄为最小数值的句柄。
#define F_GETFD 1 /* get f_flags */ // 取文件句柄标志。
#define F_SETFD 2 /* set f_flags */ // 设置文件句柄标志。
#define F_GETFL 3 /* more flags (cloexec) */ // 取文件状态标志和访问模式。
#define F_SETFL 4 // 设置文件状态标志和访问模式。
// 下面是文件锁定命令。fcntl()的第三个参数lock 是指向flock 结构的指针。
#define F_GETLK 5 /* not implemented */ // 返回阻止锁定的flock 结构。
#define F_SETLK 6 // 设置(F_RDLCK 或F_WRLCK)或清除(F_UNLCK)锁定。
#define F_SETLKW 7 // 等待设置或清除锁定。
/* for F_[GET|SET]FL */
/* 用于 F_GETFL 或F_SETFL */
// 在执行exec()簇函数时关闭文件句柄。(执行时关闭 - Close On EXECution)
#define FD_CLOEXEC 1 /* actually anything with low bit set goes */
/* 实际上只要低位为1 即可 */
/* Ok, these are locking features, and aren't implemented at any
* level. POSIX wants them.
*/
/* OK,以下是锁定类型,任何函数中都还没有实现。POSIX 标准要求这些类型。
*/
#define F_RDLCK 0 // 共享或读文件锁定。
#define F_WRLCK 1 // 独占或写文件锁定。
#define F_UNLCK 2 // 文件解锁。
/* Once again - not implemented, but ... */
/* 同样 - 也还没有实现,但是... */
// 文件锁定操作数据结构。描述了受影响文件段的类型(l_type)、开始偏移(l_whence)、
// 相对偏移(l_start)、锁定长度(l_len)和实施锁定的进程id。
struct flock
{
short l_type; // 锁定类型(F_RDLCK,F_WRLCK,F_UNLCK)。
short l_whence; // 开始偏移(SEEK_SET,SEEK_CUR 或SEEK_END)。
off_t l_start; // 阻塞锁定的开始处。相对偏移(字节数)。
off_t l_len; // 阻塞锁定的大小;如果是0 则为到文件末尾。
pid_t l_pid; // 加锁的进程id。
};
// 以下是使用上述标志或命令的函数原型。
// 创建新文件或重写一个已存在文件。
// 参数filename 是欲创建文件的文件名,mode 是创建文件的属性(参见include/sys/stat.h)。
extern int creat (const char *filename, mode_t mode);
// 文件句柄操作,会影响文件的打开。
// 参数fildes 是文件句柄,cmd 是操作命令,见上面。
extern int fcntl (int fildes, int cmd, ...);
// 打开文件。在文件与文件句柄之间建立联系。
// 参数filename 是欲打开文件的文件名,flags 是上面的标志的组合。
extern int open (const char *filename, int flags, ...);
#endif
#ifndef _CONFIG_H
#define _CONFIG_H
/*
* The root-device is no longer hard-coded. You can change the default
* root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s
*/
/*
* 根文件系统设备已不再是硬编码的了。通过修改boot/bootsect.s 文件中行
* ROOT_DEV = XXX,你可以改变根设备的默认设置值。
*/
/*
* define your keyboard here -
* KBD_FINNISH for Finnish keyboards
* KBD_US for US-type
* KBD_GR for German keyboards
* KBD_FR for Frech keyboard
*/
/*
* 在这里定义你的键盘类型 -
* KBD_FINNISH 是芬兰键盘。
* KBD_US 是美式键盘。
* KBD_GR 是德式键盘。
* KBD_FR 是法式键盘。
*/
#define KBD_US
/*#define KBD_GR */
/*#define KBD_FR */
/*#define KBD_FINNISH */
/*
* Normally, Linux can get the drive parameters from the BIOS at
* startup, but if this for some unfathomable reason fails, you'd
* be left stranded. For this case, you can define HD_TYPE, which
* contains all necessary info on your harddisk.
*
* The HD_TYPE macro should look like this:
*
* #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl}
*
* In case of two harddisks, the info should be sepatated by
* commas:
*
* #define HD_TYPE { h,s,c,wpcom,lz,ctl },{ h,s,c,wpcom,lz,ctl }
*/
/*
* 通常,Linux 能够在启动时从BIOS 中获取驱动器德参数,但是若由于未知原因
* 而没有得到这些参数时,会使程序束手无策。对于这种情况,你可以定义HD_TYPE,
* 其中包括硬盘的所有信息。
*
* HD_TYPE 宏应该象下面这样的形式:
*
* #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl}
*
* 对于有两个硬盘的情况,参数信息需用逗号分开:
*
* #define HD_TYPE { h,s,c,wpcom,lz,ctl }, {h,s,c,wpcom,lz,ctl }
*/
/*
This is an example, two drives, first is type 2, second is type 3:
#define HD_TYPE { 4,17,615,300,615,8 }, { 6,17,615,300,615,0 }
NOTE: ctl is 0 for all drives with heads<=8, and ctl=8 for drives
with more than 8 heads.
If you want the BIOS to tell what kind of drive you have, just
leave HD_TYPE undefined. This is the normal thing to do.
*/
/*
* 下面是一个例子,两个硬盘,第1 个是类型2,第2 个是类型3:
*
11.23 fdreg.h 头文件
* #define HD_TYPE { 4,17,615,300,615,8 }, {6,17,615,300,615,0 }
*
* 注意:对应所有硬盘,若其磁头数<=8,则ctl 等于0,若磁头数多于8 个,
* 则ctl=8。
*
* 如果你想让BIOS 给出硬盘的类型,那么只需不定义HD_TYPE。这是默认操作。
*/
#endif
/*
* This file contains some defines for the floppy disk controller.
* Various sources. Mostly "IBM Microcomputers: A Programmers
* Handbook", Sanches and Canton.
*/
/*
* 该文件中含有一些软盘控制器的一些定义。这些信息有多处来源,大多数取自
* Sanches 和 Canton编著的“IBM微型计算机:程序员手册”一书。
*/
#ifndef _FDREG_H
#define _FDREG_H
// 一些软盘类型函数的原型说明
extern int ticks_to_floppy_on(unsigned int nr);
extern void floppy_on(unsigned int nr);
extern void floppy_off(unsigned int nr);
extern void floppy_select(unsigned int nr);
extern void floppy_deselect(unsigned int nr);
// 下面是有关软盘控制器一些端口和符号的定义。
/* Fd controller regs. S&C, about page 340 */
/* 软盘控制器(FDC)寄存器端口。摘自S&C书中约340页*/
#define FD_STATUS 0x3f4 // 主状态寄存器端口。
#define FD_DATA 0x3f5 // 数据端口
#define FD_DOR 0x3f2 /* Digital Output Register */
// 数字输出寄存器(也称为数字控制寄存器)。
#define FD_DIR 0x3f7 /* Digital Input Register (read) */
// 数字输入寄存器。
#define FD_DCR 0x3f7 /* Diskette Control Register (write)*/
// 数字传输率控制寄存器。
/* Bits of main status register */
/* 主状态寄存器各位的含义*/
#define STATUS_BUSYMASK 0x0F /* drive busy mask */
// 驱动器忙位(每位对应一个驱动器)
#define STATUS_BUSY 0x10 /* FDC busy */
// 软盘控制器忙。
#define STATUS_DMA 0x20 /* 0- DMA mode */
// 0 - 为DMA数据传输模式, 1- 为非DMA模式
#define STATUS_DIR 0x40 /* 0- cpu->fdc */
// 传输方向:0-CPU-> fdc, 1 - 相反.
#define STATUS_READY 0x80 /* Data reg ready */
// 数据寄存器就绪位
/* Bits of FD_ST0 */ // 状态字节0 (ST0)各比特位的含义。
#define ST0_DS 0x03 /* drive select mask */
// 驱动器选择号(发生中断时驱动器号)。
#define ST0_HA 0x04 /* Head (Address) */
// 磁头号
#define ST0_NR 0x08 /* Not Ready */
// 磁盘驱动器为准备好。
#define ST0_ECE 0x10 /* Equipment chech error */
// 设备检测出错(零磁道校准出错)
#define ST0_SE 0x20 /* Seek end */
// 寻道或重新校正操作执行结束
#define ST0_INTR 0xC0 /* Interrupt code mask */
// 中断代码位(中断原因),00 - 命令正常结束;
// 01 - 命令异常结束; 10 - 命令无效; 11 - fdd就绪状态改变
/* Bits of FD_ST1 */ // 状态字节1(ST1)各比特位的含义
#define ST1_MAM 0x01 /* Missing Address Mark */
// 未找到地址标志(ID AM).
#define ST1_WP 0x02 /* Write Protect */
// 写保护
#define ST1_ND 0x04 /* No Data - unreadable */
// 未找到指定的扇区.
#define ST1_OR 0x10 /* OverRun */
// 数据传输超时(DMA控制器故障)
#define ST1_CRC 0x20 /* CRC error in data or addr */
// CRC 检验出错
#define ST1_EOC 0x80 /* End Of Cylinder */
// 访问超过一个磁道上的最大扇区号。
/* Bits of FD_ST2 */ // 状态字节2(ST2)各比特位的含义
#define ST2_MAM 0x01 /* Missing Addess Mark (again) */
// 未找到数据地址标志
#define ST2_BC 0x02 /* Bad Cylinder */
// 磁道坏。
#define ST2_SNS 0x04 /* Scan Not Satisfied */
// 检索(扫描)条件不满足
#define ST2_SEH 0x08 /* Scan Equal Hit */
// 检索条件满足
#define ST2_WC 0x10 /* Wrong Cylinder */
// 磁道(柱面号)号不符
#define ST2_CRC 0x20 /* CRC error in data field */
// CRC 检验出错
#define ST2_CM 0x40 /* Control Mark = deleted */
// 访问超过一个磁道上的最大扇区号。
/* Bits of FD_ST3 */ // 状态字节3(ST3)各比特位的含义。
#define ST3_HA 0x04 /* Head (Address) */
// 磁头号。
#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */
// 零磁道信号。
#define ST3_WP 0x40 /* Write Protect */
// 写保护。
/* Values for FD_COMMAND */ // 软盘命令码
#define FD_RECALIBRATE 0x07 /* move to track 0 */
// 重新校正(磁头退到零磁道)。
#define FD_SEEK 0x0F /* seek track */
// 磁头寻道
#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */
// 读数据(MT多磁道操作,MFM格式,跳过删除数据)。
#define FD_WRITE 0xC5 /* write with MT, MFM */
// 写数据(MT,MFM)。
#define FD_SENSEI 0x08 /* Sense Interrupt Status */
// 检测中断状态。
#define FD_SPECIFY 0x03 /* specify HUT etc */
// 设定驱动器参数(步进速率、磁头卸载时间等)。
/* DMA commands */ //DMA 命令
#define DMA_READ 0x46 // DMA 读盘,DMA方式字(送DMA端口 12,11)。
#define DMA_WRITE 0x4A // DMA 写盘,DMA方式字。
#endif
差异被折叠。
/*
* This file contains some defines for the AT-hd-controller.
* Various sources. Check out some definitions (see comments with
* a ques).
*/
/*
* 本文件含有一些AT 硬盘控制器的定义。来自各种资料。请查证某些
* 定义(带有问号的注释)。
*/
#ifndef _HDREG_H
#define _HDREG_H
/* Hd controller regs. Ref: IBM AT Bios-listing */
/* 硬盘控制器寄存器端口。参见:IBM AT Bios 程序 */
#define HD_DATA 0x1f0 /* _CTL when writing */
#define HD_ERROR 0x1f1 /* see err-bits */
#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */
#define HD_SECTOR 0x1f3 /* starting sector */
#define HD_LCYL 0x1f4 /* starting cylinder */
#define HD_HCYL 0x1f5 /* high byte of starting cyl */
#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */
#define HD_STATUS 0x1f7 /* see status-bits */
#define HD_PRECOMP HD_ERROR /* same io address, read=error, write=precomp */
#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */
#define HD_CMD 0x3f6 // 控制寄存器端口。
/* Bits of HD_STATUS */
/* 硬盘状态寄存器各位的定义(HD_STATUS) */
#define ERR_STAT 0x01 // 命令执行错误。
#define INDEX_STAT 0x02 // 收到索引。
#define ECC_STAT 0x04 /* Corrected error */ // ECC 校验错。
#define DRQ_STAT 0x08 // 请求服务。
#define SEEK_STAT 0x10 // 寻道结束。
#define WRERR_STAT 0x20 // 驱动器故障。
#define READY_STAT 0x40 // 驱动器准备好(就绪)。
#define BUSY_STAT 0x80 // 控制器忙碌。
/* Values for HD_COMMAND */
/* 硬盘命令值(HD_CMD) */
#define WIN_RESTORE 0x10 // 驱动器重新校正(驱动器复位)。
#define WIN_READ 0x20 // 读扇区。
#define WIN_WRITE 0x30 // 写扇区。
#define WIN_VERIFY 0x40 // 扇区检验。
#define WIN_FORMAT 0x50 // 格式化磁道。
#define WIN_INIT 0x60 // 控制器初始化。
#define WIN_SEEK 0x70 // 寻道。
#define WIN_DIAGNOSE 0x90 // 控制器诊断。
#define WIN_SPECIFY 0x91 // 建立驱动器参数。
/* Bits for HD_ERROR */
/* 错误寄存器各比特位的含义(HD_ERROR) */
// 执行控制器诊断命令时含义与其它命令时的不同。下面分别列出:
// ==================================================
// 诊断命令时 其它命令时
// --------------------------------------------------
// 0x01 无错误 数据标志丢失
// 0x02 控制器出错 磁道0 错
// 0x03 扇区缓冲区错
// 0x04 ECC 部件错 命令放弃
// 0x05 控制处理器错
// 0x10 ID 未找到
// 0x40 ECC 错误
// 0x80 坏扇区
//---------------------------------------------------
#define MARK_ERR 0x01 /* Bad address mark ? */
#define TRK0_ERR 0x02 /* couldn't find track 0 */
#define ABRT_ERR 0x04 /* ? */
#define ID_ERR 0x10 /* ? */
#define ECC_ERR 0x40 /* ? */
#define BBD_ERR 0x80 /* ? */
// 硬盘分区表结构。参见下面列表后信息。
struct partition
{
unsigned char boot_ind; /* 0x80 - active (unused) */
unsigned char head; /* ? */
unsigned char sector; /* ? */
unsigned char cyl; /* ? */
unsigned char sys_ind; /* ? */
unsigned char end_head; /* ? */
unsigned char end_sector; /* ? */
unsigned char end_cyl; /* ? */
unsigned int start_sect; /* starting sector counting from 0 */
unsigned int nr_sects; /* nr of sectors in partition */
};
#endif
#ifndef _HEAD_H
#define _HEAD_H
typedef struct desc_struct
{ // 定义了段描述符的数据结构。该结构仅说明每个描述
unsigned long a, b; // 符是由8 个字节构成,每个描述符表共有256 项。
}
desc_table[256];
extern unsigned long pg_dir[1024]; // 内存页目录数组。每个目录项为4 字节。从物理地址0 开始。
extern desc_table idt, gdt; // 中断描述符表,全局描述符表。
#define GDT_NUL 0 // 全局描述符表的第0 项,不用。
#define GDT_CODE 1 // 第1 项,是内核代码段描述符项。
#define GDT_DATA 2 // 第2 项,是内核数据段描述符项。
#define GDT_TMP 3 // 第3 项,系统段描述符,Linux 没有使用。
#define LDT_NUL 0 // 每个局部描述符表的第0 项,不用。
#define LDT_CODE 1 // 第1 项,是用户程序代码段描述符项。
#define LDT_DATA 2 // 第2 项,是用户程序数据段描述符项。
#endif
/*
* 'kernel.h' contains some often-used function prototypes etc
*/
/*
* 'kernel.h'定义了一些常用函数的原型等。
*/
// 验证给定地址开始的内存块是否超限。若超限则追加内存。( kernel/fork.c)。
void verify_area (void *addr, int count);
// 显示内核出错信息,然后进入死循环。( kernel/panic.c)。
volatile void panic (const char *str);
// 标准打印(显示)函数。( init/main.c)。
int printf (const char *fmt, ...);
// 内核专用的打印信息函数,功能与printf()相同。( kernel/printk.c)。
int printk (const char *fmt, ...);
// 往tty 上写指定长度的字符串。( kernel/chr_drv/tty_io.c)。
int tty_write (unsigned ch, char *buf, int count);
// 通用内核内存分配函数。( lib/malloc.c)。
void *malloc (unsigned int size);
// 释放指定对象占用的内存。( lib/malloc.c)。
void free_s (void *obj, int size);
#define free(x) free_s((x), 0)
/*
* This is defined as a macro, but at some point this might become a
* real subroutine that sets a flag if it returns true (to do
* BSD-style accounting where the process is flagged if it uses root
* privs). The implication of this is that you should do normal
* permissions checks first, and check suser() last.
*/
/*
* 下面函数是以宏的形式定义的,但是在某方面来看它可以成为一个真正的子程序,
* 如果返回是true 时它将设置标志(如果使用root 用户权限的进程设置了标志,则用
* 于执行BSD 方式的计帐处理)。这意味着你应该首先执行常规权限检查,最后再
* 检测suser()。
*/
#define suser() (current->euid == 0) // 检测是否是超级用户。
#ifndef _MM_H
#define _MM_H
#define PAGE_SIZE 4096 // 定义内存页面的大小(字节数)。
// 取空闲页面函数。返回页面地址。扫描页面映射数组mem_map[]取空闲页面。
extern unsigned long get_free_page (void);
// 在指定物理地址处放置一页面。在页目录和页表中放置指定页面信息。
extern unsigned long put_page (unsigned long page, unsigned long address);
// 释放物理地址addr 开始的一页面内存。修改页面映射数组mem_map[]中引用次数信息。
extern void free_page (unsigned long addr);
#endif
差异被折叠。
extern int sys_setup (); // 系统启动初始化设置函数。 (kernel/blk_drv/hd.c)
extern int sys_exit (); // 程序退出。 (kernel/exit.c)
extern int sys_fork (); // 创建进程。 (kernel/system_call.s)
extern int sys_read (); // 读文件。 (fs/read_write.c)
extern int sys_write (); // 写文件。 (fs/read_write.c)
extern int sys_open (); // 打开文件。 (fs/open.c)
extern int sys_close (); // 关闭文件。 (fs/open.c)
extern int sys_waitpid (); // 等待进程终止。 (kernel/exit.c)
extern int sys_creat (); // 创建文件。 (fs/open.c)
extern int sys_link (); // 创建一个文件的硬连接。 (fs/namei.c)
extern int sys_unlink (); // 删除一个文件名(或删除文件)。 (fs/namei.c)
extern int sys_execve (); // 执行程序。 (kernel/system_call.s)
extern int sys_chdir (); // 更改当前目录。 (fs/open.c)
extern int sys_time (); // 取当前时间。 (kernel/sys.c)
extern int sys_mknod (); // 建立块/字符特殊文件。 (fs/namei.c)
extern int sys_chmod (); // 修改文件属性。 (fs/open.c)
extern int sys_chown (); // 修改文件宿主和所属组。 (fs/open.c)
extern int sys_break (); // (-kernel/sys.c)
extern int sys_stat (); // 使用路径名取文件的状态信息。 (fs/stat.c)
extern int sys_lseek (); // 重新定位读/写文件偏移。 (fs/read_write.c)
extern int sys_getpid (); // 取进程id。 (kernel/sched.c)
extern int sys_mount (); // 安装文件系统。 (fs/super.c)
extern int sys_umount (); // 卸载文件系统。 (fs/super.c)
extern int sys_setuid (); // 设置进程用户id。 (kernel/sys.c)
extern int sys_getuid (); // 取进程用户id。 (kernel/sched.c)
extern int sys_stime (); // 设置系统时间日期。 (-kernel/sys.c)
extern int sys_ptrace (); // 程序调试。 (-kernel/sys.c)
extern int sys_alarm (); // 设置报警。 (kernel/sched.c)
extern int sys_fstat (); // 使用文件句柄取文件的状态信息。(fs/stat.c)
extern int sys_pause (); // 暂停进程运行。 (kernel/sched.c)
extern int sys_utime (); // 改变文件的访问和修改时间。 (fs/open.c)
extern int sys_stty (); // 修改终端行设置。 (-kernel/sys.c)
extern int sys_gtty (); // 取终端行设置信息。 (-kernel/sys.c)
extern int sys_access (); // 检查用户对一个文件的访问权限。(fs/open.c)
extern int sys_nice (); // 设置进程执行优先权。 (kernel/sched.c)
extern int sys_ftime (); // 取日期和时间。 (-kernel/sys.c)
extern int sys_sync (); // 同步高速缓冲与设备中数据。 (fs/buffer.c)
extern int sys_kill (); // 终止一个进程。 (kernel/exit.c)
extern int sys_rename (); // 更改文件名。 (-kernel/sys.c)
extern int sys_mkdir (); // 创建目录。 (fs/namei.c)
extern int sys_rmdir (); // 删除目录。 (fs/namei.c)
extern int sys_dup (); // 复制文件句柄。 (fs/fcntl.c)
extern int sys_pipe (); // 创建管道。 (fs/pipe.c)
extern int sys_times (); // 取运行时间。 (kernel/sys.c)
extern int sys_prof (); // 程序执行时间区域。 (-kernel/sys.c)
extern int sys_brk (); // 修改数据段长度。 (kernel/sys.c)
extern int sys_setgid (); // 设置进程组id。 (kernel/sys.c)
extern int sys_getgid (); // 取进程组id。 (kernel/sched.c)
extern int sys_signal (); // 信号处理。 (kernel/signal.c)
extern int sys_geteuid (); // 取进程有效用户id。 (kenrl/sched.c)
extern int sys_getegid (); // 取进程有效组id。 (kenrl/sched.c)
extern int sys_acct (); // 进程记帐。 (-kernel/sys.c)
extern int sys_phys (); // (-kernel/sys.c)
extern int sys_lock (); // (-kernel/sys.c)
extern int sys_ioctl (); // 设备控制。 (fs/ioctl.c)
extern int sys_fcntl (); // 文件句柄操作。 (fs/fcntl.c)
extern int sys_mpx (); // (-kernel/sys.c)
extern int sys_setpgid (); // 设置进程组id。 (kernel/sys.c)
extern int sys_ulimit (); // (-kernel/sys.c)
extern int sys_uname (); // 显示系统信息。 (kernel/sys.c)
extern int sys_umask (); // 取默认文件创建属性码。 (kernel/sys.c)
extern int sys_chroot (); // 改变根系统。 (fs/open.c)
extern int sys_ustat (); // 取文件系统信息。 (fs/open.c)
extern int sys_dup2 (); // 复制文件句柄。 (fs/fcntl.c)
extern int sys_getppid (); // 取父进程id。 (kernel/sched.c)
extern int sys_getpgrp (); // 取进程组id,等于getpgid(0)。(kernel/sys.c)
extern int sys_setsid (); // 在新会话中运行程序。 (kernel/sys.c)
extern int sys_sigaction (); // 改变信号处理过程。 (kernel/signal.c)
extern int sys_sgetmask (); // 取信号屏蔽码。 (kernel/signal.c)
extern int sys_ssetmask (); // 设置信号屏蔽码。 (kernel/signal.c)
extern int sys_setreuid (); // 设置真实与/或有效用户id。 (kernel/sys.c)
extern int sys_setregid (); // 设置真实与/或有效组id。 (kernel/sys.c)
extern int sys_sigpending();
extern int sys_sigsuspend();
extern int sys_sethostname();
extern int sys_setrlimit();
extern int sys_getrlimit();
extern int sys_getrusage();
extern int sys_gettimeofday();
extern int sys_settimeofday();
extern int sys_getgroups();
extern int sys_setgroups();
extern int sys_select();
extern int sys_symlink();
extern int sys_lstat();
extern int sys_readlink();
extern int sys_uselib();
// 系统调用函数指针表。用于系统调用中断处理程序(int 0x80),作为跳转表。
// 数组元素为系统调用内核函数的函数指针,索引即系统调用号
fn_ptr sys_call_table[] = {
sys_setup, //0
sys_exit, //1
sys_fork, //2
sys_read, //3
sys_write, //4
sys_open, //5
sys_close, //6
sys_waitpid, //7
sys_creat, //8
sys_link, //9
sys_unlink, //10
sys_execve, //11
sys_chdir, //12
sys_time, //13
sys_mknod, //14
sys_chmod, //15
sys_chown, //16
sys_break, //17
sys_stat, //18
sys_lseek, //19
sys_getpid, //20
sys_mount, //21
sys_umount, //22
sys_setuid, //23
sys_getuid, //24
sys_stime, //25
sys_ptrace, //26
sys_alarm, //27
sys_fstat, //28
sys_pause, //29
sys_utime, //30
sys_stty, //31
sys_gtty, //32
sys_access, //33
sys_nice, //34
sys_ftime, //35
sys_sync, //36
sys_kill, //37
sys_rename, //38
sys_mkdir, //39
sys_rmdir, //40
sys_dup, //41
sys_pipe, //42
sys_times, //43
sys_prof, //44
sys_brk, //45
sys_setgid, //46
sys_getgid, //47
sys_signal, //48
sys_geteuid, //49
sys_getegid, //50
sys_acct, //51
sys_phys, //52
sys_lock, //53
sys_ioctl, //54
sys_fcntl, //55
sys_mpx, //56
sys_setpgid, //57
sys_ulimit, //58
sys_uname, //59
sys_umask, //60
sys_chroot, //61
sys_ustat, //62
sys_dup2, //63
sys_getppid, //64
sys_getpgrp, //65
sys_setsid, //66
sys_sigaction, //67
sys_sgetmask, //68
sys_ssetmask, //69
sys_setreuid, //70
sys_setregid, //71
sys_sigsuspend, //72
sys_sigpending, //73
sys_sethostname, //74
sys_setrlimit, //75
sys_getrlimit, //76
sys_getrusage, //77
sys_gettimeofday, //78
sys_settimeofday, //79
sys_getgroups, //80
sys_setgroups, //81
sys_select, //82
sys_symlink, //83
sys_lstat, //84
sys_readlink, //85
sys_uselib //86
};
/*
* 'tty.h' defines some structures used by tty_io.c and some defines.
*
* NOTE! Don't touch this without checking that nothing in rs_io.s or
* con_io.s breaks. Some constants are hardwired into the system (mainly
* offsets into 'tty_queue'
*/
/*
* 'tty.h'中定义了tty_io.c 程序使用的某些结构和其它一些定义。
*
* 注意!在修改这里的定义时,一定要检查rs_io.s 或con_io.s 程序中不会出现问题。
* 在系统中有些常量是直接写在程序中的(主要是一些tty_queue 中的偏移值)。
*/
#ifndef _TTY_H
#define _TTY_H
#include <termios.h> // 终端输入输出函数头文件。主要定义控制异步通信口的终端接口。
#define TTY_BUF_SIZE 1024 // tty 缓冲区大小。
// tty 等待队列数据结构。
struct tty_queue {
unsigned long data; // 等待队列缓冲区中当前数据指针字符数)。
// 对于串口终端,则存放串行端口地址。
unsigned long head; // 缓冲区中数据头指针。
unsigned long tail; // 缓冲区中数据尾指针。
struct task_struct *proc_list; // 等待进程列表。
char buf[TTY_BUF_SIZE]; // 队列的缓冲区。
};
// 以下定义了tty 等待队列中缓冲区操作宏函数。(tail 在前,head 在后)。
// a 缓冲区指针前移1 字节,并循环。
#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
// a 缓冲区指针后退1 字节,并循环。
#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
// 清空指定队列的缓冲区。
#define EMPTY(a) ((a).head == (a).tail)
// 缓冲区还可存放字符的长度(空闲区长度)。
#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1))
// 缓冲区中最后一个位置。
#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)])
// 缓冲区满(如果为1 的话)。
#define FULL(a) (!LEFT(a))
// 缓冲区中已存放字符的长度。
#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1))
// 从queue 队列项缓冲区中取一字符(从tail 处,并且tail+=1)。
#define GETCH(queue,c) \
(void)({c=(queue).buf[(queue).tail];INC((queue).tail);})
// 往queue 队列项缓冲区中放置一字符(在head 处,并且head+=1)。
#define PUTCH(c,queue) \
(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})
// 判断终端键盘字符类型。
#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) // 中断符。
#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT]) // 退出符。
#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) // 削除符。
#define KILL_CHAR(tty) ((tty)->termios.c_cc[VKILL]) // 终止符。
#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF]) // 文件结束符。
#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART]) // 开始符。
#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) // 结束符。
#define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP]) // 挂起符。
// tty 数据结构。
struct tty_struct
{
struct termios termios; // 终端io 属性和控制字符数据结构。
int pgrp; // 所属进程组。
int stopped; // 停止标志。
void (*write) (struct tty_struct * tty); // tty 写函数指针。
struct tty_queue read_q; // tty 读队列。
struct tty_queue write_q; // tty 写队列。
struct tty_queue secondary; // tty 辅助队列(存放规范模式字符序列),
}; // 可称为规范(熟)模式队列。
extern struct tty_struct tty_table[];
/* intr=^C quit=^| erase=del kill=^U
eof=^D vtime=\0 vmin=\1 sxtc=\0
start=^Q stop=^S susp=^Z eol=\0
reprint=^R discard=^U werase=^W lnext=^V
eol2=\0
*/
/*
中断intr=^C 退出quit=^| 删除erase=del 终止kill=^U
文件结束eof=^D vtime=\0 vmin=\1 sxtc=\0
开始start=^Q 停止stop=^S 挂起susp=^Z 行结束eol=\0
重显reprint=^R 丢弃discard=^U werase=^W lnext=^V
行结束eol2=\0
*/
// 控制字符对应的ASCII 码值。[8 进制]
#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
void rs_init (void); // 异步串行通信初始化。 (kernel/chr_drv/serial.c)
void con_init (void); // 控制终端初始化。 (kernel/chr_drv/console.c)
void tty_init (void); // tty 初始化。 (kernel/chr_drv/tty_io.c)
int tty_read (unsigned c, char *buf, int n); // (kernel/chr_drv/tty_io.c)
int tty_write (unsigned c, char *buf, int n); // (kernel/chr_drv/tty_io.c)
void rs_write (struct tty_struct *tty); // (kernel/chr_drv/serial.c)
void con_write (struct tty_struct *tty); // (kernel/chr_drv/console.c)
void copy_to_cooked (struct tty_struct *tty); // (kernel/chr_drv/tty_io.c)
#endif
#ifndef _SIGNAL_H
#define _SIGNAL_H
#include <sys/types.h> // 类型头文件。定义了基本的系统数据类型。
typedef int sig_atomic_t; // 定义信号原子操作类型。
typedef unsigned int sigset_t; /* 32 bits */// 定义信号集类型。
#define _NSIG 32 // 定义信号种类 -- 32 种。
#define NSIG _NSIG // NSIG = _NSIG
// 以下这些是Linux 0.11 内核中定义的信号。
#define SIGHUP 1 // Hang Up -- 挂断控制终端或进程。
#define SIGINT 2 // Interrupt -- 来自键盘的中断。
#define SIGQUIT 3 // Quit -- 来自键盘的退出。
#define SIGILL 4 // Illeagle -- 非法指令。
#define SIGTRAP 5 // Trap -- 跟踪断点。
#define SIGABRT 6 // Abort -- 异常结束。
#define SIGIOT 6 // IO Trap -- 同上。
#define SIGUNUSED 7 // Unused -- 没有使用。
#define SIGFPE 8 // FPE -- 协处理器出错。
#define SIGKILL 9 // Kill -- 强迫进程终止。
#define SIGUSR1 10 // User1 -- 用户信号1,进程可使用。
#define SIGSEGV 11 // Segment Violation -- 无效内存引用。
#define SIGUSR2 12 // User2 -- 用户信号2,进程可使用。
#define SIGPIPE 13 // Pipe -- 管道写出错,无读者。
#define SIGALRM 14 // Alarm -- 实时定时器报警。
#define SIGTERM 15 // Terminate -- 进程终止。
#define SIGSTKFLT 16 // Stack Fault -- 栈出错(协处理器)。
#define SIGCHLD 17 // Child -- 子进程停止或被终止。
#define SIGCONT 18 // Continue -- 恢复进程继续执行。
#define SIGSTOP 19 // Stop -- 停止进程的执行。
#define SIGTSTP 20 // TTY Stop -- tty 发出停止进程,可忽略。
#define SIGTTIN 21 // TTY In -- 后台进程请求输入。
#define SIGTTOU 22 // TTY Out -- 后台进程请求输出。
/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
/* OK,我还没有实现sigactions 的编制,但在头文件中仍希望遵守POSIX 标准 */
//////////////////////// 【标号一】 //////////////////////////
#define SA_NOCLDSTOP 1 // 当子进程处于停止状态,就不对SIGCHLD 处理。
#define SA_NOMASK 0x40000000 // 不阻止在指定的信号处理程序(信号句柄)中再收到该信号。
#define SA_ONESHOT 0x80000000 // 信号句柄一旦被调用过就恢复到默认处理句柄。
// 以下参数用于sigprocmask()-- 改变阻塞信号集(屏蔽码)。这些参数可以改变该函数的行为。
#define SIG_BLOCK 0 /* for blocking signals */
// 在阻塞信号集中加上给定的信号集。
#define SIG_UNBLOCK 1 /* for unblocking signals */
// 从阻塞信号集中删除指定的信号集。
#define SIG_SETMASK 2 /* for setting the signal mask */
// 设置阻塞信号集(信号屏蔽码)。
#define SIG_DFL ((void (*)(int))0) /* default signal handling */
// 默认的信号处理程序(信号句柄)。
#define SIG_IGN ((void (*)(int))1) /* ignore signal */
// 忽略信号的处理程序。
// 下面是sigaction 的数据结构。
// sa_handler 是对应某信号指定要采取的行动。可以是上面的SIG_DFL,或者是SIG_IGN 来忽略
// 该信号,也可以是指向处理该信号函数的一个指针。
// sa_mask 给出了对信号的屏蔽码,在信号程序执行时将阻塞对这些信号的处理。
// sa_flags 指定改变信号处理过程的信号集。它是由【标号一】的位标志定义的。
// sa_restorer 恢复过程指针,是用于保存原返回的过程指针。
// 另外,引起触发信号处理的信号也将被阻塞,除非使用了SA_NOMASK 标志。
struct sigaction
{
void (*sa_handler) (int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
};
// 为信号_sig 安装一个新的信号处理程序(信号句柄),与sigaction()类似。
void (*signal (int _sig, void (*_func) (int))) (int);
// 向当前进程发送一个信号。其作用等价于kill(getpid(),sig)。
int raise (int sig);
// 可用于向任何进程组或进程发送任何信号。
int kill (pid_t pid, int sig);
// 向信号集中添加信号。
int sigaddset (sigset_t * mask, int signo);
// 从信号集中去除指定的信号。
int sigdelset (sigset_t * mask, int signo);
// 从信号集中清除指定信号集。
int sigemptyset (sigset_t * mask);
// 向信号集中置入所有信号。
int sigfillset (sigset_t * mask);
// 判断一个信号是否是信号集中的。1 -- 是, 0 -- 不是, -1 -- 出错。
int sigismember (sigset_t * mask, int signo); /* 1 - is, 0 - not, -1 error */
// 对set 中的信号进行检测,看是否有挂起的信号。
int sigpending (sigset_t * set);
// 改变目前的被阻塞信号集(信号屏蔽码)。
int sigprocmask (int how, sigset_t * set, sigset_t * oldset);
// 用sigmask 临时替换进程的信号屏蔽码,然后暂停该进程直到收到一个信号。
int sigsuspend (sigset_t * sigmask);
// 用于改变进程在收到指定信号时所采取的行动。
int sigaction (int sig, struct sigaction *act, struct sigaction *oldact);
#endif /* _SIGNAL_H */
#ifndef _STDARG_H
#define _STDARG_H
typedef char *va_list; // 定义va_list 是一个字符指针类型。
/* Amount of space required in an argument list for an arg of type TYPE.
TYPE may alternatively be an expression whose type is used. */
/* 下面给出了类型为TYPE 的arg 参数列表所要求的空间容量。
TYPE 也可以是使用该类型的一个表达式 */
// 下面这句定义了取整后的TYPE 类型的字节长度值。是int 长度(4)的倍数。
#define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
// 下面这个函数(用宏实现)使AP 指向传给函数的可变参数表的第一个参数。
// 在第一次调用va_arg 或va_end 之前,必须首先调用该函数。
// 下面的__builtin_saveregs()是在gcc 的库程序libgcc2.c 中定义的,用于保存寄存器。
// 它的说明可参见gcc 手册章节“Target Description Macros”中的
// “Implementing the Varargs Macros”小节。
#ifndef __sparc__
#define va_start(AP, LASTARG) \
(AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#else
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), \
AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#endif
// 下面该宏用于被调用函数完成一次正常返回。va_end 可以修改AP 使其在重新调用
// va_start 之前不能被使用。va_end 必须在va_arg 读完所有的参数后再被调用。
void va_end (va_list); /* Defined in gnulib *//* 在gnulib 中定义 */
#define va_end(AP)
// 下面该宏用于扩展表达式使其与下一个被传递参数具有相同的类型和值。
// 对于缺省值,va_arg 可以用字符、无符号字符和浮点类型。
// 在第一次使用va_arg 时,它返回表中的第一个参数,后续的每次调用都将返回表中的
// 下一个参数。这是通过先访问AP,然后把它增加以指向下一项来实现的。
// va_arg 使用TYPE 来完成访问和定位下一项,每调用一次va_arg,它就修改AP 以指示
// 表中的下一参数。
#define va_arg(AP, TYPE) \
(AP += __va_rounded_size (TYPE), \
*((TYPE *) (AP - __va_rounded_size (TYPE))))
#endif /* _STDARG_H */
#ifndef _STDDEF_H
#define _STDDEF_H
#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef long ptrdiff_t; // 两个指针相减结果的类型.
#endif
#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned long size_t; // sizeof返回的类型.
#endif
#undef NULL
#define NULL ((void *)0) // 空指针
// 下面定义了一个计算某成员在类型中偏移位置的宏。使用该宏可以确定一个成员(字段)
// 在包含它的结构类型中从结构开始处算起的字节偏移量。宏的结果时类型位size_t的整数
// 常量表达式。这里是一个技巧手法。((TYPE *)0)是将一个整数0类型投射(typecast)
// 成数据对象指针类型,然后在该结果上进行运算.
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
差异被折叠。
#ifndef _SYS_STAT_H
#define _SYS_STAT_H
#include <sys/types.h>
struct stat {
dev_t st_dev; // 含有文件的设备号。
ino_t st_ino; // 文件i 节点号。
umode_t st_mode; // 文件属性(见下面)。
nlink_t st_nlink; // 指定文件的连接数。
uid_t st_uid; // 文件的用户(标识)号。
gid_t st_gid; // 文件的组号。
dev_t st_rdev; // 设备号(如果文件是特殊的字符文件或块文件)。
off_t st_size; // 文件大小(字节数)(如果文件是常规文件)。
time_t st_atime; // 上次(最后)访问时间。
time_t st_mtime; // 最后修改时间。
time_t st_ctime; // 最后节点修改时间。
};
// 以下这些是st_mode 值的符号名称。
// 文件类型:
#define S_IFMT 00170000 // 文件类型(8 进制表示)。
#define S_IFREG 0100000 // 常规文件。
#define S_IFBLK 0060000 // 块特殊(设备)文件,如磁盘dev/fd0。
#define S_IFDIR 0040000 // 目录文件。
#define S_IFCHR 0020000 // 字符设备文件。
#define S_IFIFO 0010000 // FIFO 特殊文件。
// 文件属性位:
#define S_ISUID 0004000 // 执行时设置用户ID(set-user-ID)。
#define S_ISGID 0002000 // 执行时设置组ID。
#define S_ISVTX 0001000 // 对于目录,受限删除标志。
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) // 测试是否常规文件。
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) // 是否目录文件。
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) // 是否字符设备文件。
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) // 是否块设备文件。
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) // 是否FIFO 特殊文件。
#define S_IRWXU 00700 // 宿主可以读、写、执行/搜索。
#define S_IRUSR 00400 // 宿主读许可。
#define S_IWUSR 00200 // 宿主写许可。
#define S_IXUSR 00100 // 宿主执行/搜索许可。
#define S_IRWXG 00070 // 组成员可以读、写、执行/搜索。
#define S_IRGRP 00040 // 组成员读许可。
#define S_IWGRP 00020 // 组成员写许可。
#define S_IXGRP 00010 // 组成员执行/搜索许可。
#define S_IRWXO 00007 // 其他人读、写、执行/搜索许可。
#define S_IROTH 00004 // 其他人读许可。
#define S_IWOTH 00002 // 其他人写许可。
#define S_IXOTH 00001 // 其他人执行/搜索许可。
extern int chmod (const char *_path, mode_t mode); // 修改文件属性。
extern int fstat (int fildes, struct stat *stat_buf); // 取指定文件句柄的文件状态信息。
extern int mkdir (const char *_path, mode_t mode); // 创建目录。
extern int mkfifo (const char *_path, mode_t mode); // 创建管道文件。
extern int stat (const char *filename, struct stat *stat_buf); // 取指定文件名的文件状态信息。
extern mode_t umask (mode_t mask); // 设置属性屏蔽码。
extern int lstat(const char *, struct stat *);
#endif
#ifndef _TIMES_H
#define _TIMES_H
#include <sys/types.h> // 类型头文件。定义了基本的系统数据类型。
struct tms
{
time_t tms_utime; // 用户使用的CPU 时间。
time_t tms_stime; // 系统(内核)CPU 时间。
time_t tms_cutime; // 已终止的子进程使用的用户CPU 时间。
time_t tms_cstime; // 已终止的子进程使用的系统CPU 时间。
};
extern time_t times(struct tms * tp);
#endif
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H
#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t; // 用于对象的大小(长度)。
#endif
#ifndef _TIME_T
#define _TIME_T
typedef long time_t; // 用于时间(以秒计)。
#endif
#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef long ptrdiff_t;
#endif
#ifndef NULL
#define NULL ((void *) 0)
#endif
typedef int pid_t; // 用于进程号和进程组号。
typedef unsigned short uid_t; // 用于用户号(用户标识号)。
typedef unsigned char gid_t; // 用于组号。
typedef unsigned short dev_t; // 用于设备号。
typedef unsigned short ino_t; // 用于文件序列号。
typedef unsigned short mode_t; // 用于某些文件属性。
typedef unsigned short umode_t; //
typedef unsigned char nlink_t; // 用于连接计数。
typedef int daddr_t;
typedef long off_t; // 用于文件长度(大小)。
typedef unsigned char u_char; // 无符号字符类型。
typedef unsigned short ushort; // 无符号短整数类型。
typedef struct
{
int quot, rem;
}
div_t; // 用于DIV 操作。
typedef struct
{
long quot, rem;
}
ldiv_t; // 用于长DIV 操作。
struct ustat
{
daddr_t f_tfree;
ino_t f_tinode;
char f_fname[6];
char f_fpack[6];
};
#endif
#ifndef _SYS_UTSNAME_H
#define _SYS_UTSNAME_H
#include <sys/types.h> // 类型头文件。定义了基本的系统数据类型。
struct utsname
{
char sysname[9]; // 本版本操作系统的名称。
char nodename[9]; // 与实现相关的网络中节点名称。
char release[9]; // 本实现的当前发行级别。
char version[9]; // 本次发行的版本级别。
char machine[9]; // 系统运行的硬件类型名称。
};
extern int uname (struct utsname *utsbuf);
#endif
#ifndef _SYS_WAIT_H
#define _SYS_WAIT_H
#include <sys/types.h>
#define _LOW(v) ( (v) & 0377) // 取低字节(8 进制表示)。
#define _HIGH(v) ( ((v) >> 8) & 0377) // 取高字节。
/* options for waitpid, WUNTRACED not supported */
/* waitpid 的选项,其中WUNTRACED 未被支持 */
#define WNOHANG 1 // 如果没有状态也不要挂起,并立刻返回。
#define WUNTRACED 2 // 报告停止执行的子进程状态。
#define WIFEXITED(s) (!((s)&0xFF) // 如果子进程正常退出,则为真。
#define WIFSTOPPED(s) (((s)&0xFF)==0x7F) // 如果子进程正停止着,则为true。
#define WEXITSTATUS(s) (((s)>>8)&0xFF) // 返回退出状态。
#define WTERMSIG(s) ((s)&0x7F) // 返回导致进程终止的信号值(信号量)。
#define WSTOPSIG(s) (((s)>>8)&0xFF) // 返回导致进程停止的信号值。
#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) // 如果由于未捕捉到信号
// 而导致子进程退出则为真。
// wait()和waitpit()函数允许进程获取与其子进程之一的状态信息。各种选项允许获取已经终止或
// 停止的子进程状态信息。如果存在两个或两个以上子进程的状态信息,则报告的顺序是不指定的。
// wait()将挂起当前进程,直到其子进程之一退出(终止),或者收到要求终止该进程的信号,
// 或者是需要调用一个信号句柄(信号处理程序)。
// waitpid()挂起当前进程,直到pid 指定的子进程退出(终止)或者收到要求终止该进程的信号,
// 或者是需要调用一个信号句柄(信号处理程序)。
// 如果pid= -1,options=0,则waitpid()的作用与wait()函数一样。否则其行为将随pid 和options
// 参数的不同而不同。(参见kernel/exit.c)
pid_t wait (int *stat_loc);
pid_t waitpid (pid_t pid, int *stat_loc, int options);
#endif
差异被折叠。
#ifndef _TIME_H
#define _TIME_H
#ifndef _TIME_T
#define _TIME_T
typedef long time_t; // 从GMT 1970 年1 月1 日开始的以秒计数的时间(日历时间)。
#endif
#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif
#define CLOCKS_PER_SEC 100 // 系统时钟滴答频率,100HZ。
typedef long clock_t; // 从进程开始系统经过的时钟滴答数。
struct tm
{
int tm_sec; // 秒数 [0,59]。
int tm_min; // 分钟数 [ 0,59]。
int tm_hour; // 小时数 [0,59]。
int tm_mday; // 1 个月的天数 [0,31]。
int tm_mon; // 1 年中月份 [0,11]。
int tm_year; // 从1900 年开始的年数。
int tm_wday; // 1 星期中的某天 [0,6](星期天 =0)。
int tm_yday; // 1 年中的某天 [0,365]。
int tm_isdst; // 夏令时标志。
};
// 以下是有关时间操作的函数原型。
// 确定处理器使用时间。返回程序所用处理器时间(滴答数)的近似值。
clock_t clock (void);
// 取时间(秒数)。返回从1970.1.1:0:0:0 开始的秒数(称为日历时间)。
time_t time (time_t * tp);
// 计算时间差。返回时间time2 与time1 之间经过的秒数。
double difftime (time_t time2, time_t time1);
// 将tm 结构表示的时间转换成日历时间。
time_t mktime (struct tm *tp);
// 将tm 结构表示的时间转换成一个字符串。返回指向该串的指针。
char *asctime (const struct tm *tp);
// 将日历时间转换成一个字符串形式,如“Wed Jun 30 21:49:08:1993\n”。
char *ctime (const time_t * tp);
// 将日历时间转换成tm 结构表示的UTC 时间(UTC - 世界时间代码Universal Time Code)。
struct tm *gmtime (const time_t * tp);
// 将日历时间转换成tm 结构表示的指定时间区(timezone)的时间。
struct tm *localtime (const time_t * tp);
// 将tm 结构表示的时间利用格式字符串fmt 转换成最大长度为smax 的字符串并将结果存储在s 中。
size_t strftime (char *s, size_t smax, const char *fmt, const struct tm *tp);
// 初始化时间转换信息,使用环境变量TZ,对zname 变量进行初始化。
// 在与时间区相关的时间转换函数中将自动调用该函数。
void tzset (void);
#endif
差异被折叠。
#ifndef _UTIME_H
#define _UTIME_H
#include <sys/types.h> /* I know - shouldn't do this, but .. */
/* 我知道 - 不应该这样做,但是.. */
struct utimbuf {
time_t actime; // 文件访问时间。 从1970.1.1:0:0:0开始的秒数.
time_t modtime; // 文件修改时间。 从1970.1.1:0:0:0开始的秒数.
};
// 设置文件访问和修改时间函数.
extern int utime(const char *filename, struct utimbuf *times);
#endif
差异被折叠。
/*
* linux/kernel/asm.s
*
* (C) 1991 Linus Torvalds
*/
/*
* asm.s contains the low-level code for most hardware faults.
* page_exception is handled by the mm, so that isn't here. This
* file also handles (hopefully) fpu-exceptions due to TS-bit, as
* the fpu must be properly saved/resored. This hasn't been tested.
*/
/*
* asm.s 程序中包括大部分的硬件故障(或出错)处理的底层次代码。页异常是由内存管理程序
* mm 处理的,所以不在这里。此程序还处理(希望是这样)由于TS-位而造成的fpu 异常,
* 因为fpu 必须正确地进行保存/恢复处理,这些还没有测试过。
*/
# 本代码文件主要涉及对Intel 保留的中断int0--int16 的处理(int17-int31 留作今后使用)。
# 以下是一些全局函数名的声明,其原形在traps.c 中说明。
.globl _divide_error, _debug, _nmi, _int3, _overflow,_bounds,_invalid_op
.globl _double_fault, _coprocessor_segment_overrun
.globl _invalid_TSS, _segment_not_present, _stack_segment
.globl _general_protection, _coprocessor_error,_irq13, _reserved
# 下面这段程序处理无出错的情况
# int0-- 处理被零除出错的情况。 类型:错误;错误号:无
# 在执行 DIV 或 IDIV 指令时,若除数是 0,CPU 就会产生这个异常,当 EAX (或AX , AL)容纳不了
# 一个合法除操作的结果时也会产生这个异常。标号‘_do_divide_error’实际上是 C 语言函数
# ‘do_divide_error()’编译后所生产模块中对应的函数。函数‘do_divide_error’在trap.c中实现
_divide_error:
pushl $_do_divide_error # 首先把将要调用的函数地址入栈。这段程序的出错号为0。
no_error_code: # 这里是无出错号处理的入口处
xchgl %eax,(%esp) # _do_divide_error 的地址 -> eax,eax 被交换入栈。
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds # 16 位的段寄存器入栈后也要占用4 个字节。
push %es
push %fs
pushl $0 # "error code" # 将出错码入栈。
lea 44(%esp),%edx # 取原调用返回地址处堆栈指针位置,并压入堆栈。
pushl %edx
movl $0x10,%edx # 初始化段寄存器,内核代码数据段选择符。
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
call *%eax # 调用C 函数do_divide_error()。
addl $8,%esp # 相当于执行两次pop操作,即丢弃最后入栈的两个 C 函数参数,
# 让堆栈指针重新指向寄存器fs 入栈处。
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax # 弹出原来eax 中的内容。
iret
# int1 -- debug 调试中断入口点。处理过程同上。
_debug:
pushl $_do_int3 # _do_debug
jmp no_error_code
# int2 -- 非屏蔽中断调用入口点。
_nmi:
pushl $_do_nmi
jmp no_error_code
# int3 -- 断点指令引起中断的入口点
_int3:
pushl $_do_int3
jmp no_error_code
# int4 -- 溢出出错处理中断入口点。
_overflow:
pushl $_do_overflow
jmp no_error_code
# int5 -- 边界检查出错中断入口点。
_bounds:
pushl $_do_bounds
jmp no_error_code
# int6 -- 无效操作指令出错中断入口点。
_invalid_op:
pushl $_do_invalid_op
jmp no_error_code
# int9 -- 协处理器段超出出错中断入口点。
_coprocessor_segment_overrun:
pushl $_do_coprocessor_segment_overrun
jmp no_error_code
# int15 – 保留。
_reserved:
pushl $_do_reserved
jmp no_error_code
# int45 -- ( = 0x20 + 13 ) 数学协处理器(Coprocessor)发出的中断。
# 当协处理器执行完一个操作时就会发出IRQ13 中断信号,以通知CPU 操作完成。
_irq13:
pushl %eax
xorb %al,%al # 80387 在执行计算时,CPU 会等待其操作的完成。
outb %al,$0xF0 # 通过写0xF0 端口,本中断将消除CPU 的BUSY 延续信号,并重新
# 激活80387 的处理器扩展请求引脚PEREQ。该操作主要是为了确保
# 在继续执行80387 的任何指令之前,响应本中断。
movb $0x20,%al
outb %al,$0x20 # 向8259 主中断控制芯片发送EOI(中断结束)信号。
jmp 1f # 这两个跳转指令起延时作用。
1: jmp 1f
1: outb %al,$0xA0 # 再向8259 从中断控制芯片发送EOI(中断结束)信号。
popl %eax
jmp _coprocessor_error # _coprocessor_error 原来在本文件中,现在已经放到system_call.s中
# 以下中断在调用时会在中断返回地址之后将出错号压入堆栈,因此返回时也需要将出错号弹出。
_double_fault:
pushl $_do_double_fault # C 函数地址入栈。
error_code:
xchgl %eax,4(%esp) # error code <-> %eax # error code <-> %eax,eax 原来的值被保存在堆栈上。
xchgl %ebx,(%esp) # &function <-> %ebx # &function <-> %ebx,ebx 原来的值被保存在堆栈上。
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl %eax # error code # 出错号入栈。
lea 44(%esp),%eax # offset # 程序返回地址处堆栈指针位置值入栈。
pushl %eax
movl $0x10,%eax # 置内核数据段选择符。
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
call *%ebx # 调用相应的C 函数,其参数已入栈。
addl $8,%esp # 堆栈指针重新指向栈中放置fs 内容的位置。
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
# int10 -- 无效的任务状态段(TSS)。
_invalid_TSS:
pushl $_do_invalid_TSS
jmp error_code
# int11 -- 段不存在。
_segment_not_present:
pushl $_do_segment_not_present
jmp error_code
# int12 -- 堆栈段错误。
_stack_segment:
pushl $_do_stack_segment
jmp error_code
# int13 -- 一般保护性出错。
_general_protection:
pushl $_do_general_protection
jmp error_code
# int7 -- 设备不存在(_device_not_available)在(kernel/system_call.s)
# int14 -- 页错误(_page_fault)在(mm/page.s)
# int16 -- 协处理器错误(_coprocessor_error)在(kernel/system_call.s)
# 时钟中断int 0x20 (_timer_interrupt)在(kernel/system_call.s)
# 系统调用int 0x80 (_system_call)在(kernel/system_call.s)
#ifndef _BLK_H
#define _BLK_H
#define NR_BLK_DEV 7
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*
* 32 seems to be a reasonable number: enough to get some benefit
* from the elevator-mechanism, but not so much as to lock a lot of
* buffers when they are in the queue. 64 seems to be too many (easily
* long pauses in reading when heavy writing/syncing is going on)
*/
/*
* 下面定义的NR_REQUEST 是请求队列中所包含的项数。
* 注意,读操作仅使用这些项低端的2/3;读操作优先处理。
*
* 32 项好象是一个合理的数字:已经足够从电梯算法中获得好处,
* 但当缓冲区在队列中而锁住时又不显得是很大的数。64 就看上
* 去太大了(当大量的写/同步操作运行时很容易引起长时间的暂停)。
*/
#define NR_REQUEST 32
/*
* Ok, this is an expanded form so that we can use the same
* request for paging requests when that is implemented. In
* paging, 'bh' is NULL, and 'waiting' is used to wait for
* read/write completion.
*/
/*
* OK,下面是request 结构的一个扩展形式,因而当实现以后,我们就可以在分页请求中
* 使用同样的request 结构。在分页处理中,'bh'是NULL,而'waiting'则用于等待读/写的完成。
*/
// 下面是请求队列中项的结构。其中如果字段 dev=-1,则表示队列中该项没有被使用
struct request {
int dev; /* -1 if no request */ // 发请求的设备号
int cmd; /* READ or WRITE */ // READ 或WRITE命令
int errors; //操作时产生的错误次数。
unsigned long sector; // 起始扇区。(1 块=2 扇区)
unsigned long nr_sectors; // 读/写扇区数。
char * buffer; // 数据缓冲区。
struct task_struct * waiting; // 任务等待操作执行完成的地方。
struct buffer_head * bh; // 缓冲区头指针(include/linux/fs.h)。
struct request * next; // 指向下一请求项。
};
/*
* This is used in the elevator algorithm: Note that
* reads always go before writes. This is natural: reads
* are much more time-critical than writes.
*/
/*
* 下面的定义用于电梯算法:注意读操作总是在写操作之前进行。
* 这是很自然的:读操作对时间的要求要比写严格得多。
*/
#define IN_ORDER(s1,s2) \
((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \
((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector))))
// 块设备结构。
struct blk_dev_struct {
void (*request_fn)(void); // 请求操作的函数指针。
struct request * current_request; // 请求信息结构。
};
extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; // 块设备数组,每种块设备占用一项。
extern struct request request[NR_REQUEST]; // 请求队列数组。
extern struct task_struct * wait_for_request; // 等待请求的任务结构。
#ifdef MAJOR_NR // 主设备号。
/*
* Add entries as needed. Currently the only block devices
* supported are hard-disks and floppies.
*/
/*
* 需要时加入条目。目前块设备仅支持硬盘和软盘(还有虚拟盘)。
*/
#if (MAJOR_NR == 1) // RAM 盘的主设备号是1。根据这里的定义可以推理内存块主设备号也为1。
/* ram disk */ /* RAM 盘(内存虚拟盘) */
#define DEVICE_NAME "ramdisk" // 设备名称ramdisk。
#define DEVICE_REQUEST do_rd_request // 设备请求函数do_rd_request()。
#define DEVICE_NR(device) ((device) & 7)// 设备号(0--7)。
#define DEVICE_ON(device) // 开启设备。虚拟盘无须开启和关闭。
#define DEVICE_OFF(device) // 关闭设备。
#elif (MAJOR_NR == 2) // 软驱的主设备号是2。
/* floppy */
#define DEVICE_NAME "floppy" // 设备名称floppy。
#define DEVICE_INTR do_floppy // 设备中断处理程序do_floppy()。
#define DEVICE_REQUEST do_fd_request // 设备请求函数do_fd_request()。
#define DEVICE_NR(device) ((device) & 3) // 设备号(0--3)。
#define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) // 开启设备函数floppyon()。
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) // 关闭设备函数floppyoff()。
#elif (MAJOR_NR == 3) // 硬盘主设备号是3。
/* harddisk */
#define DEVICE_NAME "harddisk" // 硬盘名称harddisk。
#define DEVICE_INTR do_hd // 设备中断处理程序do_hd()。
#define DEVICE_REQUEST do_hd_request // 设备请求函数do_hd_request()。
#define DEVICE_NR(device) (MINOR(device)/5) // 设备号(0--1)。每个硬盘可以有4 个分区。
#define DEVICE_ON(device ) // 硬盘一直在工作,无须开启和关闭。
#define DEVICE_OFF(device)
#elif
/* unknown blk device *//* 未知块设备 */
#error "unknown blk device"
#endif
#define CURRENT (blk_dev[MAJOR_NR].current_request) // CURRENT 为指定主设备号的当前请求结构。
#define CURRENT_DEV DEVICE_NR(CURRENT->dev) // CURRENT_DEV 为CURRENT 的设备号。
#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;
#endif
static void (DEVICE_REQUEST)(void);
// 释放锁定的缓冲区。
static inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock) // 如果指定的缓冲区bh 并没有被上锁,则显示警告信息。
printk(DEVICE_NAME ": free buffer being unlocked\n");
bh->b_lock=0; // 否则将该缓冲区解锁。
wake_up(&bh->b_wait); // 唤醒等待该缓冲区的进程。
}
// 结束请求。
static inline void end_request(int uptodate)
{
DEVICE_OFF(CURRENT->dev); // 关闭设备。
if (CURRENT->bh) {
// CURRENT 为指定主设备号的当前请求结构。
CURRENT->bh->b_uptodate = uptodate; // 置更新标志。
unlock_buffer(CURRENT->bh); // 解锁缓冲区。
}
if (!uptodate) {
// 如果更新标志为0 则显示设备错误信息。
printk(DEVICE_NAME " I/O error\n\r");
printk("dev %04x, block %d\n\r",CURRENT->dev,
CURRENT->bh->b_blocknr);
}
wake_up(&CURRENT->waiting); // 唤醒等待该请求项的进程。
wake_up(&wait_for_request); // 唤醒等待请求的进程。
CURRENT->dev = -1; // 释放该请求项。
CURRENT = CURRENT->next; // 从请求链表中删除该请求项。
}
// 定义初始化请求宏。
#define INIT_REQUEST \
repeat: \
if (!CURRENT) \
return; \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
if (!CURRENT->bh->b_lock) \
panic(DEVICE_NAME ": block not locked"); \
}
#endif
#endif
差异被折叠。
差异被折叠。
/*
* linux/kernel/blk_dev/ll_rw.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This handles all read/write requests to block devices
*/
/*
* 该程序处理块设备的所有读/写操作。
*/
#include <errno.h> // 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)
#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,
// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。
#include <linux/kernel.h> // 内核头文件。含有一些内核常用函数的原形定义。
#include <asm/system.h> // 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。
#include "blk.h" // 块设备头文件。定义请求数据结构、块设备数据结构和宏函数等信息。
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
*/
/*
* 请求结构中含有加载nr 扇区数据到内存的所有必须的信息。
*/
// 请求项数据队列。共有 NR_REQUEST = 32个请求项.
struct request request[NR_REQUEST];
/*
* used to wait on when there are no free requests
*/
/* 是用于请求数组没有空闲项时的临时等待处 */
struct task_struct * wait_for_request = NULL;
/* blk_dev_struct is:
* do_request-address
* next-request
*/
/* blk_dev_struct 块设备结构是:(kernel/blk_drv/blk.h)
* do_request-address //对应主设备号的请求处理程序指针。
* current-request // 该设备的下一个请求。
*/
// 该数组使用主设备号作为索引(下标)。
struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
{NULL, NULL}, /* no_dev */ // 0 - 无设备。
{NULL, NULL}, /* dev mem */ // 1 - 内存。
{NULL, NULL}, /* dev fd */ // 2 - 软驱设备。
{NULL, NULL}, /* dev hd */ // 3 - 硬盘设备。
{NULL, NULL}, /* dev ttyx */ // 4 - ttyx 设备。
{NULL, NULL}, /* dev tty */ // 5 - tty 设备。
{NULL, NULL} /* dev lp */ // 6 - lp 打印机设备。
};
// 锁定指定的缓冲区bh。如果指定的缓冲区已经被其它任务锁定,则使自己睡眠(不可中断地等待),
// 直到被执行解锁缓冲区的任务明确地唤醒。
static inline void
lock_buffer (struct buffer_head *bh)
{
cli (); // 清中断许可。
while (bh->b_lock) // 如果缓冲区已被锁定,则睡眠,直到缓冲区解锁。
sleep_on (&bh->b_wait);
bh->b_lock = 1; // 立刻锁定该缓冲区。
sti (); // 开中断。
}
// 释放(解锁)锁定的缓冲区。
static inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock) // 如果该缓冲区并没有被锁定,则打印出错信息。
printk ("ll_rw_block.c: buffer not locked\n\r");
bh->b_lock = 0; // 清锁定标志。
wake_up (&bh->b_wait); // 唤醒等待该缓冲区的任务。
}
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*/
/* add-request()向连表中加入一项请求。它关闭中断,
* 这样就能安全地处理请求连表了
*/
//// 向链表中加入请求项。参数dev 指定块设备,req 是请求的结构信息。
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
req->next = NULL;
cli();
if (req->bh)
req->bh->b_dirt = 0; // 清缓冲区“脏”标志。
// 如果dev 的当前请求(current_request)子段为空,则表示目前该设备没有请求项,本次是第1 个
// 请求项,因此可将块设备当前请求指针直接指向请求项,并立刻执行相应设备的请求函数。
if (!(tmp = dev->current_request)) {
dev->current_request = req;
sti();
(dev->request_fn)();// 执行设备请求函数,对于硬盘(3)是do_hd_request()。
return;
}
// 如果目前该设备已经有请求项在等待,则首先利用电梯算法搜索最佳位置,然后将当前请求插入
// 请求链表中。
for ( ; tmp->next ; tmp=tmp->next)
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
req->next=tmp->next;
tmp->next=req;
sti();
}
//// 创建请求项并插入请求队列。参数是:主设备号major,命令rw,存放数据的缓冲区头指针bh。
static void make_request(int major,int rw, struct buffer_head * bh)
{
struct request * req;
int rw_ahead;
/* WRITEA/READA is special case - it is not really needed, so if the */
/* buffer is locked, we just forget about it, else it's a normal read */
/* WRITEA/READA 是特殊的情况 - 它们并不是必要的,所以如果缓冲区已经上锁,*/
/* 我们就不管它而退出,否则的话就执行一般的读/写操作。 */
// 这里'READ'和'WRITE'后面的'A'字符代表英文单词Ahead,表示提前预读/写数据块的意思。
// 当指定的缓冲区正在使用,已被上锁时,就放弃预读/写请求。
if ((rw_ahead = (rw == READA || rw == WRITEA))) {
if (bh->b_lock)
return;
if (rw == READA)
rw = READ;
else
rw = WRITE;
}
// 如果命令不是READ 或WRITE 则表示内核程序有错,显示出错信息并死机。
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W/RA/WA");
// 锁定缓冲区,如果缓冲区已经上锁,则当前任务(进程)就会睡眠,直到被明确地唤醒。
lock_buffer(bh);
// 如果命令是写并且缓冲区数据不脏,或者命令是读并且缓冲区数据是更新过的,则不用添加
// 这个请求。将缓冲区解锁并退出。
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
unlock_buffer(bh);
return;
}
repeat:
/* we don't allow the write-requests to fill up the queue completely:
* we want some room for reads: they take precedence. The last third
* of the requests are only for reads.
*/
/* 我们不能让队列中全都是写请求项:我们需要为读请求保留一些空间:读操作
* 是优先的。请求队列的后三分之一空间是为读准备的。
*/
// 请求项是从请求数组末尾开始搜索空项填入的。根据上述要求,对于读命令请求,可以直接
// 从队列末尾开始操作,而写请求则只能从队列的2/3 处向头上搜索空项填入。
if (rw == READ)
req = request+NR_REQUEST; // 对于读请求,将队列指针指向队列尾部。
else
req = request+((NR_REQUEST*2)/3);// 对于写请求,队列指针指向队列2/3 处。
/* find an empty request */
/* 搜索一个空请求项 */
// 从后向前搜索,当请求结构request 的dev 字段值=-1 时,表示该项未被占用。
while (--req >= request)
if (req->dev<0)
break;
/* if none found, sleep on new requests: check for rw_ahead */
/* 如果没有找到空闲项,则让该次新请求睡眠:需检查是否提前读/写 */
// 如果没有一项是空闲的(此时request 数组指针已经搜索越过头部),则查看此次请求是否是
// 提前读/写(READA 或WRITEA),如果是则放弃此次请求。否则让本次请求睡眠(等待请求队列
// 腾出空项),过一会再来搜索请求队列。
if (req < request) { // 如果请求队列中没有空项
if (rw_ahead) { // 如果是提前读/写请求,则解锁缓冲区,退出。
unlock_buffer(bh);
return;
}
sleep_on(&wait_for_request);// 否则让本次请求睡眠,过会再查看请求队列。
goto repeat;
}
/* fill up the request-info, and add it to the queue */
/* 向空闲请求项中填写请求信息,并将其加入队列中 */
// 请求结构参见(kernel/blk_drv/blk.h)。
req->dev = bh->b_dev; // 设备号。
req->cmd = rw; // 命令(READ/WRITE)。
req->errors = 0; // 操作时产生的错误次数。
req->sector = bh->b_blocknr << 1; // 起始扇区。(1 块=2 扇区)
req->nr_sectors = 2; // 读写扇区数。
req->buffer = bh->b_data; // 数据缓冲区。
req->waiting = NULL; // 任务等待操作执行完成的地方。
req->bh = bh; // 缓冲区头指针。
req->next = NULL; // 指向下一请求项。
add_request (major + blk_dev, req); // 将请求项加入队列中(blk_dev[major],req)。
}
//// 低层读写数据块函数。
// 该函数主要是在fs/buffer.c 中被调用。实际的读写操作是由设备的request_fn()函数完成。
// 对于硬盘操作,该函数是do_hd_request()。(kernel/blk_drv/hd.c)
void ll_rw_block(int rw, struct buffer_head * bh)
{
unsigned int major; // 主设备号(对于硬盘是3)。
// 如果设备的主设备号不存在或者该设备的读写操作函数不存在,则显示出错信息,并返回。
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
!(blk_dev[major].request_fn)) {
printk("Trying to read nonexistent block-device\n\r");
return;
}
make_request(major,rw,bh); // 创建请求项并插入请求队列。
}
//// 块设备初始化函数,由初始化程序main.c 调用(init/main.c)。
// 初始化请求数组,将所有请求项置为空闲项(dev = -1)。有32 项(NR_REQUEST = 32)。
void blk_dev_init(void)
{
int i;
for (i=0 ; i<NR_REQUEST ; i++) {
request[i].dev = -1;
request[i].next = NULL;
}
}
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论