UNIX环境高级编程-线程控制

目录

相关函数列表

线程属性的系统限制

线程属性的例子

线程互斥属性的例子

flock的例子

线程特定数据

线程和fork

线程和信号

pthread_kill

参考


 

 

 

相关函数列表

//线程属性,pthread_attr_t结构体保护的就是操作系统实现支持的所有线程属性  
//下面所有函数都是是成功返回0,否则返回错误编号  
#include <pthread.h>  
int pthread_attr_init(pthread_attr_t *attr);  
int pthread_attr_destroy(pthread_attr_t *attr);  
  
//如果对某个线程终止状态不感兴趣,可以使用pthread_detach函数让系统在  
//线程退出时收回所占用的资源  
//可以detachstate为以下两个值  
//1.PTHREAD_CREATE_DETACHED  分离状态启动线程  
//2.PTHREAD_CREATE_JOINABLE    正常启动线程  
#include <pthread.h>  
int pthread_attr_getdetachstate(const pthread_attr_t *attr,  
                                                     int *detachstate);  
int pthread_attr_setdetachstate(pthread_attr_t *attr, in *detachstate);  
  
  
//对线程栈属性进行管理  
#include <pthread.h>  
int pthread_attr_getstack(const pthread_attr_t *restrict attr,  
                          void **restrict stackaddr, size_t *restrict stacksize);  
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,  
                                         size_t stacksize);  
  
//通过下面函数设置stacksize  
#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);  
  
  
//线程属性guardsize控制线程栈末尾以后用以避免溢出的口占内存的大小,这个  
//属性默认值是由具体实现来定义,但常用值是系统页大小,可以把其设置为0,  
//不允许属性的这种特征行为发生,在这种情况下,不会提供警戒缓冲区。同样,  
//如果修改了线程属性stackaddr,系统就认为我们将自己管理栈,警戒缓冲区  
//机制无效  
#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);  
  
  
//互斥量属性,互斥量的返回值都是成功返回0,否则返回错误编号  
#include <pthread.h>  
int pthread_mutexattr_init(pthread_mutexattr_t *attr);  
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);  
  
//修改进程共享属性  
#include <pthread.h>  
int pthread_mutexattr_getpshared(const pthread_mutexattr_t   
                             *restrict attr, int *restrict pshared);  
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,  
                                                        int pshared);  
  
//修改和获取健壮的互斥量属性  
#include <ptherad.h>  
int pthread_mutexattr_getrobust(const pthread_mutexattr_t   
                                *restrict attr, int *restrict robust);  
int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr,   
                                                  int robust);  
  
//线程可以调用下面函数,指明与该互斥量相关的状态在互斥量解锁之前是一致的  
#include <pthread.h>  
int pthread_mutex_consistent(pthread_mutex_t *mutex);  
  
//修改和获取互斥量属性  
#include <pthread.h>  
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict  
                                             attr, int *restirct type);  
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);  
  
  
//读写锁属性,读写锁的返回值都是成功返回0,否则返回错误编号  
#include <pthread.h>  
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);  
int phtread_rwlockattr_destroy(pthread_rwlockattr_t *attr);  
  
//和互斥量一样,读写锁也有一对函数用于读取和设置进程共享属性  
#include <pthraed.h>  
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr,  
                                                     int *restrict pshared);  
int pthraed_rwlockattr_setpshared(pthread_rwlockattr_t *attr,int pshared);  
  
  
  
//条件变量属性  
#include <pthrad.h>  
int pthread_condattr_init(pthread_condattr_t *attr);  
int pthread_condattr_destroy(pthread_condattr_t *attr);  
  
//条件变量也支持进程共享属性,可以被单个进程的多个线程使用  
//条件变量的返回值都是成功返回0,否则返回错误编号  
#include <pthread.h>  
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr,  
                                   int *restrict pshared);  
int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared);  
  
//条件变量可以设置时钟属性  
#include <pthread.h>  
int pthread_condattr_getclock(const pthread_condattr_t *restrict attr,  
                              clockid_t *restrict clock_id);  
int pthread_condattr_setclock(pthread_condattr_t *atr, clockid_t clock_id);  
  
  
  
//屏障属性(屏障的返回值都是成功返回0,否则返回错误编号)  
#include <pthread.h>  
int pthread_barrierattr_init(pthread_barrierattr_t *attr);  
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);  
  
