程序代写代做代考 ECS 150 – System Calls

ECS 150 – System Calls
Joël Porquet

UC Davis – Spring Quarter 2017

Readings: OSPP Chap 3

Copyright © 2017 Joël Porquet – CC BY-NC-SA 4.0 International Licence 1 / 44

https://creativecommons.org/licenses/by-nc-sa/4.0/

System calls
Example
#include

int main(int argc, char **argv)
{
int fd, nread;
char buf[1024];

fd = open(“/path/to/myfile”, 0);
nread = read(fd, buf, 1024);

close(fd);

return 0;
}

2 / 44

System calls
Overview

API to the kernel
Hide kernel and hardware complexity from applications

Dispatch Syscall handler
(sys_read())

Push nbyte
Push &buf
Push fd
Call read()
Increment SP

Put code for read in register
Trap to kernel
Return to caller

user space

kernel space

User program
calling read()

C Library
procedure call

1

2

34

3 / 44

System calls
Inter-communication
How the system calls communicate back to applications?

Return value
Usually -1 on error, >= 0 on success
C library functions set global variable errno to encode the error

E2BIG, EACCESS, EAGAIN, EBADF, ENOMEM, … (man errno)
Use perror() to display a string

void *ptr = malloc(1);
if (!ptr) {
int errno_save = errno;
perror(“malloc”);
fprintf(sderr, “malloc: %s\n”, strerror(errno_save));
exit(EXIT_FAILURE);
}

Buffers
Part of system calls arguments
Values need to be copied between user and kernel space

4 / 44

System calls
Process

Files

Pipe

Signals

Memory

5 / 44

Process
Definition

A process is a program in execution

Each process has its own memory space and process control block

Process control block

Kernel structure
Stores all information associated with a process
Register values for context switching, open files, user ID, group ID, etc.

Processes are indexed by the process ID (PID)

6 / 44

Process
Definition

A process is a program in execution

Each process has its own memory space and process control block

Process control block

Kernel structure
Stores all information associated with a process
Register values for context switching, open files, user ID, group ID, etc.

Processes are indexed by the process ID (PID)

Process 1 Process 2 Process 3

Kernel
Syscall API

User

PCB
PID=1
uid
gid
ctxt
files

PCB
PID=2
uid
gid
ctxt
files

PCB
PID=3
uid
gid
ctxt
files

7 / 44

Process
Related system calls

fork(): Create a new process

exec(): Change executed program in process

exit(): End process

wait()/waitpid(): Wait for a child process and collect exit code

getpid(): Get process PID

getpgrp(): Get process GID

8 / 44

Process
fork()

Syntax: pid = fork()

The child gets almost identical copy of the parent

File descriptors, arguments, memory, stack, etc. are all copied

Even the program counter

Only one thing differs… Which one?

9 / 44

Process
Forgetful forking
int main(int argc, char **argv)
{
fork();
prinft(“I am the process!\n”);
return 0;
}

What gets printed?

10 / 44

Process
Forgetful forking
int main(int argc, char **argv)
{
fork();
prinft(“I am the process!\n”);
return 0;
}

What gets printed?

$ ./simple-fork
I am the process!
I am the process!

11 / 44

Process
Forgetful forking
int main(int argc, char **argv)
{
fork();
prinft(“I am the process!\n”);
return 0;
}

What gets printed?

$ ./simple-fork
I am the process!
I am the process!

Using the return value
fork() returns:

zero for the child
PID of the child for the parent
(-1 in case of error)

12 / 44

Process
Fork illustrated

Process 1 Process 2

Kernel
Syscall API

User

PID=1
uid
gid
ctxt
files

PID=2
uid
gid
ctxt
files

fork()

1

2

3

2

3

=2 =0

13 / 44

Process
Fork example
int main(int argc, char **argv)
{
pid_t pid;
pid = fork();
if (pid > 0)
prinft(“I am the parent!\n”);
else if (pid == 0)
prinft(“I am the child!\n”);
else
printf(“I am the initial process! But something went wrong…”);

printf(“I am here now!”);
return 0;
}

