Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

ECE 434: Intro to Computer Systems -- Spring 2023

Homework 3 (40 points, 8+12+8+12) choose 4 out of 6 problems

Issue Date: Sat 04-08-23

Due Date: Wed 04-19-23, at 8.00pm

Problem 1: (8 marks) PROCESSES AND PIPES

Assume that all system calls execute without errors and function f1 never returns. Processes that have entered the wait queue may be unblocked at a random order (no FIFO is assumed). Only one process can read at a time.

List all the possible trees in the steady state. Write the function or system call and argument with which a process reaches at steady state. In which case do we have the fewest processes at steady state and in which cases the maximum processes at steady state?

int pg[2];

void handler(int signum) { exit(4); f1(11, -1); }

int main(void) {

int i, j, n; char c[10]; pid_t pid[4]; //variable declarations

signal(SIGUSR1, handler);

pipe(pg);

for (i = 0; i < 5; i++) {

pid[i] = fork();

if (pid[i] == 0) {

sleep(3);

n = read(pg[0], c, 2*i+1);

if (i<3) exit(2-i); else f1(i,n);

}

}

kill(pid[i - 2], SIGUSR1);

for (i = 0; i < 5; i++) {

wait(&n);

write(pg[1], c, WEXITSTATUS(n));

}

f1(i, -1);

return 0;

}

Problem 2 (12 pts): Signal registration function signal and handlers.

Part 1 (6 pts): Please describe what happens when:

Case 1) An external process sends a terminating signal to the parent (must investigate when within the program this signal is delivered),

Case 2) An external process sends a terminating signal to the child (must investigate when within the program this signal is delivered),

Case 3) We change the signal handler within the signal registration function to: SIG_IGN and re-apply Case 1 and Case 2,

Case 4) Change the signal handler within the signal registration function to: SIG_DFL and apply Case 1 and Case 2.

Case 5) In the above cases assume that: a) signal function does not re-install itself (older version), b) signal function re-installs itself (newer version).

void chldhandler() {

int pid1, status;

pid1 = wait(&status);

printf(child done in time\n”);

exit(status);

}

void main(int argc, char* argv[]) {

int pid;

signal(SIGCHLD, chldhandler);

pid = fork();

if (!pid) execvp(argv[2], &argv[2]);

else {

sleep(10);

printf(“child too slow\n”);

kill(pid, SIGINT);

}

}

Part 2 (6 pts): In this system there is a parent process and 10 children processes. The parent has

registered the handler defined below at the beginning of its life: signal(sigchld, chldhandler); This is the only place in the code where the parent places waitpid() call. The previous handler of signal SIGCHLD is the default handler. The 10 child process present the following behavior: The first 4 terminate almost simultaneously, while the other 6 are active into the system. At a much later time, the other 6 terminate also simultaneously. Describe exactly how each of the 10 children processes are going to be handled for Case1 and for Case2 described next.

Then, describe what will happen to the 10 children processes, if in between handling the first 4 processes and the next 6 processes, the parent process does the following action: signal(sigchild, SIG_IGN);

Note that: SIG_DFL for SIGCHILD causes child processes to become zombies on exit, until their exit status is retrieved. Setting SIGCHLD to SIG_IGN indicates that the parent doesnt care about its children’s exit codes, and they are reaped immediately on exit.

Case 1): using an outdated OS that does not automatically re-install the handler

Case 2): using a modern OS that does automatically re-install the handler.

void chldhandler(int signum) {

pid_t p;

int status;

do  {

p = waitpid(-1, &status, WUNTRACED|WNOHANG);

if (p<0) {

perror(“waitpid”);

exit(1);

}

explain_wait_status(p,status);

if (WIFEXITED(status) || WIFSIGNALED(status))

printf(“ a child has died\n”);

if (WIFSTOPPED(status))

printf(“A child has stopped due to SIGSTOP/SIGTSTP \n”); } while (p>0);

}

Problem 3: (8 pts)

The Fibonacci sequence is the series of numbers 0, 1, 1, 2, 3, 5, 8, ....

Formally, it can be expressed as:

fib0 = 0

fib1 = 1

fibn =fibn−1 +fibn−2

Write a multithreaded program that generates the Fibonacci sequence. This program should work as follows: On the command line, the user will enter the number of Fibonacci numbers that the program is to generate. The program will then create a separate thread that will generate the Fibonacci numbers, placing the sequence in data that can be shared by the threads (an array is probably the most convenient data  structure).  When  the  thread  finishes  execution,  the  parent  thread  will  output  the  sequence generated by the child thread. Because the parent thread cannot begin outputting the Fibonacci sequence until the child thread finishes, the parent thread will have to wait for the child thread to finish.