//屏障的进程共享属性  
int pthraed_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr,  
                                   int *restrict pshared);  
int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared);  
  
  
  
//POSIX.1提供了安全方式管理FILE对象的方法,可以使用下面方式获取给定FILE对象关联的锁,这个  
//锁是递归的,当占有这把锁后可以再次获取该锁,不会导致死锁。虽然这种锁的具体实现并无规定,  
//但要求所有操作FILE对象的标准I/O例程的动作行为必须看起来就像他们内部调用了flockfile和  
//funlockfile,以下函数都是成功返回0,否则返回错误编号  
#include <stdio.h>  
int ftrylockfile(FILE *fp);  
void flockfile(FILE *fp);  
void funlockfile(FILE *fp);  
  
//如果标准I/O例程都获取他们各自的锁,那么在做一次一个字符的I/O时就会出现严重的性能下降,在  
//这种情况下,需要对每一个字符的读写操作都进行获取锁和释放锁的动作。为了避免这种开销,出现  
//了不加锁版本的基于字符的标准I/O例程  
#include <stdio.h>  
int getchar_unlocked(void);  
int getc_unlocked(FILE *fp);   //这两个函数若成功返回下一个字符,若遇到文件尾或出错返回EOF  
int putchar_unlocked(int c);  
int putc_unlocked(int c, FILE *fp);  
  
  
//线程特定数据(thread0specific data),也成为线程私有数据(thread-private data),是存储和存储  
//某个特定线程相关数据的一种机制,每个线程都有自己单独的数据副本,而不需要担心与其他线程的  
//同步访问问题  
#include <pthread.h>  
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void*));  
  
//取消与现场特定数据值之间关联关系  
#include <pthread.h>  
int pthread_key_delete(pthread_key_t key);  
  
//解决竞争条件  
#inlude <pthread.h>  
pthread_once_t initflag = PHTREAD_ONCE_INIT;  
int pthread_once(pthread_once_t *initflag, void (*initfn)(void));  
  
//通过下面函数设置和获取线程特定数据的地址  
#include <pthread.h>  
void *pthread_getspecific(pthread_key_t key);  
int pthread_setspecific(pthread_key_t key, const void *value);  
  
  
  
//线程取消,取消状态可以是PTHREAD_CANCEL_ENABLE或者PTHREAD_CANCEL_DISABLE  
#include <pthread.h>  
int pthread_setcancelstate(int state, int *oldstate);  
  
//添加自己的取消点  
#include <pthread.h>  
void pthread_testcancel(void);  
  
//修改取消类型,参数type可以使PTHREAD_CANCEL_DEFERRED,或者PTHREAD_CANCEL_ASYNCHRONOUS  
#include <pthread.h>  
int pthread_setcanceltype(int type,int *oldtype);  
  
  
//线程和信号  
#include <signal.h>  
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);  
  
//等待一个或多个信号出现  
#include <signal.h>  
int sigwait(const sigset_t *restirct set, int *restrict signop);  
  
//要把信号发给进程可以调用kill,要把信号发给线程可以调用pthread_kill  
#include <signal.h>  
int pthread_kill(pthread_t thread, int signo);  
  
//线程和fork,最多可以安装3个帮助清理锁的函数  
//prepare fork处理程序由父进程在fork创建子进程前调用,这个fork处理程序的任务是获取父进程  
//   定义的所有锁  
//parent fork处理程序是在fork创建子进程以后,返回之前在父进程上下文中调用的。这个fork处理  
//   程序的任务是对prepare fork处理程序获取的所有锁进行解锁  
//child fork处理程序在fork返回之前在子进程上下文中调用。与parent fork处理程序一样,child  
//   fork处理程序也必须释放prepare fork处理程序获取的所有锁  
#include <pthread.h>  
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*chiild)(void));   

 

 

线程属性的系统限制

可以通过sysconf函数查询

限制名称描述name参数

PTHREAD_DESTRUCTOR

_ITERATIONS

 线程退出时操作系统实现试图销毁

特定数据段的最大次数

_SC_THREAD_DESTRUCTOR_

ITERATIONS 

PTHREAD_KEYS_MAX进程可以创建的键的最大E数目 _SC_THREAD_KEYS_MAX 
PTHRAD_STACK_MIN一个线程的栈可用的最小字节数 _SC_THREAD_STACK_MIN 
PTHREAD_THREADS_MAX进程可以创建的最大线程数_SC_THREAD_THREADS_MAX

 

