跳到主要內容

檔案系統-將 TTY 納入檔案系統

寫入 TTY 跟寫入普通檔是很類似的,不同之處在於 TTY 不需要進行任何埠操作,只是寫入顯示卡記憶體即可。而對於讀出操作,則有很大的不同。TTY 收到處理序讀出請求的訊息,並不能馬上返回資料,因為此時並沒有任何輸入,而需要使用者輸入字元,等輸入結束後,TTY才可以返回給處理序。問題在於:

  • 怎樣是「輸入結束」:是美一次鍵盤敲擊後算結束(面向字元,Raw mode)?還是等敲 Enter 才算結果(面向行,Cooked mode)?或者其它?
  • 是否要讓檔案系統等待輸入過程結束?
    • TTY 需要馬上向檔案系統發送訊息以示返回。而檔案系統則完全應該阻塞想要得到輸入的處理序,直到輸入結束。
讀 TTY 的過程:
  1. 假設處理序 P 要求讀取 TTY,它會發送訊息給檔案系統
  2. 檔案系統將訊息傳遞給 TTY
  3. TTY 記下發出請求的處理序號等資訊後立即返回
  4. 檔案系統此時並不對 P 接觸阻塞,因為結果尚未準備好
  5. 檔案系統一如往常等待任何處理序之請求
  6. TTY 將鍵盤輸入複製進 P 傳入的記憶體位址
  7. 一旦遇到 Enter,TTY 就告訴檔案系統,P 的請求已被滿足
  8. 檔案系統會解除對 P 的阻塞
  9. 讀取工作結束
寫 TTY 的過程:
  1. P 發訊息給檔案系統
  2. 檔案系統傳遞給 TTY
  3. TTY 受到訊息候立即將字元寫入顯示卡記憶體(保持 P 和 FS 處理序的阻塞)
  4. 完成後發訊息給檔案系統
  5. 檔案系統在發訊息給 P
  6. 整個過程結束

程式碼

fs/main.c

/*************************************************************************//**
 *****************************************************************************
 * @file   main.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

#include "hd.h"

PRIVATE void init_fs();
PRIVATE void mkfs();
PRIVATE void read_super_block(int dev);

/*****************************************************************************
 *                                task_fs
 *****************************************************************************/
/**
 * <Ring 1> The main loop of TASK FS.
 *
 *****************************************************************************/
PUBLIC void task_fs()
{
 printl("Task FS begins.\n");

 init_fs();

 while (1) {
  send_recv(RECEIVE, ANY, &fs_msg);

  int msgtype = fs_msg.type;
  int src = fs_msg.source;
  pcaller = &proc_table[src];

  switch (msgtype) {
  case OPEN:
   fs_msg.FD = do_open();
   break;
  case CLOSE:
   fs_msg.RETVAL = do_close();
   break;
  case READ:
  case WRITE:
   fs_msg.CNT = do_rdwt();
   break;
  case UNLINK:
   fs_msg.RETVAL = do_unlink();
   break;
  /* case LSEEK: */
  /*  fs_msg.OFFSET = do_lseek(); */
  /*  break; */
  /* case RESUME_PROC: */
  /*  src = fs_msg.PROC_NR; */
  /*  break; */
  /* case FORK: */
  /*  fs_msg.RETVAL = fs_fork(); */
  /*  break; */
  /* case EXIT: */
  /*  fs_msg.RETVAL = fs_exit(); */
  /*  break; */
  /* case STAT: */
  /*  fs_msg.RETVAL = do_stat(); */
  /*  break; */
  default:
   dump_msg("FS::unknown message:", &fs_msg);
   assert(0);
   break;
  }

#ifdef ENABLE_DISK_LOG
  char * msg_name[128];
  msg_name[OPEN]   = "OPEN";
  msg_name[CLOSE]  = "CLOSE";
  msg_name[READ]   = "READ";
  msg_name[WRITE]  = "WRITE";
  msg_name[LSEEK]  = "LSEEK";
  msg_name[UNLINK] = "UNLINK";
  /* msg_name[FORK]   = "FORK"; */
  /* msg_name[EXIT]   = "EXIT"; */
  /* msg_name[STAT]   = "STAT"; */

  switch (msgtype) {
  case CLOSE:
  case UNLINK:
   //dump_fd_graph("%s just finished.", msg_name[msgtype]);
   //panic("");
  case OPEN:
  case READ:
  case WRITE:
  /* case FORK: */
  /* case LSEEK: */
  /* case EXIT: */
  /* case STAT: */
   break;
  /* case RESUME_PROC: */
  case DISK_LOG:
   break;
  default:
   assert(0);
  }
#endif

  /* reply */
  fs_msg.type = SYSCALL_RET;
  send_recv(SEND, src, &fs_msg);
 }
}

/*****************************************************************************
 *                                init_fs
 *****************************************************************************/
/**
 * <Ring 1> Do some preparation.
 *
 *****************************************************************************/
PRIVATE void init_fs()
{
 int i;

 /* f_desc_table[] */
 for (i = 0; i < NR_FILE_DESC; i++)
  memset(&f_desc_table[i], 0, sizeof(struct file_desc));

 /* inode_table[] */
 for (i = 0; i < NR_INODE; i++)
  memset(&inode_table[i], 0, sizeof(struct inode));

 /* super_block[] */
 struct super_block * sb = super_block;
 for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
  sb->sb_dev = NO_DEV;

 /* open the device: hard disk */
 MESSAGE driver_msg;
 driver_msg.type = DEV_OPEN;
 driver_msg.DEVICE = MINOR(ROOT_DEV);
 assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);

 /* make FS */
 mkfs();

 /* load super block of ROOT */
 read_super_block(ROOT_DEV);

 sb = get_super_block(ROOT_DEV);
 assert(sb->magic == MAGIC_V1);

 root_inode = get_inode(ROOT_DEV, ROOT_INODE);
}

/*****************************************************************************
 *                                mkfs
 *****************************************************************************/
/**
 * <Ring 1> Make a available Orange'S FS in the disk. It will
 *          - Write a super block to sector 1.
 *          - Create three special files: dev_tty0, dev_tty1, dev_tty2
 *          - Create the inode map
 *          - Create the sector map
 *          - Create the inodes of the files
 *          - Create `/', the root directory
 *****************************************************************************/
PRIVATE void mkfs()
{
 MESSAGE driver_msg;
 int i, j;

 int bits_per_sect = SECTOR_SIZE * 8; /* 8 bits per byte */

 /* get the geometry of ROOTDEV */
 struct part_info geo;
 driver_msg.type  = DEV_IOCTL;
 driver_msg.DEVICE = MINOR(ROOT_DEV);
 driver_msg.REQUEST = DIOCTL_GET_GEO;
 driver_msg.BUF  = &geo;
 driver_msg.PROC_NR = TASK_FS;
 assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);

 printl("dev size: 0x%x sectors\n", geo.size);

 /************************/
 /*      super block     */
 /************************/
 struct super_block sb;
 sb.magic   = MAGIC_V1;
 sb.nr_inodes   = bits_per_sect;
 sb.nr_inode_sects = sb.nr_inodes * INODE_SIZE / SECTOR_SIZE;
 sb.nr_sects   = geo.size; /* partition size in sector */
 sb.nr_imap_sects  = 1;
 sb.nr_smap_sects  = sb.nr_sects / bits_per_sect + 1;
 sb.n_1st_sect   = 1 + 1 +   /* boot sector & super block */
  sb.nr_imap_sects + sb.nr_smap_sects + sb.nr_inode_sects;
 sb.root_inode   = ROOT_INODE;
 sb.inode_size   = INODE_SIZE;
 struct inode x;
 sb.inode_isize_off= (int)&x.i_size - (int)&x;
 sb.inode_start_off= (int)&x.i_start_sect - (int)&x;
 sb.dir_ent_size   = DIR_ENTRY_SIZE;
 struct dir_entry de;
 sb.dir_ent_inode_off = (int)&de.inode_nr - (int)&de;
 sb.dir_ent_fname_off = (int)&de.name - (int)&de;

 memset(fsbuf, 0x90, SECTOR_SIZE);
 memcpy(fsbuf, &sb, SUPER_BLOCK_SIZE);

 /* write the super block */
 WR_SECT(ROOT_DEV, 1);

 printl("devbase:0x%x00, sb:0x%x00, imap:0x%x00, smap:0x%x00\n"
        "        inodes:0x%x00, 1st_sector:0x%x00\n",
        geo.base * 2,
        (geo.base + 1) * 2,
        (geo.base + 1 + 1) * 2,
        (geo.base + 1 + 1 + sb.nr_imap_sects) * 2,
        (geo.base + 1 + 1 + sb.nr_imap_sects + sb.nr_smap_sects) * 2,
        (geo.base + sb.n_1st_sect) * 2);

 /************************/
 /*       inode map      */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 for (i = 0; i < (NR_CONSOLES + 2); i++)
  fsbuf[0] |= 1 << i;

 assert(fsbuf[0] == 0x1F);/* 0001 1111 :
      *    | ||||
      *    | |||`--- bit 0 : reserved
      *    | ||`---- bit 1 : the first inode,
      *    | ||              which indicates `/'
      *    | |`----- bit 2 : /dev_tty0
      *    | `------ bit 3 : /dev_tty1
      *    `-------- bit 4 : /dev_tty2
      */
 WR_SECT(ROOT_DEV, 2);

 /************************/
 /*      secter map      */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 int nr_sects = NR_DEFAULT_FILE_SECTS + 1;
 /*             ~~~~~~~~~~~~~~~~~~~|~   |
  *                                |    `--- bit 0 is reserved
  *                                `-------- for `/'
  */
 for (i = 0; i < nr_sects / 8; i++)
  fsbuf[i] = 0xFF;

 for (j = 0; j < nr_sects % 8; j++)
  fsbuf[i] |= (1 << j);

 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects);

 /* zeromemory the rest sector-map */
 memset(fsbuf, 0, SECTOR_SIZE);
 for (i = 1; i < sb.nr_smap_sects; i++)
  WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + i);

 /************************/
 /*       inodes         */
 /************************/
 /* inode of `/' */
 memset(fsbuf, 0, SECTOR_SIZE);
 struct inode * pi = (struct inode*)fsbuf;
 pi->i_mode = I_DIRECTORY;
 pi->i_size = DIR_ENTRY_SIZE * 4; /* 4 files:
       * `.',
       * `dev_tty0', `dev_tty1', `dev_tty2',
       */
 pi->i_start_sect = sb.n_1st_sect;
 pi->i_nr_sects = NR_DEFAULT_FILE_SECTS;
 /* inode of `/dev_tty0~2' */
 for (i = 0; i < NR_CONSOLES; i++) {
  pi = (struct inode*)(fsbuf + (INODE_SIZE * (i + 1)));
  pi->i_mode = I_CHAR_SPECIAL;
  pi->i_size = 0;
  pi->i_start_sect = MAKE_DEV(DEV_CHAR_TTY, i);
  pi->i_nr_sects = 0;
 }
 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + sb.nr_smap_sects);

 /************************/
 /*          `/'         */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 struct dir_entry * pde = (struct dir_entry *)fsbuf;

 pde->inode_nr = 1;
 strcpy(pde->name, ".");

 /* dir entries of `/dev_tty0~2' */
 for (i = 0; i < NR_CONSOLES; i++) {
  pde++;
  pde->inode_nr = i + 2; /* dev_tty0's inode_nr is 2 */
  sprintf(pde->name, "dev_tty%d", i);
 }
 WR_SECT(ROOT_DEV, sb.n_1st_sect);
}

