Signals, Scheduling, and Threads

Last time:

In the last lecture, the professor mentioned two tabs used with open: O_TRUNC, and O_CREAT. O_TRUNC is used to truncate existing files, while O_CREAT is used to creat a file if it doesn't exist.

Unix solution: O_EXCL

O_EXCL is a flag that causes the syscall to fail if the file already exists. O_EXCL is an example of a non-orthogonal flag: it only makes sense if used with O_CREAT. It doesn't work well on its own.

Example:

fd = open("/tmp/foo", O_RDWR | O_CREAT | O_EXCL, 0666);

If fd = -1, that means open has returned an error. errno = EEXISTS if the file already exists.

A sort program would use something like this:

while( (fd = open("/tmp/foo", O_RDWR | O_CREAT | O_EXCL, 0666)) == -1 && errorno == EEXIST)
	sleep(1);

Sidenote: temporary files

If we only want to use temporary files, why not have a syscall for creating them? Here's a suggested syscall:

fd = opentmp(flags);

Benefits of opentmp:

However, it is harder for related processes to share temporary files. In addition, it is also harder to manage resources, because usual tools for managing files do not apply to temporary files.

Unix/linus aproach:

File locking (in Unix):

A function for locking files:

fcntl(fd, cmd, P)

File locking properties:

When using F_SETLKW, there is a potential issue of running into a Deadlock.
An example of a dead lock is with two files, A and B, and two processes, 1 and 2:

Process 1:
Step 1. lockw( file A )
Step 3. lockw( file B )

Process 2:
Step 2. lockw( file B )
Step 4. lockw( file A )

The system will catch this potential error. When the processes reach step 4, it will notice the situation and will fail. The syscall will return -1 with errno = E_DEADLK.

Another example:

gzip FOO (gzip &temp files)
1. creates an empty file FOO.gz
2. compresses ( read from FOO, writing to FOO.gz)
3. closes FOO.gz
4. removes FOO
If there is an error on write, FOO.gz is removed and an error is reported

Possible problems:

Interrupts vs. Signals

Interrupts: Signals: Particular way a kernel notifies a process of an asynchronous event.

Possible Signal mechanisms:

  1. stick it into a "file": /dev/power
  2. "power fail" thread
  3. Signal handling in Unix

Example:

void handle_child_signal( int sig ) { error(); }
void printdate( void ) {
	pid_t p = fork();
	if(p < 0) error();
	if(p == 0) {
		char* const args[] = {"/bin/date", NULL};
		signal(SIGUSR1, handle_child_signal); // the only problem handled here is a verrry slow disk
		execvp(args[0], args);
		error();
	}
	sleep(5);	// normally, the child will have exited: 
			// it's a zombie (data structure representing a dead child for parent to waitpid)
	kill(p, SIGUSR1);
	if(waitpid(p, &status, 0) < 0) error();
What happens to /bin/date when it gets a signal? After the execvp, the signal handlers for USR1 and CHLD are changed back to default values.