I suggest using functions like _go32_xxx to handle interrupt ISRs written in C. The DJGPP FAQ explains it very clearly.
As for chain, it should be avoided, because it will call the old ISR. If there are real mode instructions in it, a mode switch will occur, which greatly affects efficiency.
The variables in the ISR and the code segment of the ISR itself should both be locked. Usually there are macros like these:
#define END_FUNCTION(x) void x##_end(){}
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void *)&x, (long)sizeof(x));
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x, (long)x##_end - (long)x);
...
void my_isr(void)
{
...
outp(0x20, 0x20);
}
END_FUNCTION(my_isr);
...
Interrupt initialization part:
_go32_dpmi_seginfo OldISR, NewISR;
LOCK_FUNCTION(my_isr);
LOCK_VARIABLE(my_variable);
...
_go32_dpmi_get_protected_mode_interrupt_vector(IRQNum, &OldISR);
NewISR.pm_offset = (int)my_isr;
NewISR.pm_selector = _go32_my_cs();
_go32_dpmi_set_protected_mode_interrupt_vector(IRQNum,&NewISR);
//also set the interrupt mask, outp(0x21, inp(0x21) & xxH);
...
// main loop here
...
_go32_dpmi_set_protected_mode_interrupt_vector(IRQNum, &OldISR);
You should avoid mode switching as much as possible, so avoid directly or indirectly calling DOS/BIOS functions.