Update markdown and tabs and stuff
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
layout: post
|
||||
title: "Pipes"
|
||||
subtitle: "... and keyboard."
|
||||
tags: [osdev]
|
||||
tags: [osdev, filesystems]
|
||||
|
||||
In most unix-like systems, pipes can be used to make processes
|
||||
communicate with each other. To the processes the pipe looks just like
|
||||
@@ -33,51 +33,52 @@ Consumer/Reader:
|
||||
|
||||
In code this translates to
|
||||
|
||||
uint32_t pipe_write(INODE ino, void *buffer, uint32_t size, uint32_t offset)
|
||||
{
|
||||
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
|
||||
char *buf = (char *)buffer;
|
||||
uint32_t bytes_written = 0;
|
||||
while(bytes_written < size)
|
||||
{
|
||||
while((pipe->write_pos - pipe->read_pos) < pipe->size && bytes_written < size)
|
||||
{
|
||||
pipe->buffer[pipe->write_pos % pipe->size] = buf[bytes_written];
|
||||
bytes_written++;
|
||||
pipe->write_pos++;
|
||||
}
|
||||
scheduler_wake(&pipe->waiting);
|
||||
if(bytes_written < size)
|
||||
{
|
||||
scheduler_sleep(current, &pipe->waiting);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
uint32_t pipe_read(INODE ino, void *buffer, uint32_t size, uint32_t offset)
|
||||
{
|
||||
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
|
||||
char *buf = (char *)buffer;
|
||||
uint32_t bytes_read = 0;
|
||||
while(bytes_read == 0)
|
||||
{
|
||||
while((pipe->write_pos - pipe->read_pos) > 0 && bytes_read < size)
|
||||
{
|
||||
buf[bytes_read] = pipe->buffer[pipe->read_pos % pipe->size];
|
||||
bytes_read++;
|
||||
pipe->read_pos++;
|
||||
}
|
||||
scheduler_wake(&pipe->waiting);
|
||||
if(bytes_read == 0)
|
||||
{
|
||||
scheduler_sleep(current, &pipe->waiting);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
:::c
|
||||
uint32_t pipe_write(INODE ino, void *buffer, uint32_t size, uint32_t offset)
|
||||
{
|
||||
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
|
||||
char *buf = (char *)buffer;
|
||||
uint32_t bytes_written = 0;
|
||||
while(bytes_written < size)
|
||||
{
|
||||
while((pipe->write_pos - pipe->read_pos) < pipe->size && bytes_written < size)
|
||||
{
|
||||
pipe->buffer[pipe->write_pos % pipe->size] = buf[bytes_written];
|
||||
bytes_written++;
|
||||
pipe->write_pos++;
|
||||
}
|
||||
scheduler_wake(&pipe->waiting);
|
||||
if(bytes_written < size)
|
||||
{
|
||||
scheduler_sleep(current, &pipe->waiting);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
uint32_t pipe_read(INODE ino, void *buffer, uint32_t size, uint32_t offset)
|
||||
{
|
||||
vfs_pipe_t *pipe = (vfs_pipe_t *)ino->data;
|
||||
char *buf = (char *)buffer;
|
||||
uint32_t bytes_read = 0;
|
||||
while(bytes_read == 0)
|
||||
{
|
||||
while((pipe->write_pos - pipe->read_pos) > 0 && bytes_read < size)
|
||||
{
|
||||
buf[bytes_read] = pipe->buffer[pipe->read_pos % pipe->size];
|
||||
bytes_read++;
|
||||
pipe->read_pos++;
|
||||
}
|
||||
scheduler_wake(&pipe->waiting);
|
||||
if(bytes_read == 0)
|
||||
{
|
||||
scheduler_sleep(current, &pipe->waiting);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
Of course there should also be:
|
||||
|
||||
@@ -95,87 +96,91 @@ two separate inodes - one that can be written to and one that can be
|
||||
read from. The `data` field of the `vfs_node_st` struct of the two
|
||||
inodes point to the same pipe struct.
|
||||
|
||||
typedef struct vfs_pipe
|
||||
{
|
||||
char *buffer;
|
||||
uint32_t size;
|
||||
uint32_t read_pos;
|
||||
uint32_t write_pos;
|
||||
uint32_t readers;
|
||||
uint32_t writers;
|
||||
semaphore_t semaphore;
|
||||
list_head_t waiting;
|
||||
} vfs_pipe_t;
|
||||
:::c
|
||||
typedef struct vfs_pipe
|
||||
{
|
||||
char *buffer;
|
||||
uint32_t size;
|
||||
uint32_t read_pos;
|
||||
uint32_t write_pos;
|
||||
uint32_t readers;
|
||||
uint32_t writers;
|
||||
semaphore_t semaphore;
|
||||
list_head_t waiting;
|
||||
} vfs_pipe_t;
|
||||
|
||||
The `readers` and `writers` fields are incremented or decremented when the read or
|
||||
write end respectively are opened or closed... respectively...
|
||||
|
||||
Creating a new pipe (somewhat simplified):
|
||||
|
||||
uint32_t new_pipe(uint32_t size, INODE *nodes)
|
||||
{
|
||||
vfs_pipe_t *pipe = calloc(1, sizeof(vfs_pipe_t));
|
||||
pipe->buffer = malloc(size);
|
||||
pipe->size = size;
|
||||
:::c
|
||||
uint32_t new_pipe(uint32_t size, INODE *nodes)
|
||||
{
|
||||
vfs_pipe_t *pipe = calloc(1, sizeof(vfs_pipe_t));
|
||||
pipe->buffer = malloc(size);
|
||||
pipe->size = size;
|
||||
|
||||
nodes[0] = calloc(1, sizeof(vfs_node_t));
|
||||
nodes[0]->d = &pipe_driver;
|
||||
nodes[0]->data = pipe;
|
||||
nodes[0]->flags = PIPE_READ;
|
||||
nodes[0] = calloc(1, sizeof(vfs_node_t));
|
||||
nodes[0]->d = &pipe_driver;
|
||||
nodes[0]->data = pipe;
|
||||
nodes[0]->flags = PIPE_READ;
|
||||
|
||||
nodes[1] = calloc(1, sizeof(vfs_node_t));
|
||||
nodes[1]->d = &pipe_driver;
|
||||
nodes[1]->data = pipe;
|
||||
nodes[1]->flags = PIPE_WRITE;
|
||||
nodes[1] = calloc(1, sizeof(vfs_node_t));
|
||||
nodes[1]->d = &pipe_driver;
|
||||
nodes[1]->data = pipe;
|
||||
nodes[1]->flags = PIPE_WRITE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
###Using pipes
|
||||
When starting up the keyboard driver, a pipe is created and connected to
|
||||
`stdin` - the first file descriptor - of the current process.
|
||||
|
||||
void keyboard_init()
|
||||
{
|
||||
INODE tmp[2];
|
||||
new_pipe(1024, tmp);
|
||||
:::c
|
||||
void keyboard_init()
|
||||
{
|
||||
INODE tmp[2];
|
||||
new_pipe(1024, tmp);
|
||||
|
||||
keyboard_pipe = tmp[1];
|
||||
vfs_open(keyboard_pipe, O_WRONLY);
|
||||
keyboard_pipe = tmp[1];
|
||||
vfs_open(keyboard_pipe, O_WRONLY);
|
||||
|
||||
process_t *p = current->proc;
|
||||
p->fd[0] = calloc(1, sizeof(file_desc_t));
|
||||
fd_get(p->fd[0]);
|
||||
p->fd[0]->ino = tmp[0];
|
||||
p->fd[0]->flags = O_RDONLY;
|
||||
vfs_open(tmp[0], O_RDONLY);
|
||||
process_t *p = current->proc;
|
||||
p->fd[0] = calloc(1, sizeof(file_desc_t));
|
||||
fd_get(p->fd[0]);
|
||||
p->fd[0]->ino = tmp[0];
|
||||
p->fd[0]->flags = O_RDONLY;
|
||||
vfs_open(tmp[0], O_RDONLY);
|
||||
|
||||
register_int_handler(IRQ2INT(IRQ_KBD), keyboard_handler);
|
||||
}
|
||||
register_int_handler(IRQ2INT(IRQ_KBD), keyboard_handler);
|
||||
}
|
||||
|
||||
The keyboard handler (based off
|
||||
The keyboard handler (based off
|
||||
[Brandon Friesen's tutorial](http://www.osdever.net/bkerndev/Docs/keyboard.htm)
|
||||
writes each decoded key to the pipe:
|
||||
|
||||
registers_t *keyboard_handler(registers_t *r)
|
||||
{
|
||||
char code[2];
|
||||
|
||||
[...]
|
||||
|
||||
while(inb(KBD_STATUS_PORT) & 0x2);
|
||||
scancode = inb(KBD_DATA_PORT);
|
||||
code[0] = keyboard_decode(scancode);
|
||||
code[1] = '\0';
|
||||
if(code[0])
|
||||
{
|
||||
vfs_write(keyboard_pipe, (char *)code, 1, 0);
|
||||
}
|
||||
|
||||
[...]
|
||||
|
||||
}
|
||||
:::c
|
||||
registers_t *keyboard_handler(registers_t *r)
|
||||
{
|
||||
char code[2];
|
||||
|
||||
[...]
|
||||
|
||||
while(inb(KBD_STATUS_PORT) & 0x2);
|
||||
scancode = inb(KBD_DATA_PORT);
|
||||
code[0] = keyboard_decode(scancode);
|
||||
code[1] = '\0';
|
||||
if(code[0])
|
||||
{
|
||||
vfs_write(keyboard_pipe, (char *)code, 1, 0);
|
||||
}
|
||||
|
||||
[...]
|
||||
|
||||
}
|
||||
|
||||
At a later stage, I'll probably make the keyboard driver in the kernel
|
||||
just pass the scancodes to the pipe and have the decoding done by
|
||||
|
||||
Reference in New Issue
Block a user