/*****************************************************************************
 *                                rw_sector
 *****************************************************************************/
/**
 * <Ring 1> R/W a sector via messaging with the corresponding driver.
 *
 * @param io_type  DEV_READ or DEV_WRITE
 * @param dev      device nr
 * @param pos      Byte offset from/to where to r/w.
 * @param bytes    r/w count in bytes.
 * @param proc_nr  To whom the buffer belongs.
 * @param buf      r/w buffer.
 *
 * @return Zero if success.
 *****************************************************************************/
PUBLIC int rw_sector(int io_type, int dev, u64 pos, int bytes, int proc_nr,
       void* buf)
{
 MESSAGE driver_msg;

 driver_msg.type  = io_type;
 driver_msg.DEVICE = MINOR(dev);
 driver_msg.POSITION = pos;
 driver_msg.BUF  = buf;
 driver_msg.CNT  = bytes;
 driver_msg.PROC_NR = proc_nr;
 assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);

 return 0;
}


/*****************************************************************************
 *                                read_super_block
 *****************************************************************************/
/**
 * <Ring 1> Read super block from the given device then write it into a free
 *          super_block[] slot.
 *
 * @param dev  From which device the super block comes.
 *****************************************************************************/
PRIVATE void read_super_block(int dev)
{
 int i;
 MESSAGE driver_msg;

 driver_msg.type  = DEV_READ;
 driver_msg.DEVICE = MINOR(dev);
 driver_msg.POSITION = SECTOR_SIZE * 1;
 driver_msg.BUF  = fsbuf;
 driver_msg.CNT  = SECTOR_SIZE;
 driver_msg.PROC_NR = TASK_FS;
 assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);

 /* find a free slot in super_block[] */
 for (i = 0; i < NR_SUPER_BLOCK; i++)
  if (super_block[i].sb_dev == NO_DEV)
   break;
 if (i == NR_SUPER_BLOCK)
  panic("super_block slots used up");

 assert(i == 0); /* currently we use only the 1st slot */

 struct super_block * psb = (struct super_block *)fsbuf;

 super_block[i] = *psb;
 super_block[i].sb_dev = dev;
}


/*****************************************************************************
 *                                get_super_block
 *****************************************************************************/
/**
 * <Ring 1> Get the super block from super_block[].
 *
 * @param dev Device nr.
 *
 * @return Super block ptr.
 *****************************************************************************/
PUBLIC struct super_block * get_super_block(int dev)
{
 struct super_block * sb = super_block;
 for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
  if (sb->sb_dev == dev)
   return sb;

 panic("super block of devie %d not found.\n", dev);

 return 0;
}


/*****************************************************************************
 *                                get_inode
 *****************************************************************************/
/**
 * <Ring 1> Get the inode ptr of given inode nr. A cache -- inode_table[] -- is
 * maintained to make things faster. If the inode requested is already there,
 * just return it. Otherwise the inode will be read from the disk.
 *
 * @param dev Device nr.
 * @param num I-node nr.
 *
 * @return The inode ptr requested.
 *****************************************************************************/
