Advanced Programming in the UNIX Environment: Second Edition [Electronic resources] نسخه متنی

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

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

Advanced Programming in the UNIX Environment: Second Edition [Electronic resources] - نسخه متنی

W. Richard Stevens; Stephen A. Rago

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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












  • 12.3. Thread Attributes


    In all the examples in which we called pthread_create in Chapter 11, we passed in a null pointer instead of passing in a pointer to a pthread_attr_t structure. We can use the pthread_attr_t structure to modify the default attributes, and associate these attributes with threads that we create. We use the pthread_attr_init function to initialize the pthread_attr_t structure. After calling pthread_attr_init, the pthread_attr_t structure contains the default values for all the thread attributes supported by the implementation. To change individual attributes, we need to call other functions, as described later in this section.


    #include <pthread.h>
    int pthread_attr_init(pthread_attr_t *

    attr );
    int pthread_attr_destroy(pthread_attr_t *

    attr );

    Both return: 0 if OK, error number on failure

    To deinitialize a pthread_attr_t structure, we call pthread_attr_destroy. If an implementation of pthread_attr_init allocated any dynamic memory for the attribute object, pthread_attr_destroy will free that memory. In addition, pthread_attr_destroy will initialize the attribute object with invalid values, so if it is used by mistake, pthread_create will return an error.

    The pthread_attr_t structure is opaque to applications. This means that applications aren't supposed to know anything about its internal structure, thus promoting application portability. Following this model, POSIX.1 defines separate functions to query and set each attribute.

    The thread attributes defined by POSIX.1 are summarized in Figure 12.3. POSIX.1 defines additional attributes in the real-time threads option, but we don't discuss those here. In Figure 12.3, we also show which platforms support each thread attribute. If the attribute is accessible through an obsolete interface, we show

    ob in the table entry.

    Figure 12.3. POSIX.1 thread attributes

    Name

    Description

    FreeBSD 5.2.1

    Linux 2.4.22

    Mac OS X 10.3

    Solaris 9

    detachstate

    detached thread attribute

    guardsize

    guard buffer size in bytes at end of thread stack

    stackaddr

    lowest address of thread stack

    ob

    ob

    stacksize

    size in bytes of thread stack

    In Section 11.5, we introduced the concept of detached threads. If we are no longer interested in an existing thread's termination status, we can use pthread_detach to allow the operating system to reclaim the thread's resources when the thread exits.

    If we know that we don't need the thread's termination status at the time we create the thread, we can arrange for the thread to start out in the detached state by modifying the

    detachstate thread attribute in the pthread_attr_t structure. We can use the pthread_attr_setdetachstate function to set the

    detachstate thread attribute to one of two legal values: PTHREAD_CREATE_DETACHED to start the thread in the detached state or PTHREAD_CREATE_JOINABLE to start the thread normally, so its termination status can be retrieved by the application.


    [View full width]


    #include <pthread.h>
    int pthread_attr_getdetachstate(const
    pthread_attr_t *restrict

    attr ,
    int *

    detachstate );
    int pthread_attr_setdetachstate(pthread_attr_t
    *

    attr , int

    detachstate );

    Both return: 0 if OK, error number on failure

    We can call pthread_attr_getdetachstate to obtain the current

    detachstate attribute. The integer pointed to by the second argument is set to either PTHREAD_CREATE_DETACHED or PTHREAD_CREATE_JOINABLE, depending on the value of the attribute in the given pthread_attr_t structure.


    Example

    Figure 12.4 shows a function that can be used to create a thread in the detached state.

    Note that we ignore the return value from the call to pthread_attr_destroy. In this case, we initialized the thread attributes properly, so pthread_attr_destroy shouldn't fail. Nonetheless, if it does fail, cleaning up would be difficult: we would have to destroy the thread we just created, which is possibly already running, asynchronous to the execution of this function. By ignoring the error return from pthread_attr_destroy, the worst that can happen is that we leak a small amount of memory if pthread_attr_init allocated any. But if pthread_attr_init succeeded in initializing the thread attributes and then pthread_attr_destroy failed to clean up, we have no recovery strategy anyway, because the attributes structure is opaque to the application. The only interface defined to clean up the structure is pthread_attr_destroy, and it just failed.


    Figure 12.4. Creating a thread in the detached state


    #include "apue.h"
    #include <pthread.h>
    int
    makethread(void *(*fn)(void *), void *arg)
    {
    int err;
    pthread_t tid;
    pthread_attr_t attr;
    err = pthread_attr_init(&attr);
    if (err != 0)
    return(err);
    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (err == 0)
    err = pthread_create(&tid, &attr, fn, arg);
    pthread_attr_destroy(&attr);
    return(err);
    }

    [View full width]


    #include <pthread.h>
    int pthread_attr_getstack(const pthread_attr_t
    *restrict

    attr ,
    void **restrict

    stackaddr ,
    size_t *restrict

    stacksize );
    int pthread_attr_setstack(const pthread_attr_t *

    attr ,
    void *

    stackaddr , size_t
    *

    stacksize );

    Both return: 0 if OK, error number on failure

    These two functions are used to manage both the

    stackaddr and the

    stacksize thread attributes.

    With a process, the amount of virtual address space is fixed. Since there is only one stack, its size usually isn't a problem. With threads, however, the same amount of virtual address space must be shared by all the thread stacks. You might have to reduce your default thread stack size if your application uses so many threads that the cumulative size of their stacks exceeds the available virtual address space. On the other hand, if your threads call functions that allocate large automatic variables or call functions many stack frames deep, you might need more than the default stack size.

    If you run out of virtual address space for thread stacks, you can use malloc or mmap (see Section 14.9) to allocate space for an alternate stack and use pthread_attr_setstack to change the stack location of threads you create. The address specified by the

    stackaddr parameter is the lowest addressable address in the range of memory to be used as the thread's stack, aligned at the proper boundary for the processor architecture.

    The

    stackaddr thread attribute is defined as the lowest memory address for the stack. This is not necessarily the start of the stack, however. If stacks grow from higher address to lower addresses for a given processor architecture, the

    stackaddr thread attribute will be the end of the stack instead of the beginning.


    The drawback with pthread_attr_getstackaddr and pthread_attr_setstackaddr is that the

    stackaddr parameter was underspecified. It could have been interpreted as the start of the stack or as the lowest memory address of the memory extent to use as the stack. On architectures in which the stacks grow down from higher memory addresses to lower addresses, if the

    stackaddr parameter is the lowest memory address of the stack, then you need to know the stack size to determine the start of the stack. The pthread_attr_getstack and pthread_attr_setstack functions correct these shortcomings.


    An application can also get and set the

    stacksize thread attribute using the pthread_attr_getstacksize and pthread_attr_setstacksize functions.

    [View full width]


    #include <pthread.h>
    int pthread_attr_getstacksize(const pthread_attr_t
    *restrict

    attr ,
    size_t *restrict

    stacksize );
    int pthread_attr_setstacksize(pthread_attr_t *

    attr
    , size_t

    stacksize );

    Both return: 0 if OK, error number on failure

    The pthread_attr_setstacksize function is useful when you want to change the default stack size but don't want to deal with allocating the thread stacks on your own.

    The

    guardsize thread attribute controls the size of the memory extent after the end of the thread's stack to protect against stack overflow. By default, this is set to PAGESIZE bytes. We can set the

    guardsize thread attribute to 0 to disable this feature: no guard buffer will be provided in this case. Also, if we change the

    stackaddr thread attribute, the system assumes that we will be managing our own stacks and disables stack guard buffers, just as if we had set the

    guardsize thread attribute to 0.

    [View full width]


    #include <pthread.h>
    int pthread_attr_getguardsize(const pthread_attr_t
    *restrict

    attr ,
    size_t *restrict

    guardsize );
    int pthread_attr_setguardsize(pthread_attr_t *

    attr
    , size_t

    guardsize );

    Both return: 0 if OK, error number on failure

    If the

    guardsize thread attribute is modified, the operating system might round it up to an integral multiple of the page size. If the thread's stack pointer overflows into the guard area, the application will receive an error, possibly with a signal.

    The Single UNIX Specification defines several other optional thread attributes as part of the real-time threads option. We will not discuss them here.


    More Thread Attributes


    Threads have other attributes not represented by the pthread_attr_t structure:

    • The cancelability state (discussed in Section 12.7)

    • The cancelability type (also discussed in Section 12.7)

    • The concurrency level


    The concurrency level controls the number of kernel threads or processes on top of which the user-level threads are mapped. If an implementation keeps a one-to-one mapping between kernel-level threads and user-level threads, then changing the concurrency level will have no effect, since it is possible for all user-level threads to be scheduled. If the implementation multiplexes user-level threads on top of kernel-level threads or processes, however, you might be able to improve performance by increasing the number of user-level threads that can run at a given time. The pthread_setconcurrency function can be used to provide a hint to the system of the desired level of concurrency.


    #include <pthread.h>
    int pthread_getconcurrency(void);

    Returns: current concurrency level


    int pthread_setconcurrency(int

    level );

    Returns: 0 if OK, error number on failure

    The pthread_getconcurrency function returns the current concurrency level. If the operating system is controlling the concurrency level (i.e., if no prior call to pthread_setconcurrency has been made), then pthread_getconcurrency will return 0.

    The concurrency level specified by pthread_setconcurrency is only a hint to the system. There is no guarantee that the requested concurrency level will be honored. You can tell the system that you want it to decide for itself what concurrency level to use by passing a

    level of 0. Thus, an application can undo the effects of a prior call to pthread_setconcurrency with a nonzero value of

    level by calling it again with

    level set to 0.


  • / 369