Basic ^C interrupt implementation.

For testing, I made cat put itself into a new process group.
This should eventually be done by sh between fork() and exec().
This commit is contained in:
Andreas Kling 2018-11-02 14:06:48 +01:00
parent 621217ffeb
commit 10b666f69a
Notes: sideshowbarker 2024-07-19 18:34:25 +09:00
11 changed files with 94 additions and 38 deletions

View file

@ -40,9 +40,12 @@ static char shift_map[0x100] =
void Keyboard::emit(byte ch)
{
Key key;
key.character = ch;
key.modifiers = m_modifiers;
if (m_client)
m_client->onKeyPress(ch);
m_queue.enqueue(ch);
m_client->onKeyPress(key);
m_queue.enqueue(key);
}
void Keyboard::handleIRQ()
@ -50,12 +53,12 @@ void Keyboard::handleIRQ()
while (IO::in8(0x64) & 1) {
BYTE ch = IO::in8(0x60);
switch (ch) {
case 0x38: m_modifiers |= MOD_ALT; break;
case 0xB8: m_modifiers &= ~MOD_ALT; break;
case 0x1D: m_modifiers |= MOD_CTRL; break;
case 0x9D: m_modifiers &= ~MOD_CTRL; break;
case 0x2A: m_modifiers |= MOD_SHIFT; break;
case 0xAA: m_modifiers &= ~MOD_SHIFT; break;
case 0x38: m_modifiers |= Mod_Alt; break;
case 0xB8: m_modifiers &= ~Mod_Alt; break;
case 0x1D: m_modifiers |= Mod_Ctrl; break;
case 0x9D: m_modifiers &= ~Mod_Ctrl; break;
case 0x2A: m_modifiers |= Mod_Shift; break;
case 0xAA: m_modifiers &= ~Mod_Shift; break;
case 0x1C: /* enter */ emit('\n'); break;
case 0xFA: /* i8042 ack */ break;
default:
@ -75,16 +78,13 @@ void Keyboard::handleIRQ()
break;
}
}
if (!m_modifiers) {
if (!m_modifiers)
emit(map[ch]);
} else if (m_modifiers & MOD_SHIFT) {
else if (m_modifiers & Mod_Shift)
emit(shift_map[ch]);
} else if (m_modifiers & MOD_CTRL) {
// FIXME: This is obviously not a good enough way to process ctrl+whatever.
emit('^');
else if (m_modifiers & Mod_Ctrl)
emit(shift_map[ch]);
}
}
//break;
}
}
@ -127,7 +127,7 @@ ssize_t Keyboard::read(byte* buffer, size_t size)
while (nread < size) {
if (m_queue.isEmpty())
break;
buffer[nread++] = m_queue.dequeue();
buffer[nread++] = m_queue.dequeue().character;
}
return nread;
}

View file

@ -6,15 +6,25 @@
#include <VirtualFileSystem/CharacterDevice.h>
#include "IRQHandler.h"
class KeyboardClient {
public:
virtual ~KeyboardClient();
virtual void onKeyPress(byte) = 0;
};
class KeyboardClient;
class Keyboard final : public IRQHandler, public CharacterDevice {
AK_MAKE_ETERNAL
public:
enum Modifier {
Mod_Alt = 0x01,
Mod_Ctrl = 0x02,
Mod_Shift = 0x04,
};
struct Key {
byte character { 0 };
byte modifiers { 0 };
bool alt() { return modifiers & Mod_Alt; }
bool ctrl() { return modifiers & Mod_Ctrl; }
bool shift() { return modifiers & Mod_Shift; }
};
static Keyboard& the() PURE;
virtual ~Keyboard() override;
@ -34,7 +44,12 @@ private:
void emit(byte);
KeyboardClient* m_client { nullptr };
CircularQueue<byte, 16> m_queue;
CircularQueue<Key, 16> m_queue;
byte m_modifiers { 0 };
};
class KeyboardClient {
public:
virtual ~KeyboardClient();
virtual void onKeyPress(Keyboard::Key) = 0;
};

View file