PUBLIC struct inode * get_inode(int dev, int num)
{
 if (num == 0)
  return 0;

 struct inode * p;
 struct inode * q = 0;
 for (p = &inode_table[0]; p < &inode_table[NR_INODE]; p++) {
  if (p->i_cnt) { /* not a free slot */
   if ((p->i_dev == dev) && (p->i_num == num)) {
    /* this is the inode we want */
    p->i_cnt++;
    return p;
   }
  }
  else {  /* a free slot */
   if (!q) /* q hasn't been assigned yet */
    q = p; /* q <- the 1st free slot */
  }
 }

 if (!q)
  panic("the inode table is full");

 q->i_dev = dev;
 q->i_num = num;
 q->i_cnt = 1;

 struct super_block * sb = get_super_block(dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
  ((num - 1) / (SECTOR_SIZE / INODE_SIZE));
 RD_SECT(dev, blk_nr);
 struct inode * pinode =
  (struct inode*)((u8*)fsbuf +
    ((num - 1 ) % (SECTOR_SIZE / INODE_SIZE))
     * INODE_SIZE);
 q->i_mode = pinode->i_mode;
 q->i_size = pinode->i_size;
 q->i_start_sect = pinode->i_start_sect;
 q->i_nr_sects = pinode->i_nr_sects;
 return q;
}

/*****************************************************************************
 *                                put_inode
 *****************************************************************************/
/**
 * Decrease the reference nr of a slot in inode_table[]. When the nr reaches
 * zero, it means the inode is not used any more and can be overwritten by
 * a new inode.
 *
 * @param pinode I-node ptr.
 *****************************************************************************/
PUBLIC void put_inode(struct inode * pinode)
{
 assert(pinode->i_cnt > 0);
 pinode->i_cnt--;
}

/*****************************************************************************
 *                                sync_inode
 *****************************************************************************/
/**
 * <Ring 1> Write the inode back to the disk. Commonly invoked as soon as the
 *          inode is changed.
 *
 * @param p I-node ptr.
 *****************************************************************************/
PUBLIC void sync_inode(struct inode * p)
{
 struct inode * pinode;
 struct super_block * sb = get_super_block(p->i_dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
  ((p->i_num - 1) / (SECTOR_SIZE / INODE_SIZE));
 RD_SECT(p->i_dev, blk_nr);
 pinode = (struct inode*)((u8*)fsbuf +
     (((p->i_num - 1) % (SECTOR_SIZE / INODE_SIZE))
      * INODE_SIZE));
 pinode->i_mode = p->i_mode;
 pinode->i_size = p->i_size;
 pinode->i_start_sect = p->i_start_sect;
 pinode->i_nr_sects = p->i_nr_sects;
 WR_SECT(p->i_dev, blk_nr);
}

include/sys/console.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
         console.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#ifndef _ORANGES_CONSOLE_H_
#define _ORANGES_CONSOLE_H_

/* CONSOLE */
typedef struct s_console
{
 unsigned int crtc_start; /* set CRTC start addr reg */
 unsigned int orig;     /* start addr of the console */
 unsigned int con_size;   /* how many words does the console have */
 unsigned int cursor;
 int  is_full;
}CONSOLE;


#define SCR_UP 1 /* scroll upward */
#define SCR_DN -1 /* scroll downward */

#define SCR_SIZE  (80 * 25)
#define SCR_WIDTH   80

#define DEFAULT_CHAR_COLOR (MAKE_COLOR(BLACK, WHITE))
#define GRAY_CHAR  (MAKE_COLOR(BLACK, BLACK) | BRIGHT)
#define RED_CHAR  (MAKE_COLOR(BLUE, RED) | BRIGHT)

#endif /* _ORANGES_CONSOLE_H_ */

include/sys/const.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            const.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#ifndef _ORANGES_CONST_H_
#define _ORANGES_CONST_H_

/* max() & min() */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

/* Color */
/*
 * e.g. MAKE_COLOR(BLUE, RED)
 *      MAKE_COLOR(BLACK, RED) | BRIGHT
 *      MAKE_COLOR(BLACK, RED) | BRIGHT | FLASH
 */
#define BLACK   0x0     /* 0000 */
#define WHITE   0x7     /* 0111 */
#define RED     0x4     /* 0100 */
#define GREEN   0x2     /* 0010 */
#define BLUE    0x1     /* 0001 */
#define FLASH   0x80    /* 1000 0000 */
#define BRIGHT  0x08    /* 0000 1000 */
#define MAKE_COLOR(x,y) ((x<<4) | y) /* MAKE_COLOR(Background,Foreground) */

/* GDT 和 IDT 中描述符的個數 */
#define GDT_SIZE 128
#define IDT_SIZE 256

/* 權限 */
#define PRIVILEGE_KRNL 0
#define PRIVILEGE_TASK 1
#define PRIVILEGE_USER 3
/* RPL */
#define RPL_KRNL SA_RPL0
#define RPL_TASK SA_RPL1
#define RPL_USER SA_RPL3

/* Process */
#define SENDING   0x02 /* set when proc trying to send */
#define RECEIVING 0x04 /* set when proc trying to recv */

/* TTY */
#define NR_CONSOLES 3 /* consoles */

/* 8259A interrupt controller ports. */
#define INT_M_CTL 0x20 /* I/O port for interrupt controller         <Master> */
#define INT_M_CTLMASK 0x21 /* setting bits in this port disables ints   <Master> */
#define INT_S_CTL 0xA0 /* I/O port for second interrupt controller  <Slave>  */
#define INT_S_CTLMASK 0xA1 /* setting bits in this port disables ints   <Slave>  */

/* 8253/8254 PIT (Programmable Interval Timer) */
#define TIMER0         0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE     0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
        * Counter0 - LSB then MSB - rate generator - binary
        */
#define TIMER_FREQ     1193182L/* clock frequency for timer in PC and AT */
#define HZ             100  /* clock freq (software settable on IBM-PC) */

/* AT keyboard */
/* 8042 ports */
#define KB_DATA  0x60 /* I/O port for keyboard data
     Read : Read Output Buffer
     Write: Write Input Buffer(8042 Data&8048 Command) */
#define KB_CMD  0x64 /* I/O port for keyboard command
     Read : Read Status Register
     Write: Write Input Buffer(8042 Command) */
#define LED_CODE 0xED
#define KB_ACK  0xFA

/* VGA */
#define CRTC_ADDR_REG 0x3D4 /* CRT Controller Registers - Addr Register */
#define CRTC_DATA_REG 0x3D5 /* CRT Controller Registers - Data Register */
#define START_ADDR_H 0xC /* reg index of video mem start addr (MSB) */
#define START_ADDR_L 0xD /* reg index of video mem start addr (LSB) */
#define CURSOR_H 0xE /* reg index of cursor position (MSB) */
#define CURSOR_L 0xF /* reg index of cursor position (LSB) */
#define V_MEM_BASE 0xB8000 /* base of color video memory */
#define V_MEM_SIZE 0x8000 /* 32K: B8000H -> BFFFFH */

/* Hardware interrupts */
#define NR_IRQ  16 /* Number of IRQs */
#define CLOCK_IRQ 0
#define KEYBOARD_IRQ 1
#define CASCADE_IRQ 2 /* cascade enable for 2nd AT controller */
#define ETHER_IRQ 3 /* default ethernet interrupt vector */
#define SECONDARY_IRQ 3 /* RS232 interrupt vector for port 2 */
#define RS232_IRQ 4 /* RS232 interrupt vector for port 1 */
#define XT_WINI_IRQ 5 /* xt winchester */
#define FLOPPY_IRQ 6 /* floppy disk */
#define PRINTER_IRQ 7
#define AT_WINI_IRQ 14 /* at winchester */

/* tasks */
/* 注意 TASK_XXX 的定義要與 global.c 中對應 */
#define INVALID_DRIVER -20
#define INTERRUPT -10
#define TASK_TTY 0
#define TASK_SYS 1
#define TASK_HD  2
#define TASK_FS  3
/* #define TASK_MM 4 */
#define ANY  (NR_TASKS + NR_PROCS + 10)
#define NO_TASK  (NR_TASKS + NR_PROCS + 20)

#define MAX_TICKS 0x7FFFABCD

/* system call */
#define NR_SYS_CALL 3

/* ipc */
#define SEND  1
#define RECEIVE  2
#define BOTH  3 /* BOTH = (SEND | RECEIVE) */

/* magic chars used by `printx' */
#define MAG_CH_PANIC '\002'
#define MAG_CH_ASSERT '\003'

/**
 * @enum msgtype
 * @brief MESSAGE types
 */
enum msgtype {
 /*
  * when hard interrupt occurs, a msg (with type==HARD_INT) will
  * be sent to some tasks
  */
 HARD_INT = 1,

 /* SYS task */
 GET_TICKS, GET_PID,

 /* FS */
 OPEN, CLOSE, READ, WRITE, LSEEK, STAT, UNLINK,

 /* FS & TTY */
 SUSPEND_PROC, RESUME_PROC,

 /* TTY, SYS, FS, MM, etc */
 SYSCALL_RET,

 /* message type for drivers */
 DEV_OPEN = 1001,
 DEV_CLOSE,
 DEV_READ,
 DEV_WRITE,
 DEV_IOCTL,

 /* for debug */
 DISK_LOG
};

/* macros for messages */
#define FD  u.m3.m3i1
#define PATHNAME u.m3.m3p1
#define FLAGS  u.m3.m3i1
#define NAME_LEN u.m3.m3i2
#define CNT  u.m3.m3i2
#define REQUEST  u.m3.m3i2
#define PROC_NR  u.m3.m3i3
#define DEVICE  u.m3.m3i4
#define POSITION u.m3.m3l1
#define BUF  u.m3.m3p2
#define OFFSET  u.m3.m3i2
#define WHENCE  u.m3.m3i3

#define PID  u.m3.m3i2
/* #define STATUS  u.m3.m3i1 */
#define RETVAL  u.m3.m3i1









#define DIOCTL_GET_GEO 1

/* Hard Drive */
#define SECTOR_SIZE  512
#define SECTOR_BITS  (SECTOR_SIZE * 8)
#define SECTOR_SIZE_SHIFT 9

/* major device numbers (corresponding to kernel/global.c::dd_map[]) */
#define NO_DEV   0
#define DEV_FLOPPY  1
#define DEV_CDROM  2
#define DEV_HD   3
#define DEV_CHAR_TTY  4
#define DEV_SCSI  5
/* make device number from major and minor numbers */
#define MAJOR_SHIFT  8
#define MAKE_DEV(a,b)  ((a << MAJOR_SHIFT) | b)
/* separate major and minor numbers from device number */
#define MAJOR(x)  ((x >> MAJOR_SHIFT) & 0xFF)
#define MINOR(x)  (x & 0xFF)

#define INVALID_INODE  0
#define ROOT_INODE  1

#define MAX_DRIVES  2
#define NR_PART_PER_DRIVE 4
#define NR_SUB_PER_PART  16
#define NR_SUB_PER_DRIVE (NR_SUB_PER_PART * NR_PART_PER_DRIVE)
#define NR_PRIM_PER_DRIVE (NR_PART_PER_DRIVE + 1)

/**
 * @def MAX_PRIM
 * Defines the max minor number of the primary partitions.
 * If there are 2 disks, prim_dev ranges in hd[0-9], this macro will
 * equals 9.
 */
#define MAX_PRIM  (MAX_DRIVES * NR_PRIM_PER_DRIVE - 1)

#define MAX_SUBPARTITIONS (NR_SUB_PER_DRIVE * MAX_DRIVES)

/* device numbers of hard disk */
#define MINOR_hd1a  0x10
#define MINOR_hd2a  (MINOR_hd1a+NR_SUB_PER_PART)

#define ROOT_DEV  MAKE_DEV(DEV_HD, MINOR_BOOT)

#define P_PRIMARY 0
#define P_EXTENDED 1

#define ORANGES_PART 0x99 /* Orange'S partition */
#define NO_PART  0x00 /* unused entry */
#define EXT_PART 0x05 /* extended partition */

#define NR_FILES 64
#define NR_FILE_DESC 64 /* FIXME */
#define NR_INODE 64 /* FIXME */
#define NR_SUPER_BLOCK 8


/* INODE::i_mode (octal, lower 12 bits reserved) */
#define I_TYPE_MASK     0170000
#define I_REGULAR       0100000
#define I_BLOCK_SPECIAL 0060000
#define I_DIRECTORY     0040000
#define I_CHAR_SPECIAL  0020000
#define I_NAMED_PIPE 0010000

#define is_special(m) ((((m) & I_TYPE_MASK) == I_BLOCK_SPECIAL) || \
    (((m) & I_TYPE_MASK) == I_CHAR_SPECIAL))

#define NR_DEFAULT_FILE_SECTS 2048 /* 2048 * 512 = 1MB */



#endif /* _ORANGES_CONST_H_ */

include/sys/global.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            global.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/* EXTERN is defined as extern except in global.c */
#ifdef GLOBAL_VARIABLES_HERE
#undef EXTERN
#define EXTERN
#endif

EXTERN int ticks;

EXTERN int disp_pos;

EXTERN u8   gdt_ptr[6]; /* 0~15:Limit  16~47:Base */
EXTERN struct descriptor gdt[GDT_SIZE];
EXTERN u8   idt_ptr[6]; /* 0~15:Limit  16~47:Base */
EXTERN struct gate  idt[IDT_SIZE];

EXTERN u32 k_reenter;
EXTERN int current_console;

EXTERN int key_pressed; /**
         * used for clock_handler
         * to wake up TASK_TTY when
         * a key is pressed
         */

EXTERN struct tss tss;
EXTERN struct proc* p_proc_ready;

extern char  task_stack[];
extern struct proc proc_table[];
extern  struct task task_table[];
extern  struct task user_proc_table[];
extern irq_handler irq_table[];
extern TTY  tty_table[];
extern  CONSOLE  console_table[];

/* FS */
EXTERN struct file_desc f_desc_table[NR_FILE_DESC];
EXTERN struct inode  inode_table[NR_INODE];
EXTERN struct super_block super_block[NR_SUPER_BLOCK];
extern u8 *   fsbuf;
extern const int  FSBUF_SIZE;
EXTERN MESSAGE   fs_msg;
EXTERN struct proc *  pcaller;
EXTERN struct inode *  root_inode;
extern struct dev_drv_map dd_map[];

include/sys/keyboard.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                              keyboard.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                           Forrest Yu, December, 2003
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#ifndef _ORANGES_KEYBOARD_H_
#define _ORANGES_KEYBOARD_H_


/************************************************************************/
/*                          Macros Declaration                          */
/************************************************************************/
#define KB_IN_BYTES 32 /* size of keyboard input buffer */
#define MAP_COLS 3 /* Number of columns in keymap */
#define NR_SCAN_CODES 0x80 /* Number of scan codes (rows in keymap) */

#define FLAG_BREAK 0x0080  /* Break Code   */
#define FLAG_EXT 0x0100  /* Normal function keys  */
#define FLAG_SHIFT_L 0x0200  /* Shift key   */
#define FLAG_SHIFT_R 0x0400  /* Shift key   */
#define FLAG_CTRL_L 0x0800  /* Control key   */
#define FLAG_CTRL_R 0x1000  /* Control key   */
#define FLAG_ALT_L 0x2000  /* Alternate key  */
#define FLAG_ALT_R 0x4000  /* Alternate key  */
#define FLAG_PAD 0x8000  /* keys in num pad  */

#define MASK_RAW 0x01FF  /* raw key value = code passed to tty & MASK_RAW
        the value can be found either in the keymap column 0
        or in the list below */

/* Special keys */
#define ESC  (0x01 + FLAG_EXT) /* Esc  */
#define TAB  (0x02 + FLAG_EXT) /* Tab  */
#define ENTER  (0x03 + FLAG_EXT) /* Enter */
#define BACKSPACE (0x04 + FLAG_EXT) /* BackSpace */

#define GUI_L  (0x05 + FLAG_EXT) /* L GUI */
#define GUI_R  (0x06 + FLAG_EXT) /* R GUI */
#define APPS  (0x07 + FLAG_EXT) /* APPS */

/* Shift, Ctrl, Alt */
#define SHIFT_L  (0x08 + FLAG_EXT) /* L Shift */
#define SHIFT_R  (0x09 + FLAG_EXT) /* R Shift */
#define CTRL_L  (0x0A + FLAG_EXT) /* L Ctrl */
#define CTRL_R  (0x0B + FLAG_EXT) /* R Ctrl */
#define ALT_L  (0x0C + FLAG_EXT) /* L Alt */
#define ALT_R  (0x0D + FLAG_EXT) /* R Alt */

/* Lock keys */
#define CAPS_LOCK (0x0E + FLAG_EXT) /* Caps Lock */
#define NUM_LOCK (0x0F + FLAG_EXT) /* Number Lock */
#define SCROLL_LOCK (0x10 + FLAG_EXT) /* Scroll Lock */

/* Function keys */
#define F1  (0x11 + FLAG_EXT) /* F1  */
#define F2  (0x12 + FLAG_EXT) /* F2  */
#define F3  (0x13 + FLAG_EXT) /* F3  */
#define F4  (0x14 + FLAG_EXT) /* F4  */
#define F5  (0x15 + FLAG_EXT) /* F5  */
#define F6  (0x16 + FLAG_EXT) /* F6  */
#define F7  (0x17 + FLAG_EXT) /* F7  */
#define F8  (0x18 + FLAG_EXT) /* F8  */
#define F9  (0x19 + FLAG_EXT) /* F9  */
#define F10  (0x1A + FLAG_EXT) /* F10  */
#define F11  (0x1B + FLAG_EXT) /* F11  */
#define F12  (0x1C + FLAG_EXT) /* F12  */

/* Control Pad */
#define PRINTSCREEN (0x1D + FLAG_EXT) /* Print Screen */
#define PAUSEBREAK (0x1E + FLAG_EXT) /* Pause/Break */
#define INSERT  (0x1F + FLAG_EXT) /* Insert */
#define DELETE  (0x20 + FLAG_EXT) /* Delete */
#define HOME  (0x21 + FLAG_EXT) /* Home  */
#define END  (0x22 + FLAG_EXT) /* End  */
#define PAGEUP  (0x23 + FLAG_EXT) /* Page Up */
#define PAGEDOWN (0x24 + FLAG_EXT) /* Page Down */
#define UP  (0x25 + FLAG_EXT) /* Up  */
#define DOWN  (0x26 + FLAG_EXT) /* Down  */
#define LEFT  (0x27 + FLAG_EXT) /* Left  */
#define RIGHT  (0x28 + FLAG_EXT) /* Right */

/* ACPI keys */
#define POWER  (0x29 + FLAG_EXT) /* Power */
#define SLEEP  (0x2A + FLAG_EXT) /* Sleep */
#define WAKE  (0x2B + FLAG_EXT) /* Wake Up */

/* Num Pad */
#define PAD_SLASH (0x2C + FLAG_EXT) /* /  */
#define PAD_STAR (0x2D + FLAG_EXT) /* *  */
#define PAD_MINUS (0x2E + FLAG_EXT) /* -  */
#define PAD_PLUS (0x2F + FLAG_EXT) /* +  */
#define PAD_ENTER (0x30 + FLAG_EXT) /* Enter */
#define PAD_DOT  (0x31 + FLAG_EXT) /* .  */
#define PAD_0  (0x32 + FLAG_EXT) /* 0  */
#define PAD_1  (0x33 + FLAG_EXT) /* 1  */
#define PAD_2  (0x34 + FLAG_EXT) /* 2  */
#define PAD_3  (0x35 + FLAG_EXT) /* 3  */
#define PAD_4  (0x36 + FLAG_EXT) /* 4  */
#define PAD_5  (0x37 + FLAG_EXT) /* 5  */
#define PAD_6  (0x38 + FLAG_EXT) /* 6  */
#define PAD_7  (0x39 + FLAG_EXT) /* 7  */
#define PAD_8  (0x3A + FLAG_EXT) /* 8  */
#define PAD_9  (0x3B + FLAG_EXT) /* 9  */
#define PAD_UP  PAD_8   /* Up  */
#define PAD_DOWN PAD_2   /* Down  */
#define PAD_LEFT PAD_4   /* Left  */
#define PAD_RIGHT PAD_6   /* Right */
#define PAD_HOME PAD_7   /* Home  */
#define PAD_END  PAD_1   /* End  */
#define PAD_PAGEUP PAD_9   /* Page Up */
#define PAD_PAGEDOWN PAD_3   /* Page Down */
#define PAD_INS  PAD_0   /* Ins  */
#define PAD_MID  PAD_5   /* Middle key */
#define PAD_DEL  PAD_DOT   /* Del  */


/************************************************************************/
/*                         Stucture Definition                          */
/************************************************************************/
/* Keyboard structure, 1 per console. */
struct kb_inbuf {
 char* p_head;   /* 指向緩衝區中下一個空閒位置 */
 char* p_tail;   /* 指向鍵盤任務應處理的字節 */
 int count;   /* 緩衝區中共有多少字節 */
 char buf[KB_IN_BYTES]; /* 緩衝區 */
};



#endif /* _ORANGES_KEYBOARD_H_ */

include/sys/proto.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            proto.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/* kliba.asm */
PUBLIC void out_byte(u16 port, u8 value);
PUBLIC u8 in_byte(u16 port);
PUBLIC void disp_str(char * info);
PUBLIC void disp_color_str(char * info, int color);
PUBLIC void disable_irq(int irq);
PUBLIC void enable_irq(int irq);
PUBLIC void disable_int();
PUBLIC void enable_int();
PUBLIC void port_read(u16 port, void* buf, int n);
PUBLIC void port_write(u16 port, void* buf, int n);
PUBLIC void glitter(int row, int col);

/* string.asm */
PUBLIC char* strcpy(char* dst, const char* src);

/* protect.c */
PUBLIC void init_prot();
PUBLIC u32 seg2phys(u16 seg);

/* klib.c */
PUBLIC void delay(int time);
PUBLIC void disp_int(int input);
PUBLIC char * itoa(char * str, int num);

/* kernel.asm */
PUBLIC void restart();

/* main.c */
PUBLIC int  get_ticks();
PUBLIC void TestA();
PUBLIC void TestB();
PUBLIC void TestC();
PUBLIC void panic(const char *fmt, ...);

/* i8259.c */
PUBLIC void init_8259A();
PUBLIC void put_irq_handler(int irq, irq_handler handler);
PUBLIC void spurious_irq(int irq);

/* clock.c */
PUBLIC void clock_handler(int irq);
PUBLIC void init_clock();
PUBLIC void milli_delay(int milli_sec);

/* kernel/hd.c */
PUBLIC void task_hd();
PUBLIC void hd_handler(int irq);

/* keyboard.c */
PUBLIC void init_keyboard();
PUBLIC void keyboard_read(TTY* p_tty);

/* tty.c */
PUBLIC void task_tty();
PUBLIC void in_process(TTY* p_tty, u32 key);
PUBLIC void dump_tty_buf(); /* for debug only */

/* systask.c */
PUBLIC void task_sys();

/* fs/main.c */
PUBLIC void   task_fs();
PUBLIC int   rw_sector(int io_type, int dev, u64 pos,
       int bytes, int proc_nr, void * buf);
PUBLIC struct inode *  get_inode(int dev, int num);
PUBLIC void   put_inode(struct inode * pinode);
PUBLIC void   sync_inode(struct inode * p);
PUBLIC struct super_block * get_super_block(int dev);

/* fs/open.c */
PUBLIC int  do_open();
PUBLIC int  do_close();

/* fs/read_write.c */
PUBLIC int  do_rdwt();

/* fs/link.c */
PUBLIC int  do_unlink();

/* fs/misc.c */
PUBLIC int  do_stat();
PUBLIC int  strip_path(char * filename, const char * pathname,
       struct inode** ppinode);
PUBLIC int  search_file(char * path);

/* fs/disklog.c */
PUBLIC int  do_disklog();
PUBLIC int  disklog(char * logstr); /* for debug */
PUBLIC void  dump_fd_graph(const char * fmt, ...);

/* console.c */
PUBLIC void out_char(CONSOLE* p_con, char ch);
PUBLIC void scroll_screen(CONSOLE* p_con, int direction);
PUBLIC void select_console(int nr_console);
PUBLIC void init_screen(TTY* p_tty);
PUBLIC int  is_current_console(CONSOLE* p_con);

/* printf.c */
PUBLIC  int     printf(const char *fmt, ...);
PUBLIC  int     printl(const char *fmt, ...);

/* vsprintf.c */
PUBLIC  int     vsprintf(char *buf, const char *fmt, va_list args);
PUBLIC int sprintf(char *buf, const char *fmt, ...);

/* proc.c */
PUBLIC void schedule();
PUBLIC void* va2la(int pid, void* va);
PUBLIC int ldt_seg_linear(struct proc* p, int idx);
PUBLIC void reset_msg(MESSAGE* p);
PUBLIC void dump_msg(const char * title, MESSAGE* m);
PUBLIC void dump_proc(struct proc * p);
PUBLIC int send_recv(int function, int src_dest, MESSAGE* msg);
PUBLIC void inform_int(int task_nr);

/* lib/misc.c */
PUBLIC void spin(char * func_name);

/* 以下是系統調用相關 */

/* 系統調用 - 系統級 */
/* proc.c */
PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p);
PUBLIC int sys_printx(int _unused1, int _unused2, char* s, struct proc * p_proc);

