UNIX Systems Programming

 (lectures programs)


UNIX File System

 

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

 

Example FILE1:   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     // make another copy of  append.c  to t
% append t  t                    // endlessly  copies t to t !

 

Example FILE2:   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:



% t > 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

 

Example FILE3:   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);

    }

 

 

 Examples of usage:

 

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

 

 

NOTE:   you may use:  fdopen(fd) & fileno(fp)

       to convert between file pointer (fp)  and file descriptor (fd).

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:  (See next Figure)

 


NOTE:   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#

 

 

 

Exampl 4:     lstat.c

 
main ...

 
 
{

 
 
    char *filename;

 
 
    struct stat st;    

 
 
    lstat (filename, &st);               

 
 
    outputStatInfo(filename, &st);

 
 
}

 

 

 


 

Signals

 

Ø          Basic Signal Handling:

 

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

     To wait for a signal

    When the signal sig occurs, call the function handler .


 

Example SIG1:  signal1.c
 
main ...

 
{

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

}


handler(int sig)
{

/* Print out what we received */
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 e.g., 1234
 Recived signal: User Signal 1

% kill -16 12345        //signal #16 is USR1
 Recived signal: User Signal 1

% kill -KILL 12345

Killed

 

Ø          Using Signals for Timeouts:

 

alarm ( T )

To send the ALRM signal to the current process  after T seconds.
 alarm ( 0 )   will cancel the alarm signal.

 

Example SIG2:   timeout1.c

 
main ... 

 
{

signal(SIGALRM, handler);
alarm(atoi(argv[1]));
printf("Enter a string: ");
fgets(buf, sizeof(buf), stdin);

alarm(0);

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

}

 
handler(int sig)

 
{    
   printf("Timeout alarm\n"); 
}

 
 
Example of usage:

 
% timeout1 3
Enter a string:
 

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(),

it is called a zombie process.

 

Orphans Processes:

 

If a parent process dies,

it's children becomes orphans  and

are inherited by the syetm's  init  process (pid is #1).

 

Example PROC1:  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 {
 
 
        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 PROC2:  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);

 
 
        sleep(1);

 
 
 
        printf("my foster parent id is: %d\n", getppid());

 
 
 
 
    }

 
 
 
    pause();

 
 
}
  
 
Example of usage:

 
 
 
% killparent 

 

Ø          File Sharing between processes: (see next Figure)

 

Example PROC3:  fileshare.c

This example shows that both parent and 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,"\n", 1);

 

    } 

 
 
Example of usage:
% fileshare 
  

Ø          EXEC:  many variations, encluding:

 

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

 

execv (path, *argv[])

 

 

Example PRO4:   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 PROC5:   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 PROC6:   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);

}

      

        fileswaitsig1.c  & waitsig2.c

 

main()

 
{

 
  pause();

 
}

 
 
&
 
main()

 
{

 
  signal(SIGINT, SIG_DFL);

 
  pause();

 
}
 

Testing:

 

% ps   -a

        to find the pid of  waitsig1 and  waitsig2 and Use


      
% kill    -INT    <pid>
        to send an
INT signal to waitsig1 and waitsig2

 

Ø          WAIT:  for a child.

 

Example PROC7:    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.
 
 
 

Ø          Simple Shell: wahab's shell (wash)

 

Example PROC8:     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 the command  & the shell).

 

Ø          Putting it all together:  A semi-real (modified) mosh:

 

-  It can accept commands with arguments.

-  It can handle input/output redirection< file  and  > file.

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

Examples:

 % mosh

mosh:  ls   lt  > lsfile

mosh:  wc  <  lsfile

mosh:  cb  <  infile.c  >  infile.b


Example PROC9:  mosh.c

 
main(void)
 
{        

 
    for (;;) {

 
     printf("mosh> ");       

 
 
     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; and 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);
 
 
 siggnal (SIGINT, SIG_DFL);
 
 
 
 
 signal(SIGQUIT, SIG_DFL);
 
 
 
 signal(SIGCHLD, SIG_DFL);

 
 
    close(outfd);

 
    close(infd);

  

 }

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

 
{

..........

}
 
 

Example use:

% mosh
mosh> ls -lt

mosh> execsignal
(CTRL-C will kill the command  not the shell).

compare this with wahab’s shell (wash)!

 


 

 

Interprocess Communications

 

Pipes:   Figure for illustrating pipe creation


Example PIPE1:   child writes to parent

pipedate.c

main()



{

pid_t pid;


int pfd[2];


char line[64];



 
    pipe(pfd);

 
    pid = fork();

 
 
    if (pid == 0) {

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


    dup2(pfd[1], 1);

 
        close(pfd[1]);

 
        
        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);

 
    exit(0);

}

 

 

Example PIPE2:    parent writes to child

 

pipemail.c



 
 
main()

 
{

    pid_t pid;

 
    int pfd[2];


    char *username;

 
    username = cuserid(NULL);

 
    pipe(pfd); 

 
    pid = fork();

 
    if (pid == 0) {        

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

 
        
        dup2(pfd[0], 0);

 
        execl("/bin/mail", "mail", username, 0);

 
    }

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

 
    write(pfd[1], "Greetings and salutations,\n\n", 28);

 
    close(pfd[1]);

 
    waitpid(NULL);

 
    exit(0);

}
 
 
 
 

Example PIPE3:    mash: extending mosh to handle pipe.

 

mash.c

 

Examples:

          % mash

mash: ls | wc

mash: ls  -lt > f1

mash: cat < f1 | wc > f2

mash: cat f2

 

 
 
Sockets:   See Network Programming