I’m curious about whether Julia’s (or stdlib) Linux-specific parts ever call syscalls directly (without libc involvement)? Does Linux specific code even exist in the implementation of Julia or its stdlib?
I’m not a Julia dev, but if you check the code in src/
against the system calls listed by man syscalls
you will see quite a few are being used. For example, file related system calls in src/init.c
, on non-Windows systems:
#if defined(_OS_WINDOWS_)
CloseHandle(fd);
fd = CreateFile("NUL", readable ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
#else
{
int nullfd;
nullfd = open("/dev/null", O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH /* 0666 */);
assert(nullfd != -1);
dup2(nullfd, fd);
close(nullfd);
}
#endif
Or mmap()
:
gc.c: void *pool = mmap(0, GC_PERM_POOL_SIZE, PROT_READ | PROT_WRITE,
gc-pages.c: char *mem = (char*)mmap(0, pages_sz, PROT_READ | PROT_WRITE,
gc-pages.c: // boundary if mmap didn't already do so.
gc-pages.c: fprintf(stderr, "%d jl_gc_try_alloc_pages() %d mmap %d -> 0x%016x (0x%016x)\n", pthread_self(), pg_cnt, pages_sz, mem, mem_saved);
gc-stacks.c: void* stk = mmap(0, bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
safepoint.c: char *addr = (char*)mmap(0, pgsz * 3, PROT_READ,
signals-unix.c: else if (sig == SIGSEGV && info->si_code == SEGV_ACCERR && is_write_fault(context)) { // writing to read-only memory (e.g., mmap)
signals-unix.c: jl_errorf("fatal error allocating signal stack: mmap: %s", strerror(errno));
signals-win.c: if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) { // writing to read-only memory (e.g. mmap)
sys.c:JL_DLLEXPORT void *jl_mmap(void *addr, size_t length, int prot, int flags,
sys.c: return mmap(addr, length, prot, flags, fd, (off_t)offset);
task.c: // fall back to stack copying if mmap fails
These are not specifically Linux-specific calls, btw, but definitely POSIX-specific.
I asked about direct syscalls (without going through libc):
I noticed, but in most cases the wrapper in glibc is pretty thin (and e.g. open()
is definitely closer to a syscall that fopen()
). Also, why would you expect a programming language like Julia to ever invoke a syscall other than through glibc? I would only expect kernel code or embedded systems code to ever do that.
Grepping the Julia source for syscall()
shows at least this one:
$ cat src/jlapi.c
...
#ifdef _OS_LINUX_
static void rr_detach_teleport(void) {
#define RR_CALL_BASE 1000
#define SYS_rrcall_detach_teleport (RR_CALL_BASE + 9)
int err = syscall(SYS_rrcall_detach_teleport, 0, 0, 0, 0, 0, 0);
if (err < 0 || jl_running_under_rr(1)) {
jl_error("Failed to detach from rr session");
}
}
#endif
...
Also, why would you expect a programming language like Julia to ever invoke a syscall other than through glibc? I would only expect kernel code or embedded systems code to ever do that.
Golang does that. It used not to depend on libc at all, but then added a libc dependency only for DNS resolution, as far as I remember.
BTW, I’m actually more interested in whether raw syscalls ever happen in Julia code (.jl files), rather than C or C++ code.
Seems there’s some testing code that does this:
$ cat src/test/threads.jl
...
# issue #34415 - make sure external affinity settings work
if Sys.islinux()
const SYS_rrcall_check_presence = 1008
global running_under_rr() = 0 == ccall(:syscall, Int,
(Int, Int, Int, Int, Int, Int, Int),
SYS_rrcall_check_presence, 0, 0, 0, 0, 0, 0)
else
global running_under_rr() = false
end
...
This is actually a really interesting example (although not at all what I was looking for). The syscall is actually not a Linux syscall, rather it is emulated by the rr
debugging tool: rr/syscalls.py at master · rr-debugger/rr · GitHub