UNIX Systems Programming

(lectures programs)

 

UNIX File System

 

  Low-level I/O:   open, read, write, lseek, close, unlink

Example 1:   append.c

main (int argc, char **argv)
{

int n, in, out;
char buf[1024];
/* Open the first file for reading */
in = open (argv[1], O_RDONLY);
/* Open the second file for writing */
out = open (argv[2], O_WRONLY | O_APPEND);
/* Copy data from the first file to the second */
while ((n = read (in, buf, sizeof(buf))) > 0)

write (out, buf, n);

}

Examples of usage:

% touch  t                 // creat empty file t
% append  append.c   t     // copy  append.c  to  t
% append  append.c   t     // add a 2nd copy of  append.c  to t
% append t   t             // endlessly  copies t to t !

Example 2:   fileseek.c

main(…)  {

   int  fd, length;

   off_t FileLength, position;

   char buf[1024];

   printf("Usage: fileseek <file> <seek_position> <#bytes>\n");

   fd = open (argv[1], O_RDONLY);   

   position = atoi(argv[2]);

   length = atoi(argv[3]);

   FileLength = lseek (fd, 0, SEEK_END);

   if ((position+length) > FileLength){

     printf("exceeds file limit\n");

     exit(0);

   }

   lseek (fd, position, SEEK_SET);

   read (fd, buf, length);

   write (1, buf, length);

} 

Examples of usage:

% ls -lt fileseek.c

-rwxr-xr-x 1 cs476  cs476  782 Sep 26  2006 fileseek.c

 

% fileseek fileseek.c 700 100

File Length 782

exceeds file limit t  

 

% fileseek fileseek.c  0  100

File Length 782

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

#include <stdlib    

     

 Standard I/O:   fopen, fread, fwrite, fseek, fclose, fflush, fscanf

 

Example 3:   ffileseek.c

main(..)

{

   FILE  *fp;

   int   length;

   off_t FileLength, position;

   char buf[1024];

   printf("Usage: fileseek <file> <seek_position> <#bytes>\n");

   fp = fopen (argv[1], "r") ;       

   position = atoi(argv[2]);

   length = atoi(argv[3]);

   fseek(fp, 0, SEEK_END);

   FileLength = ftell (fp);

   if ((position+length) > FileLength){

     printf("exceeds file limit\n");

     exit(0);

   }

   fseek(fp, position, SEEK_SET);

   fread (buf, sizeof(char), length, fp);

fwrite (buf, sizeof(char), length, stdout);

fflush (stdout);

}

Examples of usage:

% ffileseek fileseek.c 10 200    // display 200 bytes starting at position 10 

 

Example 4:   ffilescan.c

main( )

{

     int i;

     char name[50];

     fscanf  (stdin, "%d%s", &i, name);

     fprintf (stdout, "%d  %s\n", i, name);

}

 

Examples of usage:

% ffilescan

25  wahab

Assigns:  i  the value 25  &  

name will contain the string wahab.

 