@ -139,6 +139,15 @@ static void forEachProcess(Callback callback)
}
}
void Process::for_each_in_pgrp(pid_t pgid, Function<void(Process&)> callback)
{
ASSERT_INTERRUPTS_DISABLED();
for (auto* process = s_processes->head(); process; process = process->next()) {
if (process->pgid() == pgid)
callback(*process);
}
}
Vector<Process*> Process::allProcesses()
{
InterruptDisabler disabler;
@ -575,10 +584,10 @@ void Process::sys$exit(int status)
switchNow();
}
void Process::murder(int signal)
void Process::send_signal(int signal, Process* sender)
{
ASSERT_INTERRUPTS_DISABLED();
bool wasCurrent = current == this;
bool wasCurrent = current == sender;
set_state(Exiting);
s_processes->remove(this);
@ -587,7 +596,7 @@ void Process::murder(int signal)
if (wasCurrent) {
kprintf("Current process committing suicide!\n");
if (!scheduleNewProcess()) {
kprintf("Process::murder: Failed to schedule a new process :(\n");
kprintf("Process::send_signal: Failed to schedule a new process :(\n");
HANG;
}
}
@ -1014,9 +1023,8 @@ int Process::sys$uname(utsname* buf)
return 0;
}
int Process::sys$kill(pid_t pid, int sig)
int Process::sys$kill(pid_t pid, int signal)
{
(void) sig;
if (pid == 0) {
// FIXME: Send to same-group processes.
ASSERT(pid != 0);
@ -1030,13 +1038,8 @@ int Process::sys$kill(pid_t pid, int sig)
auto* peer = Process::fromPID(pid);
if (!peer)
return -ESRCH;
if (sig == SIGKILL) {
peer->murder(SIGKILL);
return 0;
} else {
ASSERT_NOT_REACHED();
}
return -1;
peer->send_signal(signal, this);
return 0;
}
int Process::sys$sleep(unsigned seconds)

View file

@ -73,6 +73,8 @@ public:
void setWakeupTime(DWORD t) { m_wakeupTime = t; }
DWORD wakeupTime() const { return m_wakeupTime; }
static void for_each_in_pgrp(pid_t pgid, Function<void(Process&)>);
static void prepForIRETToNewProcess();
bool tick() { ++m_ticks; return --m_ticksLeft; }
@ -151,6 +153,8 @@ public:
FileHandle* file_descriptor(size_t i) { return m_file_descriptors[i].ptr(); }
const FileHandle* file_descriptor(size_t i) const { return m_file_descriptors[i].ptr(); }
void send_signal(int signal, Process* sender);
private:
friend class MemoryManager;
friend bool scheduleNewProcess();
@ -211,7 +215,6 @@ private:
pid_t m_parentPID { 0 };
static void notify_waiters(pid_t waitee, int exit_status, int signal);
void murder(int signal);
Vector<String> m_arguments;
Vector<String> m_initialEnvironment;

View file

@ -1,4 +1,5 @@
#include "TTY.h"
#include "Process.h"
TTY::TTY(unsigned major, unsigned minor)
: CharacterDevice(major, minor)
@ -38,3 +39,16 @@ void TTY::emit(byte ch)
{
m_buffer.append(ch);
}
void TTY::interrupt()
{
dbgprintf("%s: Interrupt ^C pressed!\n", ttyName().characters());
if (pgid()) {
dbgprintf("%s: Send SIGINT to everyone in pgrp %d\n", ttyName().characters(), pgid());
InterruptDisabler disabler;
Process::for_each_in_pgrp(pgid(), [this] (auto& process) {
dbgprintf("%s: Send SIGINT to %d\n", ttyName().characters(), process.pid());
process.send_signal(SIGINT, nullptr);
});
}
}

View file

@ -23,6 +23,8 @@ protected:
virtual void onTTYWrite(byte) = 0;
void interrupt();
private:
Vector<byte> m_buffer;
pid_t m_pgid { 0 };

View file

@ -384,9 +384,15 @@ void VirtualConsole::on_char(byte ch, bool shouldEmit)
set_cursor(m_cursor_row, m_cursor_column);
}
void VirtualConsole::onKeyPress(byte ch)
void VirtualConsole::onKeyPress(Keyboard::Key key)
{
emit(ch);
if (key.ctrl() && key.character == 'C') {
interrupt();
return;
}
if (key.ctrl())
emit('^');
emit(key.character);
}
void VirtualConsole::onConsoleReceive(byte ch)

View file

@ -17,7 +17,7 @@ public:
private:
// ^KeyboardClient
virtual void onKeyPress(byte) override;
virtual void onKeyPress(Keyboard::Key) override;
// ^ConsoleImplementation
virtual void onConsoleReceive(byte) override;

View file

@ -39,6 +39,11 @@ off_t lseek(int fd, off_t, int whence);
#define WIFEXITED(status) (WTERMSIG(status) == 0)
#define WIFSIGNALED(status) (((char) (((status) & 0x7f) + 1) >> 1) > 0)
#define SIGINT 2
#define SIGKILL 9
#define SIGSEGV 11
#define SIGTERM 15
#define HOST_NAME_MAX 64
#define S_IFMT 0170000

View file

@ -5,6 +5,7 @@
int main(int argc, char** argv)
{
setpgid(0, 0);
if (argc != 2) {
printf("usage: cat <file>\n");
return 1;

View file

@ -157,7 +157,14 @@ static int runcmd(char* cmd)
//printf("Exited normally with status %d\n", WEXITSTATUS(wstatus));
} else {
if (WIFSIGNALED(wstatus)) {
printf("Terminated by signal %d\n", WTERMSIG(wstatus));
switch (WTERMSIG(wstatus)) {
case SIGINT:
printf("Interrupted\n");
break;
default:
printf("Terminated by signal %d\n", WTERMSIG(wstatus));
break;
}
} else {
printf("Exited abnormally\n");
}