pthread接口允许我们通过设置每个对象关联的不同属性来细调线程和同步对象的行为。通常,管理这些

属性的函数都遵循相同的模式

1)每个对象与它自己类型的属性对象进行关联(线程与线程属性关联,互斥量与互斥量属性关联等)。一个

   属性对象可以代表多个属性。属性对象对应用程序来说是不透明的。这意味着应用程序并不需要了解

   有关属性对象内部结构的详细细节,这样可以增强应用程序的可移植性。取而代之的是需要提供相应的

   函数来管理这些属性对象

2)有一个初始化函数,把属性设置为默认值

3)还有一个销毁属性对象的函数。如果初始化函数分配了与属性对象管理的资源,销毁函数负责释放这些

    资源

4)每个属性都有一个从属性对象中获取属性值的函数。由于函数成功时会返回0,失败时会返回错误编号,

   所以可以通过把属性值存储在函数的某一个参数执行的内存单元中,把属性值返回给调用者

5)每个属性都有一个设置属性值的函数。在这种情况下,属性值作为参数按值传递

 

POSIX.1定义的线程属性

名称描述
detachstat线程的分离状态属性
guardsize线程栈末尾的警戒缓冲区大小(字节)
stackaddr线程栈的最低地址
stacksize线程栈的最小长度(字节)

线程属性的例子

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#define false 0
#define true 1
#define bool int



int stack_size = 1024*1024*20;

void *fun() {
    printf("fun thread %d\n",pthread_self());
    pthread_exit(NULL);
}



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

    pthread_attr_t attr;
    pthread_t p1;
    void *stack = malloc(sizeof(stack_size));
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    //pthread_attr_setstack(&attr,stack,stack_size);
    pthread_attr_setdetachstate(&attr,stack_size);
    pthread_create(&p1,&attr,fun,(void*)1);
    

    sleep(2);
    pthread_attr_destroy(&attr);
    free(stack);
    printf("end main...\n");
    return 0;
}

//执行结果
fun thread -913426688
end main...

 

 

互斥量类型行为

互斥量类型

没有解锁时

重新加载?

不占用

时解锁?

在已解锁

时解锁?

说明

PTHREAD_MUTEX

_NORMAL

死锁未定义未定义

一种标准互斥量类型,不做任何

特殊的错误检查或死锁检测

PTHREAD_MUTEX_

ERRORCHECK

返回错误返回错误返回错误

此互斥量类型提供错误检查

PTHREAD_MUTEX_

RECURSIVE

允许返回错误返回错误

此互斥量类型允许同一线程在互斥量解锁

之前对该互斥量进行多次加锁。递归互斥量维护

锁的计数,在解锁次数和加锁次数不相同的情况

下,不会释放锁。所以如果对一个递归互斥量

加锁两次,然后解锁一次,那么这个互斥量将

依然处于加锁状态,对它再次解锁以前不能释放

该锁。

PTHREAD_MUTEX_

DEFAULT

未定义未定义未定义

此互斥量类型可以提供默认特性和行为。操作

系统再实现它的时候可以把这种类型自由的映射

到掐互斥量类型中的一种。如Linux3.2.0把这种

类型映射为普通的互斥量类型而FreeBSD则把

它映射为错误检查互斥量类型

线程互斥属性的例子

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#define false 0
#define true 1
#define bool int

pthread_mutex_t mutex;
int num = 100;

void lock() {
    pthread_mutex_lock(&mutex);
}

void unlock() {
    pthread_mutex_unlock(&mutex);
}

void *foo() {
    int i=0;
    for(;i<10;i++) {
        lock();
        num++;
    }
    sleep(1);
    i=0;
    for(;i<10;i++) {
        unlock();
    }
    printf("foo ok...\n");
    pthread_exit(NULL);
}


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

    pthread_mutexattr_t  attr;
    pthread_t p1;   
    void *ret1;

    pthread_mutexattr_init(&attr);
    //PTHREAD_PROCESS_SHARED设置为多进程共享模式
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    //PTHREAD_MUTEX_ROBUST设置为健壮的互斥量,即使线程挂了也会返回
    pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
    //PTHREAD_MUTEX_RECURSIVE允许多次锁定一个对象
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    pthread_mutex_init(&mutex,&attr);
    pthread_mutex_consistent(&mutex);

    pthread_create(&p1,NULL,foo,(void*)1);
    pthread_join(p1,&ret1);

    pthread_mutex_destroy(&mutex);
    pthread_mutexattr_destroy(&attr);
    printf("num->%d\n",num);
    printf("main ok...\n");
    return 0;
}

