Processes and Threads
CSCI 156
Project 2 Server
• How would you write it?– Multiple connections on multiple ports– Communication between input handlers and
output generators
• Some Options– Multiple processes– Multiple threads– One monolithic program
Multiple Processespid_t fork();
Child process has value of 0 for the returned pidParent has process id of the child
int pid = fork(); if(pid) { printf("I'm the parent! pid=%d\n", pid); } else { printf("I'm just a baby! pid=%d\n", pid); }
This is frequently used to open a new program with exec:fork then have the child exec.
Multiple Processes (cont.)int execve(const char *filename, char *const argv [], char *const envp[]);
execve() runs the program at path “filename”, with arguments argv and optional environment variables in envp. argv[0] must be the same as filename.
pid_t wait(int *status);
wait() blocks until a child process exits. It then returns the child pid, and fills the status variable with the exit return code of the child.
pid_t waitpid(pid_t pid, int *status, int options);
waitpid() lets you specify a child pid to wait for, and also allows options to be passed
Multiple Processes (cont.) int pid, status, error; char *name[2], *foo; foo = (char*) malloc(20 * sizeof(char)); printf("give us a string!\n"); scanf("%s", foo);
pid = fork(); if(pid) { printf("I'm the parent! pid=%d\n", pid); if(waitpid(pid, &status, 0) == pid) { printf("my baby died!\n"); } } else { printf("I'm just a baby! pid=%d\n", pid); name[0] = "/bin/echo"; name[1] = foo; execve(name[0], name, NULL); }
Any problems with this code?
Command Line Argumentsint main (int argc, char* argv[])
{
}
argv - is an array of character pointers to the words that were on the command line
argc - holds the number of words specified in the command (length of argv array)
./myPrg 5
results in argc=2 with argv[0]=“./myPrg” and argv[1]=“5”
int x;
x = atoi(argv[1]); /* Now x=5 */
Multiple Processes (cont.)int main (int argc, char* argv[]){ int pid, status, error; char *name[2];
pid = fork(); if(pid) { printf("I'm the parent! pid=%d\n", pid); if(waitpid(pid, &status, 0) == pid) { printf("my baby died!\n"); } } else { printf("I'm just a baby! pid=%d\n", pid); name[0] = "/bin/echo"; name[1] = argv[1]; execve(name[0], name, NULL); }}
Multiple Processes (cont.)forkbomb (n) – 'explodes' by recursively spawning copies of itself. eventually overloads the number of available processes, and stalls the machine.
DO NOT try this on hobbes: Sheryl will destroy you.
main() {for(;;)fork();}
expanded:
int main(){
while(1)fork();
}
Multiple Processes (cont.)Back to the server: we fork once for each new connection and have the children handle the incoming messages:
create sockets, bind to both portsloop forever
if(incoming sender connection)fork if(child – sender process)
loop waiting for messages on connected socketif a msg/file, send to all recver processes
doneif(incoming recver connection)
forkif(child – recver process)
loop waiting for messages from sender processesif a valid msg/file, send to connected socket
donedone
Multiple Processes (cont.)Problem with multiple processes is use of Unix IPC methods to communicate between processes:
SIGNALS: sigaction() sets a function to be called when the program gets a signal. kill(pid, signal) sends the signal to a process. Can't send actual data, just two instructions: SIGUSR1 and SIGUSER2
SHARED MEMORY: shmget() is used to create/get a reference to shared memory, then shmat is used to get a normal pointer to it (shmdt to give it up). Use a mutex to control access.
PIPES: pipe() creates two file descriptors that can be used with write/read (like send/recv on sockets). Should be used one-way, created before forking
FIFO (named pipes): special file on disk, created with mkfifo. Uses write/read, but can be used between non-forked programs
Lots more! http://www.ecst.csuchico.edu/~beej/guide/ipc/
One Monolithic ProgramMust keep an array of open sockets, and when a connection is accept()-ed, add to the list, then have one large loop that checks for incoming information on each open socket, then checks for new connections. Overall structure:
create sockets, bind to both portsloop forever
loop through open sender socketscheck for messages on open “sender” connectionsif(incoming message)
if a msg/fileloop on all open “recver” connections
send message to recverwhile(there are incoming sender connections)
add to sender socket arraywhile(there are incoming recver connections)
add to recver socket arraydone
Advantages/Disadvantages?
ThreadsThreads are similar to processes, but all run under a single process ID, and share more information than forked processes (exam T/F question?)
Threads have some advantages:
1) less overhead (creating a process = expensive)2) can take more advantage of SMP support3) no need to use Unix IPC: can use mutex locks
But also disadvantages:
1) debugging them can be a pain (better with gdb 6+, kernel 2.6+, but still no fun at all)
2) code is complicated: for simple programs, maybe easier to fork
Let’s create a thread… How?Need to include the library
#include <pthread.h>
And compile with the -lpthread flag:
gcc -lpthread mySource.c
Like any other type: need to declare and malloc
pthread_t *ghostThread;ghostThread = (pthread_t*)malloc(sizeof(pthread_t));
Let’s create a thread (cont.)We can then set up the thread and actually start it running:void *our_function (void *parameter) { printf(“Hello World\n”);}
int main () { void* parameter=NULL; pthread_t* pacmanThread; pacmanThread = (pthread_t*)malloc(sizeof(pthread_t));
pthread_create(pacmanThread, NULL, our_function, parameter);
}
Let’s create a thread (cont.)int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*),
void *arg);
The pthread_create() function is used to create a new thread, with attributes specified by attr, within a process. If attr is NULL, the default attributes are used. If the attributes specified by attr are modified later, the thread's attributes are not affected. Upon successful completion, pthread_create() stores the ID of the created thread in the location referenced by thread. The thread is created executing start_routine with arg as its sole argument. If the start_routine returns, the effect is as if there was an implicit call to pthread_exit() using the return value of start_routine as the exit status.
Thread Attribute TypeUsed to pass desired attributes to create
Declaration:pthread_attr_t pthread_custom_attr;
Functions used with:
pthread_attr_init(&pthread_custom_attr);
int pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy);
Use with policy constants: SCHED_FIFO, SCHED_RR and SCHED_OTHER
int pthread_attr_setstacksize (pthread_attr_t *attr,
size_t stacksize);
Lets create a thread continuedWe can then set up the thread using the attributes and
actually start it running:
pthread_create(ghostThread, pthread_custom_attr, our_thread_function, parameter);
Some useful pthread functionsvoid pthread_exit(void *value_ptr);
Called inside our threaded function. The pthread_exit function terminates the thread that calls it and makes the value value_ptr available to its parent (if the parent has joined on it)
int pthread_join(pthread_t thread, void **value_ptr);
The pthread_join() function suspends execution of the calling thread until the target thread terminates, unless the target thread has already terminated. On return from a successful pthread_join() call with a non-NULL value_ptr argument, the value passed to pthread_exit() by the terminating thread is made available in the location referenced by value_ptr.
Locks Declaration:
/* Global Variables */int myInt;pthread_mutex_t myIntLock;
Must be Initialized prior to use:pthread_mutex_init(&myIntLock, NULL);
Locks also have an attribute field (much like threads):
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
Locking and Unlockingint pthread_mutex_lock(pthread_mutex_t *mutex);
Blocks if someone else has the lock..
int pthread_mutex_trylock (pthread_mutex_t *mutex);
Doesn’t block if someone else has lock. Call returns with failure. Lets you do other things…
Finished with the lock- let it go:
int pthread_mutex_unlock (pthread_mutex_t *mutex);
Locks FinaleWhen all threads finished with the lock:int pthread_mutex_destroy (pthread_mutex_t *mutex);
pthread_mutex_destroy(&myIntLock);
Spin Waiting – Not preferred way to go. Why?while(!pthread_mutex_trylock(&myIntLock)) {
/* Do nothing */
}
/* critical section */