Added pipe(2), write(2), and read(2).

This commit is contained in:
Jonas 'Sortie' Termansen 2011-11-16 08:37:29 +01:00
parent c5605b6693
commit a7de7b4905
18 changed files with 578 additions and 105 deletions

View File

@ -102,7 +102,6 @@ debsource: all
mkdir -p $(DEBSRCDIR)
for D in `ls | grep -v builds | grep -v sysroot`; do cp -r $$D $(DEBSRCDIR); done
(cd $(DEBSRCDIR) && make distclean)
rm $(DEBSRCDIR)/git-daemon-export-ok
rm -rf $(DEBSRCDIR)/sysroot
(cd builds && tar cfzv $(DEBSRCNAME)-src.tar.gz $(DEBSRCNAME)-src)
rm -rf $(DEBSRCDIR)

View File

@ -125,10 +125,8 @@ off_t lseek(int, off_t, int);
int nice(int);
long pathconf(const char*, int);
int pause(void);
int pipe(int [2]);
ssize_t pread(int, void*, size_t, off_t);
ssize_t pwrite(int, const void*, size_t, off_t);
ssize_t read(int, void*, size_t);
ssize_t readlink(const char* restrict, char* restrict, size_t);
ssize_t readlinkat(int, const char* restrict, char* restrict, size_t);
int rmdir(const char*);
@ -152,7 +150,6 @@ char* ttyname(int);
int ttyname_r(int, char*, size_t);
int unlink(const char*);
int unlinkat(int, const char*, int);
ssize_t write(int, const void*, size_t);
#if __POSIX_OBSOLETE <= 200801
pid_t setpgrp(void);
@ -166,10 +163,13 @@ void _exit(int);
pid_t fork(void);
pid_t getpid(void);
pid_t getppid(void);
int pipe(int [2]);
ssize_t read(int, void*, size_t);
unsigned sleep(unsigned);
#if __POSIX_OBSOLETE <= 200112
int usleep(useconds_t useconds);
#endif
ssize_t write(int, const void*, size_t);
__END_DECLS

View File

@ -46,6 +46,8 @@ namespace Maxsi
const int ENOTBLK = 12;
const int ENODEV = 13;
const int EWOULDBLOCK = 14;
const int EBADF = 15;
extern int _errornumber;

View File

@ -30,6 +30,9 @@
namespace Maxsi
{
DEFN_SYSCALL1(size_t, SysPrint, 4, const char*);
DEFN_SYSCALL3(ssize_t, SysRead, 18, int, void*, size_t);
DEFN_SYSCALL3(ssize_t, SysWrite, 19, int, const void*, size_t);
DEFN_SYSCALL1(int, SysPipe, 20, int*);
size_t Print(const char* Message)
{
@ -59,6 +62,22 @@ namespace Maxsi
va_end(list);
return (int) result;
}
extern "C" ssize_t read(int fd, void* buf, size_t count)
{
return SysRead(fd, buf, count);
}
extern "C" ssize_t write(int fd, const void* buf, size_t count)
{
return SysWrite(fd, buf, count);
}
extern "C" int pipe(int pipefd[2])
{
return SysPipe(pipefd);
}
#endif
}

View File

@ -69,6 +69,8 @@ elf.o \
process.o \
initrd.o \
thread.o \
io.o \
pipe.o \
../libmaxsi/libmaxsi-sortix.a
JSOBJS:=$(subst .o,-js.o,$(OBJS))

View File

@ -25,6 +25,7 @@
#include "platform.h"
#include <libmaxsi/memory.h>
#include "descriptors.h"
#include "device.h"
using namespace Maxsi;
@ -91,7 +92,7 @@ namespace Sortix
if ( devices[index] != reserveddevideptr )
{
// TODO: Unref device here?
devices[index]->Unref();
}
devices[index] = NULL;

View File

@ -49,7 +49,7 @@ namespace Sortix
public:
inline Device* Get(int index)
{
if ( numdevices <= index ) { return NULL; }
if ( index < 0 || numdevices <= index ) { return NULL; }
return devices[index];
}

View File

