LINUX总结
crazyacking
2016-02-26
主要对socket编程,多线程,定时器,条件变量总结
多线程篇
概念:
多线程就是允许一个进程内存存在多个控制权,实现多个线程并发执行。
一个进程中的所有线程共享相同的全局内存,还共享进程指令,打开的文件,描述字,信号处理程序(可以想象成一种线程)和信号设置(signal),当前工作目录,用户ID,组ID。
每个线程都有自己的线程ID,寄存器指令,栈,errno,信号掩码
进程和线程的区别
fork是昂贵的,内存镜像要把父进程拷贝的子进程,所有描述字要在子进程复制。创建子进程后需要用进程间的通信在父子进程之间拷贝信息。fork之前的信息容易传递。因为子进程一开始就会有父进程数据空间及所有描述字的拷贝。但是从子进程返回信息给父进程需要做更多的工作。
线程是轻权进程。因为线程比进程轻权,创建线程要比进程快10~100倍(N年之前的理论不知道现在如何);
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。
方法简介
Int pthread_create(pthread_t *tid, pthread_attr_t *attr, void * (*fun), void * arg);
返回值: 成功为0,出错时为值Exxx(>0)
*tid 线程ID,如果线程创建成功,其ID将通过tid指针返回。
*attr 线程的属性,(优先级,起始栈大小,是否为守护进程),通常使用缺省值,在这种情况下我们将attr参数说明为空指针(NULL);
void * (* func)(void *),void* arg); 线程的执行函数,创建成功是将执行此函数。函数的地址由func参数指定,
*arg 是func执行函数的参数 ,是一个指针。 如何我们需要多个调用参数我们必须将它们打包成一个结构,然后将其地址当做唯一的参数传给其执行函数,
Remark:
与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运自己的执行函数。
Pthread函数不设置errno
Pthread_join
Int pthread_join(pthread_t tid,void ** status);
返回值:成功时为0,出错时返回EXXX()0)值
tid 指定要等待的线程的tid,
**stauts : 如果stauts 不为NULL,线程的返回值将被传递给status;
该函数:是一个线程阻塞函数;调用它的函数一直等待的线程终止为止,当执行函数返回时,被等待的线程资源被收回。代码继续往下执行
Remark:
疑问:status 可不可以是个结构。当有多个值返回的时候?
一个线程不能被多个线程等待,否则第一个接收到信号对的线程成功返回,其余的调用pthread_join的线程则返回错误代码ESRCH。
,pthread_join 一个不可 join 的线程(及游离态), 其返回结果是不确定的
pthread_t pthread_self(void)
返回值:调用线程的线程ID
Remark:
每个线程都有一个ID以在给定的进程内标示自己。线程ID由pthread_create返回,线程ID仅仅保证在一个进程内部的唯一性
Int pthread_detach(pthread_t tid)
作用:将制定线程为脱离的
返回值:成功返回0出错返回EXXX(>0)值
线程或者是可汇合的(joinable)(缺省值),或者是脱离态(detached)两种状态。当可汇合的线程终止时,其线程ID和退出状态将保留,直到另外一个线程调用pthread_join。脱离的线程像守护进程,当它终止时,素有的资源全部释放,我们不能等待他终止。如果一个线程需要知道另一个线程什么时候终止最好保留第二个线程的可汇合性。
Remark:
如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。
void Pthread_exit(void *status)
作用:终止线程
返回值:不返回调用者。
*status : 进程退出的返回值。Pthread_join的第二个参数,可以为NULL。
Remark:
线程退出分为隐式退出,显示退出
线程退出有三种形式
- 执行函数执行完(隐式退出)
- Return;
- Pthread_exit();(显示退出)
这个函数的功能就是使一个线程正常退出,终止线程,因为我们知道线程它是依赖进程存在的,如果在线程中使用
exit()函数退出,那么整个的进程将会退出,那么如果此时你还有一些其它需要做的事情没有完成呢,这并不是我们所希望的。
。 status:一个返回值是指针,多个返回值是结构体
调用此函数设置status 参数返回,也可以return返回。
Status:不能指向线程内的对象(线程中的变量),因为线程终止时这些对象也消失。
Remark:套接口函数及大多数系统调用出错时返回-1,并置errno为正值不通。
互斥锁的作用,只有在时间足够的情况下才能体现出来,即有时线程内需要延时;
互斥锁
概念:
在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
互斥锁,是一种信号量,常用来防止两个进程或线程在同一时刻访问相同的共享资源。可以保证以下三点:
原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量。
唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量。
非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
从以上三点,我们看出可以用互斥量来保证对变量(关键的代码段)的排他性访问
常用的5个函数
互斥锁初始化:pthread_mutex_init
互斥锁上锁:pthread_mutex_lock
互斥锁判断上锁:pthread_mutex_trylock
互斥锁接锁:pthread_mutex_unlock
消除互斥锁:pthread_mutex_destroy
Pthread_mutex_t
描述: 是个结构体来表示互斥锁。
Remark: 多个线程 ,同一时间只能有一个加锁成功。????
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
描述:用*mutexatt来初始化一个互斥锁mutex.如果mutexattr 为NULL,默认的互斥锁属性被使用
成功返回 0;失败返回Exxx(>0)Exxx为错误代码;
*mutex: 结构体,
*mutexattr 互斥锁的属性,
* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
如果互斥锁类型为 PTHREAD_MUTEX_NORMAL,则不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或未锁定,则将产生不确定的行为。
如果互斥锁类型为 PTHREAD_MUTEX_ERRORCHECK,则会提供错误检查。如果某个线程尝试重新锁定的互斥锁已经由该线程锁定,则将返回错误。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
如果互斥锁类型为 PTHREAD_MUTEX_RECURSIVE,则该互斥锁会保留锁定计数这一概念。线程首次成功获取互斥锁时,锁定计数会设置为 1。线程每重新锁定该互斥锁一次,锁定计数就增加 1。线程每解除锁定该互斥锁一次,锁定计数就减小 1。 锁定计数达到 0 时,该互斥锁即可供其他线程获取。如果某个线程尝试解除锁定的互斥锁不是由该线程锁定或者未锁定,则将返回错误。
如果互斥锁类型是 PTHREAD_MUTEX_DEFAULT,则尝试以递归方式锁定该互斥锁将产生不确定的行为。对于不是由调用线程锁定的互斥锁,如果尝试解除对它的锁定,则会产生不确定的行为。如果尝试解除锁定尚未锁定的互斥锁,则会产生不确定的行为
Remark:
初始化互斥锁有两种方法
- 调用pthread_mutex_init();动态初始化。调用pthread_mutex_destroy来销毁一个互斥锁;
- pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER 静态初始化;
int pthread_mutex_lock(pthread_mutex_t *mutex); 阻塞函数
描述 成功返回0 错误返回Exxx(>0)设置错误,
EAGAIN:由于已超出了互斥锁递归锁定的最大次数,因此无法获取该互斥锁。
EDEADLK:当前线程已经拥有互斥锁。
*mutex 互斥锁(已经被初始化)
如果我们试图为一个已被其他线程锁住的互斥锁加锁,程序便会阻塞直到该互斥锁被解锁,
Remark:
不论那个类型的锁,都不可能被两个不同的线程同时得到。而必须等待解锁。
对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。加锁必须解锁。
nt pthread_mutex_unlock(pthread_mutex_t *mutex);
函数说明::pthread_mutex_unlock() 可释放 mutex 引用的互斥锁对象
返回值:成功完成之后会返回零
错误返回Exxx(>0)设置错误ex:EPERM :当前线程不拥有互斥锁。
Remark:
互斥锁的释放方式取决于互斥锁的类型属性。如果调用 pthread_mutex_unlock() 时有多个线程被 mutex 对象阻塞,则互斥锁变为可用时调度策略可确定获取该互斥锁的线程。
int pthread_mutex_destroy(pthread_mutex_t *mp);
说明:销毁互斥锁。
返回值:成功完成之后会返回零
错误返回Exxx(>0)设置错误
int pthread_mutex_trylock( pthread_mutex_t *mutex );
说明:尝试加锁;
返回值:成功完成之后会返回零
错误返回Exxx(>0)设置错误
函数是pthread_mutex_lock函数的非阻塞版本。如果mutex参数所指定的互斥锁已经被锁定的话,调用pthread_mutex_trylock函数不会阻塞当前线程,而是立即返回一个值来描述互斥锁的状况。
了解了互斥锁。1.可以一个主线程 激活两个线程,一个输出奇数,一个输出偶数按序输出。观察一下有啥情况。奇数线程没有输出奇数,而是连续输出。偶数线程没有输出偶数,而是连续输出。多线程是并发执行,不是并行执行;并行是:某一个瞬时两个人都在干活。并发是某一段时间两个人都在干活。对于理解多线程很重要
2想让前面的奇数线程输出奇数,偶数线程输出偶数并且按序输出。这时候都用到条件变量
有时间在总结。