Unix™ Systems Programming [Electronic resources] : Communication, Concurrency, and Threads نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Unix™ Systems Programming [Electronic resources] : Communication, Concurrency, and Threads - نسخه متنی

Prentice Hall

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










12.2 Use of Threads to Monitor Multiple File Descriptors


Multiple threads can simplify the problem of monitoring multiple file descriptors because a dedicated thread with relatively simple logic can handle each file descriptor. Threads also make the overlap of I/O and processing transparent to the programmer.

We begin by comparing the execution of a function by a separate thread to the execution of an ordinary function call within the same thread of execution. Figure 12.1 illustrates a call to the processfd function within the same thread of execution. The calling mechanism creates an activation record (usually on the stack) that contains the return address. The thread of execution jumps to processfd when the calling mechanism writes the starting address of processfd in the processor's program counter. The thread uses the newly created activation record as the environment for execution, creating automatic variables on the stack as part of the record. The thread of execution continues in processfd until reaching a return statement (or the end of the function). The return statement copies the return address that is stored in the activation record into the processor program counter, causing the thread of execution to jump back to the calling program.


Figure 12.1. Program that makes an ordinary call to processfd has a single thread of execution.


Figure 12.2 illustrates the creation of a separate thread to execute the processfd function. The pthread_create call creates a new "schedulable entity" with its own value of the program counter, its own stack and its own scheduling parameters. The "schedulable entity" (i.e., thread) executes an independent stream of instructions, never returning to the point of the call. The calling program continues to execute concurrently. In contrast, when processfd is called as an ordinary function, the caller's thread of execution moves through the function code and returns to the point of the call, generating a single thread of execution rather than two separate ones.


Figure 12.2. Program that creates a new thread to execute processfd has two threads of execution.


We now turn to the specific problem of handling multiple file descriptors. The processfd function of Program 4.3 instead of read to restart reading if the thread is interrupted by a signal. However, we recommend a dedicated thread for signal handling, as explained in Section 13.5. In this case, the thread that executes processfd would have all signals blocked and could call read.

Program 12.1 processfd.c

The processfd function monitors a single file descriptor for input.


#include <stdio.h>
#include "restart.h"
#define BUFSIZE 1024
void docommand(char *cmd, int cmdsize);
void *processfd(void *arg) { /* process commands read from file descriptor */
char buf[BUFSIZE];
int fd;
ssize_t nbytes;
fd = *((int *)(arg));
for ( ; ; ) {
if ((nbytes = r_read(fd, buf, BUFSIZE)) <= 0)
break;
docommand(buf, nbytes);
}
return NULL;
}

Example 12.1

The following code segment calls processfd as an ordinary function. The code assumes that fd is open for reading and passes it by reference to processfd.


void *processfd(void *);
int fd;
processfd(&fd);

Example 12.2

The following code segment creates a new thread to run processfd for the open file descriptor fd.


void *processfd(void *arg);
int error;
int fd;
pthread_t tid;
if (error = pthread_create(&tid, NULL, processfd, &fd))
fprintf(stderr, "Failed to create thread: %s\n", strerror(error));

The code of Program 4.14 and Program 4.17. The threaded version is considerably simpler and takes advantage of parallelism. If docommand causes the calling thread to block for some reason, the thread runtime system schedules another runnable thread. In this way, processing and reading are overlapped in a natural way. In contrast, blocking of docommand in the single-threaded implementation causes the entire process to block.

If monitorfd fails to create thread i, it sets the corresponding thread ID to itself to signify that creation failed. The last loop uses pthread_join, described in Section 12.3, to wait until all threads have completed.

Program 12.2 monitorfd.c

A function to monitor an array of file descriptors, using a separate thread for each descriptor.


#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *processfd(void *arg);
void monitorfd(int fd[], int numfds) { /* create threads to monitor fds */
int error, i;
pthread_t *tid;
if ((tid = (pthread_t *)calloc(numfds, sizeof(pthread_t))) == NULL) {
perror("Failed to allocate space for thread IDs");
return;
}
for (i = 0; i < numfds; i++) /* create a thread for each file descriptor */
if (error = pthread_create(tid + i, NULL, processfd, (fd + i))) {
fprintf(stderr, "Failed to create thread %d: %s\n",
i, strerror(error));
tid[i] = pthread_self();
}
for (i = 0; i < numfds; i++) {
if (pthread_equal(pthread_self(), tid[i]))
continue;
if (error = pthread_join(tid[i], NULL))
fprintf(stderr, "Failed to join thread %d: %s\n", i, strerror(error));
}
free(tid);
return;
}


    / 276