You are on page 1of 14

UNIX Systems Programming

(lectures programs)
Part 1: UNIX File System

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


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
% append append.c t
% append append.c t
% append t t
Example 2: seeker.c
#define NSTRINGS
#define STRSIZE

// creat empty file t


// copies append.c to t
// make another copy of append.c to t
// endlessly copies t to t !

5
3

char *strings[] = {
"aaa", "bbb", "ccc", "ddd", "eee"
};
main ....
{

int fd;
fd = open ( fname, O_RDWR | O_CREAT | O_TRUNC, 0666
);
for (n = 0; n < NSTRINGS; n++)
write(fd, strings[n], STRSIZE);
/* user provides the value of n */
lseek ( fd, (n-1) * STRSIZE, SEEK_SET );
read ( fd, buf, STRSIZE );
write(1, buf, STRSIZE);
}

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


Example 3: fseeker.c
main ....
{
FILE *fp;
fp = fopen(fname, "w+");
for (n = 0; n < NSTRINGS; n++)
fwrite(strings[n], sizeof(char), STRSIZE, fp);
fseek(fp, (n-1) * STRSIZE, SEEK_SET);
fread(buf, sizeof(char), STRSIZE, fp);
printf("String %d = %.*s\n\n", n, STRSIZE, buf);
}
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 Figure)

Exampl 4: lstat.c
main ...
{
char *filename;
struct stat st;
lstat (filename, &st);
outputStatInfo(filename, &st);
}
Example 5: listfiles.c
main ...
{
DIR *dp;
char *dirname;
struct stat st;
struct dirent *d;
dp = opendir(dirname);
while ((d = readdir(dp)) != NULL) {
sprintf(filename, "%s/%s", dirname, d>d_name);
lstat(filename, &st);
outputStatInfo(filename, d->d_name, &st);

}
}

Part 2: Signals

Basic Signal Handling:

kill (pid, sig)


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

pause()
To wait for a signal

signal (sig, handler)


To call the function handler
when the signal sig occurs.

Example 6:

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
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 7: timeout1.c
main ...
{
signal(SIGALRM, handler);
alarm(atoi(argv[1]));
printf("Enter a string: ");
fgets(buf, sizeof(buf), stdin);
alarm(0);
}
handler(int sig)
{
printf("inside intr hand\n");
}

Part 3: 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.
Orphaned Process:
If a parent process dies, it's chldren are inherited by the init process
(pid is 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 {
printf("I am a parent\n");
printf("my pid is: %d\n", getpid());
printf("my child pid is: %d\n", pid);
}
/* This code executes in both processes */
printf("exiting ... %d\n", getpid());
exit(0);
}
Example 9: killparent.c
This example shows that when the child kills its parent, it is 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();
}

File Sharing between processes: (see Figure)

Example 10: 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) {
read(in, buf,
write(1, buf,
}
else {
read(in, buf,
write(1, buf,
}
write(1,"\n", 1);

3);
3);

3);
3);

EXEC: many variations, encluding:


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 ("/bin/date", "date", "+%m/%d/%y", NULL);
args[0] = "date";
args[1] = "+%m/%d/%y";
args[2] = NULL;
execv ("/bin/date", args);
}

Example 12: execfileshare.c


This example shows that exec preserves the file descriptors.
main()
{
int in;
pid_t pid;
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 13: execsignal.c


This example shows that exec preserves some siganl settings (e.g., SIG_IGN).
main()
{
pid_t pid;
signal(SIGINT, SIG_IGN);
pid = fork();
if (pid == 0)
execl("./waitsignal1", "waitsignal1", 0);
else
execl("./waitsignal2", "waitsignal2", 0);
}
The exec a file called: waitsignal1.c & waitsignal2.c
main()
{
pause();
}
&
main()
{
signal(SIGINT, SIG_DFL)
pause();
}

Use:
% kill -INT <pid>
to send a INT signal to <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's date is:",
NULL);
while (wait(&status) != pid)
continue;
execl("/bin/date", "date", "+%m/%d/%y", NULL);
}

Simple Shell:
Example 15: wsh.c
main()
{
int status;
pid_t pid;
char command[BUFSIZ];
for (;;) {
printf("wsh> ");
if (fgets(command, sizeof(command), stdin) ==
NULL) {
putchar('\n');
exit(0);
}
command[strlen(command)-1] = NULL;
if ((pid = fork()) == 0)
execlp(command, command, 0);

while (wait(&status) != pid )


continue;
putchar('\n');
}
}
Example use:
% wsh
wsh> ls
% wsh
wsh> execsignal
(CTRL-C will kill the shell not the command!).

Putting it all together: A semi-real shell:


- It can handle input/output redirection ( <file >file ).
- It ignores INT and QUIT signals.
Example 16: shell.c
% shell
--> execsignal
(CTRL-C will kill the command not the shell).

Part 4: Interprocess Communications

Pipes: See figure for pipe creation

Example 17: child writes to paernt


pipedate.c
main()
{
pid_t pid;
int pfd[2];
int i, status;
char line[64];
pipe(pfd);
pid = fork();
if (pid == 0) {
dup2(pfd[1], 1);
close(pfd[1]);
close(pfd[0]);

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


}
close(pfd[1]);
read (pfd[0], line, sizeof(line));
printf("date from child is: %s\n", line);
close(pfd[0]);
waitpid(pid, &status, 0);
exit(0);
}
Example 18: parent writes to child
pipemail.c
main()
{
pid_t pid;
int pfd[2];
int i, status;
char *username;
username = cuserid(NULL);
pipe(pfd);
pid = fork();
if (pid == 0) {
dup2(pfd[0], 0);
close(pfd[1]);
execl("/bin/mail", "mail", username, 0);
}
close(pfd[0]);
write(pfd[1], "Greetings and salutations,\n\n",
28);
write(pfd[1], "This is your program saying
hello.\n", 35);
write(pfd[1], "Have a nice day.\n\n", 18);
write(pfd[1], "Bye.\n", 5);

close(pfd[1]);
waitpid(pid, &status, 0);
exit(0);
}