//执行结果
foo ok...
num->110
main ok...

 

重入

如果一个函数在相同 时间点可以被多个线程安全的调用,就称该函数是线程安全的

 

flock的例子

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#define false 0
#define true 1
#define bool int


char *file_name = "x.log";
void *foo(void *msg) {
    FILE *f = fopen(file_name,"r");
    flockfile(f);
    char buf[100];
    int read_count = fread(buf,sizeof(char),100,f);
    printf("foo.....\n");
    fwrite(buf,sizeof(char),read_count,stdout);
    printf("\nfoo... end\n");
    funlockfile(f);
    pthread_exit(NULL);
}


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

    pthread_t p1;
    void *ret1;
    char buf[100];
    pthread_create(&p1,NULL,foo,(void*)1);
    //sleep(1);
    FILE *f = fopen(file_name,"r");

    flockfile(f);
    int read_count = fread(buf,sizeof(char),100,f);   
    printf("main...\n");
    fwrite(buf,sizeof(char),read_count,stdout);
    printf("\nmain write ok\n");
    funlockfile(f);
    pthread_join(p1,&ret1);
    printf("###################   main end!\n");
    return 0;
}

//执行结果
main...
aaaaa
bbbbb
ccccc
ddddd

main write ok
foo.....
aaaaa
bbbbb
ccccc
ddddd

foo... end
###################   main end!

 

 

 

线程特定数据

线程特定数据(thread-specific data),也称为线程私有数据(thread-private data),是存储和查询某个特定

线程相关数据的一种机制。我们把这种数据称为线程特定数据或线程私有数据的原因是  希望每个线程可以

访问他们自己单独的数据副本,而不需要担心与其他线程的同步访问问题。

线程模型促进了线程中数据和属性的共享,为什么有人想在这种的模型中促进阻止共享的接口呢?

1)有时候需要维护每线程(per-thread)的数据,因为线程ID并不能保证是小而连续的整数,所以就不能简单

   的分配一个每线程数据数组,用线程ID作为数组的索引。即使线程ID确实是小而连续的整数,我们可能

   还希望有一些额外的保护,防止某个线程的数据与其他线程的数据相混淆

2)提供了让基于进程的接口适应多线程环境的机制,一个明显的实例就是errno,以前的接口都是进程

  上下文全局可访问的整数,系统调用和库例程调用或执行失败时设置的,把它作为操作失败时负数的结果。

  所以把errno定义为线程私有数据

 

线程特定数据的例子

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#define false 0
#define true 1
#define bool int

pthread_key_t key;


void destroy(void *msg) {
    printf("end foo....\n");
}

void *foo(void *arg) {
    pthread_setspecific(key,arg);
    int *num = (int*)pthread_getspecific(key);
    *num = (*num) + 100;
    printf("modify num->%d, running in %s\n",*num, pthread_self());
    pthread_exit(NULL);

}

int main(int argc, char *argv[]) {
    pthread_t p1,p2;
    void *ret1, *ret2;
    int a = 1;
    int b = 2;
    pthread_key_create(&key,destroy);
    pthread_create(&p1,NULL,foo,&a);
    pthread_create(&p2,NULL,foo,&b);


    pthread_join(p1,&ret1);
    pthread_join(p2,&ret2);
    pthread_key_delete(key);
    printf("end main...\n");
    return 0;
}

//执行结果
modify num->101, running in 
end foo....
modify num->102, running in 
end foo....
end main...

 

 

线程和信号

每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的

这意味着单个线程可以阻止某些信号,但当某个线程修改了与某个给定信号相关的处理行为以后,所有的

线程都必须共享这个处理行为的改变

 

线程和fork

子进程从父进程继承了每个互斥量,读写锁和条件变量,如果父进程包含一个以上的线程,子进程在fork

返回以后,如果紧接着不是马上调用exec的话,就需要清理锁状态