Convertion between  file pointer (fp & file descriptor (fd).

 

 fdopen(fd) & fileno(fp)

 

Example:

int fd;
FILE *fp;

fd = open ("filename",O_TRUNC|O_CREAT|O_WRONLY, 0777 );

fp = fdopen(fd, "w+");
fd = fileno(fp);

 


 UNIX Directory Structure:  

 

unixdir.jpg

NOTES:  

The filename is not stored in the I-node.

It is available in the parent directory.

Every file has a record in the parent directory in the form:

 

Text Box: FileName | Inode#

 

 


 

Signals

 

 Basic Signal Handling:  

kill (pid, sig)

To send  signal to  process.

Send process pid   signal  sig
 (To list all available signals type:  % kill -l )

It is called kill since the default action is to kill the process.

signal (sig, handler)

To catch & handle signal.

When signal  sig  occurs, call function  handler  

pause ()

To wait for the arrival of any signal 

 

Example 5:  signal1.c
 
main ..
{

signal(SIGUSR1, handler);
signal(SIGUSR2, handler);

for (;;)  pause();

}

handler(int sig)
{

/* Print received signal */
psignal(sig, "Received signal");

}

Examples of usage:

% kill -l
HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM USR1 USR2 CLD PWR WINCH URG POLL STOP TSTP CONT TTIN TTOU VTALRM PROF XCPU XFSZ WAITING LWP FREEZE THAW CANCEL LOST RTMIN RTMIN+1 RTMIN+2 RTMIN+3 RTMAX-3 RTMAX-2 RTMAX-1 RTMAX

 

% kill -USR1 12345    //use ps to find pid of signal1 e.g., 1234
 Recived signal: User Signal 1

% kill -17 12345      //signal #17 is USR2
 Recived signal: User Signal 2

% kill -KILL 12345    //signal #9 is KILL
Killed

A process can NOT catch or ignore (using SIG_IGN) the KILL signal. 

 

Example 6:  listsignals.c

main(void)
{
   int             i;
   int             pid = getpid();
   for (i = 1; i < 50; i++) {
      if (i == 9) {
         printf("can’t catch sig 9 (KILL)\n");
         continue;
      }
      if (i == 23) {
         printf("can not’t catch sig 23 (STOP)\n");
         continue;
      }
      if (signal(i, handler) == SIG_ERR) {
         printf("can’t set handler for sig %d\n", i);
         exit(-1);
      }
      sleep(1);
      kill(pid, i);
   }
}

void handler(int sig)
{
   printf("sig number: %d ", sig);
   fflush(stdout);
   psignal(sig, "sig name");
}

 

 Using Signals for Timeouts:


alarm ( T )

sends the ALRM signal to the current process after T seconds.


alarm ( 0 )  

cancels   the alarm signal.

 

Example 7:   timeout1.c

 
int flag = 0;
main ..
{

int timeout;

signal(SIGALRM, handler);
timeout =
alarm(atoi(argv[1]));
printf("Enter a string (within %d seconds): ",timeout);
fgets(buf, sizeof(buf), stdin);

alarm(0);

if (!flag)

      printf (“Typed: %s\n”, buf);

}
handler(int sig)
{    
   printf("Timeout alarm\n"); 
   flag = 1;
}
ple of usage:
 
% timeout1 3
Enter a string (within 3 seconds): 
The association between a signal and the handling function is only valid for one time only.
It must be renewed after its occurrence. 

 

Processes

Basic Concepts:

 

Process identifiers:

getpid(): to get the process id.

getppid(): to get the parent process id.

Create a new process:

fork(): to create a new child process.

Terminate:

exit(): to terminate the current process.

Zombie Process:

If a process dies and its parent did not call wait() 

the process is called a zombie process.

Orphans Processes:

If a parent process dies, it's children becomes orphans  and

are inherited by the system's  init  process (pid 1).

 

Example 8:  fork1.c

main ()
{
   pid_t pid;
   pid = fork();
    if (pid == 0) {
        printf("I am child\n");
        printf("my pid is: %d\n", getpid());
        printf("my parent pid is: %d\n", getppid())
    }
    else {
        usleep(1); /*so child goes first*/
        printf("I am a parent\n");
        printf("my child pid is: %d\n", pid);
        printf("my pid is: %d\n", getpid());
    }
    /* This code executes in both processes */
    printf("exiting ... %d\n", getpid());
    exit(0);
}
Example of usage:
% fork1 
 

Example 9:  killparent.c

This example shows that when the parent is dead,

it’s children are inherited by the init process (pid # 1).

main ...
{    
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        printf("my real parent id is: %d\n", getppid());
        kill ( getppid(), 9);
        usleep(1);
        printf("my foster parent id is: %d\n",getppid());
    }
    pause();
}
Example of usage:
% killparent 
 

 File Sharing between processes:

 

fileSharing.JPG

Example 10:  fileshare.c
This example shows that both parent & child share the same file pointer.

main ()
{
   int in;
   pid_t pid;
   char buf[1024];
   system ("echo abc123 > junk");
   in = open("junk", O_RDONLY);
   pid = fork();
   if (pid == 0) {
        write(1,"Child: ",7);
        read(in, buf, 3);
        write(1, buf, 3); 
    }
    else {
        write(1,"Parent: ",8);
   read(in, buf, 3); 
        write(1, buf, 3);   
    }

  write(1," end\n", 5);

} 
Example of usage:
% fileshare
 

 EXEC: 

Several variations, including:

execl  (path, arg1, arg2, ...,NULL)

execv (path, *argv[])

 

Example 11:   forkexec.c

main(void) 
 {
     pid_t pid;
     char *args[4];
     pid = fork();
     if (pid == 0)
         execl ("/usr/bin/paste","paste","f1",”f2”, NULL);
      args[0] = "comm";
      args[1] = "f1";
      args[1] = "f2";
      args[2] = NULL;
      execv ("/usr/bin/comm", args);
 }
Example of usage: 
% forkexec
 

Example 12:   execfileshare.c
Exec preserves file descriptors as illustrated by next example.

main()
{
    int in;

    pid_t pid;
    system ("echo abc123 > junk");
    in = open("junk", O_RDONLY);
    pid = fork()) ;
    if (pid == 0) 
          execl("./fileread", "fileread", 0);
    else 
          execl("./fileread", "fileread", 0);
}

