New NOTIFY system call! Queued at kernel. Duplicate messages (with same source

and type) are overwritten with newer flags/ arguments. The interface from
within the kernel is lock_notify(). User processes can make a system call with
notify(). NOTIFY fully replaces the old notification mechanism.
This commit is contained in:
Jorrit Herder 2005-05-24 10:06:17 +00:00
parent 80e38daead
commit ccd17ecfed
22 changed files with 304 additions and 377 deletions

View file

@ -127,7 +127,7 @@ char **argv;
/* Give everybody a chance to die peacefully. */
printf("Sending SIGTERM to all processes ...\n");
kill(-1, SIGTERM);
sleep(3);
sleep(2);
}
reboot(flag, monitor_code, strlen(monitor_code));

View file

@ -833,6 +833,10 @@ PRIVATE void w_intr_wait()
} else if (m.m_type == HARD_INT) {
sys_inb((w_wn->base + REG_STATUS), &w_status);
}
else {
printf("AT_WINI got unexpected message %d from %d\n",
m.m_type, m.m_source);
}
}
} else {
/* Interrupt not yet allocated; use polling. */

View file

@ -12,7 +12,7 @@
#define INT2_CTLMASK 0xA1 /* setting bits in this port disables ints */
/* Magic numbers for interrupt controller. */
#define ENABLE 0x20 /* code used to re-enable after an interrupt */
#define END_OF_INT 0x20 /* code used to re-enable after an interrupt */
/* Interrupt vectors defined/reserved by processor. */

View file

@ -82,9 +82,10 @@
#define NR_NOTIFY_TYPES 5 /* nr of bits in mask */
/* Shorthands for message parameters passed with notifications. */
#define NOTIFY_TYPE m2_i1
#define NOTIFY_FLAGS m2_i2
#define NOTIFY_ARG m2_i3
#define NOTIFY_SOURCE m_source
#define NOTIFY_TYPE m_type
#define NOTIFY_FLAGS m2_i1
#define NOTIFY_ARG m2_l1
/*===========================================================================*

View file

@ -3,7 +3,7 @@
/* Minix release and version numbers. */
#define OS_RELEASE "3"
#define OS_VERSION "0.3"
#define OS_VERSION "0.4"
/* This file sets configuration parameters for the MINIX kernel, FS, and PM.
* It is divided up into two main sections. The first section contains

View file

@ -106,6 +106,7 @@ struct kinfo {
long notify_blocked;
long notify_switching;
long notify_reenter;
long notify_taskcall;
long notify_ok;
long notify_unhold;
long notify_int;

View file

@ -128,7 +128,7 @@ PUBLIC void clock_task()
*/
if (result != EDONTREPLY) {
m.m_type = result;
lock_send(proc_addr(CLOCK), m.m_source, &m);
lock_send(CLOCK, m.m_source, &m);
}
}
}
@ -222,6 +222,7 @@ irq_hook_t *hook;
*/
register struct proc *rp;
register unsigned ticks;
message m;
clock_t now;
/* Acknowledge the PS/2 clock interrupt. */
@ -247,7 +248,8 @@ irq_hook_t *hook;
if (next_timeout <= now || (sched_ticks == 1 && bill_ptr == prev_ptr
&& rdy_head[PPRI_USER] != NIL_PROC))
{
lock_notify(CLOCK, HARD_INT);
m.NOTIFY_TYPE = HARD_INT;
lock_notify(HARDWARE, CLOCK, &m);
}
else if (--sched_ticks == 0) {
sched_ticks = SCHED_RATE; /* reset quantum */

View file

@ -33,8 +33,17 @@
/* How many IRQ hooks are there in total. */
#define NR_IRQ_HOOKS 16
/* How many notification buffers (12B each) should there be? */
#define NR_NOTIFY_BUFS 128
/* How many buffers for notification messages should there be? */
#define NR_NOTIFY_BUFS 64
/* Constants and macros for bit map manipulation. */
#define BITCHUNK_BITS (sizeof(bitchunk_t) * CHAR_BIT)
#define BITMAP_CHUNKS(nr_bits) (((nr_bits)+BITCHUNK_BITS-1)/BITCHUNK_BITS)
#define MAP_CHUNK(map,bit) (map)[((bit)/BITCHUNK_BITS)]
#define CHUNK_OFFSET(bit) ((bit)%BITCHUNK_BITS))
#define GET_BIT(map,bit) ( MAP_CHUNK(map,bit) & (1 << CHUNK_OFFSET(bit) )
#define SET_BIT(map,bit) ( MAP_CHUNK(map,bit) |= (1 << CHUNK_OFFSET(bit) )
#define UNSET_BIT(map,bit) ( MAP_CHUNK(map,bit) &= ~(1 << CHUNK_OFFSET(bit) )
/* Program stack words and masks. */
#define INIT_PSW 0x0200 /* initial psw */

View file

@ -23,17 +23,12 @@ EXTERN struct machine machine; /* machine information for users */
EXTERN struct kmessages kmess; /* diagnostic messages in kernel */
EXTERN struct memory mem[NR_MEMS]; /* base and size of chunks of memory */
/* Low level notifications may be put on the 'held' queue to prevent races. */
EXTERN struct proc *held_head; /* head of queue of held-up interrupts */
EXTERN struct proc *held_tail; /* tail of queue of held-up interrupts */
EXTERN unsigned char k_reenter; /* kernel reentry count (entry count less 1)*/
EXTERN unsigned char switching; /* nonzero if process switching in progress */
/* Process table. Here to stop too many things having to include proc.h. */
EXTERN struct proc *proc_ptr; /* pointer to currently running process */
/* Miscellaneous. */
EXTERN unsigned lost_ticks; /* clock ticks counted outside the clock task */
EXTERN char k_reenter; /* kernel reentry count (entry count less 1) */
EXTERN unsigned lost_ticks; /* clock ticks counted outside clock task */
#if (CHIP == INTEL)

View file

@ -10,8 +10,10 @@
#define ICW1_AT 0x11 /* edge triggered, cascade, need ICW4 */
#define ICW1_PC 0x13 /* edge triggered, no cascade, need ICW4 */
#define ICW1_PS 0x19 /* level triggered, cascade, need ICW4 */
#define ICW4_AT 0x01 /* not SFNM, not buffered, normal EOI, 8086 */
#define ICW4_PC 0x09 /* not SFNM, buffered, normal EOI, 8086 */
#define ICW4_AT_SLAVE 0x01 /* not SFNM, not buffered, normal EOI, 8086 */
#define ICW4_AT_MASTER 0x05 /* not SFNM, not buffered, normal EOI, 8086 */
#define ICW4_PC_SLAVE 0x09 /* not SFNM, buffered, normal EOI, 8086 */
#define ICW4_PC_MASTER 0x0D /* not SFNM, buffered, normal EOI, 8086 */
#if _WORD_SIZE == 2
typedef _PROTOTYPE( void (*vecaddr_t), (void) );
@ -54,13 +56,13 @@ int mine;
outb(INT_CTLMASK, mine ? IRQ0_VECTOR : BIOS_IRQ0_VEC);
/* ICW2 for master */
outb(INT_CTLMASK, (1 << CASCADE_IRQ)); /* ICW3 tells slaves */
outb(INT_CTLMASK, ICW4_AT);
outb(INT_CTLMASK, ICW4_AT_MASTER);
outb(INT_CTLMASK, ~(1 << CASCADE_IRQ)); /* IRQ 0-7 mask */
outb(INT2_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT);
outb(INT2_CTLMASK, mine ? IRQ8_VECTOR : BIOS_IRQ8_VEC);
/* ICW2 for slave */
outb(INT2_CTLMASK, CASCADE_IRQ); /* ICW3 is slave nr */
outb(INT2_CTLMASK, ICW4_AT);
outb(INT2_CTLMASK, ICW4_AT_SLAVE);
outb(INT2_CTLMASK, ~0); /* IRQ 8-15 mask */
/* Copy the BIOS vectors from the BIOS to the Minix location, so we

View file

@ -154,13 +154,15 @@ int c; /* character to append */
/* Accumulate a single character for a kernel message. Send a notification
* the to TTY driver if the buffer if a END_OF_KMESS is encountered.
*/
message m;
if (c != END_OF_KMESS) {
kmess.km_buf[kmess.km_next] = c; /* put normal char in buffer */
if (kmess.km_size < KMESS_BUF_SIZE)
kmess.km_size += 1;
kmess.km_next = (kmess.km_next + 1) % KMESS_BUF_SIZE;
} else {
lock_notify(TTY, NEW_KMESS); /* let TTY display the message */
m.NOTIFY_TYPE = NEW_KMESS;
lock_notify(HARDWARE, TTY, &m);
}
}