pthread_atfork是POSIX标准,在编译时要加上-pthread
-lpthread是老版本的gcc编译器用的,在新版本中应该用-pthread取代-lpthread

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#define false 0
#define true 1
#define bool int

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void prepare() {
    printf("prepare lock...\n");
    pthread_mutex_lock(&lock1);
    pthread_mutex_lock(&lock2);
}

void parent() {
    printf("parent unlock\n");
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
}

void child() {
    printf("child unlock\n");
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
}

void *foo(void *msg) {
    printf("child thread foo ...\n");
    pause();
    pthread_exit(NULL);
}


int main(int argc, char *argv[]) {
    pthread_t p1;
    pid_t pid;
    pthread_atfork(prepare,parent,child);
    pthread_create(&p1,NULL,foo,(void*)0);

    sleep(2);
    printf("main -> fork\n");   
    pid = fork();
    if(pid > 0) { //parent
        printf("......parent return fork\n");
    }
    else if(pid == 0) { //child 
        printf("....child return fork\n");
    }
    else {
        printf("fork error\n)");
        exit(-1);
    }

    printf("thr program execute over!\n");
    return 0;
}

//执行结果,注意子进程的输出和父进程输出是分开的
child thread foo ...
main -> fork
prepare lock...
parent unlock
......parent return fork
thr program execute over!
[root@.......]# child unlock
....child return fork
thr program execute over!

 

线程和信号

#include<stdio.h>
#include<pthread.h>
#include<signal.h>

static void sig_alrm(int signo);
static void sig_init(int signo);
int main() {
        sigset_t set;
        int sig;
        sigemptyset(&set);
        sigaddset(&set, SIGALRM);
        pthread_sigmask(SIG_SETMASK, &set, NULL);//阻塞SIGALRM信号

        signal(SIGALRM, sig_alrm);
        signal(SIGINT, sig_init);
        sigwait(&set, &sig);//sigwait只是从未决队列中删除该信号,并不改变信号掩码。也就是,当sigwait函数返回,它监听的信号依旧被阻塞。
        switch(sig) {
            case 14:
                printf("sigwait, receive signal SIGALRM\n");
                /*do the job when catch the sigwait*/
                break;
            default:
                break;
        }
        sigdelset(&set, SIGALRM);
        pthread_sigmask(SIG_SETMASK, &set, NULL);

        for(;;) {

        }

        return 0;
}

static void sig_alrm(int signo) {
        printf("after sigwait, catch SIGALRM\n");
        fflush(stdout);
        return ;
}

static void sig_init(int signo) {
        printf("catch SIGINT\n");
        return ;
}

//执行
kill -SIGINT 32128
kill -SIGINT 32128
kill -SIGINT 32128

//结果
catch SIGINT
catch SIGINT
catch SIGINT

//执行
kill -SIGALRM 32128
kill -SIGALRM 32128
kill -SIGALRM 32128

//结果
sigwait, receive signal SIGALRM
after sigwait, catch SIGALRM
after sigwait, catch SIGALRM

 

pthread_kill

#include<stdio.h>
#include<pthread.h>
#include<signal.h>

void handler(int sig_num, siginfo_t *info, void *arg) {
    printf("catch signum->%d, thread->%u\n",sig_num, pthread_self());
}

void *foo(void *msg) {
    struct sigaction act;
    act.sa_sigaction = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGINT, &act, NULL);
   
    while(1) {
        pause();
    }
}


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

    pthread_t  p1;
    void *ret1;
    
    pthread_create(&p1,NULL,foo,(void*)1);

    sleep(1);
    printf("send sig SIGINT to thread %u\n",p1);   
    pthread_kill(p1, SIGINT);

    sleep(1);
    printf("send sig SIGQUIT to thread %u\n",p1);
    pthread_kill(p1, SIGQUIT);

    return 0;
}


//执行结果
send sig SIGINT to thread 1079043840
catch signum->2, thread->1079043840
send sig SIGQUIT to thread 1079043840