The program exec a file called: fileread.c 

main()
 {
        char buf[1024]; 
        read(3, buf, 3); 
        write(1, buf, 3); 
        write(1,"\n", 1);
 } 
Example of usage:
% execfileshare
 

Example 13:   execsignal.c
Exec preserves some siganls (e.g., SIG_IGN).

main()
{
    pid_t pid; 
    signal(SIGINT, SIG_IGN);
    pid = fork();
    if (pid == 0) 
        execl("./waitsig1", "waitsig1", 0);
    else 
        execl("./waitsig2", "waitsig2", 0);
}

      

waitsig1.c  

main()
{
  pause();
}
 
waitsig2.c
main()
{
  signal(SIGINT, SIG_DFL);
  pause();
}
Example of usage:
% execsignal

% ps   -a

To find the <pid> of  processs waitsig1 and  waitsig2 and send each  an INT signal using:

% kill    -INT    <pid>

 WAIT: 

for a child.

Example 14:    forkexecwait.c

main()
{
    pid_t pid;
    char *args[4]; 
    int status;
    pid = fork();
    if (pid == 0) 
         execl("/bin/echo", "echo", "Today is:", NULL);
    wait (NULL) ; 
    system("date");
    pause();
}
Example of usage:
% forkexecwait
Remove wait and you will see that the child process is <defunct>, i.e., zombie process. 
 
 
 WASH : WAhab SHell 

 

Example 15:    wash.c

main()
{

pid_t  pid;
char command[BUFSIZ];

for (;;) {

   printf("wash> ");
   fgets(command, sizeof(command), stdin);

   if ((pid = fork()) == 0)

execlp(command, command, 0);

   wait(NULL);
}

}

Examples use:

% wash

wash:  ls

wash:  who

wash:  execsignal

CTRL-C will kill  BOTH the command  &  the shell itself !

 

 RASH : ReAl SHell

It accepts commands with arguments.

It handles input/output redirection< file  and  > file.

It ignores INT and QUIT signals that are sent to children.

Example 16:    rash.c

main(void)

{ 

    for (;;) {

        printf("rash:");      
        fgets(command, sizeof(command), stdin);

        /*  split the command into words */

        n = bufsplit(command, args);

        args[n] = NULL;
        infile = NULL;

        outfile = NULL;

        for (cp = args; *cp != NULL; cp++) {

            if (strcmp(*cp, "<") == 0) {

                *cp++ = NULL;

                 infile = *cp;

            }

            else if (strcmp(*cp, ">") == 0) {

                *cp++ = NULL;

                outfile = *cp;

            }

        }       

        status = execute(args, infile, outfile);

     }

}
int execute(char **args, char *infile, char *outfile)

{   

     infd = -1;

     outfd = -1;

     if (infile != NULL)

        infd = open(infile, O_RDONLY

     if (outfile != NULL)       

        outfd = creat(outfile, 0666));
     /* Ignore keyboard signals; SIG_CHLD signals */

signal(SIGINT,  SIG_IGN);

signal(SIGQUIT, SIG_IGN);

signal(SIGCHLD, SIG_IGN);

pid = fork();

if (pid == 0) {

signal(SIGINT, SIG_DFL);

signal(SIGQUIT, SIG_DFL);

signal(SIGCHLD, SIG_DFL);

   if (infd > 0)

        dup2(infd, 0);

   if (outfd > 0)

        dup2(outfd, 1);

     execvp(*args, args);

   }

   wait(NULL);

signal(SIGINT,  SIG_DFL);

signal(SIGQUIT, SIG_DFL);

signal(SIGCHLD, SIG_DFL);

close(outfd);

  close(infd);

}

