
# Příklady k předmětu [[Unix]] #

## Simulace činnosti shellu ##

### Zadání ###

Napište program, který simuluje činnost shellu při interpretaci skriptu

 for F in "$@"; do /usr/bin/grep '^#' "$F"; done 2>/dev/null | less

### Řešení ###

*Především je potřeba si uvědomit, co všechno dělá shell, to samé by měl dělat i program.
*Dále je potřeba pochytat všechny konce roury, včetně těch, co duplikuje fork(), jinak roura prostě nechodí dobře.

*Nezapomeňte ukončit seznam parametrů funkcí **execl**() a **execlp**() hodnotou *NULL*, abyste se vyhnuli odpoledni zoufalého ladění, které zastihlo mě při psaní zápočťáku.
*(Pokud nevznikla chyba při editaci na wiki, tak by to mělo chodit.)

 #include <unistd.h>	*/* fork(), pipe(), dup2(), close(), exec*, ...*/*

 #include <sys/wait.h>	*/* wait()        */*
 #include <sys/types.h>	*/* pid_t         */*

 #include <stdlib.h> 	*/* exit()        */*
 #include <fcntl.h>	*/* O_WRONLY, ... */*

 void test_rval(int rval, char* msg);

 */*** main ***/*

 int main(int argc, char *argv\[])
 {

 	int pipe_fd\[2]; */* pipe descriptors */*

 	pid_t pid;      */* child PID        */*
 	int status;     */* wait status      */*

 	int null_fd;    */* fd of /dev/null  */*

 	int i, rval;

 	rval = **pipe**(pipe_fd);       */* init pipe */*
 	test_rval(rval, "pipe()");

 	pid = **fork**();               */* split to: for | less */*

 	test_rval(pid, "fork(1)");  */* leaving out the rest of the tests to be brief */*

 	if( pid == 0 )
 	{ child */

 		**close**(pipe_fd\[0]); */* duplicated by fork */*

 		for(i = 1; i < argc; i++) */* call grep for each parameter */*
 		{

 			pid = **fork**(); */* fork again to call grep */*

 			if( pid == 0 )
 			{ */* grand-child */*	

 				*/* redirect stderr to /dev/null - we could do it for our parent at once,*
 				* * but it would require another pipe to feed the parent*

 				* */*
 				null_fd = **open**("/dev/null", O_WRONLY);

 				**dup2**(null_fd, 2);
 				**close**(null_fd);

 				**dup2**(pipe_fd\[1], 1);  */* redirect output to the pipe */*

 				**close**(pipe_fd\[1]);

 				*/* call grep - remember that "'^#'" would search for '^#' including quotes */*
 				**execl**("/usr/bin/grep", "grep", "^#", argv\[i], NULL);

 				test_rval(-1, "exec(grep)");
 			}

 			else
 			{ */* parent (and also child of grand parent)*/*

 				**wait**(&status); */*wait for grep to finish*/*
 			}

 		} */* /for */*

 		**close**(pipe_fd\[1]);
 	}

 	else
 	{ parent - execute less */

 		**close**(pipe_fd\[1]);   */* duplicated by fork */* 
 		**dup2**(pipe_fd\[0], 0); */* stdin from pipe */*

 		**close**(pipe_fd\[0]);

 		**execlp**("less", "less", NULL);  */* run less */*
 		test_rval(-1, "execlp(less)"); */* should not be reached on success call */*

 	}

         return EXIT_SUCCESS;
 } */* /main */*

 void test_rval(int rval, char* msg)

 {
 	if( rval == -1 )

 	{
 		perror(msg);

 		exit(EXIT_FAILURE);
 	}

 }
 */* end */*

## Udržování konstantního počtu threadů ##

### Zadání ###

Napište aplikaci, která bude stále udržovat konstantní počet threadů. Thread vznikne a uspí se na náhodnou dobu. Potom zase umře. Důraz se klade na synchronizační primitiva.

