É o kernel. Tenha em mente que o teclado é hardware e tudo o que acontece lá passa pelo kernel; no caso da comutação VT, ele lida com o evento completamente e não passa nada para o userspace (no entanto, acredito que haja um meio relacionado ao ioctl pelo qual os programas userspace podem ser notificados de um switch que os envolva e talvez o afete, que X, sem dúvida, faz).
O kernel tem um keymap embutido nele; isso pode ser modificado durante a execução com loadkeys
e visualizado com dumpkeys
:
[...]
keycode 59 = F1 F13 Console_13 F25
alt keycode 59 = Console_1
control alt keycode 59 = Console_1
keycode 60 = F2 F14 Console_14 F26
alt keycode 60 = Console_2
control alt keycode 60 = Console_2
keycode 61 = F3 F15 Console_15 F27
alt keycode 61 = Console_3
control alt keycode 61 = Console_3
[...]
O código-fonte do kernel contém um arquivo de mapa de chaves padrão que é exatamente assim; para 3.12.2 é src/drivers/tty/vt/defkeymap.map
. Você também notará que existe um arquivo defkeymap.c correspondente (isso pode ser gerado com loadkeys --mktable
). A manipulação está em keyboard.c
(todos esses arquivos estão em o mesmo diretório) que chama set_console()
de vt.c
:
» grep set_console *.c
keyboard.c: set_console(last_console);
keyboard.c: set_console(i);
keyboard.c: set_console(i);
keyboard.c: set_console(value);
vt.c:int set_console(int nr)
vt_ioctl.c: set_console(arg);
Eu editei alguns hits dessa lista; você pode ver a assinatura da função na segunda última linha.
Então, essas são as coisas envolvidas na troca. Se você olhar para a sequência de chamadas, eventualmente você volta para kbd_event()
em keyboard.c
. Isso é registrado como um manipulador de eventos para o módulo:
(3.12.2 drivers/tty/vt/keyboard.c
line 1473)
MODULE_DEVICE_TABLE(input, kbd_ids);
static struct input_handler kbd_handler = {
.event = kbd_event, <--- function pointer HERE
.match = kbd_match,
.connect = kbd_connect,
.disconnect = kbd_disconnect,
.start = kbd_start,
.name = "kbd",
.id_table = kbd_ids,
};
int __init kbd_init(void)
{
[...]
error = input_register_handler(&kbd_handler);
Portanto, kbd_event()
deve ser chamado quando algo aparecer no driver de hardware real (provavelmente algo de drivers/hid/
ou drivers/input/
). No entanto, você não o verá como kbd_event
fora desse arquivo, já que está registrado por meio de um ponteiro de função.
Alguns recursos para investigar o kernel
- A Busca de identificadores de referência cruzada do Linux é uma ótima ferramenta.
- O Mapa interativo do kernel do Linux é um front end gráfico interessante para a ferramenta de referência cruzada.
- Existem alguns arquivos históricos da enorme LKML (Linux Kernel Mailing List), que remonta pelo menos a 1995; alguns deles não são mantidos e quebraram os recursos de pesquisa, mas o o gmane parece funcionar muito bem. As pessoas fizeram muitas perguntas na lista de e-mail e também é o principal meio de comunicação entre os desenvolvedores.
- Você pode injetar suas próprias linhas
printk
na origem como um meio simples de rastreio (nem todo o C lib padrão pode ser usado no código do kernel, incluindo printf do stdio). O material do printk acaba no syslog.
Wolfgang Mauerer escreveu um grande livro sobre o kernel 2.6, Arquitetura Profissional Linux Kernel , que passa por grande parte da fonte. Greg Kroah-Hartman , um dos principais desenvolvedores da última década, também tem muitas coisas por aí.