Waitpid seems to be working

This commit is contained in:
2024-04-14 13:08:08 +02:00
parent 4e7989e2a9
commit 180cdd7993
7 changed files with 206 additions and 28 deletions

View File

@@ -30,9 +30,9 @@ extern "C" {
#define SYSCALL_EXECVE_ID 50
#define SYSCALL_FORK_ID 51
#define SYSCALL_WAITPID_ID 52
#define SYSCALL_SBRK_ID 100
#define SYSCALL_PRINT_MEM 1000
#define SYSCALL_PRINT_TASKS 1001

View File

@@ -8,6 +8,7 @@
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/wait.h>
uint64_t _do_syscall(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_rdx, uint64_t a3_rcx) {
register uint64_t res asm("rax");
@@ -92,6 +93,11 @@ int _unlink(char *name) {
}
int _wait(int *status) {
return waitpid(-1, &status, 0);
}
pid_t waitpid(pid_t pid, int *status, int options) {
return _do_syscall(SYSCALL_WAITPID_ID, (uint64_t) pid, (uint64_t) status, (uint64_t) options);
}
int _write(int file, char *ptr, int len) {

View File

@@ -110,15 +110,15 @@ uint64_t syscall_lseek(uint64_t fd, uint64_t off, uint64_t whence) {
}
uint64_t syscall_print_tasks() {
static SkipListMap<uint64_t, std::pair<String, uint64_t>> last_times = Scheduler::getTaskTimePerPid();
static std::atomic<uint64_t> last_print_time = micros;
static SkipListMap<pid_t, std::pair<String, uint64_t>> last_times = Scheduler::getTaskTimePerPid();
static std::atomic<uint64_t> last_print_time = micros;
uint64_t prev_print_time = last_print_time;
last_print_time = micros;
SkipListMap<uint64_t, std::pair<String, uint64_t>> prev_times = std::move(last_times);
last_times = Scheduler::getTaskTimePerPid();
uint64_t prev_print_time = last_print_time;
last_print_time = micros;
SkipListMap<pid_t, std::pair<String, uint64_t>> prev_times = std::move(last_times);
last_times = Scheduler::getTaskTimePerPid();
uint64_t slice = last_print_time - prev_print_time;
uint64_t slice = last_print_time - prev_print_time;
if (slice == 0) return 0;
for (const auto &t: prev_times) {
@@ -127,7 +127,7 @@ uint64_t syscall_print_tasks() {
assert(f->second.second >= t.second.second);
String buf;
buf += "PID: ";
buf += t.first;
buf += (unsigned long) t.first;
buf += " ";
buf += t.second.first;
buf += " usage: ";
@@ -137,7 +137,7 @@ uint64_t syscall_print_tasks() {
} else {
String buf;
buf += "PID: ";
buf += t.first;
buf += (unsigned long) t.first;
buf += " ";
buf += t.second.first;
buf += " dead \n";
@@ -218,6 +218,10 @@ char *syscall_sbrk(int brk) {
return ret;
}
pid_t syscall_waitpid(pid_t pid, int *status, int options) {
return Scheduler::waitpid(pid, status, options);
}
int64_t syscall_getdents(int fd, struct dirent *dp, int count) {
auto f = FDT::current()->get(fd);
if (!f) return -1;
@@ -282,6 +286,8 @@ extern "C" uint64_t syscall_impl(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_r
return syscall_fork();
case SYSCALL_EXECVE_ID:
return syscall_execve(reinterpret_cast<const char *>(a1_rsi), reinterpret_cast<char *const *>(a2_rdx), reinterpret_cast<char *const *>(a3_rcx));
case SYSCALL_WAITPID_ID:
return syscall_waitpid(static_cast<pid_t>(a1_rsi), reinterpret_cast<int *>(a2_rdx), static_cast<int>(a3_rcx));
case SYSCALL_SBRK_ID:
return reinterpret_cast<uint64_t>(syscall_sbrk(static_cast<int64_t>(a1_rsi)));
case SYSCALL_OPENDIR_ID:

View File

@@ -58,26 +58,130 @@ Mutex WaitingTasks_mlock;
CV WaitingTasks_cv;
SkipListMap<uint64_t, Vector<List<Task *>::Node *>> WaitingTasks;
static std::atomic<bool> initialized = false;
static std::atomic<bool> initialized = false;
//
void Scheduler::remove_self() {
assert(RunningTask != nullptr);
void Scheduler::dispose_self() {
{
LockGuard l(TasksToFree_lock);
// TasksToFree is expected to do nothing with TS_RUNNING tasks
TasksToFree.emplace_front(RunningTask);
// This might not cause freeing of this task, as it might be preempted
// and still be running and task freer won't delete it
// But eventually it will get cleaned
TasksToFree_cv.notify_one();
}
// This might not cause freeing of this task, as it might be preempted
// and still be running and task freer won't delete it
// But eventually it will get cleaned
TasksToFree_cv.notify_one();
Scheduler::self_block();
assert(false);
}
//
void Scheduler::zombify_self() {
NO_INT(
{
{
SpinlockLockNoInt l(NextTasks_lock);
RunningTask->val->_state = Task::TaskState::TS_ZOMBIE;
RunningTask->val->_waitpid_node = extract_running_task_node();
AllTasks_lock.unlock_nolock();
}
Scheduler::yield_self();
})
}
void Scheduler::remove_self() {
assert(RunningTask != nullptr);
if (cur_task()->_parent == -1) {
dispose_self();
} else {
AllTasks_lock.lock();
if (auto foundp = AllTasks.find(cur_task()->_parent); foundp != AllTasks.end()) {
if (foundp->second->state() == Task::TaskState::TS_WAITPID_BLOCKED &&
(foundp->second->_woken_pid == cur_task()->_pid || foundp->second->_woken_pid == -1)) {
// Parent found and waiting, notify and wake
SpinlockLockNoInt l(NextTasks_lock);
foundp->second->_woken_pid = cur_task()->_pid;
foundp->second->_state = Task::TaskState::TS_RUNNING;
List<Task *>::Node *node = nullptr;
std::swap(foundp->second->_waitpid_node, node);
NextTasks.emplace_front(node);
}
zombify_self();
} else {
// Parent not found!
AllTasks_lock.unlock();
dispose_self();
}
}
assert2(0, "should be removed!");
}
void Scheduler::waitpid_block() {
NO_INT(
{
{
SpinlockLockNoInt l(NextTasks_lock);
RunningTask->val->_state = Task::TaskState::TS_WAITPID_BLOCKED;
RunningTask->val->_waitpid_node = extract_running_task_node();
AllTasks_lock.unlock_nolock();
}
Scheduler::yield_self();
})
AllTasks_lock.lock();
}
void Scheduler::dispose_zombie(Task *zombie) {
LockGuard l(TasksToFree_lock);
zombie->_state = Task::TaskState::TS_BLOCKED;
List<Task *>::Node *node = nullptr;
std::swap(zombie->_waitpid_node, node);
TasksToFree.emplace_front(node);
TasksToFree_cv.notify_one();
}
pid_t Scheduler::waitpid(pid_t pid, int *status, int options) {
AllTasks_lock.lock();
if (pid != -1) {
// Wait for specific thing
if (auto found = AllTasks.find(pid); found != AllTasks.end()) {
if (found->second->_parent != cur_task()->_pid) return -1; // Task not our child
if (found->second->state() != Task::TaskState::TS_ZOMBIE) { // Already dead
cur_task()->_woken_pid = pid;
waitpid_block();
assert(cur_task()->_woken_pid == pid);
}
dispose_zombie(found->second.get());
AllTasks_lock.unlock();
return pid;
} else {
AllTasks_lock.unlock();
return -1; // Task not found
}
} else {
// Wait for anything
pid_t found_zombie = -1;
for (const auto &t: AllTasks) { // Check if we have anything already dead, FIXME: it's slow
if (t.second->_parent == cur_task()->_pid && t.second->state() == Task::TaskState::TS_ZOMBIE) {
found_zombie = t.first;
break;
}
}
if (found_zombie == -1) {
cur_task()->_woken_pid = -1;
waitpid_block();
found_zombie = cur_task()->_woken_pid;
}
auto found = AllTasks.find(found_zombie);
AllTasks_lock.unlock();
dispose_zombie(found->second.get());
return found_zombie;
}
assert(false);
}
static void trampoline(void *rdi, void (*rsi_entrypoint)()) {
rsi_entrypoint();
Scheduler::remove_self();
@@ -157,8 +261,8 @@ Task::~Task() {
assert(_state != TaskState::TS_RUNNING);
}
SkipListMap<uint64_t, std::pair<String, Task::TaskPID>> Scheduler::getTaskTimePerPid() {
SkipListMap<uint64_t, std::pair<String, Task::TaskPID>> ret;
SkipListMap<pid_t, std::pair<String, uint64_t>> Scheduler::getTaskTimePerPid() {
SkipListMap<pid_t, std::pair<String, uint64_t>> ret;
{
LockGuard l(AllTasks_lock);
for (const auto &t: AllTasks) {
@@ -291,6 +395,7 @@ Task *Task::clone() {
ret->_frame.ss = Arch::GDT::gdt_data.selector();
ret->_frame.cs = Arch::GDT::gdt_code.selector();
ret->_frame.sp = ret->_entry_ksp_val;
ret->_parent = Scheduler::cur_task()->_pid;
return ret;
}

View File

@@ -12,6 +12,8 @@
#include "idt.hpp"
#include "task_arch.hpp"
#include <sys/types.h>
#define TASK_SS 65536
class Mutex;
@@ -21,8 +23,6 @@ class Spinlock;
class Task {
public:
using TaskPID = uint64_t;
enum class TaskMode {
TASKMODE_KERN,
TASKMODE_USER
@@ -30,9 +30,17 @@ public:
enum class TaskState {
TS_RUNNING,
TS_BLOCKED
TS_BLOCKED,
TS_ZOMBIE,
TS_WAITPID_BLOCKED
};
/// PID of processor that we're waiting for
/// or -1, wait for anything, then it's set by the zombified process to its PID
pid_t _woken_pid = -1;
/// Place to put list node when waiting for pid/being zombified
List<Task *>::Node *_waitpid_node;
Task(TaskMode mode, void (*entrypoint)(), const char *name);
Task(const Task &) = delete;
@@ -43,7 +51,7 @@ public:
void start();
[[nodiscard]] const String &name() const { return _name; }
[[nodiscard]] TaskPID pid() const { return _pid; }
[[nodiscard]] pid_t pid() const { return _pid; }
[[nodiscard]] uint64_t used_time() const { return _used_time; }
[[nodiscard]] TaskState state() const { return _state; }
@@ -62,7 +70,7 @@ public:
uint64_t _entry_ksp_val;
Arch::TaskFrame _frame;
TaskPID _pid;
pid_t _pid;
std::atomic<uint64_t> _used_time;
// Note that address space must be destroyed after VMA!
@@ -77,6 +85,7 @@ public:
TaskMode _mode;
uint64_t _sleep_until;
TaskState _state;
pid_t _parent = -1;
};
@@ -97,6 +106,9 @@ namespace Scheduler {
void sleep_self(uint64_t diff);
void remove_self();
void dispose_self();
void zombify_self();
void dispose_zombie(Task *zombie);
void self_block();
void self_block(Spinlock &to_unlock);
@@ -106,12 +118,15 @@ namespace Scheduler {
void unblock(List<Task *>::Node *what);
void unblock_nolock(List<Task *>::Node *what);
void waitpid_block();
pid_t waitpid(pid_t pid, int *status, int options);
extern "C" void switch_task(Arch::TaskFrame *cur_frame);
// TODO: that's quite inefficient!
SkipListMap<uint64_t, std::pair<String, Task::TaskPID>> getTaskTimePerPid();
SkipListMap<pid_t, std::pair<String, uint64_t>> getTaskTimePerPid();
void yield_self();
void yield_self();
} // namespace Scheduler
// Expects the caller to save interrupt state

View File

@@ -1,12 +1,53 @@
#include <stdio.h>
#include <sys/unistd.h>
#include <sys/wait.h>
int main() {
printf("Hi!\n");
if (fork() == 0) {
printf("I'm a fork!\n");
sleep(100);
printf("I'm exiting!\n");
return 1;
} else {
printf("Forked!\n");
wait(NULL);
printf("Fork exited!\n");
}
if (fork() == 0) {
printf("I'm a fork!\n");
printf("I'm exiting!\n");
return 1;
} else {
printf("Forked!\n");
sleep(100);
wait(NULL);
printf("Fork exited!\n");
}
pid_t forkpid = fork();
if (forkpid == 0) {
printf("I'm a fork!\n");
printf("I'm exiting!\n");
return 1;
} else {
printf("Forked!\n");
sleep(100);
waitpid(forkpid, NULL, 0);
printf("Fork exited!\n");
}
forkpid = fork();
if (forkpid == 0) {
printf("I'm a fork!\n");
sleep(100);
printf("I'm exiting!\n");
return 1;
} else {
printf("Forked!\n");
waitpid(forkpid, NULL, 0);
printf("Fork exited!\n");
}
}

View File

@@ -32,6 +32,11 @@ int main() {
close(test123);
printf("\n %s \n", buf);
}
while (1) {
execve("hello2", 0, 0);
print_mem();
sleep(500);
}
while (1) {
printf("\n> ");