Compare commits
8 Commits
f15f88ebd6
...
ac9dbd24e4
Author | SHA1 | Date |
---|---|---|
|
ac9dbd24e4 | |
|
3af9830a2e | |
|
da6b8eb2ab | |
|
da39e98adf | |
|
791a541381 | |
|
56684e753b | |
|
c7298edf65 | |
|
30215963b2 |
|
@ -22,6 +22,7 @@ namespace Kernel
|
|||
|
||||
void add_inode(BAN::StringView path, BAN::RefPtr<TmpInode>);
|
||||
|
||||
void initiate_disk_cache_drop();
|
||||
void initiate_sync(bool should_block);
|
||||
|
||||
private:
|
||||
|
@ -37,6 +38,10 @@ namespace Kernel
|
|||
ThreadBlocker m_sync_done;
|
||||
ThreadBlocker m_sync_thread_blocker;
|
||||
volatile bool m_should_sync { false };
|
||||
|
||||
SpinLock m_disk_cache_lock;
|
||||
ThreadBlocker m_disk_cache_thread_blocker;
|
||||
BAN::Atomic<bool> m_should_drop_disk_cache { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -57,6 +57,35 @@ namespace Kernel
|
|||
size_t (Process::*m_callback)(off_t, BAN::ByteSpan) const;
|
||||
};
|
||||
|
||||
class ProcSymlinkProcessInode final : public TmpInode
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<ProcSymlinkProcessInode>> create_new(Process& process, BAN::ErrorOr<BAN::String> (Process::*callback)() const, TmpFileSystem&, mode_t);
|
||||
~ProcSymlinkProcessInode() = default;
|
||||
|
||||
virtual uid_t uid() const override { return m_process.credentials().ruid(); }
|
||||
virtual gid_t gid() const override { return m_process.credentials().rgid(); }
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<BAN::String> link_target_impl() override;
|
||||
|
||||
// You may not write here and this is always non blocking
|
||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(EINVAL); }
|
||||
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(EINVAL); }
|
||||
|
||||
virtual bool can_read_impl() const override { return false; }
|
||||
virtual bool can_write_impl() const override { return false; }
|
||||
virtual bool has_error_impl() const override { return false; }
|
||||
virtual bool has_hungup_impl() const override { return false; }
|
||||
|
||||
private:
|
||||
ProcSymlinkProcessInode(Process& process, BAN::ErrorOr<BAN::String> (Process::*)() const, TmpFileSystem&, const TmpInodeInfo&);
|
||||
|
||||
private:
|
||||
Process& m_process;
|
||||
BAN::ErrorOr<BAN::String> (Process::*m_callback)() const;
|
||||
};
|
||||
|
||||
class ProcROInode final : public TmpInode
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -188,6 +188,9 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<long> sys_tty_ctrl(int fildes, int command, int flags);
|
||||
|
||||
void set_stopped(bool stopped, int signal);
|
||||
void wait_while_stopped();
|
||||
|
||||
static BAN::ErrorOr<void> kill(pid_t pid, int signal);
|
||||
BAN::ErrorOr<long> sys_kill(pid_t pid, int signal);
|
||||
BAN::ErrorOr<long> sys_sigaction(int signal, const struct sigaction* act, struct sigaction* oact);
|
||||
|
@ -225,6 +228,7 @@ namespace Kernel
|
|||
size_t proc_meminfo(off_t offset, BAN::ByteSpan) const;
|
||||
size_t proc_cmdline(off_t offset, BAN::ByteSpan) const;
|
||||
size_t proc_environ(off_t offset, BAN::ByteSpan) const;
|
||||
BAN::ErrorOr<BAN::String> proc_executable() const;
|
||||
|
||||
BAN::StringView executable() const { return m_executable; }
|
||||
|
||||
|
@ -298,12 +302,11 @@ namespace Kernel
|
|||
}
|
||||
|
||||
private:
|
||||
struct ChildExitStatus
|
||||
struct ChildWaitStatus
|
||||
{
|
||||
pid_t pid { 0 };
|
||||
pid_t pgrp { 0 };
|
||||
int exit_code { 0 };
|
||||
bool exited { false };
|
||||
pid_t pid { 0 };
|
||||
pid_t pgrp { 0 };
|
||||
BAN::Optional<int> status;
|
||||
};
|
||||
|
||||
Credentials m_credentials;
|
||||
|
@ -319,6 +322,9 @@ namespace Kernel
|
|||
|
||||
mutable Mutex m_process_lock;
|
||||
|
||||
BAN::Atomic<bool> m_stopped { false };
|
||||
ThreadBlocker m_stop_blocker;
|
||||
|
||||
VirtualFileSystem::File m_working_directory;
|
||||
VirtualFileSystem::File m_root_file;
|
||||
|
||||
|
@ -343,8 +349,9 @@ namespace Kernel
|
|||
BAN::Vector<BAN::String> m_environ;
|
||||
BAN::String m_executable;
|
||||
|
||||
BAN::Vector<ChildExitStatus> m_child_exit_statuses;
|
||||
ThreadBlocker m_child_exit_blocker;
|
||||
BAN::Vector<ChildWaitStatus> m_child_wait_statuses;
|
||||
SpinLock m_child_wait_lock;
|
||||
ThreadBlocker m_child_wait_blocker;
|
||||
|
||||
BAN::Atomic<bool> m_is_exiting { false };
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Kernel
|
|||
BAN::Vector<BAN::RefPtr<Partition>>& partitions() { return m_partitions; }
|
||||
const BAN::Vector<BAN::RefPtr<Partition>>& partitions() const { return m_partitions; }
|
||||
|
||||
size_t drop_disk_cache();
|
||||
BAN::ErrorOr<void> sync_disk_cache();
|
||||
virtual bool is_storage_device() const override { return true; }
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace Kernel
|
|||
BAN::ErrorOr<void> initialize_userspace(vaddr_t entry, BAN::Span<BAN::String> argv, BAN::Span<BAN::String> envp, BAN::Span<LibELF::AuxiliaryVector> auxv);
|
||||
|
||||
// Returns true, if thread is going to trigger signal
|
||||
bool is_interrupted_by_signal() const;
|
||||
bool is_interrupted_by_signal(bool skip_stop_and_cont = false) const;
|
||||
|
||||
// Returns true if pending signal can be added to thread
|
||||
bool can_add_signal_to_execute() const;
|
||||
|
@ -63,6 +63,13 @@ namespace Kernel
|
|||
void add_signal(int signal);
|
||||
void set_suspend_signal_mask(uint64_t sigmask);
|
||||
|
||||
static bool is_stopping_signal(int signal);
|
||||
static bool is_continuing_signal(int signal);
|
||||
static bool is_terminating_signal(int signal);
|
||||
static bool is_abnormal_terminating_signal(int signal);
|
||||
static bool is_realtime_signal(int signal);
|
||||
bool will_exit_because_of_signal() const;
|
||||
|
||||
BAN::ErrorOr<void> sigaltstack(const stack_t* ss, stack_t* oss);
|
||||
|
||||
// blocks current thread and returns either on unblock, eintr, spuriously or after timeout
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <kernel/FS/TmpFS/Inode.h>
|
||||
#include <kernel/Input/InputDevice.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Lock/SpinLockAsMutex.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/Scheduler.h>
|
||||
#include <kernel/Storage/StorageDevice.h>
|
||||
|
@ -62,6 +63,36 @@ namespace Kernel
|
|||
));
|
||||
MUST(Processor::scheduler().add_thread(updater_thread));
|
||||
|
||||
auto* disk_cache_drop_thread = MUST(Thread::create_kernel(
|
||||
[](void* _devfs)
|
||||
{
|
||||
auto* devfs = static_cast<DevFileSystem*>(_devfs);
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
SpinLockGuard guard(devfs->m_disk_cache_lock);
|
||||
bool expected = true;
|
||||
if (!devfs->m_should_drop_disk_cache.compare_exchange(expected, false))
|
||||
{
|
||||
SpinLockGuardAsMutex smutex(guard);
|
||||
devfs->m_disk_cache_thread_blocker.block_indefinite(&smutex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LockGuard _(devfs->m_device_lock);
|
||||
|
||||
size_t pages_dropped = 0;
|
||||
for (auto& device : devfs->m_devices)
|
||||
if (device->is_storage_device())
|
||||
pages_dropped += static_cast<StorageDevice*>(device.ptr())->drop_disk_cache();
|
||||
dprintln("Dropped {} pages from disk cache", pages_dropped);
|
||||
}
|
||||
}, s_instance
|
||||
));
|
||||
MUST(Processor::scheduler().add_thread(disk_cache_drop_thread));
|
||||
|
||||
auto* disk_sync_thread = MUST(Thread::create_kernel(
|
||||
[](void* _devfs)
|
||||
{
|
||||
|
@ -94,6 +125,13 @@ namespace Kernel
|
|||
MUST(Processor::scheduler().add_thread(disk_sync_thread));
|
||||
}
|
||||
|
||||
void DevFileSystem::initiate_disk_cache_drop()
|
||||
{
|
||||
SpinLockGuard _(m_disk_cache_lock);
|
||||
m_should_drop_disk_cache = true;
|
||||
m_disk_cache_thread_blocker.unblock();
|
||||
}
|
||||
|
||||
void DevFileSystem::initiate_sync(bool should_block)
|
||||
{
|
||||
LockGuard _(m_device_lock);
|
||||
|
|
|
@ -17,16 +17,7 @@ namespace Kernel
|
|||
TRY(inode->link_inode(*MUST(ProcROProcessInode::create_new(process, &Process::proc_meminfo, fs, 0400)), "meminfo"_sv));
|
||||
TRY(inode->link_inode(*MUST(ProcROProcessInode::create_new(process, &Process::proc_cmdline, fs, 0400)), "cmdline"_sv));
|
||||
TRY(inode->link_inode(*MUST(ProcROProcessInode::create_new(process, &Process::proc_environ, fs, 0400)), "environ"_sv));
|
||||
|
||||
TRY(inode->link_inode(*MUST(ProcSymlinkInode::create_new(
|
||||
[](void* process) -> BAN::ErrorOr<BAN::String>
|
||||
{
|
||||
BAN::String result;
|
||||
TRY(result.append(static_cast<Process*>(process)->executable()));
|
||||
return result;
|
||||
},
|
||||
&process, fs, 0400, process.credentials().ruid(), process.credentials().ruid()
|
||||
)), "exe"_sv));
|
||||
TRY(inode->link_inode(*MUST(ProcSymlinkProcessInode::create_new(process, &Process::proc_executable, fs, 0400)), "exe"_sv));
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
@ -70,6 +61,29 @@ namespace Kernel
|
|||
return (m_process.*m_callback)(offset, buffer);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<ProcSymlinkProcessInode>> ProcSymlinkProcessInode::create_new(Process& process, BAN::ErrorOr<BAN::String> (Process::*callback)() const, TmpFileSystem& fs, mode_t mode)
|
||||
{
|
||||
auto inode_info = create_inode_info(Mode::IFLNK | mode, 0, 0);
|
||||
|
||||
auto* inode_ptr = new ProcSymlinkProcessInode(process, callback, fs, inode_info);
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return BAN::RefPtr<ProcSymlinkProcessInode>::adopt(inode_ptr);
|
||||
}
|
||||
|
||||
ProcSymlinkProcessInode::ProcSymlinkProcessInode(Process& process, BAN::ErrorOr<BAN::String> (Process::*callback)() const, TmpFileSystem& fs, const TmpInodeInfo& inode_info)
|
||||
: TmpInode(fs, MUST(fs.allocate_inode(inode_info)), inode_info)
|
||||
, m_process(process)
|
||||
, m_callback(callback)
|
||||
{
|
||||
m_inode_info.mode |= Inode::Mode::IFLNK;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::String> ProcSymlinkProcessInode::link_target_impl()
|
||||
{
|
||||
return (m_process.*m_callback)();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<ProcROInode>> ProcROInode::create_new(size_t (*callback)(off_t, BAN::ByteSpan), TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto inode_info = create_inode_info(Mode::IFREG | mode, uid, gid);
|
||||
|
|
|
@ -155,6 +155,9 @@ namespace Kernel
|
|||
case LibInput::keycode_function(1):
|
||||
Processor::toggle_should_print_cpu_load();
|
||||
break;
|
||||
case LibInput::keycode_function(11):
|
||||
DevFileSystem::get().initiate_disk_cache_drop();
|
||||
break;
|
||||
case LibInput::keycode_function(12):
|
||||
Kernel::panic("Keyboard kernel panic :)");
|
||||
break;
|
||||
|
|
|
@ -260,7 +260,7 @@ namespace Kernel::Input
|
|||
|
||||
const auto* fadt = static_cast<const ACPI::FADT*>(ACPI::ACPI::get().get_header("FACP"_sv, 0));
|
||||
if (!fadt || fadt->revision < 3 || fadt->length < iapc_flag_end)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
if (!(fadt->iapc_boot_arch & (1 << 1)))
|
||||
return false;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
#include <kernel/Lock/SpinLockAsMutex.h>
|
||||
#include <kernel/Memory/FileBackedRegion.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/Memory/MemoryBackedRegion.h>
|
||||
|
@ -295,21 +296,20 @@ namespace Kernel
|
|||
|
||||
if (parent_process)
|
||||
{
|
||||
LockGuard _(parent_process->m_process_lock);
|
||||
SpinLockGuard _(parent_process->m_child_wait_lock);
|
||||
|
||||
for (auto& child : parent_process->m_child_exit_statuses)
|
||||
for (auto& child : parent_process->m_child_wait_statuses)
|
||||
{
|
||||
if (child.pid != pid())
|
||||
continue;
|
||||
|
||||
child.exit_code = __WGENEXITCODE(status, signal);
|
||||
child.exited = true;
|
||||
child.status = __WGENEXITCODE(status, signal);
|
||||
|
||||
parent_process->add_pending_signal(SIGCHLD);
|
||||
if (!parent_process->m_threads.empty())
|
||||
Processor::scheduler().unblock_thread(parent_process->m_threads.front());
|
||||
|
||||
parent_process->m_child_exit_blocker.unblock();
|
||||
parent_process->m_child_wait_blocker.unblock();
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -463,6 +463,14 @@ namespace Kernel
|
|||
return read_from_vec_of_str(m_environ, offset, buffer);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::String> Process::proc_executable() const
|
||||
{
|
||||
LockGuard _(m_process_lock);
|
||||
BAN::String result;
|
||||
TRY(result.append(m_executable));
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<VirtualFileSystem::File> Process::find_file(int fd, const char* path, int flags) const
|
||||
{
|
||||
ASSERT(m_process_lock.is_locked());
|
||||
|
@ -580,23 +588,30 @@ namespace Kernel
|
|||
|
||||
LockGuard _(m_process_lock);
|
||||
|
||||
ChildExitStatus* child_exit_status = nullptr;
|
||||
for (auto& child : m_child_exit_statuses)
|
||||
ChildWaitStatus* child_exit_status = nullptr;
|
||||
|
||||
{
|
||||
if (child.pid != 0)
|
||||
continue;
|
||||
child_exit_status = &child;
|
||||
break;
|
||||
}
|
||||
if (child_exit_status == nullptr)
|
||||
{
|
||||
TRY(m_child_exit_statuses.emplace_back());
|
||||
child_exit_status = &m_child_exit_statuses.back();
|
||||
SpinLockGuard _(m_child_wait_lock);
|
||||
for (auto& child : m_child_wait_statuses)
|
||||
{
|
||||
if (child.pid != 0)
|
||||
continue;
|
||||
child_exit_status = &child;
|
||||
break;
|
||||
}
|
||||
if (child_exit_status == nullptr)
|
||||
{
|
||||
TRY(m_child_wait_statuses.emplace_back());
|
||||
child_exit_status = &m_child_wait_statuses.back();
|
||||
}
|
||||
}
|
||||
|
||||
auto working_directory = TRY(m_working_directory.clone());
|
||||
auto root_file = TRY(m_root_file.clone());
|
||||
|
||||
BAN::String executable;
|
||||
TRY(executable.append(m_executable));
|
||||
|
||||
BAN::Vector<BAN::String> cmdline;
|
||||
TRY(cmdline.resize(m_cmdline.size()));
|
||||
for (size_t i = 0; i < m_cmdline.size(); i++)
|
||||
|
@ -621,6 +636,7 @@ namespace Kernel
|
|||
forked->m_root_file = BAN::move(root_file);
|
||||
forked->m_cmdline = BAN::move(cmdline);
|
||||
forked->m_environ = BAN::move(environ);
|
||||
forked->m_executable = BAN::move(executable);
|
||||
forked->m_page_table = BAN::move(page_table);
|
||||
forked->m_open_file_descriptors = BAN::move(*open_file_descriptors);
|
||||
forked->m_mapped_regions = BAN::move(mapped_regions);
|
||||
|
@ -782,7 +798,7 @@ namespace Kernel
|
|||
// FIXME: Add WCONTINUED and WUNTRACED when stopped/continued processes are added
|
||||
|
||||
const auto pid_matches =
|
||||
[&](const ChildExitStatus& child)
|
||||
[&](const ChildWaitStatus& child)
|
||||
{
|
||||
if (pid == -1)
|
||||
return true;
|
||||
|
@ -793,47 +809,67 @@ namespace Kernel
|
|||
return child.pid == pid;
|
||||
};
|
||||
|
||||
LockGuard _(m_process_lock);
|
||||
pid_t child_pid = 0;
|
||||
int child_status = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
pid_t exited_pid = 0;
|
||||
int exit_code = 0;
|
||||
{
|
||||
bool found = false;
|
||||
for (auto& child : m_child_exit_statuses)
|
||||
{
|
||||
if (!pid_matches(child))
|
||||
continue;
|
||||
found = true;
|
||||
if (!child.exited)
|
||||
continue;
|
||||
exited_pid = child.pid;
|
||||
exit_code = child.exit_code;
|
||||
child = {};
|
||||
break;
|
||||
}
|
||||
bool found = false;
|
||||
|
||||
if (!found)
|
||||
return BAN::Error::from_errno(ECHILD);
|
||||
SpinLockGuard sguard(m_child_wait_lock);
|
||||
|
||||
for (auto& child : m_child_wait_statuses)
|
||||
{
|
||||
if (!pid_matches(child))
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
if (!child.status.has_value())
|
||||
continue;
|
||||
|
||||
const int status = child.status.value();
|
||||
|
||||
bool should_report = false;
|
||||
if (WIFSTOPPED(status))
|
||||
should_report = !!(options & WUNTRACED);
|
||||
else if (WIFCONTINUED(status))
|
||||
should_report = !!(options & WCONTINUED);
|
||||
else
|
||||
should_report = true;
|
||||
|
||||
if (!should_report)
|
||||
continue;
|
||||
|
||||
child_pid = child.pid;
|
||||
child_status = status;
|
||||
child.status = {};
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (exited_pid != 0)
|
||||
{
|
||||
if (stat_loc)
|
||||
{
|
||||
TRY(validate_pointer_access(stat_loc, sizeof(stat_loc), true));
|
||||
*stat_loc = exit_code;
|
||||
}
|
||||
remove_pending_signal(SIGCHLD);
|
||||
return exited_pid;
|
||||
}
|
||||
if (child_pid != 0)
|
||||
break;
|
||||
|
||||
if (!found)
|
||||
return BAN::Error::from_errno(ECHILD);
|
||||
|
||||
if (options & WNOHANG)
|
||||
return 0;
|
||||
|
||||
TRY(Thread::current().block_or_eintr_indefinite(m_child_exit_blocker, &m_process_lock));
|
||||
SpinLockGuardAsMutex smutex(sguard);
|
||||
TRY(Thread::current().block_or_eintr_indefinite(m_child_wait_blocker, &smutex));
|
||||
}
|
||||
|
||||
LockGuard _(m_process_lock);
|
||||
|
||||
if (stat_loc)
|
||||
{
|
||||
TRY(validate_pointer_access(stat_loc, sizeof(stat_loc), true));
|
||||
*stat_loc = child_status;
|
||||
}
|
||||
|
||||
remove_pending_signal(SIGCHLD);
|
||||
return child_pid;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_sleep(int seconds)
|
||||
|
@ -2571,6 +2607,69 @@ namespace Kernel
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Process::set_stopped(bool stopped, int signal)
|
||||
{
|
||||
SpinLockGuard _(m_signal_lock);
|
||||
|
||||
Process* parent = nullptr;
|
||||
|
||||
for_each_process(
|
||||
[&parent, this](Process& process) -> BAN::Iteration
|
||||
{
|
||||
if (process.pid() != m_parent)
|
||||
return BAN::Iteration::Continue;
|
||||
parent = &process;
|
||||
return BAN::Iteration::Break;
|
||||
}
|
||||
);
|
||||
|
||||
if (parent != nullptr)
|
||||
{
|
||||
{
|
||||
SpinLockGuard _(parent->m_child_wait_lock);
|
||||
|
||||
for (auto& child : parent->m_child_wait_statuses)
|
||||
{
|
||||
if (child.pid != pid())
|
||||
continue;
|
||||
if (!child.status.has_value() || WIFCONTINUED(*child.status) || WIFSTOPPED(*child.status))
|
||||
child.status = stopped
|
||||
? __WGENSTOPCODE(signal)
|
||||
: __WGENCONTCODE();
|
||||
break;
|
||||
}
|
||||
|
||||
parent->m_child_wait_blocker.unblock();
|
||||
}
|
||||
|
||||
if (!(m_signal_handlers[SIGCHLD].sa_flags & SA_NOCLDSTOP))
|
||||
{
|
||||
parent->add_pending_signal(SIGCHLD);
|
||||
if (!parent->m_threads.empty())
|
||||
Processor::scheduler().unblock_thread(parent->m_threads.front());
|
||||
}
|
||||
}
|
||||
|
||||
m_stopped = stopped;
|
||||
m_stop_blocker.unblock();
|
||||
}
|
||||
|
||||
void Process::wait_while_stopped()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
while (Thread::current().will_exit_because_of_signal())
|
||||
Thread::current().handle_signal();
|
||||
|
||||
SpinLockGuard guard(m_signal_lock);
|
||||
if (!m_stopped)
|
||||
break;
|
||||
|
||||
SpinLockGuardAsMutex smutex(guard);
|
||||
m_stop_blocker.block_indefinite(&smutex);
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::kill(pid_t pid, int signal)
|
||||
{
|
||||
if (pid == 0 || pid == -1)
|
||||
|
@ -2582,18 +2681,26 @@ namespace Kernel
|
|||
for_each_process(
|
||||
[&](Process& process)
|
||||
{
|
||||
if (pid == process.pid() || -pid == process.pgrp())
|
||||
if (pid != process.pid() && -pid != process.pgrp())
|
||||
return BAN::Iteration::Continue;
|
||||
|
||||
found = true;
|
||||
|
||||
if (signal == 0)
|
||||
;
|
||||
// NOTE: stopped signals go through thread's signal handling code
|
||||
// because for example SIGTSTP can be ignored
|
||||
else if (Thread::is_continuing_signal(signal))
|
||||
process.set_stopped(false, signal);
|
||||
else
|
||||
{
|
||||
found = true;
|
||||
if (signal)
|
||||
{
|
||||
process.add_pending_signal(signal);
|
||||
if (!process.m_threads.empty())
|
||||
Processor::scheduler().unblock_thread(process.m_threads.front());
|
||||
}
|
||||
return (pid > 0) ? BAN::Iteration::Break : BAN::Iteration::Continue;
|
||||
process.add_pending_signal(signal);
|
||||
if (!process.m_threads.empty())
|
||||
Processor::scheduler().unblock_thread(process.m_threads.front());
|
||||
process.m_stop_blocker.unblock();
|
||||
}
|
||||
return BAN::Iteration::Continue;
|
||||
|
||||
return (pid > 0) ? BAN::Iteration::Break : BAN::Iteration::Continue;
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -2611,8 +2718,17 @@ namespace Kernel
|
|||
|
||||
if (pid == m_pid)
|
||||
{
|
||||
if (signal)
|
||||
if (signal == 0)
|
||||
;
|
||||
// NOTE: stopped signals go through thread's signal handling code
|
||||
// because for example SIGTSTP can be ignored
|
||||
else if (Thread::is_continuing_signal(signal))
|
||||
set_stopped(false, signal);
|
||||
else
|
||||
{
|
||||
add_pending_signal(signal);
|
||||
m_stop_blocker.unblock();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -216,6 +216,14 @@ namespace Kernel
|
|||
m_disk_cache.emplace(sector_size(), *this);
|
||||
}
|
||||
|
||||
size_t StorageDevice::drop_disk_cache()
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
if (m_disk_cache.has_value())
|
||||
return m_disk_cache->release_pages(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> StorageDevice::sync_disk_cache()
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace Kernel
|
|||
|
||||
Processor::set_interrupt_state(InterruptState::Enabled);
|
||||
|
||||
Process::current().wait_while_stopped();
|
||||
|
||||
BAN::ErrorOr<long> ret = BAN::Error::from_errno(ENOSYS);
|
||||
|
||||
const char* process_path = Process::current().name();
|
||||
|
@ -92,6 +94,8 @@ namespace Kernel
|
|||
if (ret.is_error() && ret.error().is_kernel_error())
|
||||
Kernel::panic("Kernel error while returning to userspace {}", ret.error());
|
||||
|
||||
Process::current().wait_while_stopped();
|
||||
|
||||
Processor::set_interrupt_state(InterruptState::Disabled);
|
||||
|
||||
auto& current_thread = Thread::current();
|
||||
|
|
|
@ -69,6 +69,77 @@ namespace Kernel
|
|||
s_default_sse_storage_initialized = true;
|
||||
}
|
||||
|
||||
bool Thread::is_stopping_signal(int signal)
|
||||
{
|
||||
switch(signal)
|
||||
{
|
||||
case SIGSTOP:
|
||||
case SIGTSTP:
|
||||
case SIGTTIN:
|
||||
case SIGTTOU:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::is_continuing_signal(int signal)
|
||||
{
|
||||
switch(signal)
|
||||
{
|
||||
case SIGCONT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::is_terminating_signal(int signal)
|
||||
{
|
||||
switch (signal)
|
||||
{
|
||||
case SIGALRM:
|
||||
case SIGHUP:
|
||||
case SIGINT:
|
||||
case SIGKILL:
|
||||
case SIGPIPE:
|
||||
case SIGTERM:
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
case SIGPOLL:
|
||||
case SIGPROF:
|
||||
case SIGVTALRM:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::is_abnormal_terminating_signal(int signal)
|
||||
{
|
||||
switch (signal)
|
||||
{
|
||||
case SIGABRT:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
case SIGQUIT:
|
||||
case SIGSEGV:
|
||||
case SIGSYS:
|
||||
case SIGTRAP:
|
||||
case SIGXCPU:
|
||||
case SIGXFSZ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Thread::is_realtime_signal(int signal)
|
||||
{
|
||||
return SIGRTMIN <= signal && signal <= SIGRTMAX;
|
||||
}
|
||||
|
||||
static bool is_default_ignored_signal(int signal)
|
||||
{
|
||||
switch (signal)
|
||||
|
@ -79,7 +150,7 @@ namespace Kernel
|
|||
case SIGCANCEL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
return Thread::is_realtime_signal(signal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,7 +529,7 @@ namespace Kernel
|
|||
memset(&m_interrupt_registers, 0, sizeof(InterruptRegisters));
|
||||
}
|
||||
|
||||
bool Thread::is_interrupted_by_signal() const
|
||||
bool Thread::is_interrupted_by_signal(bool skip_stop_and_cont) const
|
||||
{
|
||||
if (!is_userspace() || m_state != State::Executing)
|
||||
return false;
|
||||
|
@ -472,6 +543,8 @@ namespace Kernel
|
|||
{
|
||||
if (!(signals & ((uint64_t)1 << i)))
|
||||
continue;
|
||||
if (skip_stop_and_cont && (is_stopping_signal(i) || is_continuing_signal(i)))
|
||||
continue;
|
||||
|
||||
vaddr_t signal_handler;
|
||||
{
|
||||
|
@ -501,13 +574,23 @@ namespace Kernel
|
|||
return interrupt_stack.ip == (uintptr_t)signal_trampoline;
|
||||
}
|
||||
|
||||
bool Thread::will_exit_because_of_signal() const
|
||||
{
|
||||
const uint64_t full_pending_mask = m_signal_pending_mask | process().signal_pending_mask();
|
||||
const uint64_t signals = full_pending_mask & ~m_signal_block_mask;
|
||||
for (size_t sig = _SIGMIN; sig <= _SIGMAX; sig++)
|
||||
if (signals & (static_cast<uint64_t>(1) << sig))
|
||||
if (is_terminating_signal(sig) || is_abnormal_terminating_signal(sig))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thread::handle_signal(int signal)
|
||||
{
|
||||
ASSERT(&Thread::current() == this);
|
||||
ASSERT(is_userspace());
|
||||
|
||||
auto state = m_signal_lock.lock();
|
||||
ASSERT(state == InterruptState::Disabled);
|
||||
|
||||
auto& interrupt_stack = *reinterpret_cast<InterruptStack*>(kernel_stack_top() - sizeof(InterruptStack));
|
||||
ASSERT(GDT::is_user_segment(interrupt_stack.cs));
|
||||
|
@ -613,52 +696,31 @@ namespace Kernel
|
|||
}
|
||||
else
|
||||
{
|
||||
switch (signal)
|
||||
if (is_abnormal_terminating_signal(signal))
|
||||
{
|
||||
process().exit(128 + signal, signal | 0x80);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
else if (is_terminating_signal(signal))
|
||||
{
|
||||
process().exit(128 + signal, signal);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
else if (is_stopping_signal(signal))
|
||||
{
|
||||
process().set_stopped(true, signal);
|
||||
}
|
||||
else if (is_continuing_signal(signal))
|
||||
{
|
||||
process().set_stopped(false, signal);
|
||||
}
|
||||
else if (is_default_ignored_signal(signal))
|
||||
{
|
||||
// Abnormal termination of the process with additional actions.
|
||||
case SIGABRT:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
case SIGQUIT:
|
||||
case SIGSEGV:
|
||||
case SIGSYS:
|
||||
case SIGTRAP:
|
||||
case SIGXCPU:
|
||||
case SIGXFSZ:
|
||||
process().exit(128 + signal, signal | 0x80);
|
||||
ASSERT_NOT_REACHED();
|
||||
|
||||
// Abnormal termination of the process
|
||||
case SIGALRM:
|
||||
case SIGHUP:
|
||||
case SIGINT:
|
||||
case SIGKILL:
|
||||
case SIGPIPE:
|
||||
case SIGTERM:
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
case SIGPOLL:
|
||||
case SIGPROF:
|
||||
case SIGVTALRM:
|
||||
process().exit(128 + signal, signal);
|
||||
ASSERT_NOT_REACHED();
|
||||
|
||||
// Stop the process:
|
||||
case SIGSTOP:
|
||||
case SIGTSTP:
|
||||
case SIGTTIN:
|
||||
case SIGTTOU:
|
||||
ASSERT_NOT_REACHED();
|
||||
|
||||
// Continue the process, if it is stopped; otherwise, ignore the signal.
|
||||
case SIGCONT:
|
||||
ASSERT_NOT_REACHED();
|
||||
|
||||
default:
|
||||
if (is_default_ignored_signal(signal))
|
||||
break;
|
||||
panic("Executing unhandled signal {}", signal);
|
||||
}
|
||||
else
|
||||
{
|
||||
panic("Executing unhandled signal {}", signal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,20 +801,20 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<void> Thread::sleep_or_eintr_ns(uint64_t ns)
|
||||
{
|
||||
if (is_interrupted_by_signal())
|
||||
if (is_interrupted_by_signal(true))
|
||||
return BAN::Error::from_errno(EINTR);
|
||||
SystemTimer::get().sleep_ns(ns);
|
||||
if (is_interrupted_by_signal())
|
||||
if (is_interrupted_by_signal(true))
|
||||
return BAN::Error::from_errno(EINTR);
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Thread::block_or_eintr_indefinite(ThreadBlocker& thread_blocker, BaseMutex* mutex)
|
||||
{
|
||||
if (is_interrupted_by_signal())
|
||||
if (is_interrupted_by_signal(true))
|
||||
return BAN::Error::from_errno(EINTR);
|
||||
thread_blocker.block_indefinite(mutex);
|
||||
if (is_interrupted_by_signal())
|
||||
if (is_interrupted_by_signal(true))
|
||||
return BAN::Error::from_errno(EINTR);
|
||||
return {};
|
||||
}
|
||||
|
@ -765,10 +827,10 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<void> Thread::block_or_eintr_or_waketime_ns(ThreadBlocker& thread_blocker, uint64_t wake_time_ns, bool etimedout, BaseMutex* mutex)
|
||||
{
|
||||
if (is_interrupted_by_signal())
|
||||
if (is_interrupted_by_signal(true))
|
||||
return BAN::Error::from_errno(EINTR);
|
||||
thread_blocker.block_with_wake_time_ns(wake_time_ns, mutex);
|
||||
if (is_interrupted_by_signal())
|
||||
if (is_interrupted_by_signal(true))
|
||||
return BAN::Error::from_errno(EINTR);
|
||||
if (etimedout && SystemTimer::get().ms_since_boot() >= wake_time_ns)
|
||||
return BAN::Error::from_errno(ETIMEDOUT);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='bash'
|
||||
VERSION='5.2.37'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/bash/bash-$VERSION.tar.gz#9599b22ecd1d5787ad7d3b7bf0c59f312b3396d1e281175dd1f8a4014da621ff"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/bash/bash-$VERSION.tar.gz#9599b22ecd1d5787ad7d3b7bf0c59f312b3396d1e281175dd1f8a4014da621ff"
|
||||
DEPENDENCIES=('ncurses')
|
||||
CONFIG_SUB=('support/config.sub')
|
||||
CONFIGURE_OPTIONS=(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='binutils'
|
||||
VERSION='2.44'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/binutils/binutils-$VERSION.tar.gz#0cdd76777a0dfd3dd3a63f215f030208ddb91c2361d2bcc02acec0f1c16b6a2e"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/binutils/binutils-$VERSION.tar.gz#0cdd76777a0dfd3dd3a63f215f030208ddb91c2361d2bcc02acec0f1c16b6a2e"
|
||||
DEPENDENCIES=('zlib' 'zstd')
|
||||
MAKE_INSTALL_TARGETS=('install-strip')
|
||||
CONFIGURE_OPTIONS=(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='gcc'
|
||||
VERSION='15.2.0'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/gcc/gcc-$VERSION/gcc-$VERSION.tar.xz#438fd996826b0c82485a29da03a72d71d6e3541a83ec702df4271f6fe025d24e"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/gcc/gcc-$VERSION/gcc-$VERSION.tar.xz#438fd996826b0c82485a29da03a72d71d6e3541a83ec702df4271f6fe025d24e"
|
||||
DEPENDENCIES=('binutils' 'gmp' 'mpfr' 'mpc')
|
||||
MAKE_INSTALL_TARGETS=('install-strip-gcc' 'install-strip-target-libgcc' 'install-strip-target-libstdc++-v3')
|
||||
CONFIGURE_OPTIONS=(
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
NAME='gmp'
|
||||
VERSION='6.3.0'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/gmp/gmp-$VERSION.tar.xz#a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/gmp/gmp-$VERSION.tar.xz#a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898"
|
||||
CONFIG_SUB=('configfsf.sub')
|
||||
|
||||
# configure does not work with (default) c23
|
||||
export CFLAGS="--std=c17 $CFLAGS"
|
||||
CONFIGURE_OPTIONS=(
|
||||
'CFLAGS=-std=c17'
|
||||
)
|
||||
|
||||
post_install() {
|
||||
# remove libtool file
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='libiconv'
|
||||
VERSION='1.18'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$VERSION.tar.gz#3b08f5f4f9b4eb82f151a7040bfd6fe6c6fb922efe4b1659c66ea933276965e8"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/pub/gnu/libiconv/libiconv-$VERSION.tar.gz#3b08f5f4f9b4eb82f151a7040bfd6fe6c6fb922efe4b1659c66ea933276965e8"
|
||||
CONFIG_SUB=('build-aux/config.sub' 'libcharset/build-aux/config.sub')
|
||||
CONFIGURE_OPTIONS=(
|
||||
'--disable-nls'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='make'
|
||||
VERSION='4.4.1'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/make/make-$VERSION.tar.gz#dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/make/make-$VERSION.tar.gz#dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3"
|
||||
CONFIG_SUB=('build-aux/config.sub')
|
||||
CONFIGURE_OPTIONS=(
|
||||
'--with-sysroot=/'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='mpc'
|
||||
VERSION='1.3.1'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/mpc/mpc-$VERSION.tar.gz#ab642492f5cf882b74aa0cb730cd410a81edcdbec895183ce930e706c1c759b8"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/mpc/mpc-$VERSION.tar.gz#ab642492f5cf882b74aa0cb730cd410a81edcdbec895183ce930e706c1c759b8"
|
||||
DEPENDENCIES=('gmp' 'mpfr')
|
||||
CONFIG_SUB=('build-aux/config.sub')
|
||||
CONFIGURE_OPTIONS=(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='mpfr'
|
||||
VERSION='4.2.2'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/mpfr/mpfr-$VERSION.tar.gz#826cbb24610bd193f36fde172233fb8c009f3f5c2ad99f644d0dea2e16a20e42"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/mpfr/mpfr-$VERSION.tar.gz#826cbb24610bd193f36fde172233fb8c009f3f5c2ad99f644d0dea2e16a20e42"
|
||||
DEPENDENCIES=('gmp')
|
||||
CONFIG_SUB=('config.sub')
|
||||
CONFIGURE_OPTIONS=(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
NAME='ncurses'
|
||||
VERSION='6.5'
|
||||
DOWNLOAD_URL="https://ftp.gnu.org/gnu/ncurses/ncurses-$VERSION.tar.gz#136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6"
|
||||
DOWNLOAD_URL="https://ftpmirror.gnu.org/gnu/ncurses/ncurses-$VERSION.tar.gz#136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6"
|
||||
CONFIG_SUB=('config.sub')
|
||||
CONFIGURE_OPTIONS=(
|
||||
"--with-pkg-config='$PKG_CONFIG'"
|
||||
|
|
|
@ -10,6 +10,8 @@ CONFIGURE_OPTIONS=(
|
|||
'--without-gl-libs'
|
||||
'--with-tcl-lib-name=tcl8.6'
|
||||
'--without-x'
|
||||
'ac_cv_func_isnan=yes'
|
||||
'CFLAGS="-std=c99 -Wno-implicit-int -Wno-incompatible-pointer-types'
|
||||
)
|
||||
|
||||
post_install() {
|
||||
|
@ -29,6 +31,3 @@ post_install() {
|
|||
|
||||
popd
|
||||
}
|
||||
|
||||
export CFLAGS="-std=c99 -Wno-implicit-int -Wno-incompatible-pointer-types $CFLAGS"
|
||||
export ac_cv_func_isnan=yes
|
||||
|
|
|
@ -15,6 +15,7 @@ CONFIGURE_OPTIONS=(
|
|||
'vim_cv_getcwd_broken=no'
|
||||
'vim_cv_stat_ignores_slash=yes'
|
||||
'vim_cv_memmove_handles_overlap=yes'
|
||||
'CFLAGS=-Wno-incompatible-pointer-types'
|
||||
)
|
||||
|
||||
post_configure() {
|
||||
|
|
|
@ -3,15 +3,15 @@ set -e
|
|||
|
||||
BINUTILS_VERSION="binutils-2.45"
|
||||
BINUTILS_TAR="$BINUTILS_VERSION.tar.xz"
|
||||
BINUTILS_URL="https://ftp.gnu.org/gnu/binutils/$BINUTILS_TAR"
|
||||
BINUTILS_URL="https://ftpmirror.gnu.org/gnu/binutils/$BINUTILS_TAR"
|
||||
|
||||
GCC_VERSION="gcc-15.2.0"
|
||||
GCC_TAR="$GCC_VERSION.tar.xz"
|
||||
GCC_URL="https://ftp.gnu.org/gnu/gcc/$GCC_VERSION/$GCC_TAR"
|
||||
GCC_URL="https://ftpmirror.gnu.org/gnu/gcc/$GCC_VERSION/$GCC_TAR"
|
||||
|
||||
GRUB_VERSION="grub-2.06"
|
||||
GRUB_TAR="$GRUB_VERSION.tar.xz"
|
||||
GRUB_URL="https://ftp.gnu.org/gnu/grub/$GRUB_TAR"
|
||||
GRUB_URL="https://ftpmirror.gnu.org/gnu/grub/$GRUB_TAR"
|
||||
|
||||
CMAKE_VERSION="cmake-3.26.6-linux-x86_64"
|
||||
CMAKE_TAR="$CMAKE_VERSION.tar.gz"
|
||||
|
|
|
@ -20,14 +20,17 @@ __BEGIN_DECLS
|
|||
#define WNOWAIT 0x10
|
||||
#define WSTOPPED 0x20
|
||||
|
||||
#define WEXITSTATUS(status) (((status) >> 8) & 0xFF)
|
||||
#define WSTOPSIG(status) WEXITSTATUS(status)
|
||||
#define WTERMSIG(status) ((status) & 0x7F)
|
||||
#define WIFEXITED(status) (WTERMSIG(status) == 0)
|
||||
#define WIFSIGNALED(status) (((status) & 0x7F) > 0 && ((status) & 0x7F) < 0x7F)
|
||||
#define WIFSTOPPED(status) (((status) & 0xFF) == 0x7F)
|
||||
#define WEXITSTATUS(status) (((status) >> 8) & 0xFF)
|
||||
#define WSTOPSIG(status) WEXITSTATUS(status)
|
||||
#define WTERMSIG(status) ((status) & 0x7F)
|
||||
#define WIFEXITED(status) (WTERMSIG(status) == 0)
|
||||
#define WIFSIGNALED(status) (((status) & 0x7F) > 0 && ((status) & 0x7F) < 0x7E)
|
||||
#define WIFSTOPPED(status) (((status) & 0x7F) == 0x7F)
|
||||
#define WIFCONTINUED(status) (((status) & 0x7F) == 0x7E)
|
||||
|
||||
#define __WGENEXITCODE(ret, sig) (((ret) << 8) | (sig))
|
||||
#define __WGENSTOPCODE(sig) (((sig) << 8) | 0x7F)
|
||||
#define __WGENCONTCODE() ( 0x7E)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue