Autumn 2021
Jerry Cain
PDF
open
, read
, write
, and close
. We'll see many others in the coming weeks.printf
, malloc
, fopen
, and opendir
are not syscalls. They're C library functions that themselves rely on syscalls to get their jobs done.libc
and libstdc++
library functions), system calls need to execute in some privileged mode so they can access data structures, system information, and other OS resources intentionally and necessarily hidden from user code.open
, for instance, needs to access all of the filesystem data structures for existence and permissioning. Filesystem implementation details should be hidden from the user, and permission information should be respected as private.open
musn’t be visible to the user functions that call open
. Restated, privileged information shouldn't be discoverable.malloc
, realloc
, free
, and their C++ equivalents. It's initially very small, but grows as needed for processes requiring a good amount of dynamically allocated memory.libc
and libstdc++
with code for routines like C's printf
, C's malloc
, or C++'s getline
. Shared libraries get their own segment so all processes can trampoline through some glue code—that is, the minimum amount of code necessary—to jump into the one copy of the library code that exists on behalf of all processes.%rsp
to track the address boundary between the in-use portion of the user stack and the portion that's on deck to be used should the currently executing function invoke a subroutine.callq
and retq
instructions for user function call and return.%rdi
, %rsi
, %rdx
, %rcx
, %r8
,%r9
. The stackloadFiles
as per the diagram below. Because loadFiles
's stack frame is directly below that of its caller, it can use pointer arithmetic to advance beyond its frame and examine—or even update—the stack frame above it.loadFiles
returns, main
could use pointer arithmetic to descend into the ghost of loadFiles
's stack frame and accessloadFiles
open
and close
need access to OS implementation detail that should not be exposed or otherwise accessible to the user program.callq
is used to invoke a user function, but callq
isn't the correct instruction for system calls.callq
.%rax
. Each system call has its own opcode (e.g. 0 for read
, 1 for write
, 2 for open
, 3 for close
, and so forth).%rdi
, %rsi
, %rdx
, %r10
(not %rcx
), %r8
, and %r9
.syscall
, which prompts an interrupt handler to execute in superuser mode.%rax
, and then executes iretq
to return from the handler, revert from superuser mode.%rax
is negative, errno
is set to abs(%rax
) and %rax
is updated to contain a -1. If %rax
is nonnegative, it's left as is. The value in %rax
is then extracted by the caller as any return value would be.fork
fork
, getpid
, and getppid
. The full program can be viewed right here.int main(int argc, char *argv[]) {
printf("Greetings from process %d! (parent %d)\n", getpid(), getppid());
pid_t pid = fork();
assert(pid >= 0);
printf("Bye-bye from process %d! (parent %d)\n", getpid(), getppid());
return 0;
}
myth60$ ./basic-fork
Greetings from process 29686! (parent 29351)
Bye-bye from process 29686! (parent 29351)
Bye-bye from process 29687! (parent 29686)
myth60$ ./basic-fork
Greetings from process 29688! (parent 29351)
Bye-bye from process 29688! (parent 29351)
Bye-bye from process 29689! (parent 29688)
fork
is called once, but it returns twice.
getpid
and getppid
return the process id of the caller and the process id of the caller's parent, respectively.fork
knows how to clone the calling process, synthesize a nearly identical copy of it, and schedule the copy to run as if it’s been running all along.
Illustration courtesy of Roz Cyrus.
basic-fork
processes—with pids of 29686 and 29688—are direct child processes of the terminal. The output tells us so.fork
and child generated by it:
fork
's return value in the two processes
fork
returns in the parent process, it returns the pid of the new child.fork
returns in the child process, it returns 0. That isn't to say the child's pid is 0, but rather that fork
elects to return a 0 as a way of allowing the child to easily self-identify as the child.Illustration courtesy of Roz Cyrus.
gdb
has built-in support for debugging multiple processes, as follows:
set detach-on-fork off
gdb
to capture all fork
'd processes, though it pauses each at fork
.
info inferiors
gdb
has captured.inferior X
detach inferior X
gdb
to stop watching the process before continuing itbasic-fork
program right here.fork
so far:
fork
is a system call that creates a near duplicate of the current process.fork
is the child's pid
, and in the child, the return value is 0. This enables both the parent and the child to self-identify which of the two processes they are.fork
, there is virtually no difference in the two processes, and they both continue after fork
as if they were the original process.wait
(more next time) for child processes to complete.fork
calls. More on that in discussion section.static const char const *kTrail = "abcd";
int main(int argc, char *argv[]) {
size_t trailLength = strlen(kTrail);
for (size_t i = 0; i < trailLength; i++) {
printf("%c\n", kTrail[i]);
pid_t pid = fork();
}
return 0;
}
myth59$ ./fork-puzzle
a
b
b
c
c
c
d
d
c
d
d
d
d
d
d
myth59$
myth59$ ./fork-puzzle
a
b
c
b
d
c
c
d
c
d
d
myth59$ d
d
d
d
myth59$ ./fork-puzzle
a
b
c
b
d
c
c
d
d
c
d
d
d
myth59$ d
d
myth59$ ./fork-puzzle
a
b
b
c
c
c
d
d
c
d
d
d
d
d
d
myth59$
myth59$ ./fork-puzzle
a
b
c
b
d
c
c
d
c
d
d
myth59$ d
d
d
d
myth59$ ./fork-puzzle
a
b
c
b
d
c
c
d
d
c
d
d
d
myth59$ d
d
pid_t waitpid(pid_t pid, int *status, int options);
int main(int argc, char *argv[]) {
printf("Before.\n");
pid_t pid = fork();
printf("After.\n");
if (pid == 0) {
printf("I am the child, parent will wait for me.\n");
return 110;
} else {
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
printf("Child exited with status %d.\n",
WEXITSTATUS(status));
} else {
printf("Child terminated abnormally.\n");
}
return 0;
}
}
myth59$ ./separate
Before.
After.
After.
I am the child, parent will wait for me.
Child exited with status 110.
myth59$
myth59$ ./separate
Before.
After.
I am the child, parent will wait for me.
After.
Child exited with status 110.
myth59$
Illustration courtesy of Roz Cyrus.