View file

@ -217,6 +217,7 @@ int how; /* 0 = halt, 1 = reboot, 2 = panic!, ... */
* sure it is only executed once. Unless a CPU exception occurred, the
* stop_sequence() is started.
*/
message m;
if (shutting_down)
return;
@ -225,7 +226,6 @@ int how; /* 0 = halt, 1 = reboot, 2 = panic!, ... */
* We rely on TTY to call sys_abort() when it is done with the dumps.
*/
if (how == RBT_PANIC) {
message m;
m.m_type = PANIC_DUMPS;
if (nb_send(TTY, &m) == OK) /* don't block if TTY isn't ready */
return; /* await sys_abort() from TTY */
@ -234,7 +234,8 @@ int how; /* 0 = halt, 1 = reboot, 2 = panic!, ... */
/* The TTY expects two HARD_STOP notifications. One to switch to the
* primary console for stop sequence output, and one to actually exit.
*/
lock_notify(TTY, HARD_STOP); /* let TTY switch to console 0 */
m.NOTIFY_TYPE = HARD_STOP;
lock_notify(HARDWARE, TTY, &m);
/* Run the stop sequence. The timer argument passes the shutdown status.
* The stop sequence is skipped for fatal CPU exceptions.
@ -266,6 +267,7 @@ timer_t *tp;
static int level = P_SERVER; /* start at the highest level */
static struct proc *p = NIL_PROC; /* next process to stop */
static char *types[] = {"task","system","driver","server","user"};
static message m;
/* See if the last process' shutdown was successful. Else, force exit. */
if (p != NIL_PROC) {
@ -288,7 +290,9 @@ timer_t *tp;
kprintf("- Stopping %s ", karg(p->p_name));
kprintf("%s ... ", karg(types[p->p_type]));
shutdown_process = p; /* directly continue if exited */
lock_notify(proc_number(p), HARD_STOP);
m.NOTIFY_TYPE = HARD_STOP;
m.NOTIFY_ARG = tmr_arg(tp)->ta_int; /* how */
lock_notify(HARDWARE, proc_number(p), &m);
set_timer(tp, get_uptime()+STOP_TICKS, stop_sequence);
return; /* allow the process to shut down */
}
@ -330,7 +334,7 @@ PRIVATE void shutdown(int how)
* For RBT_MONITOR, the MM has provided the program.
*/
if (how == RBT_HALT) {
phys_copy(vir2phys("delay;"), kinfo.params_base, 7);
phys_copy(vir2phys("delay;menu"), kinfo.params_base, 11);
} else if (how == RBT_REBOOT) {
phys_copy(vir2phys("delay;boot"), kinfo.params_base, 11);
}

View file

@ -2,6 +2,9 @@
* panic abort MINIX due to a fatal error
* bad_assertion for debugging
* bad_compare for debugging
* alloc_bit bit map manipulation
* free_bit bit map manipulation
* print_bitmap bit map manipulation
*/
#include "kernel.h"
@ -35,6 +38,78 @@ int n;
}
/*===========================================================================*
* print_bitmap *
*===========================================================================*/
PUBLIC void print_bitmap(bitmap, nr_bits)
bitchunk_t *bitmap;
bit_t nr_bits;
{
bit_t bit_i;
for (bit_i=0; bit_i < nr_bits; bit_i++) {
kprintf("%d", GET_BIT(bitmap, bit_i) > 0 );
if (! ((bit_i+1) % 8) ) kprintf(" ", NO_ARG);
if (! ((bit_i+1) % 64) ) kprintf("\n", NO_ARG);
}
kprintf("\n", NO_ARG);
}
/*===========================================================================*
* free_bit *
*===========================================================================*/
PUBLIC void free_bit(bit_nr, bitmap, nr_bits)
bit_t bit_nr;
bitchunk_t *bitmap;
bit_t nr_bits;
{
bitchunk_t *chunk;
if (bit_nr >= nr_bits) {
kprintf("Warning, free_bit: %d illegal index\n", bit_nr);
return;
}
chunk = &bitmap[(bit_nr/BITCHUNK_BITS)];
*chunk &= ~(1 << (bit_nr % BITCHUNK_BITS));
}
/*===========================================================================*
* alloc_bit *
*===========================================================================*/
PUBLIC int alloc_bit(bitmap, nr_bits)
bitchunk_t *bitmap;
bit_t nr_bits;
{
bitchunk_t *chunk;
int nr_chunks;
int bit_nr;
int i;
/* Iterate over the words in block. */
nr_chunks = BITMAP_CHUNKS(nr_bits);
for (chunk = &bitmap[0]; chunk < &bitmap[nr_chunks]; chunk++) {
/* Does this chunk contain a free bit? */
if (*chunk == (bitchunk_t) ~0) continue;
/* Get bit number from the start of the bit map. */
for (i = 0; (*chunk & (1 << i)) != 0; ++i) {}
bit_nr = (chunk - &bitmap[0]) * BITCHUNK_BITS + i;
/* Don't allocate bits beyond the end of the map. */
if (bit_nr >= nr_bits) break;
*chunk |= 1 << bit_nr % BITCHUNK_BITS;
return(bit_nr);
}
kprintf("Warning, all %d bits in map busy\n", nr_bits);
return(-1);
}
#if !NDEBUG
/*=========================================================================*
* bad_assertion *

View file

@ -55,6 +55,7 @@ begbss:
#include <minix/config.h>
#include <minix/const.h>
#include <minix/com.h>
#include <ibm/interrupt.h>
#include "const.h"
#include "protect.h"
#include "sconst.h"
@ -212,24 +213,48 @@ csinit:
!* hwint00 - 07 *
!*===========================================================================*
! Note this is a macro, it just looks like a subroutine.
#define hwint_master_slave_fail(irq) \
call save /* save interrupted process state */;\
cli ;\
inb INT2_CTLMASK /* get current mask */ ;\
movb ah, al ;\
inb INT_CTLMASK /* get current mask */ ;\
push eax ;\
cli ;\
movb al, ~[0] ;\
outb INT_CTLMASK /* mask all */;\
outb INT2_CTLMASK /* */;\
cli ;\
push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\
call _intr_handle /* intr_handle(irq_handlers[irq]) */;\
pop ecx ;\
pop eax ;\
cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\
jz 0f ;\
or eax, [1<<irq] /* mask irq */ ;\
0: outb INT_CTLMASK /* restore master irq mask */;\
movb al, ah ;\
outb INT2_CTLMASK /* restore slave irq mask */;\
movb al, END_OF_INT ;\
outb INT_CTL /* reenable master 8259 */;\
cmp (irq), 8 ;\
jb 1f ;\
outb INT2_CTL /* reenable slave 8259 */;\
1: ret /* restart (another) process */
#define hwint_master(irq) \
call save /* save interrupted process state */;\
inb INT_CTLMASK ;\
orb al, [1<<irq] ;\
outb INT_CTLMASK /* disable the irq */;\
movb al, ENABLE ;\
outb INT_CTL /* reenable master 8259 */;\
push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\
sti /* enable interrupts */;\
call _intr_handle /* intr_handle(irq_handlers[irq]) */;\
cli /* disable interrupts */;\
pop ecx ;\
cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\
jnz 0f ;\
inb INT_CTLMASK ;\
andb al, ~[1<<irq] ;\
outb INT_CTLMASK /* enable the irq */;\
0: ret /* restart (another) process */
jz 0f ;\
inb INT_CTLMASK /* get current mask */ ;\
orb al, [1<<irq] /* mask irq */ ;\
outb INT_CTLMASK /* disable the irq */;\
0: movb al, END_OF_INT ;\
outb INT_CTL /* reenable master 8259 */;\
ret /* restart (another) process */
! Each of these entry points is an expansion of the hwint_master macro
.align 16
@ -270,23 +295,18 @@ _hwint07: ! Interrupt routine for irq 7 (printer)
! Note this is a macro, it just looks like a subroutine.
#define hwint_slave(irq) \
call save /* save interrupted process state */;\
push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\
call _intr_handle /* intr_handle(irq_handlers[irq]) */;\
pop ecx ;\
cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\
jz 0f ;\
inb INT2_CTLMASK ;\
orb al, [1<<[irq-8]] ;\
outb INT2_CTLMASK /* disable the irq */;\
movb al, ENABLE ;\
0: movb al, END_OF_INT ;\
outb INT_CTL /* reenable master 8259 */;\
push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\
outb INT2_CTL /* reenable slave 8259 */;\
sti /* enable interrupts */;\
call _intr_handle /* intr_handle(irq_handlers[irq]) */;\
cli /* disable interrupts */;\
pop ecx ;\
cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\
jnz 0f ;\
inb INT2_CTLMASK ;\
andb al, ~[1<<[irq-8]] ;\
outb INT2_CTLMASK /* enable the irq */;\
0: ret /* restart (another) process */
ret /* restart (another) process */
! Each of these entry points is an expansion of the hwint_slave macro
.align 16
@ -375,7 +395,6 @@ _p_s_call:
mov esp, k_stktop
xor ebp, ebp ! for stacktrace
! end of inline save
sti ! allow SWITCHER to be interrupted
! now set up parameters for sys_call()
push ebx ! pointer to user message
push eax ! src/dest
@ -383,7 +402,6 @@ _p_s_call:
call _sys_call ! sys_call(function, src_dest, m_ptr)
! caller is now explicitly in proc_ptr
mov AXREG(esi), eax ! sys_call MUST PRESERVE si
cli ! disable interrupts
! Fall into code to restart proc/task running.
@ -392,17 +410,8 @@ _p_s_call:
!*===========================================================================*
_restart:
! Flush any held-up notifications.
! This reenables interrupts, so the current interrupt handler may reenter.
! This does not matter, because the current handler is about to exit and no
! other handlers can reenter since flushing is only done when k_reenter == 0.
! Restart the current process or the next process if it is set.
cmp (_held_head), 0 ! do fast test to usually avoid function call
jz over_call_unhold
cmp (_switching), 0 ! do fast test to usually avoid function call
jnz over_call_unhold
call _unhold ! this is rare so overhead acceptable
over_call_unhold:
mov esp, (_proc_ptr) ! will assume P_STACKBASE == 0
lldt P_LDT_SEL(esp) ! enable segment descriptors for task
lea eax, P_STACKTOP(esp) ! arrange for next interrupt
@ -522,7 +531,6 @@ exception1: ! Common for all exceptions.
call _exception ! (ex_number, trap_errno, old_eip,
! old_cs, old_eflags)
add esp, 5*4
cli
ret
!*===========================================================================*

