Process Definitions- A program in execution
- An instance of a program running on a computer
- A process has
- User-level state: memory + register values
- Memory: stack, heap, code
- Kernel-level state: stack, process info, memory maps, file descriptors, network connections, etc.
Process Execution- A kernel provides a mechanism to create new processes
- On creation, memory is allocated for the process
- Code is loaded into memory, the stack is initialized
- The process begins at a pre-determined location
The Stack- The stack is a special region of memory used to support program execution
- Primarily used for supporting function calls
- Passing parameters, local variables, return address
- Used by compilers to "spill" registers to memory
- Most programming languages use a stack to support program execution
- Process execution needs
- Stack pointer (sp)
- Instruction pointer (ip or pc)
- General registers (r1, r2, eax, ebx, etc.)
UNIX Concepts- Processes
- System calls
- File descriptors
- File and directories
- Pipes
Overview of System Calls- The OS kernel is an extended machine
- The kernel provides convenient abstractions for system resources:
- Processes
- Memory
- Files
- Network
- User mode processes access kernel abstractions using system calls
- In C, a system call looks like a function call:
- However, unlike normal functions, a system call transfers control to the kernel
- Draw picture of getpid() and sys_getpid()
System Call Templateint syscall(type1 arg1, type2 arg2, ...) {
put arguments into registers
trap into kernel using special instruction
(e.g., int 0x80)
extract return value and return
}
System Calls vs Library Functions- A system call is executed in the kernel
- A library function is executed in user space
- Some library calls are implemented with system calls
- printf() really calls the write() system call
- Programs use both system calls and library functions
UNIX System Calls- Files and I/O
- Processes
- Pipes
- Signals
- Time
- Network I/O with sockets
UNIX Man Pages- UNIX man pages (manual pages, documentation) are divided into sections:
- Section 1: commands (issued from command line)
- Section 2: system calls
- Section 3: library functions
- Examples:
System Call Basics- Since system calls are functions, we need to include the proper header files
- E.g., for getpid() we need
- #include <sys/types.h>
- #include <unistd.h>
- See man pages for details.
- Most system calls have a meaningful return value
- Usually, -1 or a negative value indicates an error
- A specific error code is place in a global variable called
- To access errno you must declare it:
Using errno- Consider the read() system call
- ssize_t read(int fd, void *buf, size_t nbytes);
- On success it returns the number of bytes read
- Otherwise it returns -1
- Actual error code is errno, some errors include
- [EBADF] fd is invalid
- [EFAULT] buf is an invalid address
- [EIO] I/O error
- [EISDIR] fs is a directory, not a file, cannot read
- Use char *strerror(int code) to get a text version of errno
UNIX Files and I/O- Steps for accessing a file (and devices):
- open a file/device (existing or new)
- open returns a file descriptor (fd)
- Use the fd to access the file/device (read/write/etc.)
- close(fd) when finished
- It is possible to open more than one file at a time
- You should alway close a file when you are finished
File Descriptors- A small non-negative integer
- Returned by open()
- Used as first argument in read(), write(), close(), etc.
- The fd is an index into the process's file table
- The kernel uses the file table to keep track of open files
- By default, the table consists of:
- 0 : stdin
- 1 : stdout
- 2 : stderr
Default File Descriptors- You do not need to open the default file descriptors
- The are used to obtain console input and give console output
- Examples
write(1, "Hello World!\n", 13);
char buf[128]; read(0, buf, 127);
The open() System Call#include <fcntl.h> int open(char *path, int flags [, mode_t mode]); - Create a new file or open an existing file
- Flags
- O_RDONLY
- O_WRONLY
- O_RDWR
- O_CREAT
- use bitwise or ("|") (e.g., O_WRONLY | O_CREAT)
- Mode: octal value for rwx bits (e.g. 0400 read only by owner)
More File I/O System Callsint close(int fd);
Close a previously opened file
ssize_t read(int fd, void *buf, ssize_t nbytes);
Extract date from a file or device
ssize_t write(int fd, void *buf, ssize_t nbytes):
Write to a file or device
off_t lseek(int fd, off_t offset, int whence);
Move to a specified byte offset in file
File I/O Examplescount.c/* count.c - count the number of bytes in a file */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSIZE 512 /* number of bytes to read at a time */
int main(int argc, char *argv[])
{
char buffer[BUFSIZE];
int fd;
int count;
long total = 0;
if ( (fd = open(argv[1], O_RDONLY)) < 0) {
printf("Cannot open %s\n", argv[1]);
exit(1);
}
printf("fd = %d\n", fd);
while ( (count = read(fd, buffer, BUFSIZE)) > 0 ) {
total += count;
}
printf("Total bytes in %s is %ld\n", argv[1], total);
close(fd);
return(0);
}
caps.c/* caps.c - convert user input to caps and write to a file */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
char buffer[2];
int fd;
int count;
long total = 0;
if (argc != 2) {
printf("usage: caps filename\n");
exit(1);
}
if ( (fd = open(argv[1], O_CREAT | O_WRONLY, 0600)) < 0) {
printf("Cannot open %s\n", argv[1]);
exit(1);
}
while ( (count = read(0, buffer, 1)) > 0 ) {
buffer[0] = toupper(buffer[0]);
write(fd, buffer, 1);
}
close(fd);
return(0);
}
Process Details- In user space
- Code (C functions)
- Data (static and dynamic)
- Stack
- Registers
- In kernel space
- Process Control Block
- Priority
- File descriptors
- Memory map
- Others