/* syscall.asm */
PUBLIC  void    sys_call();             /* int_handler */

/* 系統調用 - 用戶級 */
PUBLIC int sendrec(int function, int src_dest, MESSAGE* p_msg);
PUBLIC int printx(char* str);

include/sys/tty.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    tty.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#ifndef _ORANGES_TTY_H_
#define _ORANGES_TTY_H_


#define TTY_IN_BYTES  256 /* tty input queue size */
#define TTY_OUT_BUF_LEN  2 /* tty output buffer size */

struct s_tty;
struct s_console;

/* TTY */
typedef struct s_tty
{
 u32 ibuf[TTY_IN_BYTES]; /* TTY input buffer */
 u32* ibuf_head;  /* the next free slot */
 u32* ibuf_tail;  /* the val to be processed by TTY */
 int ibuf_cnt;  /* how many */

 int tty_caller;
 int tty_procnr;
 void* tty_req_buf;
 int tty_left_cnt;
 int tty_trans_cnt;

 struct s_console * console;
}TTY;

#endif /* _ORANGES_TTY_H_ */

kernel/clock.c

/*************************************************************************//**
 *****************************************************************************
 * @file   clock.c
 * @brief  
 * @author Forrest Y. Yu
 * @date   2005
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*****************************************************************************
 *                                clock_handler
 *****************************************************************************/
/**
 * <Ring 0> This routine handles the clock interrupt generated by 8253/8254
 *          programmable interval timer.
 * 
 * @param irq The IRQ nr, unused here.
 *****************************************************************************/
PUBLIC void clock_handler(int irq)
{
 if (++ticks >= MAX_TICKS)
  ticks = 0;

 if (p_proc_ready->ticks)
  p_proc_ready->ticks--;

 if (key_pressed)
  inform_int(TASK_TTY);

 if (k_reenter != 0) {
  return;
 }

 if (p_proc_ready->ticks > 0) {
  return;
 }

 schedule();

}

/*****************************************************************************
 *                                milli_delay
 *****************************************************************************/
/**
 * <Ring 1~3> Delay for a specified amount of time.
 * 
 * @param milli_sec How many milliseconds to delay.
 *****************************************************************************/
PUBLIC void milli_delay(int milli_sec)
{
        int t = get_ticks();

        while(((get_ticks() - t) * 1000 / HZ) < milli_sec) {}
}

/*****************************************************************************
 *                                init_clock
 *****************************************************************************/
/**
 * <Ring 0> Initialize 8253/8254 PIT (Programmable Interval Timer).
 * 
 *****************************************************************************/
PUBLIC void init_clock()
{
        /* 初始化 8253 PIT */
        out_byte(TIMER_MODE, RATE_GENERATOR);
        out_byte(TIMER0, (u8) (TIMER_FREQ/HZ) );
        out_byte(TIMER0, (u8) ((TIMER_FREQ/HZ) >> 8));

        put_irq_handler(CLOCK_IRQ, clock_handler);    /* 设定时钟中断处理程序 */
        enable_irq(CLOCK_IRQ);                        /* 让8259A可以接收时钟中断 */
}


kernel/console.c

/*************************************************************************//**
 *****************************************************************************
 * @file   console.c
 * @brief  Manipulate the console.
 * @author Forrest Y. Yu
 * @date   2005
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

/* #define __TTY_DEBUG__ */

/* local routines */
PRIVATE void set_cursor(unsigned int position);
PRIVATE void set_video_start_addr(u32 addr);
PRIVATE void flush(CONSOLE* con);
PRIVATE void w_copy(unsigned int dst, const unsigned int src, int size);
PRIVATE void clear_screen(int pos, int len);

/*****************************************************************************
 *                                init_screen
 *****************************************************************************/
/**
 * Initialize the console of a certain tty.
 *
 * @param tty  Whose console is to be initialized.
 *****************************************************************************/
PUBLIC void init_screen(TTY* tty)
{
 int nr_tty = tty - tty_table;
 tty->console = console_table + nr_tty;

 /*
  * NOTE:
  *   variables related to `position' and `size' below are
  *   in WORDs, but not in BYTEs.
  */
 int v_mem_size = V_MEM_SIZE >> 1; /* size of Video Memory */
 int size_per_con = v_mem_size / NR_CONSOLES;
 tty->console->orig = nr_tty * size_per_con;
 tty->console->con_size = size_per_con / SCR_WIDTH * SCR_WIDTH;
 tty->console->cursor = tty->console->crtc_start = tty->console->orig;
 tty->console->is_full = 0;

 if (nr_tty == 0) {
  tty->console->cursor = disp_pos / 2;
  disp_pos = 0;
 }
 else {
  /*
   * `?' in this string will be replaced with 0, 1, 2, ...
   */
  const char prompt[] = "[TTY #?]\n";

  const char * p = prompt;
  for (; *p; p++)
   out_char(tty->console, *p == '?' ? nr_tty + '0' : *p);
 }

 set_cursor(tty->console->cursor);
}