This problem requires the parent thread to wait for the child thread to finish its execution before printing out the computed values. If we let the parent thread access the Fibonacci numbers as soon as they have been computed by the child thread rather than waiting for the child thread to terminate what changes would be necessary to the solution for this exercise? Implement your modified solution.

Problem 4: (12 pts)

The Sieve of Eratosthenes is a simple, ancient algorithm for finding all prime numbers up to any given limit. It does so by iteratively marking as composite (i.e., not prime) the multiples of each prime, starting with the multiples of 2.

https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

The sieve of Eratosthenes can be expressed in pseudocode, as follows:

Input: an integer n > 1.


Let A be an array of Boolean values, indexed by integers 2 to n,

initially all set to true.

for i = 2, 3, 4, ..., not exceeding n:

if A[i] is true:

for j = i2, i2+i, i2+2i, i2+3i, ..., not exceeding n:

A[j] := false.

Output: all i such that A[i] is true.

This algorithm produces all primes <= n. It includes common optimization, which is to start enumerating the multiples of each prime i from i2 . The time complexity of this algorithm is O(n log log n).

Write a multi-threaded program that outputs prime numbers. The program should work as follows: the user will run the program and enter a number on the command line. The program creates a NEW THREAD upon every user request that outputs all the prime numbers less than or equal to the number entered by the user. Every prime number discovered is stored in an array or list in main function, in an ascending order: e.g., 1, 2, 3, 5, 7, 11, 13, etc. All threads created build this array/list cooperatively. Threads that are to list prime numbers less or equal to the number entered by the user can hence make a shortcut by first identifying what the highest prime number reported in the list is, and then starting (if needed) computations from the last element on the primes array/list of the main function.

As the array/list of primes is expanding, create a separate pool of threads that undertake a subset of these prime numbers each and investigate for each prime number if the number that emerges by reversing the digits is also prime (e.g., 37 and 73 or 113 and 311). If this is the case, then the thread appends this double prime number” to another array/list that is collaboratively built by all threads. In other words, threads are to collaboratively build an array/list in main function that stores primes with the above property. However please note: it would be best if threads are assigned their subset/pool of prime numbers atomically, so that not 2 or more threads get the same subset of numbers to investigate (use ideas and concepts from the Pthreads Workers demo to be presented to class).


Problem 5: (12 pts)

Part 1 (6 pts): Write a C-program spawning 3 Pthreads. We are interested in handling the following signals both from main function and from the threads:                                                                                                       SIGFPE, SIGSTOP, SIGSEGV.                                                               Each Pthread is spawned with the objective to catch one of the above signals only, and block or ignore the rest. Moreover, each thread with identity tid[i] should also compute the sum of integers from 0 to 1,000 x tid[i]. When one Pthread (or the main function) catches a signal, it should modify handling to just output the signal number and its own identity (thread or main).  The main function is set up to ignore the signals until the three threads are created. After the three threads join, then the main function restores default operation for all three signals.           You may select to either use signal or sigaction for setting up the signal handlers.

Part 2 (6 pts):                                                                                                                                                                  Answer the following questions using the C program you have written:

Q1: (3 pts) What will be observed when the process receives one of the above signals externally (from terminal) during execution? Briefly describe each case. What does the result depend on? Please justify your answer.

Q2: (3 pts) What will be observed when one thread sends one of the signals to the rest of the two threads and to the process itself? Study each case separately.  Please justify your answer.



Problem 6 (12 pts): PROCESSES AND SIGNALS

Study the code below very carefully. Take very detailed notes to check which signals are involved in every system call. Assume that this LINUX OS system is an outdated one. For data required but not provided make your own assumptions and solve the problem accordingly. Then, answer the following questions:

Scenario 1: First assume that the parent and the child send the signals only once, i.e.: parent sends to the

child all signals included in lines: 150-157 and child send to the parent all signals included in lines: 180- 188 only.

Q1 (3 pts) Which signals does the data structure sigismember consists of in both cases of parent and child process? Q2 (3 pts) Which signals may get blocked during handler execution and which during regular program execution? How is each signal that comes through, handled?

Scenario 2: Now assume that the parent and the child send all signals that are included in the lines of this code: i.e., parent sends to the child all signals included in lines: 150-165 and child send to the parent all signals included in lines: 180-197.

Q3 (3 pts) Which signals go through, and which get blocked? Which and how many of them are pending in the waiting_mask? Those signals that are go through, how are they implemented each time (if there are signals that terminate the process, ignore them in order to be able to answer the above).

