Midterm-II Prep
Class: CSCE-313
Notes:
1.1
1.2 Signals
Problem 1
Stacks as a Data Structure (in C). Implement a stack in C using a dynamic array (i.e., using malloc/realloc). Your implementation should support push, pop, peek, and is_empty. Make your implementation signal-safe?
Problem 2
Suppose a program installs a handler for SIGINT.
void handler(int sig) {
printf("Signal received\n");
}
- Why is this handler potentially unsafe?
- Somewhere in the execution of
printfyou invokeprintfagain in your handler - You are maintaining a user level buffer, at the end of it, there is a little file descriptor pointing to the object you want to write to
- In order to serialize invocation of a standard IO function is that it acquires a lock at the beginning of the buffer and releases the lock at the end.
- This is a requirement to serialize execution
- If you have printf in progress you may have aquire the lock, did something, but
printfagain may acquire the same log, this is where the problem is - The story is that in the handler you should only use functions that are signal-safe.
- All the standard IO functions are not async signal safe
- Two concurrent invocations of the same function can acquire the same lock
- Somewhere in the execution of
- Which function should be used instead of printf()?
- We use
write(), this is a system call and therefore it is serialized by the kernel, it won't go to deadlock. - Now you can produce results in a different order, because you are going to the kernel while doing other stuff
- In your program you will use standard IO library like printf which accumulates stuff into the buffer,
- Whenever we use
write()it instantly writes (standard IO is flushed) and all of the buffer appears on the write.
- We use
- What property must functions used in signal handlers satisfy?
- Basically what they call atomicity
- They are atomic in the sense that you are not in the middle of doing something when you receive a signal
- We have a notion of consistency
- A block of consistency is this much (we do not want to be interrupted within this block)
- Want things that our signal handler touches to be atomic
- Either you didn't start the thing or you finish it when receiving the signal
Problem 3
Write a program that installs a signal handler for SIGINT. The handler should increment a counter and print how many times the user has pressed Ctrl+C. After 3 presses, the program should exit cleanly. What constraints apply to code inside a signal handler?
Problem: Signals can be coalesced into one if received concurrently, it will not be counted, it will just tell you that you have pending signals. There are real time signals that actually count, but we are not doing that.
Problem 4
Which of the following are valid things to do safely inside a signal handler, and why?
- Call
printf() - Set a
volatile sig_atomic_tflag - Call
malloc() - Write to a global array
Notes:
- If you have a variable of the
volatiletype you can actually touch it in your handler- The compiler is free to allocate variables of this type on registers during execution
- If in your handler you tried to read x, you won't read what your program actually wrote
- When you declare a variable as
volatilethen all reads and writes go to the address- You will actually go to the address to read or write to it.
- You do not want to allocate new memory with
mallocbecause what if you get interrupted? you could lose the reference to that memory space
Example
long long x;
x++;
- There is no guarantee that the increment on x is atomic
- Since we can read some bytes first, and then the others
- x may not be on a consistent state always
Anything that is atomic is signal-safe?