What gets printed? (assuming everything goes fine)

14 / 44

Process
Fork example
int main(int argc, char **argv)
{
pid_t pid;
pid = fork();
if (pid > 0)
prinft(“I am the parent!\n”);
else if (pid == 0)
prinft(“I am the child!\n”);
else
printf(“I am the initial process! But something went wrong…”);

printf(“I am here now!”);
return 0;
}

What gets printed? (assuming everything goes fine)

$ ./complete-fork
I am the parent!
I am here now!
I am the child!
I am here now!

15 / 44

1

2 3

5 7 8

Additional ways to group processes:

Process groups (signalling)
Sessions (job control)

Process
Process hierarchy

Notion of a hierarchy (tree) of processes

Each process has a single parent

In Unix, all user processes have init as their ultimate ancestor

16 / 44

Process
exec()

Change executed program in current process

Several different forms with slightly different syntax:

exec[lv]p?e?() (see man page for details)

void exec_ls(void)
{
char *cmd = “/bin/ls”;
char *args[] = { cmd, “.”, NULL };
char *env[] = { NULL };

status = execve(“/bin/ls”, args, env);

printf(“Did everything go well?\n”);
printf(“status=%d\n”, status);
}

Will the printf()’s be executed?

17 / 44

Process
wait()/waitpid()

When a process is done, it can either explicitly call exit(status) or just return and the
exit call will be done implicitly

int retval = main(argc, argv);
exit(retval);

The status is what $? reflects in the shell

A parent can wait for its children (and by default blocks until they are done)

int statloc;
waitpid(pid, &statloc, options);
wait(&statloc);

18 / 44

Process
wait() example
pid = fork();
if (pid == 0) {
/* child */
prinft(“I’m the child and I will die soon!\n”);
exit(42);
} else {
/* parent */
int status;
wait(&status); /* could also be waitpid(pid, &status, 0) */
printf(“Child exited with return code %d\n”, WEXITSTATUS(status));
}

What gets printed?

19 / 44

Process
wait() example
pid = fork();
if (pid == 0) {
/* child */
prinft(“I’m the child and I will die soon!\n”);
exit(42);
} else {
/* parent */
int status;
wait(&status); /* could also be waitpid(pid, &status, 0) */
printf(“Child exited with return code %d\n”, WEXITSTATUS(status));
}

What gets printed?

$ ./fork-wait
I am the child and I will die soon!
Child exited with return code 42

20 / 44

Process
Putting it together: fork()+exec()+wait()
int main(int argc, char **argv)
{
pid_t pid;
int status;
char *cmd[3] = { “ls”, “.”, NULL, };

pid = fork();
if (pid == 0) {
/* Child */
execvp(cmd[0], cmd);
perror(“execvp”);
exit(1);
} else if (pid > 0) {
/* Parent */
waitpid(-1, &status, 0);
printf(“Child exited with status: %d\n”, WEXITSTATUS(status));
} else {
perror(“fork”);
exit(1);
}
return 0;
}

21 / 44

Process
Full example: the shell

A Shell is a programs that typically make heavy use of process system calls

Basic cycle:

1. Display prompt
2. Read line from input
3. Parse line
4. Fork

1. Child executes the command
2. Parent waits

Handle background jobs (&)

Handle redirections (< and >) and pipes (|) for connecting stdin or stdout of the child
to files or other processes

22 / 44

Process
Shell skeleton
while(1) { /* Repeat forever */
char **command;

display_prompt(); /* Display prompt in terminal */
read_command(&command); /* Read input from terminal */

if (fork() != 0) { /* fork off child process */
/* Parent */
waitpid(-1, &status, 0); /* wait for child to exit */
} else {
execv(command[0], commands); /* execute command */
perror(“execv”); /* coming back here is an error */
exit(1);
}
}

23 / 44