/*****************************************************************************
 *                                out_char
 *****************************************************************************/
/**
 * Print a char in a certain console.
 *
 * @param con  The console to which the char is printed.
 * @param ch   The char to print.
 *****************************************************************************/
PUBLIC void out_char(CONSOLE* con, char ch)
{
 u8* pch = (u8*)(V_MEM_BASE + con->cursor * 2);

 assert(con->cursor - con->orig < con->con_size);

 /*
  * calculate the coordinate of cursor in current console (not in
  * current screen)
  */
 int cursor_x = (con->cursor - con->orig) % SCR_WIDTH;
 int cursor_y = (con->cursor - con->orig) / SCR_WIDTH;

 switch(ch) {
 case '\n':
  con->cursor = con->orig + SCR_WIDTH * (cursor_y + 1);
  break;
 case '\b':
  if (con->cursor > con->orig) {
   con->cursor--;
   *(pch - 2) = ' ';
   *(pch - 1) = DEFAULT_CHAR_COLOR;
  }
  break;
 default:
  *pch++ = ch;
  *pch++ = DEFAULT_CHAR_COLOR;
  con->cursor++;
  break;
 }

 if (con->cursor - con->orig >= con->con_size) {
  cursor_x = (con->cursor - con->orig) % SCR_WIDTH;
  cursor_y = (con->cursor - con->orig) / SCR_WIDTH;
  int cp_orig = con->orig + (cursor_y + 1) * SCR_WIDTH - SCR_SIZE;
  w_copy(con->orig, cp_orig, SCR_SIZE - SCR_WIDTH);
  con->crtc_start = con->orig;
  con->cursor = con->orig + (SCR_SIZE - SCR_WIDTH) + cursor_x;
  clear_screen(con->cursor, SCR_WIDTH);
  if (!con->is_full)
   con->is_full = 1;
 }

 assert(con->cursor - con->orig < con->con_size);

 while (con->cursor >= con->crtc_start + SCR_SIZE ||
        con->cursor < con->crtc_start) {
  scroll_screen(con, SCR_UP);

  clear_screen(con->cursor, SCR_WIDTH);
 }

 flush(con);
}

/*****************************************************************************
 *                                clear_screen
 *****************************************************************************/
/**
 * Write whitespaces to the screen.
 *
 * @param pos  Write from here.
 * @param len  How many whitespaces will be written.
 *****************************************************************************/
PRIVATE void clear_screen(int pos, int len)
{
 u8 * pch = (u8*)(V_MEM_BASE + pos * 2);
 while (--len >= 0) {
  *pch++ = ' ';
  *pch++ = DEFAULT_CHAR_COLOR;
 }
}


/*****************************************************************************
 *                            is_current_console
 *****************************************************************************/
/**
 * Uses `nr_current_console' to determine if a console is the current one.
 *
 * @param con   Ptr to console.
 *
 * @return   TRUE if con is the current console.
 *****************************************************************************/
PUBLIC int is_current_console(CONSOLE* con)
{
 return (con == &console_table[current_console]);
}


/*****************************************************************************
 *                                set_cursor
 *****************************************************************************/
/**
 * Display the cursor by setting CRTC (6845 compatible) registers.
 *
 * @param position  Position of the cursor based on the beginning of the video
 *                  memory. Note that it counts in WORDs, not in BYTEs.
 *****************************************************************************/
PRIVATE void set_cursor(unsigned int position)
{
 disable_int();
 out_byte(CRTC_ADDR_REG, CURSOR_H);
 out_byte(CRTC_DATA_REG, (position >> 8) & 0xFF);
 out_byte(CRTC_ADDR_REG, CURSOR_L);
 out_byte(CRTC_DATA_REG, position & 0xFF);
 enable_int();
}


/*****************************************************************************
 *                                set_video_start_addr
 *****************************************************************************/
/**
 * Routine for hardware screen scrolling.
 *
 * @param addr  Offset in the video memory.
 *****************************************************************************/
PRIVATE void set_video_start_addr(u32 addr)
{
 disable_int();
 out_byte(CRTC_ADDR_REG, START_ADDR_H);
 out_byte(CRTC_DATA_REG, (addr >> 8) & 0xFF);
 out_byte(CRTC_ADDR_REG, START_ADDR_L);
 out_byte(CRTC_DATA_REG, addr & 0xFF);
 enable_int();
}


/*****************************************************************************
 *                                select_console
 *****************************************************************************/
/**
 * Select a console as the current.
 *
 * @param nr_console   Console nr, range in [0, NR_CONSOLES-1].
 *****************************************************************************/
PUBLIC void select_console(int nr_console)
{
 if ((nr_console < 0) || (nr_console >= NR_CONSOLES)) return;

 flush(&console_table[current_console = nr_console]);
}


/*****************************************************************************
 *                                scroll_screen
 *****************************************************************************/
/**
 * Scroll the screen.
 *
 * Note that scrolling UP means the content of the screen will go upwards, so
 * that the user can see lines below the bottom. Similarly scrolling DOWN means
 * the content of the screen will go downwards so that the user can see lines
 * above the top.
 *
 * When there is no line below the bottom of the screen, scrolling UP takes no
 * effects; when there is no line above the top of the screen, scrolling DOWN
 * takes no effects.
 *
 * @param con   The console whose screen is to be scrolled.
 * @param dir   SCR_UP : scroll the screen upwards;
 *              SCR_DN : scroll the screen downwards
 *****************************************************************************/
PUBLIC void scroll_screen(CONSOLE* con, int dir)
{
 /*
  * variables below are all in-console-offsets (based on con->orig)
  */
 int oldest; /* addr of the oldest available line in the console */
 int newest; /* .... .. ... latest ......... .... .. ... ....... */
 int scr_top;/* position of the top of current screen */

 newest = (con->cursor - con->orig) / SCR_WIDTH * SCR_WIDTH;
 oldest = con->is_full ? (newest + SCR_WIDTH) % con->con_size : 0;
 scr_top = con->crtc_start - con->orig;

 if (dir == SCR_DN) {
  if (!con->is_full && scr_top > 0) {
   con->crtc_start -= SCR_WIDTH;
  }
  else if (con->is_full && scr_top != oldest) {
   if (con->cursor - con->orig >= con->con_size - SCR_SIZE) {
    if (con->crtc_start != con->orig)
     con->crtc_start -= SCR_WIDTH;
   }
   else if (con->crtc_start == con->orig) {
    scr_top = con->con_size - SCR_SIZE;
    con->crtc_start = con->orig + scr_top;
   }
   else {
    con->crtc_start -= SCR_WIDTH;
   }
  }
 }
 else if (dir == SCR_UP) {
  if (!con->is_full && newest >= scr_top + SCR_SIZE) {
   con->crtc_start += SCR_WIDTH;
  }
  else if (con->is_full && scr_top + SCR_SIZE - SCR_WIDTH != newest) {
   if (scr_top + SCR_SIZE == con->con_size)
    con->crtc_start = con->orig;
   else
    con->crtc_start += SCR_WIDTH;
  }
 }
 else {
  assert(dir == SCR_DN || dir == SCR_UP);
 }

 flush(con);
}


/*****************************************************************************
 *                                flush
 *****************************************************************************/
/**
 * Set the cursor and starting address of a console by writing the
 * CRT Controller Registers.
 *
 * @param con  The console to be set.
 *****************************************************************************/
PRIVATE void flush(CONSOLE* con)
{
 if (is_current_console(con)) {
  set_cursor(con->cursor);
  set_video_start_addr(con->crtc_start);
 }

#ifdef __TTY_DEBUG__
 int lineno = 0;
 for (lineno = 0; lineno < con->con_size / SCR_WIDTH; lineno++) {
  u8 * pch = (u8*)(V_MEM_BASE +
       (con->orig + (lineno + 1) * SCR_WIDTH) * 2
       - 4);
  *pch++ = lineno / 10 + '0';
  *pch++ = RED_CHAR;
  *pch++ = lineno % 10 + '0';
  *pch++ = RED_CHAR;
 }
#endif
}

/*****************************************************************************
 *                                w_copy
 *****************************************************************************/
/**
 * Copy data in WORDS.
 *
 * Note that the addresses of dst and src are not pointers, but integers, 'coz
 * in most cases we pass integers into it as parameters.
 *
 * @param dst   Addr of destination.
 * @param src   Addr of source.
 * @param size  How many words will be copied.
 *****************************************************************************/
PRIVATE void w_copy(unsigned int dst, const unsigned int src, int size)
{
 phys_copy((void*)(V_MEM_BASE + (dst << 1)),
    (void*)(V_MEM_BASE + (src << 1)),
    size << 1);
}

kernel/keyboard.c

/*************************************************************************//**
 *****************************************************************************
 * @file   keyboard.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2005
 *
 *
 *
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "keymap.h"
#include "proto.h"

PRIVATE struct kb_inbuf kb_in;
PRIVATE int  code_with_E0;
PRIVATE int  shift_l; /* l shift state */
PRIVATE int  shift_r; /* r shift state */
PRIVATE int  alt_l;  /* l alt state  */
PRIVATE int  alt_r;  /* r left state  */
PRIVATE int  ctrl_l;  /* l ctrl state  */
PRIVATE int  ctrl_r;  /* l ctrl state  */
PRIVATE int  caps_lock; /* Caps Lock  */
PRIVATE int  num_lock; /* Num Lock  */
PRIVATE int  scroll_lock; /* Scroll Lock  */
PRIVATE int  column;

PRIVATE u8 get_byte_from_kb_buf();
PRIVATE void set_leds();
PRIVATE void kb_wait();
PRIVATE void kb_ack();


/*****************************************************************************
 *                                keyboard_handler
 *****************************************************************************/
/**
 * <Ring 0> Handles the interrupts generated by the keyboard controller.
 *
 * @param irq The IRQ corresponding to the keyboard, unused here.
 *****************************************************************************/
PUBLIC void keyboard_handler(int irq)
{
 u8 scan_code = in_byte(KB_DATA);

 if (kb_in.count < KB_IN_BYTES) {
  *(kb_in.p_head) = scan_code;
  kb_in.p_head++;
  if (kb_in.p_head == kb_in.buf + KB_IN_BYTES)
   kb_in.p_head = kb_in.buf;
  kb_in.count++;
 }

 key_pressed = 1;
}


/*****************************************************************************
 *                                init_keyboard
 *****************************************************************************/
/**
 * <Ring 1> Initialize some variables and set keyboard interrupt handler.
 *
 *****************************************************************************/
PUBLIC void init_keyboard()
{
 kb_in.count = 0;
 kb_in.p_head = kb_in.p_tail = kb_in.buf;

 shift_l = shift_r = 0;
 alt_l = alt_r   = 0;
 ctrl_l = ctrl_r  = 0;

 caps_lock = 0;
 num_lock = 1;
 scroll_lock = 0;

 column  = 0;

 set_leds();

 put_irq_handler(KEYBOARD_IRQ, keyboard_handler);
 enable_irq(KEYBOARD_IRQ);
}