bufsplit(char *buf, size_t n, char **a) {}

Examples use:

% rash
rash: ls -lt

rash: execsignal

CTRL-C will kill the command  not the shell.

Compare this with wahab’s naïve  shell (wash)!

rash:  ls   –lt   >  lsfile

rash:  wc  <   lsfile

rash:  cb   <   infile.c   >   infile.b


 

Interprocess Communications

 

 Pipe :
Description: Description: Description: http://www.cs.odu.edu/~cs476/fall06/lectures/figpipe.jpg

Example 17:    pipedate.c

child writes to parent

 

main()

{

 pipe(pfd);

 pid = fork();

 if (pid == 0) {

    close(pfd[0]);   // no need for read end

    dup2(pfd[1], 1);

    close(pfd[1]);  // since it is duplicated

    execl("/bin/date", "date", 0);

 } 

  close(pfd[1]); // no need for write end

  read (pfd[0], line, sizeof(line));

  printf("date from child is: %s\n", line);

  close(pfd[0]); 

  waitpid(NULL);

 }

 

Example of usage:
% pipedate

 

Example 18:   pipemail.c

parent writes to child

main()

{

    pid_t pid;

    int pfd[2];

    char *username =getlogin();

    char *msg = "Greetings | Salam| Shalom|

       Hola| Salut| Konichiwa | Ciao"
    pipe(pfd);

    pid = fork();

    if (pid == 0) {

        close(pfd[1]); // no need for write end

        dup2(pfd[0], 0);

        close(pfd[0]); // since it is duplicated       

        execlp("Mail","Mail","-s","pipemail",username,0);      

    }

    close(pfd[0]);     // no need for read end

    write(pfd[1], msg, strlen(msg));

    close(pfd[1]);

   waitpid(NULL);

}

 

Example of usage:
% pipemail
 
And check your mail!

 

 Shared Memory :

 

Functions:

The Process first creates shared memory segment and then attach to it:

segment id = shmget (IPC_PRIVATE, size, S_IRUSR | S_IWUSR);

char *sharedString = (char *) shmat (id, NULL, 0);

The process could write/read to/from the shared memory:

sprintf (shareString, "Writing to shared memory");

printf("%s", sharedStringy);

When done a process can detach the shared memory from its address space:

shmdt (sharedString);

 

Example 19:    shmfork.c

#define SIZE 10
main()
{
   int segment_id = shmget(IPC_PRIVATE, SIZE * sizeof(int), S_IRUSR | S_IWUSR);
   int *sharedVar = (int *) shmat(segment_id, NULL, 0);
   int *nonsharedVar = (int *) malloc(SIZE * sizeof(int));

   int             i;
   for (i = 0; i < SIZE; i++) {
      sharedVar[i] = 0;
      nonsharedVar[i] = 0;
   }
   if (fork() == 0) {
      for (i = 0; i < SIZE; i++) {
         sharedVar[i] += i;
         nonsharedVar[i] += i;
      }
      printf("\nChild : shared variables:- ");
      for (i = 0; i < SIZE; i++)
         printf("%d ", sharedVar[i]);
      printf(" non-shared variables:- ");
      for (i = 0; i < SIZE; i++)
         printf("%d ", nonsharedVar[i]);
      exit(0);
   }
   wait(NULL);
   printf("\nParent: shared variables:- ");
   for (i = 0; i < SIZE; i++)
      printf("%d ", sharedVar[i]);
   printf(" non-shared variables:- ");
   for (i = 0; i < SIZE; i++)
      printf("%d ", nonsharedVar[i]);
}

 

Useful shared memory commands:

 

Report shared memory status information:

 

% ipcs  -m

 

To remove a shared memory ID:

 

% ipcrm -m shmid

 

Shell script:

 

#!/bin/sh

for i in ` ipcs -m |  grep "^m" | awk '{print $2}'  `

do

  ipcrm -m $i

done