@ -28,29 +28,24 @@
namespace Sortix
{
bool Device::Close()
Device::Device()
{
_refCount--;
if ( _refCount == 0 )
{
delete this;
}
return true;
refcount = 0;
}
void Device::Think()
Device::~Device()
{
}
void Device::RequestThink()
void Device::Unref()
{
Think();
if ( --refcount == 0 ) { delete this; }
}
bool Device::Sync()
void Device::Refer()
{
return true;
refcount++;
}
}

View File

@ -27,61 +27,26 @@
namespace Sortix
{
class User;
class Device
{
public:
// Flags
static const nat READABLE = (1<<0);
static const nat WRITABLE = (1<<1);
static const nat SEEKABLE = (1<<2);
static const nat SIZEABLE = (1<<3);
static const nat BLOCK = (1<<4);
static const nat FLAGMASK = ((1<<5)-1);
// Types
static const nat TYPEMASK = ~FLAGMASK;
static const nat STREAM = (1<<5);
static const nat BUFFER = (2<<5);
static const nat DIRECTORY = (3<<5);
static const nat FILESYSTEM = (4<<5);
static const nat NETWORK = (5<<5);
static const nat SOUND = (6<<5);
static const nat GRAPHICS = (7<<5);
static const nat MOUSE = (8<<5);
static const nat KEYBOARD = (9<<5);
static const nat PRINTER = (10<<5);
static const nat SCANNER = (11<<5);
static const nat VGABUFFER = (12<<5);
static const nat OTHER = TYPEMASK;
static const unsigned STREAM = 0;
static const unsigned BUFFER = 1;
static const unsigned VGABUFFER = 2;
public:
volatile size_t _refCount;
public:
Device() { _refCount = 1; }
virtual ~Device() { }
Device();
virtual ~Device();
private:
User* _owner;
size_t refcount;
public:
bool IsOwner(User* candidate) { return candidate == _owner; }
void Refer();
void Unref();
public:
bool Close();
void RequestThink();
protected:
virtual void Think();
public:
virtual bool Sync();
virtual nat Flags() = 0;
public:
bool IsType(nat type) { return (Flags() & TYPEMASK) == type; }
virtual bool IsType(unsigned type) = 0;
};
}

126
sortix/io.cpp Normal file
View File

@ -0,0 +1,126 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
io.cpp
Provides system calls for input and output.
******************************************************************************/
#include "platform.h"
#include <libmaxsi/error.h>
#include "thread.h"
#include "process.h"
#include "device.h"
#include "stream.h"
#include "syscall.h"
#include "io.h"
using namespace Maxsi;
namespace Sortix
{
namespace IO
{
struct SysWrite_t
{
union { size_t align1; int fd; };
union { size_t align2; const byte* buffer; };
union { size_t align3; size_t count; };
};
STATIC_ASSERT(sizeof(SysWrite_t) <= sizeof(Thread::scstate));
ssize_t SysWrite(int fd, const byte* buffer, size_t count)
{
// TODO: Check that buffer is a valid user-space buffer.
if ( SSIZE_MAX < count ) { count = SSIZE_MAX; }
Process* process = CurrentProcess();
Device* dev = process->descriptors.Get(fd);
if ( !dev ) { return -1; /* TODO: EBADF */ }
if ( !dev->IsType(Device::STREAM) ) { return -1; /* TODO: EBADF */ }
DevStream* stream = (DevStream*) dev;
if ( !stream->IsWritable() ) { return -1; /* TODO: EBADF */ }
ssize_t written = stream->Write(buffer, count);
if ( 0 <= written ) { return written; }
if ( Error::Last() != Error::EWOULDBLOCK ) { return -1; /* TODO: errno */ }
// The stream will resume our system call once progress has been
// made. Our request is certainly not forgotten.
// Resume the system call with these parameters.
Thread* thread = CurrentThread();
thread->scfunc = (void*) SysWrite;
SysWrite_t* state = (SysWrite_t*) thread->scstate;
state->fd = fd;
state->buffer = buffer;
state->count = count;
thread->scsize = sizeof(SysWrite_t);
// Now go do something else.
Syscall::Incomplete();
return 0;
}
struct SysRead_t
{
union { size_t align1; int fd; };
union { size_t align2; byte* buffer; };
union { size_t align3; size_t count; };
};
STATIC_ASSERT(sizeof(SysRead_t) <= sizeof(Thread::scstate));
ssize_t SysRead(int fd, byte* buffer, size_t count)
{
// TODO: Check that buffer is a valid user-space buffer.
if ( SSIZE_MAX < count ) { count = SSIZE_MAX; }
Process* process = CurrentProcess();
Device* dev = process->descriptors.Get(fd);
if ( !dev ) { return -1; /* TODO: EBADF */ }
if ( !dev->IsType(Device::STREAM) ) { return -1; /* TODO: EBADF */ }
DevStream* stream = (DevStream*) dev;
if ( !stream->IsReadable() ) { return -1; /* TODO: EBADF */ }
ssize_t bytesread = stream->Read(buffer, count);
if ( 0 <= bytesread ) { return bytesread; }
if ( Error::Last() != Error::EWOULDBLOCK ) { return -1; /* TODO: errno */ }
// The stream will resume our system call once progress has been
// made. Our request is certainly not forgotten.
// Resume the system call with these parameters.
Thread* thread = CurrentThread();
thread->scfunc = (void*) SysRead;
SysRead_t* state = (SysRead_t*) thread->scstate;
state->fd = fd;
state->buffer = buffer;
state->count = count;
thread->scsize = sizeof(SysRead_t);
// Now go do something else.
Syscall::Incomplete();
return 0;
}
void Init()
{
Syscall::Register(SYSCALL_WRITE, (void*) SysWrite);
Syscall::Register(SYSCALL_READ, (void*) SysRead);
}
}
}