/*****************************************************************************
 *                                keyboard_read
 *****************************************************************************/
/**
 * Yes, it is an ugly function.
 *
 * @todo Re-write this ugly function.
 *
 * @param tty  Which TTY is reading the keyboard input.
 *****************************************************************************/
PUBLIC void keyboard_read(TTY* tty)
{
 u8 scan_code;

 /**
  * 1 : make
  * 0 : break
  */
 int make;

 /**
  * We use a integer to record a key press.
  * For instance, if the key HOME is pressed, key will be evaluated to
  * `HOME' defined in keyboard.h.
  */
 u32 key = 0;


 /**
  * This var points to a row in keymap[]. I don't use two-dimension
  * array because I don't like it.
  */
 u32* keyrow;

 while (kb_in.count > 0) {
  code_with_E0 = 0;
  scan_code = get_byte_from_kb_buf();

  /* parse the scan code below */
  if (scan_code == 0xE1) {
   int i;
   u8 pausebreak_scan_code[] = {0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5};
   int is_pausebreak = 1;
   for (i = 1; i < 6; i++) {
    if (get_byte_from_kb_buf() != pausebreak_scan_code[i]) {
     is_pausebreak = 0;
     break;
    }
   }
   if (is_pausebreak) {
    key = PAUSEBREAK;
   }
  }
  else if (scan_code == 0xE0) {
   code_with_E0 = 1;
   scan_code = get_byte_from_kb_buf();

   /* PrintScreen is pressed */
   if (scan_code == 0x2A) {
    code_with_E0 = 0;
    if ((scan_code = get_byte_from_kb_buf()) == 0xE0) {
     code_with_E0 = 1;
     if ((scan_code = get_byte_from_kb_buf()) == 0x37) {
      key = PRINTSCREEN;
      make = 1;
     }
    }
   }
   /* PrintScreen is released */
   else if (scan_code == 0xB7) {
    code_with_E0 = 0;
    if ((scan_code = get_byte_from_kb_buf()) == 0xE0) {
     code_with_E0 = 1;
     if ((scan_code = get_byte_from_kb_buf()) == 0xAA) {
      key = PRINTSCREEN;
      make = 0;
     }
    }
   }
  }

  if ((key != PAUSEBREAK) && (key != PRINTSCREEN)) {
   int caps;

   /* make or break */
   make = (scan_code & FLAG_BREAK ? 0 : 1);

   keyrow = &keymap[(scan_code & 0x7F) * MAP_COLS];

   column = 0;

   caps = shift_l || shift_r;
   if (caps_lock &&
       keyrow[0] >= 'a' && keyrow[0] <= 'z')
    caps = !caps;

   if (caps)
    column = 1;

   if (code_with_E0)
    column = 2;

   key = keyrow[column];

   switch(key) {
   case SHIFT_L:
    shift_l = make;
    break;
   case SHIFT_R:
    shift_r = make;
    break;
   case CTRL_L:
    ctrl_l = make;
    break;
   case CTRL_R:
    ctrl_r = make;
    break;
   case ALT_L:
    alt_l = make;
    break;
   case ALT_R:
    alt_l = make;
    break;
   case CAPS_LOCK:
    if (make) {
     caps_lock   = !caps_lock;
     set_leds();
    }
    break;
   case NUM_LOCK:
    if (make) {
     num_lock    = !num_lock;
     set_leds();
    }
    break;
   case SCROLL_LOCK:
    if (make) {
     scroll_lock = !scroll_lock;
     set_leds();
    }
    break;
   default:
    break;
   }
  }

  if(make){ /* Break Code is ignored */
   int pad = 0;

   /* deal with the numpad first */
   if ((key >= PAD_SLASH) && (key <= PAD_9)) {
    pad = 1;
    switch(key) { /* '/', '*', '-', '+',
       * and 'Enter' in num pad
       */
    case PAD_SLASH:
     key = '/';
     break;
    case PAD_STAR:
     key = '*';
     break;
    case PAD_MINUS:
     key = '-';
     break;
    case PAD_PLUS:
     key = '+';
     break;
    case PAD_ENTER:
     key = ENTER;
     break;
    default:
     /* the value of these keys
      * depends on the Numlock
      */
     if (num_lock) { /* '0' ~ '9' and '.' in num pad */
      if (key >= PAD_0 && key <= PAD_9)
       key = key - PAD_0 + '0';
      else if (key == PAD_DOT)
       key = '.';
     }
     else{
      switch(key) {
      case PAD_HOME:
       key = HOME;
       break;
      case PAD_END:
       key = END;
       break;
      case PAD_PAGEUP:
       key = PAGEUP;
       break;
      case PAD_PAGEDOWN:
       key = PAGEDOWN;
       break;
      case PAD_INS:
       key = INSERT;
       break;
      case PAD_UP:
       key = UP;
       break;
      case PAD_DOWN:
       key = DOWN;
       break;
      case PAD_LEFT:
       key = LEFT;
       break;
      case PAD_RIGHT:
       key = RIGHT;
       break;
      case PAD_DOT:
       key = DELETE;
       break;
      default:
       break;
      }
     }
     break;
    }
   }
   key |= shift_l ? FLAG_SHIFT_L : 0;
   key |= shift_r ? FLAG_SHIFT_R : 0;
   key |= ctrl_l ? FLAG_CTRL_L : 0;
   key |= ctrl_r ? FLAG_CTRL_R : 0;
   key |= alt_l ? FLAG_ALT_L : 0;
   key |= alt_r ? FLAG_ALT_R : 0;
   key |= pad ? FLAG_PAD : 0;

   in_process(tty, key);
  }
 }
}


/*****************************************************************************
 *                                get_byte_from_kb_buf
 *****************************************************************************/
/**
 * Read a byte from the keyboard buffer.
 *
 * @return The byte read.
 *****************************************************************************/
PRIVATE u8 get_byte_from_kb_buf()
{
 u8 scan_code;

 while (kb_in.count <= 0) {} /* wait for a byte to arrive */

 disable_int();  /* for synchronization */
 scan_code = *(kb_in.p_tail);
 kb_in.p_tail++;
 if (kb_in.p_tail == kb_in.buf + KB_IN_BYTES) {
  kb_in.p_tail = kb_in.buf;
 }
 kb_in.count--;
 enable_int();  /* for synchronization */

 return scan_code;
}


/*****************************************************************************
 *                                kb_wait
 *****************************************************************************/
/**
 * Wait until the input buffer of 8042 is empty.
 *
 *****************************************************************************/
PRIVATE void kb_wait() /* 等待 8042 的輸入緩衝區空 */
{
 u8 kb_stat;

 do {
  kb_stat = in_byte(KB_CMD);
 } while (kb_stat & 0x02);
}


/*****************************************************************************
 *                                kb_ack
 *****************************************************************************/
/**
 * Read from the keyboard controller until a KB_ACK is received.
 *
 *****************************************************************************/
PRIVATE void kb_ack()
{
 u8 kb_read;

 do {
  kb_read = in_byte(KB_DATA);
 } while (kb_read != KB_ACK);
}


/*****************************************************************************
 *                                set_leds
 *****************************************************************************/
/**
 * Set the leds according to: caps_lock, num_lock & scroll_lock.
 *
 *****************************************************************************/
PRIVATE void set_leds()
{
 u8 leds = (caps_lock << 2) | (num_lock << 1) | scroll_lock;

 kb_wait();
 out_byte(KB_DATA, LED_CODE);
 kb_ack();

 kb_wait();
 out_byte(KB_DATA, leds);
 kb_ack();
}

kernel/main.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*======================================================================*
                            kernel_main
 *======================================================================*/
PUBLIC int kernel_main()
{
 disp_str("-----\"kernel_main\" begins-----\n");

 struct task* p_task;
 struct proc* p_proc= proc_table;
 char* p_task_stack = task_stack + STACK_SIZE_TOTAL;
 u16   selector_ldt = SELECTOR_LDT_FIRST;
        u8    privilege;
        u8    rpl;
 int   eflags;
 int   i, j;
 int   prio;
 for (i = 0; i < NR_TASKS+NR_PROCS; i++) {
         if (i < NR_TASKS) {     /* 任務 */
                        p_task    = task_table + i;
                        privilege = PRIVILEGE_TASK;
                        rpl       = RPL_TASK;
                        eflags    = 0x1202; /* IF=1, IOPL=1, bit 2 is always 1 */
   prio      = 15;
                }
                else {                  /* 用戶進程 */
                        p_task    = user_proc_table + (i - NR_TASKS);
                        privilege = PRIVILEGE_USER;
                        rpl       = RPL_USER;
                        eflags    = 0x202; /* IF=1, bit 2 is always 1 */
   prio      = 5;
                }

  strcpy(p_proc->name, p_task->name); /* name of the process */
  p_proc->pid = i;   /* pid */

  p_proc->ldt_sel = selector_ldt;

  memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3],
         sizeof(struct descriptor));
  p_proc->ldts[0].attr1 = DA_C | privilege << 5;
  memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3],
         sizeof(struct descriptor));
  p_proc->ldts[1].attr1 = DA_DRW | privilege << 5;
  p_proc->regs.cs = (0 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.ds = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.es = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.fs = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.ss = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl;
  p_proc->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;

  p_proc->regs.eip = (u32)p_task->initial_eip;
  p_proc->regs.esp = (u32)p_task_stack;
  p_proc->regs.eflags = eflags;

  /* p_proc->nr_tty  = 0; */

  p_proc->p_flags = 0;
  p_proc->p_msg = 0;
  p_proc->p_recvfrom = NO_TASK;
  p_proc->p_sendto = NO_TASK;
  p_proc->has_int_msg = 0;
  p_proc->q_sending = 0;
  p_proc->next_sending = 0;

  for (j = 0; j < NR_FILES; j++)
   p_proc->filp[j] = 0;

  p_proc->ticks = p_proc->priority = prio;

  p_task_stack -= p_task->stacksize;
  p_proc++;
  p_task++;
  selector_ldt += 1 << 3;
 }

        /* proc_table[NR_TASKS + 0].nr_tty = 0; */
        /* proc_table[NR_TASKS + 1].nr_tty = 1; */
        /* proc_table[NR_TASKS + 2].nr_tty = 1; */

 k_reenter = 0;
 ticks = 0;

 p_proc_ready = proc_table;

 init_clock();
        init_keyboard();

 restart();

 while(1){}
}


/*****************************************************************************
 *                                get_ticks
 *****************************************************************************/
PUBLIC int get_ticks()
{
 MESSAGE msg;
 reset_msg(&msg);
 msg.type = GET_TICKS;
 send_recv(BOTH, TASK_SYS, &msg);
 return msg.RETVAL;
}


/*======================================================================*
                               TestA
 *======================================================================*/