//用strace分析程序,pthrea_kill最终是用 tgkill() 这个系统函数完成的
//如果线程对某个信号执行默认动作(退出程序),会导致整个程序的退出
。。。
[pid 32201] pause(
 <unfinished ...>
[pid 32200] <... nanosleep resumed> 0x7ffd3e215580) = 0
[pid 32200] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
[pid 32200] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc1da0fd000
[pid 32200] write(1, "send sig SIGINT to thread 365008"..., 37send sig SIGINT to thread 3650086656
) = 37
[pid 32200] tgkill(32200, 32201, SIGINT) = 0
[pid 32200] rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
[pid 32200] rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
[pid 32200] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 32200] nanosleep({1, 0},  <unfinished ...>
[pid 32201] <... pause resumed> )       = ? ERESTARTNOHAND (To be restarted if no handler)
[pid 32201] --- SIGINT {si_signo=SIGINT, si_code=SI_TKILL, si_pid=32200, si_uid=0} ---
[pid 32201] write(1, "catch signum->2, thread->3650086"..., 36catch signum->2, thread->3650086656
) = 36
[pid 32201] rt_sigreturn({mask=[]})     = -1 EINTR (Interrupted system call)
[pid 32201] pause( <unfinished ...>
[pid 32200] <... nanosleep resumed> 0x7ffd3e215580) = 0
[pid 32200] write(1, "send sig SIGQUIT to thread 36500"..., 38send sig SIGQUIT to thread 3650086656
) = 38
[pid 32200] tgkill(32200, 32201, SIGQUIT) = 0
[pid 32200] exit_group(0)               = ?
[pid 32201] +++ exited with 0 +++
+++ exited with 0 +++

 

 

参考

pthread_mutexattr_setpshared(3)

pthread_mutexattr_setrobust

Unix环境高级编程(十二)线程控制

linux中使用信号--sigwait()和pthread_sigmask()