37
sortix/io.h Normal file
View File

@ -0,0 +1,37 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
io.h
Provides system calls for input and output.
******************************************************************************/
#ifndef SORTIX_IO_H
#define SORTIX_IO_H
namespace Sortix
{
namespace IO
{
void Init();
}
}
#endif

View File

@ -45,6 +45,8 @@
#include "initrd.h"
#include "vga.h"
#include "sound.h"
#include "io.h"
#include "pipe.h"
using namespace Maxsi;
@ -236,6 +238,12 @@ namespace Sortix
// Initialize the process system.
Process::Init();
// Initialize the IO system.
IO::Init();
// Initialize the pipe system.
Pipe::Init();
// Initialize the scheduler.
Scheduler::Init();

289
sortix/pipe.cpp Normal file
View File

@ -0,0 +1,289 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
pipe.cpp
A device with a writing end and a reading end.
******************************************************************************/
#include "platform.h"
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#include "thread.h"
#include "process.h"
#include "syscall.h"
#include "pipe.h"
using namespace Maxsi;
namespace Sortix
{
class DevPipeStorage : public DevStream
{
public:
typedef Device BaseClass;
public:
DevPipeStorage(byte* buffer, size_t buffersize);
~DevPipeStorage();
private:
byte* buffer;
size_t buffersize;
size_t bufferoffset;
size_t bufferused;
Thread* readwaiting;
Thread* writewaiting;
public:
virtual ssize_t Read(byte* dest, size_t count);
virtual ssize_t Write(const byte* src, size_t count);
virtual bool IsReadable();
virtual bool IsWritable();
};
DevPipeStorage::DevPipeStorage(byte* buffer, size_t buffersize)
{
this->buffer = buffer;
this->buffersize = buffersize;
this->bufferoffset = 0;
this->bufferused = 0;
this->readwaiting = NULL;
this->writewaiting = NULL;
}
DevPipeStorage::~DevPipeStorage()
{
ASSERT(!readwaiting);
ASSERT(!writewaiting);
delete[] buffer;
}
bool DevPipeStorage::IsReadable() { return true; }
bool DevPipeStorage::IsWritable() { return true; }
ssize_t DevPipeStorage::Read(byte* dest, size_t count)
{
if ( count == 0 ) { return 0; }
if ( bufferused )
{
if ( bufferused < count ) { count = bufferused; }
size_t amount = count;
size_t linear = buffersize - bufferused;
if ( linear < amount ) { amount = linear; }
Memory::Copy(dest, buffer + bufferoffset, amount);
bufferoffset = (bufferoffset + amount) % buffersize;
bufferused -= amount;
if ( writewaiting )
{
Syscall::ScheduleResumption(writewaiting);
writewaiting = NULL;
}
if ( bufferused == 0 || amount == count ) { return amount; }
return amount + Read(dest + amount, count - amount);
}
Error::Set(Error::EWOULDBLOCK);
// TODO: Only one thread can wait on a pipe at the same time.
ASSERT(readwaiting == NULL);
readwaiting = CurrentThread();
return -1;
}
ssize_t DevPipeStorage::Write(const byte* src, size_t count)
{
if ( count == 0 ) { return 0; }
if ( bufferused < buffersize )
{
if ( buffersize - bufferused < count ) { count = buffersize - bufferused; }
size_t writeoffset = (bufferoffset + bufferused) % buffersize;
size_t amount = count;
size_t linear = buffersize - writeoffset;
if ( linear < amount ) { amount = linear; }
Memory::Copy(buffer + writeoffset, src, amount);
bufferused += amount;
if ( readwaiting )
{
Syscall::ScheduleResumption(readwaiting);
readwaiting = NULL;
}
if ( buffersize == bufferused || amount == count ) { return amount; }
return amount + Write(src + amount, count - amount);
}
Error::Set(Error::EWOULDBLOCK);
// TODO: Only one thread can wait on a pipe at the same time.
ASSERT(writewaiting == NULL);
writewaiting = CurrentThread();
return -1;
}
class DevPipeReading : public DevStream
{
public:
typedef Device BaseClass;
public:
DevPipeReading(DevStream* stream);
~DevPipeReading();
private:
DevStream* stream;
public:
virtual ssize_t Read(byte* dest, size_t count);
virtual ssize_t Write(const byte* src, size_t count);
virtual bool IsReadable();
virtual bool IsWritable();
};
DevPipeReading::DevPipeReading(DevStream* stream)
{
stream->Refer();
this->stream = stream;
}
DevPipeReading::~DevPipeReading()
{
stream->Unref();
}
ssize_t DevPipeReading::Read(byte* dest, size_t count)
{
return stream->Read(dest, count);
}
ssize_t DevPipeReading::Write(const byte* /*src*/, size_t /*count*/)
{
Error::Set(Error::EBADF);
return -1;
}
bool DevPipeReading::IsReadable()
{
return true;
}
bool DevPipeReading::IsWritable()
{
return false;
}
class DevPipeWriting : public DevStream
{
public:
typedef Device BaseClass;
public:
DevPipeWriting(DevStream* stream);
~DevPipeWriting();
private:
DevStream* stream;
public:
virtual ssize_t Read(byte* dest, size_t count);
virtual ssize_t Write(const byte* src, size_t count);
virtual bool IsReadable();
virtual bool IsWritable();
};
DevPipeWriting::DevPipeWriting(DevStream* stream)
{
stream->Refer();
this->stream = stream;
}
DevPipeWriting::~DevPipeWriting()
{
stream->Unref();
}
ssize_t DevPipeWriting::Read(byte* /*dest*/, size_t /*count*/)
{
Error::Set(Error::EBADF);
return -1;
}
ssize_t DevPipeWriting::Write(const byte* src, size_t count)
{
return stream->Write(src, count);
}
bool DevPipeWriting::IsReadable()
{
return false;
}
bool DevPipeWriting::IsWritable()
{
return true;
}
namespace Pipe
{
const size_t BUFFER_SIZE = 4096UL;
int SysPipe(int pipefd[2])
{
// TODO: Validate that pipefd is a valid user-space array!
size_t buffersize = BUFFER_SIZE;
byte* buffer = new byte[buffersize];
if ( !buffer ) { return -1; /* TODO: ENOMEM */ }
// Transfer ownership of the buffer to the storage device.
DevStream* storage = new DevPipeStorage(buffer, buffersize);
if ( !storage ) { delete[] buffer; return -1; /* TODO: ENOMEM */ }
DevStream* reading = new DevPipeReading(storage);
if ( !reading ) { delete storage; return -1; /* TODO: ENOMEM */ }
DevStream* writing = new DevPipeWriting(storage);
if ( !writing ) { delete reading; return -1; /* TODO: ENOMEM */ }
Process* process = CurrentProcess();
int readfd = process->descriptors.Allocate(reading);
int writefd = process->descriptors.Allocate(writing);
if ( readfd < 0 || writefd < 0 )
{
if ( 0 <= readfd ) { process->descriptors.Free(readfd); } else { delete reading; }
if ( 0 <= writefd ) { process->descriptors.Free(writefd); } else { delete writing; }
return -1; /* TODO: ENOMEM/EMFILE/ENFILE */
}
pipefd[0] = readfd;
pipefd[1] = writefd;
return 0;
}
void Init()
{
Syscall::Register(SYSCALL_PIPE, (void*) SysPipe);
}
}
}