### Řešení ###

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

 #include <getopt.h>
 #include <fcntl.h>

 #include <sys/types.h>
 #include <string.h>

 #include <unistd.h>
 #include <pthread.h>

 static struct option longopts\[] = {

    { "count", required_argument, NULL, 'c' },
    { NULL, 0, NULL, 0 }

 };

 #define MAX 10

 pthread_mutex_t mutex_stop = PTHREAD_MUTEX_INITIALIZER;
 pthread_cond_t cond_stop = PTHREAD_COND_INITIALIZER;

 int thread_stop_number = -1;

 int random_number = 0; 

 int rnd()
 {

     int cislo = rand();
     while (cislo > MAX)

         cislo = cislo % MAX;

     return cislo;
 }

 void * thread(void * x)

 {
     int time = rnd();

     int thr_id = (int) x; 

     int work=1;

     printf("thread %d started\n", thr_id);

     sleep(time);

     // inform main
     while(work){

         pthread_mutex_lock(&mutex_stop);
         if(thr_id!=-1){

             pthread_mutex_unlock(&mutex_stop);
             continue;

         }
         work=0;

     }
     thread_stop_number = thr_id;

     pthread_cond_signal(&cond_stop);
     pthread_mutex_unlock(&mutex_stop);

     printf("thread %d: after %d second(s) ended\n", thr_id, time);

     return NULL;
 }

 void start(int i)

 {
    pthread_t newthr;

    pthread_create(&newthr, NULL, thread, (void *) i);
    pthread_detach(newthr);

    return;
 }

 void * runner()

 {
    pthread_mutex_lock(&mutex_stop);

    while(1)
    {

        pthread_mutex_lock(&mutex_stop);
        while(thread_stop_number == -1)

            pthread_cond_wait(&cond_stop,&mutex_stop);
        // start new thread

        start(thread_stop_number);
        // set data

        thread_stop_number = -1;
        pthread_mutex_unlock(&mutex_stop);

    }
    pthread_mutex_unlock(&mutex_stop);

    return NULL;
 }

 void * starter(void * arg)

 {
    // start all threads at the beginning

    int count = (int) arg;
    int i;

    for(i = 0; i < count; i++)
        start(i);

    return NULL;

 }

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

    // read options
    int count = 10;

    char ch;

    while ((ch = getopt_long(argc, argv, "c:", longopts, NULL)) != -1)
        switch(ch) {

        case 'c':
            count = atoi(optarg);

            break;
        case 'd':

            break;
        default:

            puts("test\n");
            return(1);

    } 
    argc -= optind;

    argv += optind;

    pthread_t thr;
    pthread_t sthr;

    pthread_create(&thr, NULL, runner, NULL);

    pthread_create(&sthr, NULL, starter, (void *) count);

    pthread_join(thr, NULL);
    return 0;

 }
Makefile:

 EXECUTABLE = thread
 SOURCES = moje.c

 OBJECTS = $(SOURCES:.c=.o)
 CC = gcc

 CFLAGS = -c -Wall -o
 LDFLAGS = -lpthread -o

 all: $(EXECUTABLE)

 clean:
     rm *.o $(EXECUTABLE)

 $(EXECUTABLE): $(OBJECTS)

     $(CC) $(LDFLAGS) $@ $^
 $(OBJECTS): $(SOURCES)

## Bariéra pro vlákna ##

### Zadání ###

Napište barieru pro vlákna. V aplikaci vznikne několik vláken, ta se uspí na náhodnou dobu, a po probuzení
dojedou k bariéře, kde na sebe počkají. Využijte podmínkové proměnné, aktivní čekání není přípustné.

### Řešení ###

 #include <pthread.h>

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

 #include <stdio.h>
 #define NTHREADS 5

 typedef struct {

   pthread_mutex_t *lock;
   pthread_cond_t *cv;

   int *ndone;
   int id;

 } TStruct;

 void* barrier(void *arg)
 {

   TStruct *ts;
   int i;

   ts = (TStruct *) arg;

   // do something usefull here 
   {

     sleep((int)(( ((float) random())/RAND_MAX)*10 ));
   }

   printf("Thread %d -- waiting for barrier\n", ts->id);

   pthread_mutex_lock(ts->lock);
   printf("mutex %d locked by thread id: %d\n",(int) ts->lock , ts->id);

   *ts->ndone = *ts->ndone + 1;
   while (*ts->ndone < NTHREADS) {

       //printf("thread %d is waiting...: \n", ts->id);
       pthread_cond_wait(ts->cv, ts->lock);

   }
   //

   {
     for (i = 1; i <NTHREADS; i++) pthread_cond_signal(ts->cv);

   }
   printf("mutex unlocked by thread id: %d\n", ts->id);

   pthread_mutex_unlock(ts->lock);

   printf("Thread %d -- after barrier\n", ts->id);
  return NULL;

 }

 int main()
 {

   TStruct ts\[NTHREADS];
   pthread_t tids\[NTHREADS];

   int i, ndone;
   pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

   pthread_cond_t cv;
   void *retval;

   pthread_mutex_init(&lock, NULL);

   pthread_cond_init(&cv, NULL);
   ndone = 0; 

   for (i = 0; i < NTHREADS; i++) {

     ts\[i].lock = ￾
     ts\[i].cv = ￾

     ts\[i].ndone = ￾
     ts\[i].id = i;

   }

   for (i = 0; i < NTHREADS; i++) {
     pthread_create(tids+i, NULL, barrier, ts+i);

   }

   for (i = 0; i < NTHREADS; i++) {
     pthread_join(tids\[i], &retval);

   }

   pthread_mutex_destroy(&lock);
   pthread_cond_destroy(&cv);

   printf("done\n");
 return 0;

 }
