0% found this document useful (0 votes)
10 views5 pages

Process Lab1

This document contains a problem sheet for an operating systems course. It includes two problems: 1) understanding the process environment in POSIX systems and writing a C program to print environment variables, and 2) writing a multi-threaded C program called ptwc that counts words, lines, and characters in multiple files concurrently using threads. It provides sample output and discusses handling errors. The solution section includes the full C code for the ptwc program that implements word counting across files in parallel threads.

Uploaded by

Shikha Gupta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views5 pages

Process Lab1

This document contains a problem sheet for an operating systems course. It includes two problems: 1) understanding the process environment in POSIX systems and writing a C program to print environment variables, and 2) writing a multi-threaded C program called ptwc that counts words, lines, and characters in multiple files concurrently using threads. It provides sample output and discusses handling errors. The solution section includes the full C code for the ptwc program that implements word counting across files in parallel threads.

Uploaded by

Shikha Gupta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 5

Operating Systems Course: 320202

Jacobs University Bremen Date: 2012-02-15


Dr. Jürgen Schönwälder Deadline: 2012-02-22

Problem Sheet #1

Problem 1.1: process environment (2+2 = 4 points)

On Posix systems, the signature of the main() function is also defined to be

int main(int argc, char *argv[], char *envp[])

where envp is a vector of environment variables. Each environment variable is a string of the form

NAME=value

that is a simple name/value pair using the equal sign as a separator. Environment variables often
affect the behaviour of programs. In particular, the PATH variable affects how the system calls
execlp() and execvp() behave.

a) In your shell, set the PATH variable to an empty string. What happens? Which commands still
work and why?

b) Write a small C program that prints the list of environment variables to the standard output.

Solution:

a) After setting PATH to an empty string, the shell will not find programs to execute anymore
unless a full path is provided. Hence, most commands simply fail. However, commands built
into the shell (such as cd or pwd or exit) continue to function since the execution of these
commands happens in the shell process itself (no new program is loaded).
b) The following program prints its environment:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv, char **envp)


{
int i;

for (i = 0; envp[i]; i++) {


puts(envp[i]);
}

(void) fflush(stdout);

return ferror(stdout) ? EXIT_FAILURE : EXIT_SUCCESS;


}
Problem 1.2: multi-threaded wordcount (wc) (6 points)

Write a multi-threaded C program ptwc that implements the functionality of the Unix wc utility. Your
program receives file names as command line arguments and it calculates for each file the number
of characters in the file, the number of words in the file, and the number of lines in the file. A word
is a non-zero-length sequence of characters delimited by white space. Your program should at the
end also display the total numbers summed up for all files.

Your program must process the files concurrently using multiple threads (one thread per file). To
make this simple, allocate in the main thread for each file listed on the command line a data struc-
ture that includes the file name, the counters, the thread identifier and any other thread specific
state you consider useful to maintain. The main thread of your program should create the counting
threads and afterwards it collects the results and prints them to the standard output. The main
thread also calculates and prints the total numbers. Here is some sample output:

$ ./ptwc ptwc.c CMakeLists.txt


134 358 2698 ptwc.c
12 25 344 CMakeLists.txt
146 383 3042 total

Make sure your program properly handles all possible runtime errors and that it returns an error
status to its parent process (usually the shell) in case a runtime error occured.

Solution:

/*
* wc/wc.c --
*
* A simple program to count words in files concurrently. For
* each file, a separate thread is created to count the words.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

static const char *progname = "wc";

static int tflag = 0;

typedef struct task {


const char *filename;
unsigned int chars;
unsigned int words;
unsigned int lines;
pthread_t thread;
} task_t;

static void*
wc(void *data)
{
task_t *task = (task_t *) data;
FILE *f;
int c, l = ’ ’;

assert(task && task->filename);


f = fopen(task->filename, "r");
if (! f) {
fprintf(stderr, "%s: %s: %s\n",
progname, task->filename, strerror(errno));
return NULL;
}

while (EOF != (c = fgetc(f))) {


task->chars++;
if (c == ’\n’) task->lines++;
if (!isspace(c) && isspace(l)) {
task->words++;
}
l = c;
}

if (ferror(f)) {
task = NULL;
}
(void) fclose(f);
return task;
}

static int
run_threads(int argc, char *argv[])
{
task_t tasks[argc];
unsigned int chars = 0, words = 0, lines = 0;
int err, rcode = EXIT_SUCCESS;
void *result;

assert(argc >= 0);

memset(tasks, 0, sizeof(tasks));

for (int i = 0; i < argc; i++) {


tasks[i].filename = argv[i];
err = pthread_create(&tasks[i].thread, NULL, wc, &tasks[i]);
if (err != 0) {
fprintf(stderr, "%s: unable to create thread %d: %d\n",
progname, i, err);
rcode = EXIT_FAILURE;
}
}

for (int i = 0; i < argc; i++) {


if (tasks[i].thread) {
err = pthread_join(tasks[i].thread, &result);
if (err) {
fprintf(stderr, "%s: unable to join thread %d: %d\n",
progname, i, err);
rcode = EXIT_FAILURE;
}
if (result) {
printf("%5u %5u %5u %s\n",
tasks[i].lines, tasks[i].words, tasks[i].chars,
tasks[i].filename);
chars += tasks[i].chars;
words += tasks[i].words;
lines += tasks[i].lines;
} else {
rcode = EXIT_FAILURE;
}
}
}
if (argc > 1) {
printf("%5u %5u %5u total\n", lines, words, chars);
}

if (fflush(stdout) == EOF) {
rcode = EXIT_FAILURE;
}

return rcode;
}

static int
run_sequentially(int argc, char *argv[])
{
task_t task;
unsigned int chars = 0, words = 0, lines = 0;
int rcode = EXIT_SUCCESS;
void *result;

assert(argc >= 0);

for (int i = 0; i < argc; i++) {


task.filename = argv[i];
result = wc(&task);
if (result) {
printf("%5u %5u %5u %s\n",
task.lines, task.words, task.chars,
task.filename);
chars += task.chars;
words += task.words;
lines += task.lines;
} else {
rcode = EXIT_FAILURE;
}
}
if (argc > 1) {
printf("%5u %5u %5u total\n", lines, words, chars);
}

if (fflush(stdout) == EOF) {
rcode = EXIT_FAILURE;
}

return rcode;
}

int
main(int argc, char *argv[])
{
const char *usage = "Usage: %s [-t] file ...\n";
int c, code;

while ((c = getopt(argc, argv, "ht")) >= 0) {


switch (c) {
case ’h’:
printf(usage, progname);
exit(EXIT_SUCCESS);
case ’t’:
tflag = 1;
break;
case ’:’:
case ’?’:
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;

code = tflag ? run_threads(argc, argv) : run_sequentially(argc, argv);


return code;
}

You might also like