LTZ看书之APUE10

LTZ看书之APUE10

线程控制

APUE讲的destroy会free空间,这件事情看起来不太对,也许是Base on实现
起码我看到的实现是没有做这件事情的。

 

int  pthread_attr_init(pthread_attr_t  *  attr)
{
    
*attr = gDefaultPthreadAttr;
    
return 0;
}


int  pthread_attr_destroy(pthread_attr_t  *  attr)
{
    memset(attr, 
0x42sizeof(pthread_attr_t));
    
return 0;
}


Attribution有下面这些。

 

typedef  struct
{
    uint32_t flags;
    
void * stack_base;
    size_t stack_size;
    size_t guard_size;
    int32_t sched_policy;
    int32_t sched_priority;
}
 pthread_attr_t;

我们可以用malloc和mmap给Thread分配stack,指定stack大小和位置(低地址)

mutex属性
两个东西
1.进程共享属性,PTHREAD_PROCESS_PRIVATE or SHARED
意思是说如果这个Mutex是分配在两个进程共享的memory,然后设置为shared mutex就可以用于进程同步
2.类型属性,normal;errorcheck;recurive
意思是定义Mutex本身的特性,这里更正下前面那章提到的错误
只有normal的mutex才会在再次加锁时发生deadlock
errorcheck的会直接返回错误
recurive本身就允许,会有计数

rw lock和condition属性
只支持第一个

TLS变量
先看下TLS的位置

  *   +---------------------------+
 
*   |      pthread_internal_t     |
 
*   +---------------------------+
 
*   |                             |
 
*   |           TLS area          |
 
*   |                             |
 
*   +---------------------------+
 
*   |                             |
 
*  .                           .
 
*  .         stack area        .
 
*  .                           .
 
*   |                             |
 
*   +---------------------------+
 
*   |          guard page         |
 
*   +---------------------------+

pthread_internal_t是记录线程自己本身的一些属性的变量,guard page就是线程attr上设置的防止栈溢出的扩展内存

创建TLS变量的方法
1.创建Key,这个Key是这个Process中所有Thread可以共享访问的,然后每个Thread自己去关联自己的Thread Local变量
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void*))
创建key的时候需要保证唯一调用,也就是不会有两个Thread同时调用,然后发生一些base on实现的不确定的结果,可以使用pthread_once

pthread_once的实现很简单,用mutex做互斥量保证pthread_once_t的访问,然后利用pthread_once_t做flag来调用init_routine

int   pthread_once( pthread_once_t *   once_control,   void  ( * init_routine)( void ) )
{
    
static pthread_mutex_t   once_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
    
volatile pthread_once_t* ocptr = once_control;

    pthread_once_t tmp 
= *ocptr;
    ANDROID_MEMBAR_FULL();
    
if (tmp == PTHREAD_ONCE_INIT) {
        pthread_mutex_lock( 
&once_lock );
        
if (*ocptr == PTHREAD_ONCE_INIT) {
            (
*init_routine)();
            ANDROID_MEMBAR_FULL();
            
*ocptr = ~PTHREAD_ONCE_INIT;
        }

        pthread_mutex_unlock( 
&once_lock );
    }

    
return 0;
}

所以这个pthread_once_t必须是全局or静态变量
pthread_once最常用的case除了拿到TLS key外,init函数经常会使用这个,避免多线程同时调用。

然后就是thread cancel
Android Bionic库没有实现 pthread_setcancelstate(int state, int* oldstate)以及pthread_cancel()
这里thread的取消,有些是取消不掉的,例如一些blocking call,所有的取消均是需要设定一些取消点的,

有人有专门针对Android没有Thread Cancel来想办法
基本就是thread signal,在signal handler中去call pthread_exit();
or用PIPE在多路复用中break出来
http://blog.csdn.net/langresser/article/details/8531112

如果信号与硬件故障or计时器超时相关,那么信号会发送到引起事件的该线程中去,其他信号会发送到任意一个线程。
这里是说的POSIX线程模型,一般的信号都会发送给进程中没有阻塞该信号的某个线程。APUE讲的某个没有阻塞该信号的线程,其实有点模糊。

但是Linux的实现不一样,linux的thread是clone出来的,就是共享资源的独立进程,这样子其实蛮自然的,也不容易复杂。
linux中,有可能线程就不会注意到该信号,这样讲也有点模糊,总之终端驱动程序的信号会将信号通知到进程组,这样子所有的thread就会都收到信号
如果你不想所有的线程都去关心信号,那么可以使用一个专门处理信号的线程
先将其他线程的signal都pthread_sigmask SIG_BLOCK掉,然后使用sigwait在特定的线程来等
pthread_sigmask和sigprocmask很像

thread与fork
fork后的子进程会继承mutex, rw lock, condition的状态
但是子进程中只会存在一个线程,也就是那个调用fork的线程的副本,有个复杂的事情是关于上面继承下来的那些东西,子进程由于只有一个线程,没办法知道
上面那三个东西的具体状态,需要有个清理锁的动作 -->pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
我目前不知道这个东西的使用场景,而且不容易控制。
因为一般如果我们fork后都会接execu,这样地址空间就会改变,那么这些继承的东西就没用了。

然后有谈一点关于线程IO的东西
pwrite/pread
也就是原子的进行lseek和r/w,但是这个并不能保证大家常见的冲突问题,也就是一个thread在写另外一个要读,所以这两个东西并不能解决同步的问题。

作业:
12.3 理论上函数开始时屏蔽所有信号,函数结束时恢复,是可以做到异步信号安全的。如果你只是屏蔽一些信号,那么没办法做到,因为如果有其他信号进来,你call的函数里面有一些不可重入的函数,同样不能保证是异步安全的。

12.5 fork可以在拿来执行可执行程序,现在应该就这一个Case。

你可能感兴趣的:(LTZ看书之APUE10)