Process Processes and Threads A process is an abstraction that represent an executing program A program in execution An instance of a program running on a computer The entity that can be assigned to and executed on a processor A unit of activity characterized by a single sequential thread of execution, a current state, and an associated set of system resources Process Multitasking A process is an abstraction that represent an executing program Code Data Context OS information about the process Running state Process Identifier (PID) I/O operations are much slower than CPU instructions running a single application cause a very low CPU usage Process running Process waiting for I/O A single process running on a CPU
Multitasking Multitasking I/O operations are much slower than CPU instructions running a single application cause a very low CPU usage share the CPU among several processes Process management process execution, suspension, resuming Memory management Process 1 Process 2 Process 3 Three processes running on a CPU separate (and protect) data among processes Inter-process communication Inter-process Total CPU usage Process state Process state A process can be: Running A process can be: New Finished for execution, waiting for some event resumed Running suspended I/O completed or unlocked waiting for I/O or just created, no code loaded Running for execution, waiting for some event Finished New resumed Running suspended I/O completed or unlocked waiting for I/O or
Process state Process state A process can be: A process can be: New Finished New Finished just created, no code loaded just created, no code loaded Running for execution, waiting for some event Suspended Finished New resumed Suspended Running suspended I/O completed or unlocked waiting for I/O or Running for execution, waiting for some event Suspended Finished New resumed Suspended Running suspended I/O completed or unlocked waiting for I/O or Process state Process creation A process can be: New just created, no code loaded Running for execution but suspended, waiting for some event and suspended Finished Suspend Suspend Dispatch Suspended Activate -out Running Event: I/O completed or unlocked Event New Finished Suspend waiting for I/O or Suspended Activate System initialization background processes (daemons) user interaction processes e.g.: shells Running process request e.g.: user request batch job start External event handling e.g.: client request Unix system call: fork()
Unix fork: example Process hierarchy child_id= fork(); /* syscall invocation */ if (child_id == 0) { /* this is the child process */ } else if (child_id > 0) { /* parent process */ PID=500 child_id=501 PID=501 child_id=0 A created process is child of its creator ( parent ) Proc1 } else { /* parent process, but child not created */ Proc2 Proc3 } Proc4 Proc5 Proc6 Process termination Process termination Normal or error exit program auto -termination exit(), abort() Fatal error Unix existing children of a terminated process are inherited by process with PID 1 PID=1 program terminated by OS Killed by another process (through OS) Unix PID=2 PID=5 kill(<target_pid>, SIGTERM) kill(<target_pid>, SIGKILL) PID=22 kill(<target_pid>, <signal>) PID=4
Process termination Process scheduling Unix OS keep a queue (or a table) of Process Control Blocks (PCBs) enough information to interrupt and resume a process existing children of a terminated process are inherited by process with PID 1 information on a terminated process are kept in memory (zombies) until its exit code is read by parent (if any) PID=2 PID=1 PID=5 PID=4 PID=22 State: Terminated Exit code: 5 Identifiers Process ID Parent ID User ID Process name Processor state CPU registers CPU flags Stack pointer Program counter Process control information Process state Scheduling information priority reason for blocking Working directory Memory allocation Status of process' open file signals state PCB access Process creation Direct (manipulation of struct fields) fast dangerous many routines access PCB, a single bug can severely hurt the system changes hard to insert many routines have to be updated Through handlers safer 1. Assign ID 2. Allocate memory 3. Initialize PCB 4. Initialize/update control structures scheduling queues, file tables, etc. slow
Process scheduling Process scheduling No preemption 1. Run current process until it blocks 2. Save process state, CPU registers, etc. and mark process as blocked 3. Select a ready process (look at priorities) 4. Resume the new process state, CPU registers, etc., and mark process as running when an I/O ends (signaled by an interrupt) mark the blocked process as ready Preemption 1. Run current process until it blocks or its time slice expires 2. Save process state, CPU registers, etc. and mark process as blocked or ready 3. Select a ready process (look at priorities) 4. Resume the new process state, CPU registers, etc., and mark process as running when an I/O ends (signaled by an interrupt) mark the blocked process as ready Process switch Process scheduling slice expiration Example for preemptive OS Syscalls I/O Synchronization Process switch Process 1 overhead Process 2 Process 3 interrupt Process 1 continued Interrupt Error or trap slice has expired Process 2 requires I/O interrupt handling Process 2 marked ready
OS execution context Threads Non process kernel Kernel code has its own memory region Process concept applies only to user programs Execution within process Kernel code is executed into the user process context Kernel can use its own stack for interrupts A syscall causes a mode switch, but not a process switch Process based OS Kernel is implemented as a collection of system processes P 1 P 2 P 3 P 4 Kernel P 1 P 2 P 3 P 4 K K K K Proc-switching funcs Components of a process Distinct execution flows in the same address space Distinct stacks and, then, distinct local variables Threads can be suspended and resumed separately Some info are moved from PCB to TCB (thread control block) Kernel and user data are separated Every syscall can cause a process switch Multiprocessor systems can mitigate such an issue P 1 P 2 K 1 K 2 Proc-switching funcs 3 processes 7 threads Threads Threads Management level Kernel level slow switch requires system calls User level in Windows: fibers when a thread is blocked, the whole process is blocked cannot leverage multiprocessor systems only a fiber can be executing at a time explicit user switching SwitchToFiber Hybrid Management level (linux) Kernel level threads are considered as processes sharing the same address space creation clone() syscall use pthread_create
Unix threads: example Interprocess communication void *thr_routine(void *arg) { /* entry point for the new thread */ } int shared; /* variable shared among threads */ main() { pthread_t thr; int res; } res = pthread_create(&thr, /* thread pointer */ NULL, /* attribute (NULL for default) */ thr_routine, 0 /* routine argument */ ); if (res) { /* error */ } main thr_routine process threads share global vars threads are distinct on: local variables Signals to signal exceptional events through OS default handling user defined handling Shared memory Message passing fifo (pipe) socket Signals Signals Identified by a numeric identifier a) Caught by a special process routine (if any) signal handler b)handled in default way e.g.: SIGQUIT: program termination c) Ignored d) (within a signal handler) some signals cannot be caught, blocked, or ignored Library OS Application1 kill(1000, SIGUSR1) syscall signal SIGUSR1 Application2 (pid = 1000) signal handler User level Signal handlers are execute asynchronously with respect to the program flow Kernel level SIGKILL, SIGSTOP
Shared memory Example: Unix System V shared memory int memid = 0; volatile unsigned char *address; int memid = 0; volatile unsigned char *address; /* create a shared memory area (or get an existing one) */ memid = shmget(1234, /* key */ 1024, /* size */ IPC_CREAT 0600); /* flags */ /* create a shared memory area (or get an existing one) */ memid = shmget(1234, /* key */ 1024, /* size */ IPC_CREAT 0600); /* flags */ Address space Process A Process B Synchronization must be explicitly enforced Address space /* attach (get an address for shared memory) */ address = shmat(memid, NULL, 0); printf("parent: shared memory at address %p\n", address); strcpy((char*)address+10, "Data-String"); printf("parent: string written\n"); address[0] = 0xBA; address[1] = 0xAB; address[2] = 0xAC; address[3] = 0xCA; /* unlock process B */ while (address[0]!= 0x01 address[1]!= 0x02 address[2]!= 0x10 address[3]!= 0x20) ; /* wait for process B */ /* detach */ shmdt((void*)address); /* mark shared memory for removing (when all have been detached) */ shmctl(memid, IPC_RMID, NULL); Process A /* attach (get an address for shared memory) */ address = shmat(memid, NULL, 0); /* address = shmat(memid, (void *)0xc000000, 0); */ printf("child: shared memory at address %p\n", address); while (address[0]!= 0xBA address[1]!= 0xAB address[2]!= 0xAC address[3]!= 0xCA) ; /* wait for process A */ printf("child: string read is: "); printf("\"%s\"\n", address+10); address[0] = 0x01; address[1] = 0x02; address[2] = 0x10; address[3] = 0x20; /* unlock process A */ /* detach */ shmdt((void*)address); Process B int memid = 0; volatile unsigned char *address; Example: Posix shared memory /* create a shared memory area (or get an existing one) */ memid = shm_open("/shm-name", O_RDWR O_CREAT, /* flag */ S_IRUSR S_IWUSR); /* mode */ ftruncate(memid, 1024); /* attach (get an address for shared memory) */ address = mmap(0, /* start */ 1024, /* length */ PROT_READ PROT_WRITE, /* prot */ MAP_SHARED, /* flags */ memid, /* fd */ 0); /* offset */ printf("parent: shared memory at address %p\n", address); strcpy((char*)address+10, "Data-String"); printf("parent: string written\n"); address[0] = 0xBA; address[1] = 0xAB; address[2] = 0xAC; address[3] = 0xCA; /* unlock process B */ while (address[0]!= 0x01 address[1]!= 0x02 address[2]!= 0x10 address[3]!= 0x20) ; /* wait for process B */ /* detach */ munmap((void*)address, 1024); /* mark shared memory for removing (when all have been detached) */ shm_unlink("/shm-name"); Process A int memid = 0; volatile unsigned char *address; /* create a shared memory area (or get an existing one) */ memid = shm_open("/shm-name", O_RDWR O_CREAT, /* flag */ S_IRUSR S_IWUSR); /* mode */ /* attach (get an address for shared memory) */ address = mmap(0, /* start */ 1024, /* length */ PROT_READ PROT_WRITE, /* prot */ MAP_SHARED, /* flags */ memid, /* fd */ 0); /* offset */ /* address = mmap((void )0xc000000, 1024, PROT_READ PROT_WRITE, MAP_SHARED, memid, 0); */ printf("child: shared memory at address %p\n", address); while (address[0]!= 0xBA address[1]!= 0xAB address[2]!= 0xAC address[3]!= 0xCA) ; /* wait for process A */ printf("child: string read is: "); printf("\"%s\"\n", address+10); address[0] = 0x01; address[1] = 0x02; address[2] = 0x10; address[3] = 0x20; /* unlock process A */ /* detach */ munmap((void*)address, 1024); Process B shmget shared memory creation/opening shmat, shmdt attach, detach Unix: see also shmctl control: remove, change permissions, get status shm_open, ftruncate creation/opening, size setting mmap, munmap attach, detach unlink remove
Message passing Example: Unix socket Process A send blocking non-blocking receive Process B Synchronization is implicit with the data exchange struct sockaddr_un address; struct sockaddr_un client_addr; int main_socket = -1; int server_socket = -1; socklen_t clientsize; char data[datasize]; /* socket creation */ main_socket = socket( PF_UNIX, SOCK_STREAM, /* reliable, connection-based */ /*SOCK_DGRAM,*/ /* unreliable, connectionless */ 0 /* default protocol */ ); /* assigning an address to socket */ address.sun_family = AF_UNIX; memset(address.sun_path, 0, sizeof(address.sun_path)); strcpy(address.sun_path, "SOCKETNAME"); bind(main_socket, (struct sockaddr*)&address, sizeof(address)); /* put socket in listening mode */ listen(main_socket, 1); clientsize = sizeof(client_addr); /* accept a connection (wait for it) */ server_socket = accept( main_socket, (struct sockaddr*)&client_addr, &clientsize); /* read data */ read(server_socket, data, sizeof(data)); /* write data */ write(server_socket, &i, sizeof(i)); Server side int client_socket = -1; struct sockaddr_un address; char data[datasize]; client_socket = socket( PF_UNIX, SOCK_STREAM, /* reliable, connection-based */ /*SOCK_DGRAM,*/ /* unreliable, connectionless */ 0 /* default protocol */ ); /* assigning an address to socket */ address.sun_family = AF_UNIX; memset(address.sun_path, 0, sizeof(address.sun_path)); strcpy(address.sun_path, "SOCKETNAME"); /* connect to a listening socket */ connect(client_socket, (struct sockaddr*)&address, sizeof(address)); /* write data */ write(client_socket, &i, sizeof(i)); /* read data */ read(client_socket, data, sizeof(data)); Client side Example: IP socket Unix: see also struct sockaddr_in address; struct sockaddr_in client_addr; int main_socket = -1; int server_socket = -1; socklen_t clientsize; /* socket creation */ main_socket = socket(pf_inet, SOCK_STREAM, /* reliable, connection-based (TCP) */ /*SOCK_DGRAM,*/ /* unreliable, connectionless (UDP) */ 0 /* default protocol */ ); /* assigning an address to socket */ address.sin_family = AF_INET; address.sin_port = 3000; inet_aton("127.0.0.1", &address.sin_addr); bind(main_socket, (struct sockaddr*)&address, sizeof(address)); /* put socket in listening mode */ listen(main_socket, 1); clientsize = sizeof(client_addr); /* accept a connection (wait for it) */ server_socket = accept( main_socket, (struct sockaddr*)&client_addr, &clientsize); res = read(server_socket, data, sizeof(data)); write(server_socket, data, sizeof(data)); Server side int client_socket = -1; struct sockaddr_in address; char data[datasize]; client_socket = socket( PF_INET, SOCK_STREAM, /* reliable, connection-based (TCP) */ /*SOCK_DGRAM,*/ /* unreliable, connectionless (UDP) */ 0 /* default protocol */ ); /* assigning an address to socket */ address.sin_family = AF_INET; address.sin_port = 3000; inet_aton("127.0.0.1", &address.sin_addr); /* connect to a listening socket */ connect(client_socket, (struct sockaddr*)&address, sizeof(address)); write(client_socket, data, sizeof(data)); read(client_socket, data, sizeof(data)); Client side socket open a new socket (choose domain, and type) bind, listen, accept assign name, start listening, accept incoming connections connect connect to a listening socket write, send, sendto, sendmsg send data read read data