40
sortix/pipe.h Normal file
View File

@ -0,0 +1,40 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
pipe.h
A device with a writing end and a reading end.
******************************************************************************/
#ifndef SORTIX_PIPE_H
#define SORTIX_PIPE_H
#include "stream.h"
namespace Sortix
{
namespace Pipe
{
void Init();
}
}
#endif

View File

@ -25,61 +25,43 @@
#ifndef SORTIX_STREAM_H
#define SORTIX_STREAM_H
#include "device.h"
namespace Sortix
{
class Device
{
public:
Device() { };
virtual ~Device() { }
public:
virtual void close() = 0;
};
class DevStream : public Device
{
public:
DevStream() { };
virtual ~DevStream() { }
typedef Device BaseClass;
public:
virtual size_t write(const void* buffer, size_t bufferSize) = 0;
virtual size_t readSome(const void* buffer, size_t bufferSize) = 0;
virtual bool IsType(unsigned type) { return type == Device::STREAM; }
public:
inline size_t read(const void* buffer, size_t bufferSize)
{
const uint8_t* bytes = (uint8_t*) buffer;
size_t total = bufferSize;
while ( BufferSize > 0 )
{
size_t got = readSome(bytes, bufferSize);
if ( got == SIZE_MAX ) { return SIZE_MAX; }
bytes += got;
bufferSize -= got;
}
return total;
}
virtual ssize_t Read(byte* dest, size_t count) = 0;
virtual ssize_t Write(const byte* src, size_t count) = 0;
virtual bool IsReadable() = 0;
virtual bool IsWritable() = 0;
};
class DevBuffer : public DevStream
{
public:
DevBuffer() { };
virtual ~DevBuffer() { }
typedef Device BaseClass;
public:
virtual size_t write(const void* buffer, size_t bufferSize) = 0;
virtual size_t readSome(const void* buffer, size_t bufferSize) = 0;
virtual bool IsType(unsigned type)
{
return type == Device::BUFFER || BaseClass::IsType(type);
}
public:
virtual size_t blockSize() = 0;
virtual intmax_t size() = 0;
virtual intmax_t position() = 0;
virtual bool seek(intmax_t position) = 0;
virtual bool resize(intmax_t size) = 0;
virtual size_t BlockSize() = 0;
virtual uintmax_t Size() = 0;
virtual uintmax_t Position() = 0;
virtual bool Seek(uintmax_t position) = 0;
virtual bool Resize(uintmax_t size) = 0;
};
}