Linux线程-pthread_kill

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 译者序 译者简介 前言 第1章 UNIX基础知识 1 1.1 引言 1 1.2 登录 1 1.2.1 登录名 1 1.2.2 shell 1 1.3 文件和目录 2 1.3.1 文件系统 2 1.3.2 文件名 2 1.3.3 路径名 2 1.3.4 工作目录 4 1.3.5 起始目录 4 1.4 输入和输出 5 1.4.1 文件描述符 5 1.4.2 标准输入、标准输出和标准 出错 5 1.4.3 不用缓存的I/O 5 1.4.4 标准I/O 6 1.5 程序和进程 7 1.5.1 程序 7 1.5.2 进程和进程ID 7 1.5.3 进程控制 7 1.6 ANSI C 9 1.6.1 函数原型 9 1.6.2 类属指针 9 1.6.3 原始系统数据类型 10 1.7 出错处理 10 1.8 用户标识 11 1.8.1 用户ID 11 1.8.2 组ID 12 1.8.3 添加组ID 12 1.9 信号 12 1.10 UNIX时间值 14 1.11 系统调用和库函数 14 1.12 小结 16 习题 16 第2章 UNIX标准化及实现 17 2.1 引言 17 2.2 UNIX标准化 17 2.2.1 ANSI C 17 2.2.2 IEEE POSIX 18 2.2.3 X/Open XPG3 19 2.2.4 FIPS 19 2.3 UNIX实现 19 2.3.1 SVR4 20 2.3.2 4.3+BSD 20 2.4 标准和实现的关系 21 2.5 限制 21 2.5.1 ANSI C限制 22 2.5.2 POSIX限制 22 2.5.3 XPG3限制 24 2.5.4 sysconf、pathconf 和fpathconf 函数 24 2.5.5 FIPS 151-1要求 28 2.5.6 限制总结 28 2.5.7 未确定的运行时间限制 29 2.6 功能测试宏 32 2.7 基本系统数据类型 32 2.8 标准之间的冲突 33 2.9 小结 34 习题 34 第3章 文件I/O 35 3.1 引言 35 3.2 文件描述符 35 3.3 open函数 35 3.4 creat函数 37 3.5 close函数 37 3.6 lseek函数 38 3.7 read函数 40 3.8 write函数 41 3.9 I/O的效率 41 3.10 文件共享 42 3.11 原子操作 45 3.11.1 添加至一个文件 45 3.11.2 创建一个文件 45 3.12 dup和dup2函数 46 3.13 fcntl函数 47 3.14 ioctl函数 50 3.15 /dev/fd 51 3.16 小结 52 习题 52 第4章 文件和目录 54 4.1 引言 54 4.2 stat, fstat和lstat函数 54 4.3 文件类型 55 4.4 设置-用户-ID和设置-组-ID 57 4.5 文件存取许可权 58 4.6 新文件和目录的所有权 60 4.7 access函数 60 4.8 umask函数 62 4.9 chmod和fchmod函数 63 4.10 粘住位 65 4.11 chown, fchown和 lchown函数 66 4.12 文件长度 67 4.13 文件截短 68 4.14 文件系统 69 4.15 link, unlink, remove和rename 函数 71 4.16 符号连接 73 4.17 symlink 和readlink函数 76 4.18 文件的时间 76 4.19 utime函数 78 4.20 mkdir和rmdir函数 79 4.21 读目录 80 4.22 chdir, fchdir和getcwd函数 84 4.23 特殊设备文件 86 4.24 sync和fsync函数 87 4.25 文件存取许可权位小结 88 4.26 小结 89 习题 89 第5章 标准I/O库 91 5.1 引言 91 5.2 流和FILE对象 91 5.3 标准输入、标准输出和标准出错 91 5.4 缓存 91 5.5 打开流 94 5.6 读和写流 96 5.6.1 输入函数 96 5.6.2 输出函数 97 5.7 每次一行I/O 98 5.8 标准I/O的效率 99 5.9 二进制I/O 100 5.10 定位流 102 5.11 格式化I/O 103 5.11.1 格式化输出 103 5.11.2 格式化输入 103 5.12 实现细节 104 5.13 临时文件 105 5.14 标准I/O的替代软件 108 5.15 小结 108 习题 108 第6章 系统数据文件和信息 110 6.1 引言 110 6.2 口令文件 110 6.3 阴影口令 112 6.4 组文件 113 6.5 添加组ID 114 6.6 其他数据文件 115 6.7 登录会计 116 6.8 系统标识 116 6.9 时间和日期例程 117 6.10 小结 121 习题 121 第7章 UNIX进程的环境 122 7.1 引言 122 7.2 main 函数 122 7.3 进程终止 122 7.3.1 exit和_exit函数 122 7.3.2 atexit函数 124 7.4 命令行参数 125 7.5 环境表 126 7.6 C程序的存储空间布局 126 7.7 共享库 127 7.8 存储器分配 128 7.9 环境变量 130 7.10 setjmp 和longjmp函数 132 7.10.1 自动、寄存器和易失变量 134 7.10.2 自动变量的潜在问题 136 7.11 getrlimit 和setrlimit函数 136 7.12 小结 139 习题 140 第8章 进程控制 141 8.1 引言 141 8.2 进程标识 141 8.3 fork函数 142 8.4 vfork 函数 145 8.5 exit函数 147 8.6 wait和waitpid函数 148 8.7 wait3和wait4函数 152 8.8 竞态条件 153 8.9 exec函数 156 8.10 更改用户ID和组ID 160 8.10.1 setreuid 和setregid函数 162 8.10.2 seteuid和 setegid函数 163 8.10.3 组ID 163 8.11 解释器文件 164 8.12 system函数 167 8.13 进程会计 171 8.14 用户标识 175 8.15 进程时间 176 8.16 小结 178 习题 178 第9章 进程关系 180 9.1 引言 180 9.2 终端登录 180 9.2.1 4.3+BSD终端登录 180 9.2.2 SVR4终端登录 182 9.3 网络登录 182 9.3.1 4.3+BSD网络登录 182 9.3.2 SVR4网络登录 183 9.4 进程组 183 9.5 对话期 184 9.6 控制终端 185 9.7 tcgetpgrp 和tcsetpgrp函数 187 9.8 作业控制 187 9.9 shell执行程序 189 9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的信号 206 10.5 中断的系统调用 207 10.6 可再入函数 209 10.7 SIGCLD语义 211 10.8 可靠信号术语和语义 213 10.9 kill和raise函数 213 10.10 alarm和pause函数 214 10.11 信号集 219 10.12 sigprocmask 函数 220 10.13 sigpending函数 222 10.14 sigaction函数 223 10.15 sigsetjmp 和siglongjmp函数 226 10.16 sigsuspend函数 229 10.17 abort函数 234 10.18 system函数 235 10.19 sleep函数 240 10.20 作业控制信号 241 10.21 其他特征 243 10.21.1 信号名字 243 10.21.2 SVR4信号处理程序的附 加参数 244 10.21.3 4.3+BSD信号处理程序的附 加参数 244 10.22 小结 244 习题 244 第11章 终端I/O 246 11.1 引言 246 11.2 综述 246 11.3 特殊输入字符 250 11.4 获得和设置终端属性 254 11.5 终端选择标志 254 11.6 stty命令 258 11.7 波特率函数 259 11.8 行控制函数 260 11.9 终端标识 260 11.10 规范方式 263 11.11 非规范方式 266 11.12 终端的窗口大小 270 11.13 termcap, terminfo和 curses 271 11.14 小结 272 习题 272 第12章 高级I/O 273 12.1 引言 273 12.2 非阻塞I/O 273 12.3 记录锁 275 12.3.1 历史 276 12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 write至流设备 294 12.4.5 写方式 294 12.4.6 getmsg和getpmsg函数 294 12.4.7 读方式 295 12.5 I/O多路转接 296 12.5.1 select函数 298 12.5.2 poll函数 301 12.6 异步I/O 303 12.6.1 SVR4 303 12.6.2 4.3+BSD 303 12.7 readv和writev函数 304 12.8 readn和writen函数 306 12.9 存储映射I/O 307 12.10 小结 311 习题 311 第13章 精灵进程 312 13.1 引言 312 13.2 精灵进程的特征 312 13.3 编程规则 313 13.4 出错记录 314 13.4.1 SVR4流log驱动程序 315 13.4.2 4.3+BSD syslog设施 316 13.5 客户机-服务器模型 319 13.6 小结 319 习题 319 第14章 进程间通信 320 14.1 引言 320 14.2 管道 320 14.3 popen和pclose函数 325 14.4 协同进程 330 14.5 FIFO 333 14.6 系统V IPC 335 14.6.1 标识符和关键字 336 14.6.2 许可权结构 337 14.6.3 结构限制 337 14.6.4 优点和缺点 337 14.7 消息队列 338 14.8 信号量 342 14.9 共享存储 346 14.10 客户机-服务器属性 351 14.11 小结 353 习题 353 第15章 高级进程间通信 355 15.1 引言 355 15.2 流管道 355 15.3 传送文件描述符 358 15.3.1 SVR4 360 15.3.2 4.3BSD 361 15.3.3 4.3+BSD 364 15.4 open服务器第1版 366 15.5 客户机-服务器连接函数 371 15.5.1 SVR4 372 15.5.2 4.3+BSD 375 15.6 open服务器第2版 378 15.7 小结 385 习题 385 第16章 数据库函数库 386 16.1 引言 386 16.2 历史 386 16.3 函数库 386 16.4 实现概述 388 16.5 集中式或非集中式 390 16.6 并发 391 16.6.1 粗锁 391 16.6.2 细锁 391 16.7 源码 392 16.8 性能 409 16.8.1 单进程的结果 410 16.8.2 多进程的结果 410 16.9 小结 412 习题 412 第17章 与PostScript打印机通信 413 17.1 引言 413 17.2 PostScript通信机制 413 17.3 假脱机打印 415 17.4 源码 417 17.5 小结 434 习题 434 第18章 调制解调器拨号器 435 18.1 引言 435 18.2 历史 435 18.3 程序设计 436 18.4 数据文件 437 18.5 服务器设计 439 18.6 服务器源码 439 18.7 客户机设计 463 18.7.1 终端行规程 463 18.7.2 一个进程还是两个进程 464 18.8 客户机源码 465 18.9 小结 474 习题 474 第19章 伪终端 476 19.1 引言 476 19.2 概述 476 19.2.1 网络登录服务器 477 19.2.2 script程序 478 19.2.3 expect程序 479 19.2.4 运行协同进程 479 19.2.5 观看长时间运行程序的输出 479 19.3 打开伪终端设备 480 19.3.1 SVR4 481 19.3.2 4.3+BSD 482 19.4 pty_fork函数 484 19.5 pty程序 486 19.6 使用pty程序 489 19.6.1 utmp文件 489 19.6.2 作业控制交互 489 19.6.3 检查长时间运行程序的输出 491 19.6.4 script程序 491 19.6.5 运行协同进程 492 19.6.6 用非交互模式驱动交互式 程序 492 19.7 其他特性 494 19.7.1 打包模式 494 19.7.2 远程模式 494 19.7.3 窗口大小变化 495 19.7.4 信号发生 495 19.8 小结 495 习题 495 附录A 函数原型 497 附录B 其他源代码 512 附录C 习题答案 518 参考书目 536

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值