SWI015 Příklady

Z ωικι.matfyz.cz
Přejít na: navigace, hledání

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 = &lock;
    ts[i].cv = &cv;
    ts[i].ndone = &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;
}