View File

@ -43,7 +43,10 @@
#define SYSCALL_GET_FILEINFO 15
#define SYSCALL_GET_NUM_FILES 16
#define SYSCALL_WAIT 17
#define SYSCALL_MAX_NUM 18 /* index of highest constant + 1 */
#define SYSCALL_READ 18
#define SYSCALL_WRITE 19
#define SYSCALL_PIPE 20
#define SYSCALL_MAX_NUM 21 /* index of highest constant + 1 */
#endif

View File

@ -109,6 +109,8 @@ namespace Sortix
frame->physical = page;
frame->userframe = userframe;
frame->Refer();
return mapto;
}
@ -185,7 +187,7 @@ namespace Sortix
process->descriptors.Free(fd);
if ( device == NULL ) { return -1; }
if ( !device->Close() ) { return -1; }
device->Unref();
return 0;
}
@ -206,5 +208,8 @@ namespace Sortix
if ( physical != 0 ) { Page::Put(physical); }
}
nat DevVGAFrame::Flags() { return Device::VGABUFFER; }
bool DevVGAFrame::IsType(unsigned type)
{
return type == Device::VGABUFFER;
}
}

View File

@ -71,13 +71,13 @@ namespace Sortix
class DevVGAFrame : public Device
{
public:
virtual nat Flags();
public:
DevVGAFrame();
~DevVGAFrame();
public:
virtual bool IsType(unsigned type);
public:
Process* process;
addr_t physical;