View file

@ -1,27 +1,24 @@
/* This file contains essentially all of the process and message handling.
* It has two main entry points from the outside:
* It has one main entry point from the outside:
*
* sys_call: a system call, that is, the kernel is trapped with an INT
* lock_notify: send a notification to inform a process of a system event
* sys_call: a system call, i.e., the kernel is trapped with an INT
*
* It also has several minor entry points to be used from the task level:
*
* lock_notify: send a notification to inform a process of a system event
* lock_send: send a message to a process
* lock_ready: put a process on one of the ready queues so it can be run
* lock_unready: remove a process from the ready queues
* lock_sched: a process has run too long; schedule another one
* lock_pick_proc: pick a process to run (used by system initialization)
* unhold: repeat all held-up notifications
*
* Changes:
* May 24, 2005 new, queued NOTIFY system call (Jorrit N. Herder)
* Oct 28, 2004 non-blocking SEND and RECEIVE (Jorrit N. Herder)
* Oct 28, 2004 rewrite of sys_call() (Jorrit N. Herder)
* Oct 10, 2004 require BOTH for kernel sys_call() (Jorrit N. Herder)
* (to protect kernel tasks from being blocked)
* Sep 25, 2004 generalized notify() function (Jorrit N. Herder)
* Sep 23, 2004 removed PM sig check in mini_rec() (Jorrit N. Herder)
* Aug 19, 2004 generalized ready()/unready() (Jorrit N. Herder)
* Aug 18, 2004 added notify() function (Jorrit N. Herder)
*/
#include "kernel.h"
@ -31,18 +28,21 @@
#include "sendmask.h"
FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dest,
/* Scheduling and message passing functions. The functions are available to
* other parts of the kernel through lock_...(). The lock temporarily disables
* interrupts to prevent race conditions.
*/
FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst,
message *m_ptr, int may_block) );
FORWARD _PROTOTYPE( int mini_rec, (struct proc *caller_ptr, int src,
message *m_ptr, int may_block) );
FORWARD _PROTOTYPE( int mini_notify, (struct proc *caller_ptr, int dest,
FORWARD _PROTOTYPE( int mini_notify, (struct proc *caller_ptr, int dst,
message *m_ptr ) );
FORWARD _PROTOTYPE( void ready, (struct proc *rp) );
FORWARD _PROTOTYPE( void sched, (void) );
FORWARD _PROTOTYPE( void unready, (struct proc *rp) );
FORWARD _PROTOTYPE( void pick_proc, (void) );
FORWARD _PROTOTYPE( int alloc_notify_buf, (void) );
FORWARD _PROTOTYPE( void free_notify_buf, (int index) );
#if (CHIP == M68000)
FORWARD _PROTOTYPE( void cp_mess, (int src, struct proc *src_p, message *src_m,
@ -61,161 +61,9 @@ FORWARD _PROTOTYPE( void cp_mess, (int src, struct proc *src_p, message *src_m,
#endif /* (CHIP == M68000) */
/* Bit mask operations used to bits of the notification mask. */
#define set_bit(mask, n) ((mask) |= (1 << (n)))
#define clear_bit(mask, n) ((mask) &= ~(1 << (n)))
#define isset_bit(mask, n) ((mask) & (1 << (n)))
/* Constants and macros for the notification bit map. */
#define BITCHUNK_BITS (sizeof(bitchunk_t) * CHAR_BIT)
#define BITMAP_CHUNKS ((NR_NOTIFY_BUFS + BITCHUNK_BITS - 1)/BITCHUNK_BITS)
#define MAP_CHUNK(map,bit) (map)[((bit)/BITCHUNK_BITS)]
#define CHUNK_OFFSET(bit) ((bit)%BITCHUNK_BITS))
#define GET_BIT(map,bit) ( MAP_CHUNK(map,bit) & (1 << CHUNK_OFFSET(bit) )
#define SET_BIT(map,bit) ( MAP_CHUNK(map,bit) |= (1 << CHUNK_OFFSET(bit) )
#define UNSET_BIT(map,bit) ( MAP_CHUNK(map,bit) &= ~(1 << CHUNK_OFFSET(bit) )
/* Declare buffer space for notifications and bit map for administration. */
/* Declare buffer space and a bit map for notification messages. */
PRIVATE struct notification notify_buffer[NR_NOTIFY_BUFS];
PRIVATE bitchunk_t notify_bitmap[BITMAP_CHUNKS];
/*===========================================================================*
* free_notify_buf *
*===========================================================================*/
PRIVATE void free_notify_buf(buf_index)
int buf_index; /* buffer to release */
{
bitchunk_t *chunk;
if (buf_index >= NR_NOTIFY_BUFS) return;
chunk = &notify_bitmap[(buf_index/BITCHUNK_BITS)];
*chunk &= ~(buf_index % BITCHUNK_BITS);
}
/*===========================================================================*
* alloc_notify_buf *
*===========================================================================*/
PRIVATE int alloc_notify_buf()
{
bitchunk_t *chunk;
int i, bit_nr;
/* Iterate over the words in block. */
for (chunk = &notify_bitmap[0];
chunk < &notify_bitmap[BITMAP_CHUNKS]; chunk++) {
/* Does this chunk contain a free bit? */
if (*chunk == (bitchunk_t) ~0) continue;
/* Get bit number from the start of the bit map. */
for (i = 0; (*chunk & (1 << i)) != 0; ++i) {}
bit_nr = (chunk - &notify_bitmap[0]) * BITCHUNK_BITS + i;
/* Don't allocate bits beyond the end of the map. */
if (bit_nr >= NR_NOTIFY_BUFS) break;
*chunk |= 1 << bit_nr % BITCHUNK_BITS;
kprintf("Allocated bit %d\n", bit_nr);
return(bit_nr);
}
return(-1);
}
/*===========================================================================*
* lock_notify *
*===========================================================================*/
PUBLIC void lock_notify(proc_nr, notify_type)
int proc_nr; /* number of process to be started */
int notify_type; /* notification to be sent */
{
/* A system event has occurred. Send a notification with source HARDWARE to
* the given process. The notify() function was carefully designed so that it
* (1) can be used safely from both interrupt handlers and the task level, and
* (2) realizes asynchronous message passing with at least once semantics,
* that is, the notifications are not queued. If a race condition occurs, the
* notification is queued and repeated later by unhold(). If the receiver is
* not ready, the notification is blocked and checked later in receive().
*/
register struct proc *rp; /* pointer to task's proc entry */
message m; /* message to send the notification */
unsigned int notify_bit; /* bit for this notification */
/* Get notify bit and process pointer. */
notify_bit = (unsigned int) (notify_type & ~NOTIFICATION);
rp = proc_addr(proc_nr);
/* If this call would compete with other process-switching functions, put
* it on the 'held' queue to be flushed at the next non-competing restart().
* The competing conditions are:
* (1) k_reenter == (typeof k_reenter) -1:
* Call from the task level, typically from an output interrupt
* routine. An interrupt handler might reenter notify(). Rare,
* so not worth special treatment.
* (2) k_reenter > 0:
* Call from a nested interrupt handler. A previous interrupt
* handler might be inside notify() or sys_call().
* (3) switching != 0:
* A process-switching function other than notify() is being called
* from the task level, typically sched() from CLOCK. An interrupt
* handler might call notify() and pass the 'k_reenter' test.
*/
if (k_reenter != 0 || switching) {
kinfo.notify_held ++;
if (switching) kinfo.notify_switching ++;
if (k_reenter > 0) kinfo.notify_reenter ++;
switch(notify_type) {
case HARD_INT: kinfo.notify_int ++; break;
case HARD_STOP: kinfo.notify_stop ++; break;
case SYN_ALARM: kinfo.notify_alarm ++; break;
case KSIG_PENDING: kinfo.notify_sig ++; break;
case NEW_KMESS: kinfo.notify_kmess ++; break;
}
lock();
/* already on held queue? */
if (! isset_bit(rp->p_ntf_held, notify_bit)) {
if (held_head != NIL_PROC)
held_tail->p_ntf_nextheld = rp;
else
held_head = rp;
held_tail = rp;
rp->p_ntf_nextheld = NIL_PROC;
}
set_bit(rp->p_ntf_held, notify_bit); /* add bit to held mask */
unlock();
return;
}
/* If process is not waiting for a notification, record the blockage. Else,
* send it a message with source HARDWARE and type 'notify_type'. No more
* information can be reliably provided since notifications are not queued.
*/
switching = TRUE;
if ( (rp->p_flags & (RECEIVING | SENDING)) != RECEIVING ||
!isrxhardware(rp->p_getfrom)) {
kinfo.notify_blocked ++;
set_bit(rp->p_ntf_blocked, notify_bit); /* update blocked mask */
} else {
/* Assemble notification message and send it. */
m.m_source = HARDWARE;
m.m_type = notify_type;
CopyMess(HARDWARE, proc_addr(HARDWARE), &m, rp, rp->p_messbuf);
clear_bit(rp->p_ntf_blocked, notify_bit);
rp->p_flags &= ~RECEIVING;
kinfo.notify_ok ++;
/* Announce the process ready and select a fresh process to run. */
ready(rp);
pick_proc();
}
switching = FALSE;
}
PRIVATE bitchunk_t notify_bitmap[BITMAP_CHUNKS(NR_NOTIFY_BUFS)];
/*===========================================================================*
@ -223,7 +71,7 @@ int notify_type; /* notification to be sent */
*===========================================================================*/
PUBLIC int sys_call(call_nr, src_dst, m_ptr)
int call_nr; /* (NB_)SEND, (NB_)RECEIVE, BOTH */
int src_dst; /* source to receive from or dest to send to */
int src_dst; /* src to receive from or dst to send to */
message *m_ptr; /* pointer to message in the caller's space */
{
/* System calls are done by trapping to the kernel with an INT instruction.
@ -298,21 +146,21 @@ message *m_ptr; /* pointer to message in the caller's space */
/*===========================================================================*
* mini_send *
*===========================================================================*/
PRIVATE int mini_send(caller_ptr, dest, m_ptr, may_block)
PRIVATE int mini_send(caller_ptr, dst, m_ptr, may_block)
register struct proc *caller_ptr; /* who is trying to send a message? */
int dest; /* to whom is message being sent? */
int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
int may_block; /* (dis)allow blocking */
{
/* Send a message from 'caller_ptr' to 'dest'. If 'dest' is blocked waiting
* for this message, copy the message to it and unblock 'dest'. If 'dest' is
/* Send a message from 'caller_ptr' to 'dst'. If 'dst' is blocked waiting
* for this message, copy the message to it and unblock 'dst'. If 'dst' is
* not waiting at all, or is waiting for another source, queue 'caller_ptr'.
*/
register struct proc *dest_ptr, *next_ptr;
register struct proc *dst_ptr, *next_ptr;
vir_bytes vb; /* message buffer pointer as vir_bytes */
vir_clicks vlo, vhi; /* virtual clicks containing message to send */
dest_ptr = proc_addr(dest); /* pointer to destination's proc entry */
dst_ptr = proc_addr(dst); /* pointer to destination's proc entry */
#if ALLOW_GAP_MESSAGES
/* This check allows a message to be anywhere in data or stack or gap.
@ -335,9 +183,9 @@ int may_block; /* (dis)allow blocking */
return(EFAULT);
#endif
/* Check for deadlock by 'caller_ptr' and 'dest' sending to each other. */
if (dest_ptr->p_flags & SENDING) {
next_ptr = proc_addr(dest_ptr->p_sendto);
/* Check for deadlock by 'caller_ptr' and 'dst' sending to each other. */
if (dst_ptr->p_flags & SENDING) {
next_ptr = proc_addr(dst_ptr->p_sendto);
while (TRUE) {
if (next_ptr == caller_ptr) return(ELOCKED);
if (next_ptr->p_flags & SENDING)
@ -347,25 +195,25 @@ int may_block; /* (dis)allow blocking */
}
}
/* Check to see if 'dest' is blocked waiting for this message. */
if ( (dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING &&
(dest_ptr->p_getfrom == ANY ||
dest_ptr->p_getfrom == proc_number(caller_ptr))) {
/* Check to see if 'dst' is blocked waiting for this message. */
if ( (dst_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING &&
(dst_ptr->p_getfrom == ANY ||
dst_ptr->p_getfrom == proc_number(caller_ptr))) {
/* Destination is indeed waiting for this message. */
CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr, dest_ptr,
dest_ptr->p_messbuf);
dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */
if (dest_ptr->p_flags == 0) ready(dest_ptr);
CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr, dst_ptr,
dst_ptr->p_messbuf);
dst_ptr->p_flags &= ~RECEIVING; /* deblock destination */
if (dst_ptr->p_flags == 0) ready(dst_ptr);
} else if (may_block) {
/* Destination is not waiting. Block and queue caller. */
caller_ptr->p_messbuf = m_ptr;
if (caller_ptr->p_flags == 0) unready(caller_ptr);
caller_ptr->p_flags |= SENDING;
caller_ptr->p_sendto= dest;
caller_ptr->p_sendto = dst;
/* Process is now blocked. Put in on the destination's queue. */
if ( (next_ptr = dest_ptr->p_caller_q) == NIL_PROC)
dest_ptr->p_caller_q = caller_ptr;
if ( (next_ptr = dst_ptr->p_caller_q) == NIL_PROC)
dst_ptr->p_caller_q = caller_ptr;
else {
while (next_ptr->p_sendlink != NIL_PROC)
next_ptr = next_ptr->p_sendlink;
@ -422,6 +270,7 @@ int may_block; /* (dis)allow blocking */
while (*ntf_q_pp) {
if (src == ANY || src == (*ntf_q_pp)->n_source) {
/* Found notification. Assemble and copy message. */
m.NOTIFY_SOURCE = (*ntf_q_pp)->n_source;
m.NOTIFY_TYPE = (*ntf_q_pp)->n_type;
m.NOTIFY_FLAGS = (*ntf_q_pp)->n_flags;
m.NOTIFY_ARG = (*ntf_q_pp)->n_arg;
@ -431,28 +280,11 @@ int may_block; /* (dis)allow blocking */
bit_nr = ((long)(*ntf_q_pp) - (long) &notify_buffer[0]) /
sizeof(struct notification);
*ntf_q_pp = (*ntf_q_pp)->n_next;/* remove from queue */
free_notify_buf(bit_nr); /* afterwards: prevent race */
free_bit(bit_nr, notify_bitmap, NR_NOTIFY_BUFS);
return(OK); /* report success */
}
ntf_q_pp = &(*ntf_q_pp)->n_next; /* proceed to next */
}
/* Check bit mask for blocked notifications. If multiple bits are set,
* send the first notification encountered; the rest is handled later.
* This effectively prioritizes notifications. Notification also have
* priority of other messages.
*/
if (caller_ptr->p_ntf_blocked && isrxhardware(src)) {
for (i=0; i<NR_NOTIFY_TYPES; i++) {
if (isset_bit(caller_ptr->p_ntf_blocked, i)) {
m.m_source = HARDWARE;
m.m_type = NOTIFICATION | i;
CopyMess(HARDWARE, proc_addr(HARDWARE), &m, caller_ptr, m_ptr);
clear_bit(caller_ptr->p_ntf_blocked, i);
return(OK);
}
}
}
}
/* No suitable message is available. Block the process trying to receive,
@ -469,6 +301,7 @@ int may_block; /* (dis)allow blocking */
}
}
/*===========================================================================*
* mini_notify *
*===========================================================================*/
@ -477,48 +310,75 @@ register struct proc *caller_ptr; /* process trying to notify */
int dst; /* which process to notify */
message *m_ptr; /* pointer to message buffer */
{
register struct proc *dest_ptr = proc_addr(dst);
register struct proc *dst_ptr = proc_addr(dst);
register struct notification *ntf_p ;
register struct notification **ntf_q_pp;
int ntf_index;
message ntf_mess;
/* Check to see if target is blocked waiting for this message. */
if ( (dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING &&
(dest_ptr->p_getfrom == ANY ||
dest_ptr->p_getfrom == proc_number(caller_ptr))) {
if ( (dst_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING &&
(dst_ptr->p_getfrom == ANY ||
dst_ptr->p_getfrom == proc_number(caller_ptr))) {
/* Destination is indeed waiting for this message. */
CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr, dest_ptr,
dest_ptr->p_messbuf);
dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */
if (dest_ptr->p_flags == 0) ready(dest_ptr);
} else {
/* See if there is a free notification buffer. */
if ((ntf_index = alloc_notify_buf()) < 0)
return(ENOSPC); /* should be atomic! */
/* Copy details from notification message. */
CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr,
CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr, dst_ptr,
dst_ptr->p_messbuf);
dst_ptr->p_flags &= ~RECEIVING; /* deblock destination */
if (dst_ptr->p_flags == 0) ready(dst_ptr);
}
/* Destination is not ready. Add the notification to the pending queue. */
else {
/* Get pointer to notification message. */
if (! istaskp(caller_ptr)) {
CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr,
proc_addr(HARDWARE), &ntf_mess);
m_ptr = &ntf_mess;
}
/* Enqueue the message. Existing notifications are overwritten with
* the newer one. New notifications are added to the end of the list.
*/
ntf_q_pp = &dst_ptr->p_ntf_q;
while (*ntf_q_pp) {
/* Replace notifications with same source and type. */
if ((*ntf_q_pp)->n_type == m_ptr->m_type &&
(*ntf_q_pp)->n_source == m_ptr->m_source) {
(*ntf_q_pp)->n_flags = m_ptr->NOTIFY_FLAGS;
(*ntf_q_pp)->n_arg = m_ptr->NOTIFY_ARG;
break;
}
return(OK);
}
/* Add to end of queue. Get a free notification buffer. */
if ((ntf_index = alloc_bit(notify_bitmap, NR_NOTIFY_BUFS)) < 0)
return(ENOSPC); /* should be atomic! */
ntf_p = &notify_buffer[ntf_index];
ntf_p->n_source = proc_number(caller_ptr);
ntf_p->n_type = ntf_mess.NOTIFY_TYPE;
ntf_p->n_flags = ntf_mess.NOTIFY_FLAGS;
ntf_p->n_arg = ntf_mess.NOTIFY_ARG;
/* Enqueue the notification message for later. New notifications
* are added to the end of the list. First find the NULL pointer,
* then add the new pointer to the end.
*/
ntf_q_pp = &dest_ptr->p_ntf_q;
while (*ntf_q_pp) ntf_q_pp = &(*ntf_q_pp)->n_next;
ntf_p->n_type = m_ptr->NOTIFY_TYPE;
ntf_p->n_flags = m_ptr->NOTIFY_FLAGS;
ntf_p->n_arg = m_ptr->NOTIFY_ARG;
*ntf_q_pp = ntf_p;
ntf_p->n_next = NULL;
}
return(OK);
}
/*==========================================================================*
* lock_notify *
*==========================================================================*/
PUBLIC int lock_notify(src, dst, m_ptr)
int src; /* who is trying to send a message? */
int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
{
/* Safe gateway to mini_notify() for tasks. */
int result;
lock();
result = mini_notify(proc_addr(src), dst, m_ptr);
unlock();
return(result);
}
/*===========================================================================*
* pick_proc *
@ -618,16 +478,6 @@ register struct proc *rp; /* this process is no longer runnable */
rdy_tail[q] = rp;
}
}
#if DEAD_CODE
while (xp->p_nextready != rp) /* find rp */
if ( (xp = xp->p_nextready) == NIL_PROC)
return;
xp->p_nextready = xp->p_nextready->p_nextready;
qtail = &rdy_tail[q];
if (*qtail == rp) *qtail = xp;
#endif
}
/*===========================================================================*
@ -655,25 +505,25 @@ PRIVATE void sched()
PUBLIC void lock_pick_proc()
{
/* Safe gateway to pick_proc() for tasks. */
switching = TRUE;
lock();
pick_proc();
switching = FALSE;
unlock();
}
/*==========================================================================*
* lock_send *
*==========================================================================*/
PUBLIC int lock_send(caller_ptr, dest, m_ptr)
register struct proc *caller_ptr; /* who is trying to send a message? */
int dest; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
PUBLIC int lock_send(src, dst, m_ptr)
int src; /* who is trying to send a message? */
int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
{
/* Safe gateway to mini_send() for tasks. */
int result;
switching = TRUE;
result = mini_send(caller_ptr, dest, m_ptr, FALSE);
switching = FALSE;
lock();
result = mini_send(proc_addr(src), dst, m_ptr, FALSE);
unlock();
return(result);
}
@ -685,9 +535,9 @@ PUBLIC void lock_ready(rp)
struct proc *rp; /* this process is now runnable */
{
/* Safe gateway to ready() for tasks. */
switching = TRUE;
lock();
ready(rp);
switching = FALSE;
unlock();
}
/*==========================================================================*
@ -697,9 +547,9 @@ PUBLIC void lock_unready(rp)
struct proc *rp; /* this process is no longer runnable */
{
/* Safe gateway to unready() for tasks. */
switching = TRUE;
lock();
unready(rp);
switching = FALSE;
unlock();
}
/*==========================================================================*
@ -708,45 +558,8 @@ struct proc *rp; /* this process is no longer runnable */
PUBLIC void lock_sched()
{
/* Safe gateway to sched() for tasks. */
switching = TRUE;
lock();
sched();
switching = FALSE;
unlock();
}
/*==========================================================================*
* unhold *
*==========================================================================*/
PUBLIC void unhold()
{
/* Flush any held-up notifications. 'k_reenter' must be 0. 'held_head' must
* not be NIL_PROC. Interrupts must be disabled. They will be enabled but
* will be disabled when this returns.
*/
register struct proc *rp; /* current head of held queue */
int i;
kinfo.notify_unhold ++;
if (switching) return;
rp = held_head;
do {
for (i=0; i<NR_NOTIFY_TYPES; i++) {
if (isset_bit(rp->p_ntf_held,i)) {
clear_bit(rp->p_ntf_held,i);
if (! rp->p_ntf_held) /* proceed to next in queue? */
if ( (held_head = rp->p_ntf_nextheld) == NIL_PROC)
held_tail = NIL_PROC;
#if DEAD_CODE
unlock(); /* reduce latency; held queue may change! */
#endif
lock_notify(proc_number(rp), NOTIFICATION | i);
#if DEAD_CODE
lock(); /* protect the held queue again */
#endif
}
}
}
while ( (rp = held_head) != NIL_PROC);
}

View file

@ -33,12 +33,6 @@ struct proc {
proc_nr_t p_nr; /* number of this process (for fast access) */
notify_mask_t p_ntf_blocked; /* bit mask for blocked notifications */
notify_mask_t p_ntf_held; /* bit mask for held up notify() calls */
struct proc *p_ntf_nextheld; /* next in chain of held-up int processes */
struct notification *p_ntf_q; /* queue of pending notifications */
int p_flags; /* SENDING, RECEIVING, etc. */
struct mem_map p_memmap[NR_LOCAL_SEGS]; /* local memory map (T, D, S) */
struct far_mem p_farmem[NR_REMOTE_SEGS]; /* remote memory map */
@ -61,6 +55,8 @@ struct proc {
int p_getfrom; /* from whom does process want to receive? */
int p_sendto; /* to whom does process want to send? */
struct notification *p_ntf_q; /* queue of pending notifications */
struct proc *p_nextready; /* pointer to next ready process */
sigset_t p_pending; /* bit map for pending signals */
unsigned p_pendcount; /* count of pending and unfinished signals */

View file

@ -38,16 +38,18 @@ _PROTOTYPE( void stop_sequence, (struct timer *tp) );
/* misc.c */
_PROTOTYPE( void panic, (_CONST char *s, int n) );
_PROTOTYPE( int alloc_bit, (bitchunk_t *map, bit_t nr_bits) );
_PROTOTYPE( void free_bit, (bit_t nr, bitchunk_t *map, bit_t nr_bits) );
_PROTOTYPE( void print_bitmap, (bitchunk_t *map, bit_t nr_bits) );
/* proc.c */
_PROTOTYPE( int sys_call, (int function, int src_dest, message *m_ptr) );
_PROTOTYPE( void unhold, (void) );
_PROTOTYPE( int lock_notify, (int src, int dst, message *m_ptr) );
_PROTOTYPE( int lock_send, (int src, int dst, message *m_ptr) );
_PROTOTYPE( void lock_pick_proc, (void) );
_PROTOTYPE( void lock_ready, (struct proc *rp) );
_PROTOTYPE( void lock_sched, (void) );
_PROTOTYPE( void lock_unready, (struct proc *rp) );
_PROTOTYPE( void lock_notify, (int proc_nr, int notify_type) );
_PROTOTYPE( int lock_send, (struct proc *rp, int to, message *m_ptr) );
/* start.c */
_PROTOTYPE( void cstart, (U16_t cs, U16_t ds, U16_t mds,

View file

@ -89,7 +89,7 @@ PUBLIC void sys_task()
*/
if (result != EDONTREPLY) {
m.m_type = result; /* report status of call */
lock_send(proc_addr(SYSTASK), m.m_source, &m);
lock_send(SYSTASK, m.m_source, &m);
}
}
}
@ -243,7 +243,10 @@ irq_hook_t *hook;
* interrupts are transformed into messages to a driver. The IRQ line will be
* reenabled if the policy says so.
*/
lock_notify(hook->proc_nr, HARD_INT);
message m;
m.NOTIFY_TYPE = HARD_INT;
m.NOTIFY_ARG = hook->irq;
lock_notify(HARDWARE, hook->proc_nr, &m);
return(hook->policy & IRQ_REENABLE);
}
@ -268,6 +271,7 @@ int sig_nr; /* signal to be sent, 1 to _NSIG */
* do a core dump.
*/
register struct proc *rp, *mmp;
message m;
rp = proc_addr(proc_nr);
if (sigismember(&rp->p_pending, sig_nr))
@ -278,12 +282,15 @@ int sig_nr; /* signal to be sent, 1 to _NSIG */
return; /* another signal already pending */
if (rp->p_flags == 0) lock_unready(rp);
rp->p_flags |= PENDING | SIG_PENDING;
lock_notify(PM_PROC_NR, KSIG_PENDING);
m.NOTIFY_TYPE = KSIG_PENDING;
m.NOTIFY_ARG = 0;
m.NOTIFY_FLAGS = 0;
lock_notify(HARDWARE, PM_PROC_NR, &m);
}
/*===========================================================================*
* umap_bios *
* umap_bios *
*===========================================================================*/
PUBLIC phys_bytes umap_bios(rp, vir_addr, bytes)
register struct proc *rp; /* pointer to proc table entry for process */

View file

@ -164,7 +164,12 @@ timer_t *tp;
* alarm. The process number is stored in timer argument 'ta_int'. Notify that
* process given with a SYN_ALARM message.
*/
lock_notify(tmr_arg(tp)->ta_int, SYN_ALARM);
message m;
m.NOTIFY_SOURCE = HARDWARE;
m.NOTIFY_TYPE = SYN_ALARM;
m.NOTIFY_ARG = get_uptime();
m.NOTIFY_FLAGS = 0;
lock_notify(HARDWARE, tmr_arg(tp)->ta_int, &m);
}

View file

@ -13,7 +13,7 @@ typedef long karg_t; /* use largest type here */
* short we can support up to 256 user processes and more kernel tasks than
* one can ever create.
*/
typedef int proc_nr_t; /* process table entry number */
typedef short proc_nr_t; /* process table entry number */
typedef unsigned long send_mask_t; /* bit mask for sender */
struct system_image {
@ -32,8 +32,8 @@ struct memory {
};
typedef unsigned long notify_mask_t; /* bit mask for notifications */
typedef char notify_type_t; /* notification type */
typedef char notify_flags_t; /* notification flags */
typedef short notify_type_t; /* notification type */
typedef short notify_flags_t; /* notification flags */
typedef int notify_arg_t; /* notification argument */
struct notification {

View file

@ -325,11 +325,12 @@ PRIVATE void kenv_dmp()
printf("- bootdev_size: %5u\n", kinfo.bootdev_size);
printf("- params_base: %5u\n", kinfo.params_base);
printf("- params_size: %5u\n", kinfo.params_size);
printf("- notify_held: %8u\n", kinfo.notify_held);
printf("- notify_blocked:%8u\n", kinfo.notify_blocked);
printf("- notify_switch: %8u\n", kinfo.notify_switching);
printf("- notify_reenter:%8u\n", kinfo.notify_reenter);
printf("- notify_ok: %8u\n", kinfo.notify_ok);
printf("- notify_held: %8u\n", kinfo.notify_held);
printf("- notify_switch: %8u\n", kinfo.notify_switching);
printf("- notify_reenter:%8u\n", kinfo.notify_reenter);
printf("- notify_taskcall:%7u\n", kinfo.notify_taskcall);
printf("- notify_unhold: %8u\n", kinfo.notify_unhold);
printf("- hard_int: %8u\n", kinfo.notify_int);
printf("- hard_stop: %8u\n", kinfo.notify_stop);

View file

@ -28,7 +28,7 @@ $(ROOTOBJ):
rm a.out
clean:
@rm -f *.o *.s *.bak test? test?? t10a t11a t11b DIR*
rm -rf *.o *.s *.bak test? test?? t10a t11a t11b DIR*
test1: test1.c
test2: test2.c