mirror of
https://github.com/usatiuk/ficus.git
synced 2025-10-28 16:17:51 +01:00
Waitpid seems to be working
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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> ");
|
||||
|
||||
Reference in New Issue
Block a user