You are expected to use the man pages! Remember that man pages with the same title may appear in multiple sections. If you want to search all sections use man -a.
pid_t getpid(void); pid_t getppid(void);ACTIONS
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
pid_t fork_pid;
printf("Hello from main\n");
fork_pid = fork();
printf("After fork, fork_pid=%d\n", fork_pid);
return 0;
}
ACTIONS
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
pid_t fork_pid;
FILE *my_file;
my_file=fopen("MY_FORK_FILE.txt", "w");
fprintf(my_file, "Hello from main\n");
fork_pid = fork();
fprintf(my_file, "After fork fork_pid=%d\n", fork_pid);
return 0;
}
Compile and run this code, then examine the contents of
"MY_FORK_FILE.txt".
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
pid_t fork_pid;
FILE *my_file;
my_file = fopen("MY_FORK_FILE.txt","w");
fprintf(my_file, "Hello from main\n");
fflush(my_file);
fork_pid = fork();
fprintf(my_file, "After fork %d\n", fork_pid);
return 0;
}
Compile and run this code. Examine the contents of
"MY_FORK_FILE.txt"; is this now in line with what you
expected from your first experiments? Explain exactly what has happened
(it is important you understand this so verify your thoughts with your
tutor).
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#define SLEEP_TIME 5
double get_elapsed_time();
int main(void) {
pid_t fork_pid;
double elp_time;
elp_time = get_elapsed_time();
printf("Hello from main %14.6lf\n",elp_time);
fork_pid = fork();
if (fork_pid == 0) {
elp_time = get_elapsed_time();
printf("1st Hello from Child %14.6lf\n", elp_time);
sleep(SLEEP_TIME);
elp_time = get_elapsed_time();
printf("2nd Hello from Child %14.6lf\n", elp_time);
} else {
elp_time = get_elapsed_time();
printf("1st Hello from Parent %14.6lf\n", elp_time);
elp_time = get_elapsed_time();
printf("2nd Hello from Parent %14.6lf\n", elp_time);
}
elp_time = get_elapsed_time();
printf("Good bye from %s %14.6lf\n", fork_pid==0? "Child": "Parent",
elp_time);
return 0;
}
// function to get wall clock time since first call
double get_elapsed_time(void) {
struct timeval mytime;
static double start_time=0.0e0;
double new_time;
/* this function gets seconds and microseconds since the epoch */
gettimeofday(&mytime, NULL);
new_time = (double)mytime.tv_sec + mytime.tv_usec*1.0e-06;
if (start_time <= 0.0e0) start_time = new_time;
return new_time-start_time;
}
ACTIONS
Consider the following program:
#include <unistd.h>
#include <stdio.h>
int main(void) {
printf("Hello from main \n");
if (fork() != 0) {
/* THIS IS THE PARENT */
printf("Hello from Parent\n");
sleep(4);
} else {
printf("Parent PID of Child before sleep %d\n", getppid());
sleep(2);
printf("Parent PID of Child after sleep %d\n", getppid());
}
return 0;
}
The following gives a short example of the use of exec().
#include <unistd.h>
#include <stdio.h>
int main(void) {
int status;
if (fork()) {
// Parent
} else {
// Child
printf(" HELLO from Child \n");
status = execl("/bin/ls", "ls", "-l", "-t", (char *) NULL);
if (status != 0)
perror("execl error:");
}
return 0;
}
ACTIONS
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int i, n, total;
/* simple program to sum integers from 1..n, where n is given
as a command line argument */
if (argc != 2) {
printf("Error no input argument\n");
exit(-1);
} else {
// convert the character string argument to an integer
n = atoi(argv[1]);
}
total = 0;
for (i=1; i <= n; i++)
total += i;
printf("sum of 1..%d = %d \n", n, total);
return 0;
}
Test the code by passing (in the command line) a variety of different
values for n.
Although common to all flavors of Unix, pipes have two limitations:
A pipe is like an I/O buffer administered by the kernel through which data can flow. We create a pipe by a call to the pipe() function. Two file descriptors are returned, one that is used for reading and one that is used for writing to the pipe. The following illustrates a pipe in a single process.
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(void) {
int status, nbytes;
char string1[] = "Hello Mate";
char string2[] = "Get Lost Mate";
int my_pipe[2];
int len1, len2;
status = pipe(my_pipe);
len1 = strlen(string1);
len2 = strlen(string2);
printf("initial string1: \"%s\", length %d\n", string1, len1);
printf("initial string2: \"%s\", length %d\n", string2, len2);
nbytes = write(my_pipe[1], string1, len1);
printf("write(): %d bytes\n", nbytes);
nbytes = read(my_pipe[0], string2, len2);
printf("read(): %d bytes\n", nbytes);
printf("read(): dest. string: \"%s\"\n", string2);
return 0;
}
The program declares two character strings that are initialized to some
random strings. The program writes character string string1 to
the pipe and then reads from the pipe into character string
string2.
ACTIONS
In a single process a pipe is next to useless. Normally they are used together with fork to provide an IPC channel between a parent and child process. To do this two pipes (A and B) are defined before the process issues a fork() call - this gives rise to 4 file descriptors; fd_A_read, fd_A_write, fd_B_read and fd_B_write. After forking, both the parent and child has copies of all these file descriptors. To turn pipe A into a channel that provides communication from:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(void) {
char string1[] = "Hello Mate";
char string2[ ]= "XXXXXXXXXX";
char string3[] = "*Get Lost*";
int fd_pc[2], fd_cp[2];
int status;
pipe(fd_pc);
pipe(fd_cp);
if (fork()) {
/* PARENT */
close(fd_pc[0]); /* close read pc */
close(fd_cp[1]); /* close write cp */
write(fd_pc[1], string1, strlen(string1));
printf("PARENT - string2 BEFORE pipe: %s\n", string2);
read(fd_cp[0], string2, strlen(string1));
printf("PARENT - string2 AFTER pipe: %s\n", string2);
wait(&status);
} else {
/* CHILD */
close(fd_pc[1]); /* close write pc*/
close(fd_cp[0]); /* close read cp*/
printf("CHILD - string2 BEFORE pipe: %s\n", string2);
read(fd_pc[0], string2, strlen(string1));
printf("CHILD - string2 AFTER pipe: %s\n", string2);
write(fd_cp[1], string3, strlen(string3));
}
return -1;
}
ACTIONS
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int n, *buf1, *buf2, len, total, nbytes;
int fd_pc[2], fd_cp[2];
int status, i;
pipe(fd_pc);
pipe(fd_cp);
if (argc != 2) {
printf("Error no input argument\n");
exit(-1);
} else {
// convert the character string argument to an integer
n = atoi(argv[1]);
}
len = sizeof(int)*n;
buf1 = (int*) malloc(len);
buf2 = (int*) malloc(len);
if (fork()) {
/* PARENT */
close(fd_pc[0]); /* close read pc */
close(fd_cp[1]); /* close write cp */
for (i=0; i<n; i++)
buf1[i] = n;
nbytes = write(fd_pc[1], buf1, len);
nbytes = read(fd_cp[0], buf2, len);
total = 0;
for (i=0; i<n; i++)
total += buf1[i]+buf2[i];
printf("PARENT - nbytes=%d, total=%d\n", nbytes, total);
wait(&status);
} else {
/* CHILD */
close(fd_pc[1]); /* close write pc*/
close(fd_cp[0]); /* close read cp*/
for (i=0; i<n; i++)
buf2[i] = n-i;
nbytes = write(fd_cp[1], buf2, len);
nbytes = read(fd_pc[0], buf1, len);
total = 0;
for (i=0; i<n; i++)
total += buf1[i]+buf2[i];
printf("CHILD - nbytes=%d, total=%d\n", nbytes, total);
}
return -1;
}
Compile and run the above code. You will find that it works correctly
for an input value of N = 1000, but there appears to be a problem when N
= 20000. What is the problem and how can you fix it? (Again an exam
question has related to this issue.
Hint: use the program above as a template. Only a single buffer is needed, and code related to total need not be used. Noting that a child inherits the exact values of all variables before the fork() that created it, a variable can be used to store the process number. Hence, a `fork-loop' which updates this variable can be used to set up the ring. Note that the Lab 7 select program has a `fork-loop' where the parent creates all children. However, in this case, it is better to use a slightly different loop structure, so that regardless of N, no process has more than a constant number (e.g. 6) of pipe-related file descriptors. For debugging, it is recommended you use printf("%d...\n", i) statements after every fork() and write() / read() (the latter should also print nbytes ).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
int fd[2];
pipe(fd);
if (fork()) {
/* PARENT */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execlp("wc", "wc", "-l", (char *) NULL);
} else {
/* CHILD */
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execlp("ls", "ls", "-l", "-r", "-t", (char *) NULL);
}
return -1;
}
Explain what the above code is doing (try compiling and running it!).