void TestA()
{
 int fd;
 int i, n;

 char filename[MAX_FILENAME_LEN+1] = "blah";
 const char bufw[] = "abcde";
 const int rd_bytes = 3;
 char bufr[rd_bytes];

 assert(rd_bytes <= strlen(bufw));

 /* create */
 fd = open(filename, O_CREAT | O_RDWR);
 assert(fd != -1);
 printl("File created: %s (fd %d)\n", filename, fd);

 /* write */
 n = write(fd, bufw, strlen(bufw));
 assert(n == strlen(bufw));

 /* close */
 close(fd);

 /* open */
 fd = open(filename, O_RDWR);
 assert(fd != -1);
 printl("File opened. fd: %d\n", fd);

 /* read */
 n = read(fd, bufr, rd_bytes);
 assert(n == rd_bytes);
 bufr[n] = 0;
 printl("%d bytes read: %s\n", n, bufr);

 /* close */
 close(fd);

 char * filenames[] = {"/foo", "/bar", "/baz"};

 /* create files */
 for (i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) {
  fd = open(filenames[i], O_CREAT | O_RDWR);
  assert(fd != -1);
  printl("File created: %s (fd %d)\n", filenames[i], fd);
  close(fd);
 }

 char * rfilenames[] = {"/bar", "/foo", "/baz", "/dev_tty0"};

 /* remove files */
 for (i = 0; i < sizeof(rfilenames) / sizeof(rfilenames[0]); i++) {
  if (unlink(rfilenames[i]) == 0)
   printl("File removed: %s\n", rfilenames[i]);
  else
   printl("Failed to remove file: %s\n", rfilenames[i]);
 }

 spin("TestA");
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestB()
{
 char tty_name[] = "/dev_tty1";

 int fd_stdin  = open(tty_name, O_RDWR);
 assert(fd_stdin  == 0);
 int fd_stdout = open(tty_name, O_RDWR);
 assert(fd_stdout == 1);

 char rdbuf[128];

 while (1) {
  printf("$ ");
  int r = read(fd_stdin, rdbuf, 70);
  rdbuf[r] = 0;

  if (strcmp(rdbuf, "hello") == 0)
   printf("hello world!\n");
  else
   if (rdbuf[0])
    printf("{%s}\n", rdbuf);
 }

 assert(0); /* never arrive here */
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestC()
{
 spin("TestC");
 /* assert(0); */
}

/*****************************************************************************
 *                                panic
 *****************************************************************************/
PUBLIC void panic(const char *fmt, ...)
{
 int i;
 char buf[256];

 /* 4 is the size of fmt in the stack */
 va_list arg = (va_list)((char*)&fmt + 4);

 i = vsprintf(buf, fmt, arg);

 printl("%c !!panic!! %s", MAG_CH_PANIC, buf);

 /* should never arrive here */
 __asm__ __volatile__("ud2");
}

kernel/tty.c

/*************************************************************************//**
 *****************************************************************************
 * @file   kernel/tty.c
 * @brief  The terminal driver.
 *
 * As a common driver, TTY accepts these MESSAGEs:
 *   - DEV_OPEN
 *   - DEV_READ
 *   - DEV_WRITE
 *
 * Besides, it accepts the other two types of MESSAGE from clock_handler() and
 * a PROC (who is not FS):
 *
 *   - MESSAGE from clock_handler(): HARD_INT
 *      - Every time clock interrupt occurs, the clock handler will check whether
 *        any key has been pressed. If so, it'll invoke inform_int() to wake up
 *        TTY. It is a special message because it is not from a process -- clock
 *        handler is not a process.
 *
 *   - MESSAGE from a PROC: TTY_WRITE
 *      - TTY is a driver. In most cases MESSAGE is passed from a PROC to FS then
 *        to TTY. For some historical reason, PROC is allowed to pass a TTY_WRITE
 *        MESSAGE directly to TTY. Thus a PROC can write to a tty directly.
 *
 * @note   Do not get confused by these function names:
 *           - tty_dev_read() vs tty_do_read()
 *             - tty_dev_read() reads chars from keyboard buffer
 *             - tty_do_read() handles DEV_READ message
 *           - tty_dev_write() vs tty_do_write() vs tty_write()
 *             - tty_dev_write() returns chars to a process waiting for input
 *             - tty_do_write() handles DEV_WRITE message
 *             - tty_write() handles TTY_WRITE message
 *
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"


#define TTY_FIRST (tty_table)
#define TTY_END  (tty_table + NR_CONSOLES)


PRIVATE void init_tty (TTY* tty);
PRIVATE void tty_dev_read (TTY* tty);
PRIVATE void tty_dev_write (TTY* tty);
PRIVATE void tty_do_read (TTY* tty, MESSAGE* msg);
PRIVATE void tty_do_write (TTY* tty, MESSAGE* msg);
PRIVATE void put_key  (TTY* tty, u32 key);


/*****************************************************************************
 *                                task_tty
 *****************************************************************************/
/**
 * <Ring 1> Main loop of task TTY.
 *****************************************************************************/
PUBLIC void task_tty()
{
 TTY * tty;
 MESSAGE msg;

 init_keyboard();

 for (tty = TTY_FIRST; tty < TTY_END; tty++)
  init_tty(tty);

 select_console(0);

 while (1) {
  for (tty = TTY_FIRST; tty < TTY_END; tty++) {
   do {
    tty_dev_read(tty);
    tty_dev_write(tty);
   } while (tty->ibuf_cnt);
  }

  send_recv(RECEIVE, ANY, &msg);

  int src = msg.source;
  assert(src != TASK_TTY);

  TTY* ptty = &tty_table[msg.DEVICE];

  switch (msg.type) {
  case DEV_OPEN:
   reset_msg(&msg);
   msg.type = SYSCALL_RET;
   send_recv(SEND, src, &msg);
   break;
  case DEV_READ:
   tty_do_read(ptty, &msg);
   break;
  case DEV_WRITE:
   tty_do_write(ptty, &msg);
   break;
  case HARD_INT:
   /**
    * waked up by clock_handler -- a key was just pressed
    * @see clock_handler() inform_int()
    */
   key_pressed = 0;
   continue;
  default:
   dump_msg("TTY::unknown msg", &msg);
   break;
  }
 }
}


/*****************************************************************************
 *                                init_tty
 *****************************************************************************/
/**
 * Things to be initialized before a tty can be used:
 *   -# the input buffer
 *   -# the corresponding console
 *
 * @param tty  TTY stands for teletype, a cool ancient magic thing.
 *****************************************************************************/
PRIVATE void init_tty(TTY* tty)
{
 tty->ibuf_cnt = 0;
 tty->ibuf_head = tty->ibuf_tail = tty->ibuf;

 init_screen(tty);
}


/*****************************************************************************
 *                                in_process
 *****************************************************************************/
/**
 * keyboard_read() will invoke this routine after having recognized a key press.
 *
 * @param tty  The key press is for whom.
 * @param key  The integer key with metadata.
 *****************************************************************************/
PUBLIC void in_process(TTY* tty, u32 key)
{
 if (!(key & FLAG_EXT)) {
  put_key(tty, key);
 }
 else {
  int raw_code = key & MASK_RAW;
  switch(raw_code) {
  case ENTER:
   put_key(tty, '\n');
   break;
  case BACKSPACE:
   put_key(tty, '\b');
   break;
  case UP:
   if ((key & FLAG_SHIFT_L) ||
       (key & FLAG_SHIFT_R)) { /* Shift + Up */
    scroll_screen(tty->console, SCR_DN);
   }
   break;
  case DOWN:
   if ((key & FLAG_SHIFT_L) ||
       (key & FLAG_SHIFT_R)) { /* Shift + Down */
    scroll_screen(tty->console, SCR_UP);
   }
   break;
  case F1:
  case F2:
  case F3:
  case F4:
  case F5:
  case F6:
  case F7:
  case F8:
  case F9:
  case F10:
  case F11:
  case F12:
   if ((key & FLAG_ALT_L) ||
       (key & FLAG_ALT_R)) { /* Alt + F1~F12 */
    select_console(raw_code - F1);
   }
   break;
  default:
   break;
  }
 }
}


/*****************************************************************************
 *                                put_key
 *****************************************************************************/
/**
 * Put a key into the in-buffer of TTY.
 *
 * @callergraph
 *
 * @param tty  To which TTY the key is put.
 * @param key  The key. It's an integer whose higher 24 bits are metadata.
 *****************************************************************************/
PRIVATE void put_key(TTY* tty, u32 key)
{
 if (tty->ibuf_cnt < TTY_IN_BYTES) {
  *(tty->ibuf_head) = key;
  tty->ibuf_head++;
  if (tty->ibuf_head == tty->ibuf + TTY_IN_BYTES)
   tty->ibuf_head = tty->ibuf;
  tty->ibuf_cnt++;
 }
}


/*****************************************************************************
 *                                tty_dev_read
 *****************************************************************************/
/**
 * Get chars from the keyboard buffer if the TTY::console is the `current'
 * console.
 *
 * @see keyboard_read()
 *
 * @param tty  Ptr to TTY.
 *****************************************************************************/
PRIVATE void tty_dev_read(TTY* tty)
{
 if (is_current_console(tty->console))
  keyboard_read(tty);
}


/*****************************************************************************
 *                                tty_dev_write
 *****************************************************************************/
/**
 * Echo the char just pressed and transfer it to the waiting process.
 *
 * @param tty   Ptr to a TTY struct.
 *****************************************************************************/
PRIVATE void tty_dev_write(TTY* tty)
{
 while (tty->ibuf_cnt) {
  char ch = *(tty->ibuf_tail);
  tty->ibuf_tail++;
  if (tty->ibuf_tail == tty->ibuf + TTY_IN_BYTES)
   tty->ibuf_tail = tty->ibuf;
  tty->ibuf_cnt--;

  if (tty->tty_left_cnt) {
   if (ch >= ' ' && ch <= '~') { /* printable */
    out_char(tty->console, ch);
    void * p = tty->tty_req_buf +
        tty->tty_trans_cnt;
    phys_copy(p, (void *)va2la(TASK_TTY, &ch), 1);
    tty->tty_trans_cnt++;
    tty->tty_left_cnt--;
   }
   else if (ch == '\b' && tty->tty_trans_cnt) {
    out_char(tty->console, ch);
    tty->tty_trans_cnt--;
    tty->tty_left_cnt++;
   }

   if (ch == '\n' || tty->tty_left_cnt == 0) {
    out_char(tty->console, '\n');
    MESSAGE msg;
    msg.type = RESUME_PROC;
    msg.PROC_NR = tty->tty_procnr;
    msg.CNT = tty->tty_trans_cnt;
    send_recv(SEND, tty->tty_caller, &msg);
    tty->tty_left_cnt = 0;
   }
  }
 }
}


/*****************************************************************************
 *                                tty_do_read
 *****************************************************************************/
/**
 * Invoked when task TTY receives DEV_READ message.
 *
 * @note The routine will return immediately after setting some members of
 * TTY struct, telling FS to suspend the proc who wants to read. The real
 * transfer (tty buffer -> proc buffer) is not done here.
 *
 * @param tty  From which TTY the caller proc wants to read.
 * @param msg  The MESSAGE just received.
 *****************************************************************************/
PRIVATE void tty_do_read(TTY* tty, MESSAGE* msg)
{
 /* tell the tty: */
 tty->tty_caller   = msg->source;  /* who called, usually FS */
 tty->tty_procnr   = msg->PROC_NR; /* who wants the chars */
 tty->tty_req_buf  = va2la(tty->tty_procnr,
      msg->BUF);/* where the chars should be put */
 tty->tty_left_cnt = msg->CNT; /* how many chars are requested */
 tty->tty_trans_cnt= 0; /* how many chars have been transferred */

 msg->type = SUSPEND_PROC;
 msg->CNT = tty->tty_left_cnt;
 send_recv(SEND, tty->tty_caller, msg);
}


/*****************************************************************************
 *                                tty_do_write
 *****************************************************************************/
/**
 * Invoked when task TTY receives DEV_WRITE message.
 *
 * @param tty  To which TTY the calller proc is bound.
 * @param msg  The MESSAGE.
 *****************************************************************************/
PRIVATE void tty_do_write(TTY* tty, MESSAGE* msg)
{
 char buf[TTY_OUT_BUF_LEN];
 char * p = (char*)va2la(msg->PROC_NR, msg->BUF);
 int i = msg->CNT;
 int j;

 while (i) {
  int bytes = min(TTY_OUT_BUF_LEN, i);
  phys_copy(va2la(TASK_TTY, buf), (void*)p, bytes);
  for (j = 0; j < bytes; j++)
   out_char(tty->console, buf[j]);
  i -= bytes;
  p += bytes;
 }

 msg->type = SYSCALL_RET;
 send_recv(SEND, msg->source, msg);
}


/*****************************************************************************
 *                                sys_printx
 *****************************************************************************/
/**
 * System calls accept four parameters. `printx' needs only two, so it wastes
 * the other two.
 *
 * @note `printx' accepts only one parameter -- `char* s', the other one --
 * `struct proc * proc' -- is pushed by kernel.asm::sys_call so that the
 * kernel can easily know who invoked the system call.
 *
 * @note s[0] (the first char of param s) is a magic char. if it equals
 * MAG_CH_PANIC, then this syscall was invoked by `panic()', which means
 * something goes really wrong and the system is to be halted; if it equals
 * MAG_CH_ASSERT, then this syscall was invoked by `assert()', which means
 * an assertion failure has occured. @see kernel/main lib/misc.c.
 *
 * @param _unused1  Ignored.
 * @param _unused2  Ignored.
 * @param s         The string to be printed.
 * @param p_proc    Caller proc.
 *
 * @return  Zero if success.
 *****************************************************************************/
PUBLIC int sys_printx(int _unused1, int _unused2, char* s, struct proc* p_proc)
{
 const char * p;
 char ch;

 char reenter_err[] = "? k_reenter is incorrect for unknown reason";
 reenter_err[0] = MAG_CH_PANIC;

 /**
  * @note Code in both Ring 0 and Ring 1~3 may invoke printx().
  * If this happens in Ring 0, no linear-physical address mapping
  * is needed.
  *
  * @attention The value of `k_reenter' is tricky here. When
  *   -# printx() is called in Ring 0
  *      - k_reenter > 0. When code in Ring 0 calls printx(),
  *        an `interrupt re-enter' will occur (printx() generates
  *        a software interrupt). Thus `k_reenter' will be increased
  *        by `kernel.asm::save' and be greater than 0.
  *   -# printx() is called in Ring 1~3
  *      - k_reenter == 0.
  */
 if (k_reenter == 0)  /* printx() called in Ring<1~3> */
  p = va2la(proc2pid(p_proc), s);
 else if (k_reenter > 0) /* printx() called in Ring<0> */
  p = s;
 else /* this should NOT happen */
  p = reenter_err;

 /**
  * @note if assertion fails in any TASK, the system will be halted;
  * if it fails in a USER PROC, it'll return like any normal syscall
  * does.
  */
 if ((*p == MAG_CH_PANIC) ||
     (*p == MAG_CH_ASSERT && p_proc_ready < &proc_table[NR_TASKS])) {
  disable_int();
  char * v = (char*)V_MEM_BASE;
  const char * q = p + 1; /* +1: skip the magic char */

  while (v < (char*)(V_MEM_BASE + V_MEM_SIZE)) {
   *v++ = *q++;
   *v++ = RED_CHAR;
   if (!*q) {
    while (((int)v - V_MEM_BASE) % (SCR_WIDTH * 16)) {
     /* *v++ = ' '; */
     v++;
     *v++ = GRAY_CHAR;
    }
    q = p + 1;
   }
  }

  __asm__ __volatile__("hlt");
 }

 while ((ch = *p++) != 0) {
  if (ch == MAG_CH_PANIC || ch == MAG_CH_ASSERT)
   continue; /* skip the magic char */

  /* TTY * ptty; */
  /* for (ptty = TTY_FIRST; ptty < TTY_END; ptty++) */
  /*  out_char(ptty->console, ch); /\* output chars to all TTYs *\/ */
  out_char(TTY_FIRST->console, ch);
 }

 //__asm__ __volatile__("nop;jmp 1f;ud2;1: nop");
 //__asm__ __volatile__("nop;cli;1: jmp 1b;ud2;nop");

 return 0;
}

/*****************************************************************************
 *                                dump_tty_buf
 *****************************************************************************/
/**
 * For debug only.
 *
 *****************************************************************************/
PUBLIC void dump_tty_buf()
{
 TTY * tty = &tty_table[1];

 static char sep[] = "--------------------------------\n";

 printl(sep);

 printl("head: %d\n", tty->ibuf_head - tty->ibuf);
 printl("tail: %d\n", tty->ibuf_tail - tty->ibuf);
 printl("cnt: %d\n", tty->ibuf_cnt);

 int pid = tty->tty_caller;
 printl("caller: %s (%d)\n", proc_table[pid].name, pid);
 pid = tty->tty_procnr;
 printl("caller: %s (%d)\n", proc_table[pid].name, pid);

 printl("req_buf: %d\n", (int)tty->tty_req_buf);
 printl("left_cnt: %d\n", tty->tty_left_cnt);
 printl("trans_cnt: %d\n", tty->tty_trans_cnt);

 printl("--------------------------------\n");

 strcpy(sep, "\n");
}

lib/printf.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                              printf.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

/******************************************************************************************
                        可變參數函數調用原理(其中涉及的數字皆為舉例)
===========================================================================================

i = 0x23;
j = 0x78;
char fmt[] = "%x%d";
printf(fmt, i, j);

        push    j
        push    i
        push    fmt
        call    printf
        add     esp, 3 * 4


                ┃        HIGH        ┃                        ┃        HIGH        ┃
                ┃        ...         ┃                        ┃        ...         ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
                ┃                    ┃                 0x32010┃        '\0'        ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
         0x3046C┃        0x78        ┃                 0x3200c┃         d          ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
   arg = 0x30468┃        0x23        ┃                 0x32008┃         %          ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
         0x30464┃      0x32000 ───╂────┐       0x32004┃         x          ┃
                ┣━━━━━━━━━━┫        │              ┣━━━━━━━━━━┫
                ┃                    ┃        └──→ 0x32000┃         %          ┃
                ┣━━━━━━━━━━┫                        ┣━━━━━━━━━━┫
                ┃        ...         ┃                        ┃        ...         ┃
                ┃        LOW         ┃                        ┃        LOW         ┃

實際上,調用 vsprintf 的情形是這樣的:

        vsprintf(buf, 0x32000, 0x30468);

******************************************************************************************/


/*****************************************************************************
 *                                printf
 *****************************************************************************/
/**
 * The most famous one.
 *
 * @param fmt  The format string
 *
 * @return  The number of chars printed.
 *****************************************************************************/
PUBLIC int printf(const char *fmt, ...)
{
 int i;
 char buf[STR_DEFAULT_LEN];

 va_list arg = (va_list)((char*)(&fmt) + 4); /**
           * 4: size of `fmt' in
           *    the stack
           */
 i = vsprintf(buf, fmt, arg);
 int c = write(1, buf, i);

 assert(c == i);

 return i;
}

/*****************************************************************************
 *                                printl
 *****************************************************************************/
/**
 * low level print
 *
 * @param fmt  The format string
 *
 * @return  The number of chars printed.
 *****************************************************************************/
PUBLIC int printl(const char *fmt, ...)
{
 int i;
 char buf[STR_DEFAULT_LEN];

 va_list arg = (va_list)((char*)(&fmt) + 4); /**
           * 4: size of `fmt' in
           *    the stack
           */
 i = vsprintf(buf, fmt, arg);
 printx(buf);

 return i;
}

lib/syscall.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               syscall.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

%include "sconst.inc"

INT_VECTOR_SYS_CALL equ 0x90
_NR_printx     equ 0
_NR_sendrec     equ 1

; 導出符號
global printx
global sendrec

bits 32
[section .text]

; ====================================================================================
;                  sendrec(int function, int src_dest, MESSAGE* msg);
; ====================================================================================
; Never call sendrec() directly, call send_recv() instead.
sendrec:
 push ebx  ; .
 push ecx  ;  > 12 bytes
 push edx  ; /

 mov eax, _NR_sendrec
 mov ebx, [esp + 12 +  4] ; function
 mov ecx, [esp + 12 +  8] ; src_dest
 mov edx, [esp + 12 + 12] ; p_msg
 int INT_VECTOR_SYS_CALL

 pop edx
 pop ecx
 pop ebx

 ret

; ====================================================================================
;                          void printx(char* s);
; ====================================================================================
printx:
 push edx  ; 4 bytes

 mov eax, _NR_printx
 mov edx, [esp + 4 + 4] ; s
 int INT_VECTOR_SYS_CALL

 pop edx

 ret

include/sys/proc.h

int nr_tty;    // 註解

kernel/proc.c

sprintf(info, "nr_tty: 0x%x.  ", p->nr_tty); disp_color_str(info, text_color);    // 註解
執行結果(Alt + F2)

目錄結構

.
├── 80m.img
├── 80m.vdi
├── a.img
├── boot
│   ├── boot.asm
│   ├── include
│   │   ├── fat12hdr.inc
│   │   ├── load.inc
│   │   └── pm.inc
│   └── loader.asm
├── fs
│   ├── disklog.c
│   ├── link.c
│   ├── main.c
│   ├── misc.c
│   ├── open.c
│   └── read_write.c
├── include
│   ├── stdio.h
│   ├── string.h
│   ├── sys
│   │   ├── config.h
│   │   ├── console.h
│   │   ├── const.h
│   │   ├── fs.h
│   │   ├── global.h
│   │   ├── hd.h
│   │   ├── keyboard.h
│   │   ├── keymap.h
│   │   ├── proc.h
│   │   ├── protect.h
│   │   ├── proto.h
│   │   ├── sconst.inc
│   │   └── tty.h
│   └── type.h
├── kernel
│   ├── clock.c
│   ├── console.c
│   ├── global.c
│   ├── hd.c
│   ├── i8259.c
│   ├── kernel.asm
│   ├── keyboard.c
│   ├── main.c
│   ├── proc.c
│   ├── protect.c
│   ├── start.c
│   ├── systask.c
│   └── tty.c
├── lib
│   ├── close.c
│   ├── getpid.c
│   ├── kliba.asm
│   ├── klib.c
│   ├── misc.c
│   ├── open.c
│   ├── printf.c
│   ├── read.c
│   ├── string.asm
│   ├── syscall.asm
│   ├── syslog.c
│   ├── unlink.c
│   ├── vsprintf.c
│   └── write.c
└── Makefile

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...