UNIX Processes- Recall a process is a program in execution
- Processes create other processes with the fork() system call
- fork() creates an identical copy of the parent process
- We say the parent has cloned itself to create a child
- We can tell the two process apart use the return value of fork()
- In parent: fork() returns the PID of the new child
- In child: fork() returns 0
- fork() may seem strange at first, that's because it is a bit strange!
- Draw picture
main() { int id;
id = fork(); if (id == 0) { /* in child */ } else { /* in parent */ } }

Starting New Programs- fork() only allows us to create a new process that is a duplicate of the parent
- The exec() system call is used to start a new program
- exec() replaces the memory image of the calling processes with the image of the new program
- We use fork() and exec() together to start a new program
- Note: be sure to check the return value of exec()
- If exec() fails, then the memory image of the calling process remains the same
- exec() does not return success (why?)
- Draw picture:
main() { int id; id = fork(); if (id == 0) { /* in child */ exec("/bin/ls"); } else { /* in parent */ wait(); } }
Syscalls for Processes- pid_t fork(void)
- Create a new child process, which is a copy of the current process
- Parent return value is the PID of the child proces
- Child return value is 0
- int execl(char *name, char *arg0, ..., (char *) 0)
- Change program image of current process
- Reset stack and free memory
- Start at main()
- Also see other versions: execlp(), execv(), etc.
- pid_t wait(int *status)
- Wait for a child process (any child) to complete
- Also see waitpid() to wait for a specific process
- void exit(int status)
- Terminate the calling process
- Can also achieve with a return from main()
- int kill(pid_t pid, int sig)
- Send a signal to a process
- Send SIGKILL to force termination
Process Creation Details- fork() creates a copy of the current process
- It is the only way to create a new process
- What is copied on fork():
- Memory of process (code, data, stack)
- Registers (stack pointer, program counter, etc.)
- File descriptors
- Command-line arguments
- Environment
- Only the return value is different
Process Examplesfork1.c/* fork1.c - example of using the fork system call */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
pid_t id;
int x = 3;
printf("Parent: x = %d\n", x);
id = fork();
if (id == 0) {
/* we are in the child */
printf("Child: id = %d\n", id);
printf("Child PID = %d\n", getpid());
printf("Child: x = %d\n", x);
x = 99;
printf("Child: x = %d\n", x);
printf("Child: &x = 0x%8X\n", &x);
printf("Child: &x = 0x%8X\n", (void *) 0x1);
exit(0);
} else {
/* we are in the parent */
printf("Parent PID = %d\n", getpid());
printf("Parent: id = %d\n", id);
printf("Parent: wait for child\n");
id = wait(NULL);
printf("Parent: child terminated pid = %d\n", id);
printf("Parent: x = %d\n", x);
printf("Parent: &x = 0x%8X\n", &x);
}
return 0;
}
fork2.c/* fork2.c - example of using the fork and execl system calls */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
pid_t id;
id = fork();
if (id == 0) {
/* we are in the child */
execl("/bin/date", "date", NULL);
printf("Child: WE DON'T SEE THIS\n");
exit(0);
} else {
/* we are in the parent */
id = wait(NULL);
printf("Parent: child terminated\n");
}
return 0;
}
|