Q4 (3 pts) By which signals can the parent process be terminated and by which the child? Write all possibilities and describe exactly how. Do not assume that all signals are delivered in the order written in the code. However, when you are describing about an X signal that may terminate the process, you have to ignore signals that have presumably terminated the process before that one. Again, the question aims to list all possible scenarios that may terminate the process, not just the first and obvious possibility.

Hint: If you are not sure about the meaning and disposition of any of the signals listed below, please go online and check their meaning and use.

#include   //remember this is pseudo-code and

// this is on purpose

pid_t pid, p, par_pid, cid;

union sigval  payload;

void sig_handler(int signum) {

fprintf(stderr,"sig_handler: Signal: %d in proc pid=%d, i=%d\n",signum,getpid());

sleep(10);

}

void sig_usr1(int signum,siginfo_t *info) {

int_val = info->si_value.sival_int;  //this is the payload sent through sigqueue

func(int_val);

sleep(10);

}

int func(int p) {

return((int)(getpid()/((getppid())* p)));

}

int main(int argc, char *argv[])  {

int status;

sigset_t newmask, oldmask, waiting_mask, look_for_these_child, look_for_these_parent, hblock;



siginfo_t extra;

struct sigaction new_action, new_action1, old_action, old_action1;

// initialize all structures, buffers, strings, masks, sets, flags, reset to 0 or NULL where it applies

new_action.sa_handler =  sig_handler;

new_action1.sa_action =  sig_usr1;

sigaddset(&hblock, SIGTERM);

sigaddset(&hblock, SIGTSTP);

new_action.sa_mask = hblock;

sigaddset(&hblock, SIGABRT);

new_action1.sa_mask = hblock;

sigaction(SIGILL, &new_action, NULL);

sigaction(SIGQUIT, &new_action, NULL);

signal(SIGFPE, sig_handler);

signal (SIGHUP, sig_handler);

signal (SIGTSTP, sig_handler);

sigaction(SIGUSR1,&new_action1, NULL);

p = fork();

if (p) {

sigaddset(&new_action.sa_mask, SIGQUIT);

sigprocmask(SIG_BLOCK,&new_action.sa_mask,NULL);

sigaddset(&hblock, SIGSEGV);

sigaddset(&hblock, SIGFPE);

signal (SIGCHLD, sig_handler);

sigdelset(&hblock, SIGQUIT);

sigprocmask(SIG_SETMASK,&hblock,NULL);



payload.sival_int = (int)(getpid()/p); sigqueue(p,SIGUSR1, payload);

kill(p, SIGQUIT);

kill(p, SIGILL);

kill(p, SIGINT);

kill(p, SIGSEGV);

kill (p, SIGTSTP);

raise(SIGFPE);

sleep(20);


// Line 150

// Line 151



// Line 156

// Line 157


// After 20 seconds send a number of signals again.


kill(p, SIGQUIT);                                // Line 159

kill(p, SIGINT);

kill(p, SIGSEGV);                                 // Line 163

raise(SIGHUP);                                   // Line 164

raise(SIGFPE);                                    // Line 165

sleep(20);

wait(&status);

sigpending(&waiting_mask);

if (sigismember(&waiting_mask,SOME_SIGNAL))

fprintf(stderr,"SOME_SIGNAL sent while parent blocked\n"); exit(0);

}

if (!p)  {

sigaddset(&hblock, SIGSEGV);

sigemptyset (&look_for_these_child);

sigaction(SIGTSTP, &new_action, NULL);


sigaddset(&look_for_these_child, SIGILL);

sigprocmask(SIG_BLOCK,(&look_for_these_child | &hblock),NULL);

signal(SIGURG,sig_handler);

payload.sival_int = (int)(getpid()/getppid());


kill(getppid(), SIGQUIT);                    // Line 181

kill (getppid(), SIGCONT);                  // Line 182

raise(SIGFPE);                               // Line 183

kill(getppid(), SIGINT);

kill(getppid(), SIGCHLD);


kill(getppid(), SIGTSTP);                    // Line 188

sleep(20);


kill(getppid(), SIGILL);                     // Line 190

kill(getppid(), SIGCONT);

kill(getppid(), SIGCHLD);


raise(SIGHUP);                               // Line 196

raise(SIGFPE);                               // Line 197

sleep(20);

sigpending(&waiting_mask);

if (sigismember(&waiting_mask,SOME_SIGNAL))

fprintf(stderr,"SOME_SIGNAL sent while parent blocked\n");

exit(5);

}

return (0);

}