posix对线程的调整
fork
当多线程进程调用fork创建子进程时,从fork返回时,只有调用fork的线程在进程内存在(其他线程在子进程中不存在,好比调用pthread_exit退出,不再拥有私有数据destructors或清除处理函数),其他线程状态仍保留为与调用fork时相同的状态。在子进程中,线程在与父进程中有相同的状态(互斥量(在父进程中被锁住,子进程也被锁住),数据键值),调用fork时同步在对象上等待的任何线程不再等待。
子进程将自动继承父进程中互斥锁(条件变量与之类似)的状态。也就是说,父进程中已经被加锁的互斥锁在子进程中也是被锁住的。这就引起了-一个问题:子进程可能不清楚从父进程继承而来的互斥锁的具体状态(是加锁状态还是解锁状态)。这个互斥锁可能被加锁了,但并不是由调用fork函数的那个线程锁住的,而是由其他线程锁住的。如果是这种情况,则子进程若再次对该互斥锁执行加锁操作就会导致死锁
此函数允许你的代码越过fork调用保护数据和不变量
int pthread_atfork(void (*prepare)(void),void (*parent)(void), void(*child)(void) );
- prepare:在父进程调用fork之前调用,这里可以获取父进程定义的所有锁.处理器以正确的顺序锁住相关的代码,使用的互斥量以阻止死锁的发生。调用fork的线程将在prepare fork中阻塞直到他锁住了所有的互斥量,这保证其他线程不能锁住某个互斥量或修改进程所需的数据
- parent fork:在创建出子进程后fork返回前在父进程的进程环境中调用的,prepare获得的锁进行解锁。开锁所有的互斥量允许父进程和所有线程继续工作
- child fork:fork执行后子进程内调用,与parent fork一样,有时需重置程序或库的状态(重置计数器,释放堆存储器)
如果使用一个不提供fork的处理器(来为fork调用准备合适的互斥量),当调用printf时子进程可能会挂起,因为子线程调用fork时,父进程中的其他线程会锁住互斥量
exec
终止进程内除调用exec的线程外的所有线程,他们不执行清除处理器或线程私有数据destructor——线程只是简单地消失。
除shared互斥量,条件变量(使用PTHREAD_PROCESS_SHARED属性创建的互斥量和条件变量),因为共享内存要被一些进程映射,应该解锁当前进程可能锁住的任何shared互斥量
pthread_exit
在主函数中调用该函数会使整个进程终止,与进程所有相关的内存和线程消失,线程不会执行清楚处理器或线程私有数据destructor函数。
若不想使用起始线程或者让他等待其他线程结束,可以通过调用该函数,从主线程中pthread_exit不影响进程内的其他线程前提下终止起始线程,允许它们继续和正常完成。
stdio
stdio是线程安全的,包含输出缓冲区和文件状态指定静态存储区。实现用互斥量或信号灯同步机制
要实现stdio操作不被中断的顺序执行
void flockfile(FILE *filehandle);
int ftrylockfile(FILE *filehandle);
void funlockfile(FILE *filehandle);
必须要锁住两个文件流时,要在输出流之前锁住输入流。
int getc_unlocked(FILE *stream);
int putc_unlocked(int c,FILE *stream);
int getchar_unlocked(void);
int putchar_unlocked(int c);
这些函数不执行任何锁操作,因此必须在这些操作附近使用flockfile和funlockfile,如果要想读写单个字符,通常应该使用加锁的变量而非锁住流文件,调用新的get,put函数然后解锁流文件。
putchar内部被同步以确保stdio缓冲区不被破坏,但是单个字符可以以任意顺序出现。
线程安全函数
/* 用户和终端id
这些函数将返回到调用者指定的缓冲区
成功返回0失败返回一个错误数字,getlogin_r和ttyname_r还可能返回ERANGE以表明名字缓冲区大小
*/
int getlogin_r(char *name,size_t namesize);//namesize至少是LOGIN_NAME_MAX
char *ctermid(char *s);
int ttyname_r(int fildes,char *name,size_t namesize);//namesize至少是TTY_NAME_SIZE /*目录搜索
与readdir差别:不返回指向那个入口的函数指针,而是将入口拷贝到entry指定的缓冲区
成功返回0,并设置result指向的指针,
搜索到目录流的结尾,返回0并置空result
失败返回一个如EBADF之类的错误数字
*/
int readdir_r(DIR *dirp,struct dirent *entry,struct dirent **result); /*字符串token
返回字符串s的下一个token,没有更多的token时,返回null。当第一次调用时,参数s保存指向字符串的指针,在后续的调用中,
为返回该字符串的后序token,s必需被置空,。
lasts值保存函数在字符串内的位置,并在随后的每个调用中你必须返回同样的lasts值,
*/
char *strtok_r(char *s,const char *sep,char **lasts); /*
输出缓冲区(buf,result)由调用者提供,而不是返回一个指向函数内部的静态存储指针,buf至少为指向26个字节的字符串
*/
char *asctime_r(const struct tm *tm,char *buf);
char *ctime_r(const time_t *clock,char *buf);
struct tm *gmtime_r(const time_t *clock,struct tm* result);
struct tm *localtime_r(const time_t *clock,struct tm *result); /*
种子被维持在调用者提供的存储而非使用函数静态内部的静态存储,该接口的主要问题是:在一个程序内让所有的应用和代码库共享
单个种子通常是不实际的。结果是应用程序和各个库通常将产生分离的随机数流,这样即使没有创建线程(创建线程可能将rand调用的)
内部顺序改变,因此不管怎样会改变结果,用rand代替rand_r会产生不同的结果
*/
int readr(unsigned int *seed); /*组和用户数据库
针对特定的组和用户,将组和用户记录一个拷贝(分别是grp和pwd),保存到由buffer和bufsize指定的一个缓冲区中
返回0成功,一个错误是数字失败(如:缓冲区大小ERANE),如果记录不在组或passwd数据库中,则函数可能返回成功但将result置空
如果记录被发现且缓冲区够大,则result指针指向buffer内的struct group或struct passwd结构记录
缓冲区大小可以通过sysconf来设置,其中参数_SC_GETGR_R_SIZEMAX对于组数据,_SC_GETPW_R_SIZE_MAX对于用户数据
*/
//组数据库
int getgrgid_r(gid_t gid,struct group *grp,char *buffer,size_t bufszie,struct group **result);
int getgrnam_r(const char *name,struct group *grp,struct group **result);
//用户数据库
int getpwuid_r(uid_t uid,struct passwd *pwd,char *buffer,size_t bufsize,struct passwd **result);
int getpwnam_r(char *name,struct passwd *pwd,char *buffer,size_t bufsize,struct passwd **result);
pthread_kill
int pthread_kill(thread_t tid, int sig);
将信号 sig 发送到由 tid 指定的线程。tid 所指定的线程必须与调用线程在同一个进程中。sig 参数必须来自 signal(5) 提供的列表。
向指定ID的线程发送sig信号如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。
成功完成之后返回零。其他任何返回值都表示出现了错误。
- EINVAL:sig 是无效的信号量。
- ESRCH:当前的进程中找不到 tid。
pthread_sigmask
当一个线程被创建时,他继承了创造它的线程的信号掩码(如果你想在所有地方屏蔽一个信号,首先在主线程中屏蔽它)
#include <pthread.h>
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *new, sigset_t *old); //eg
sigset_t old, new;
int ret = pthread_sigmask(SIG_SETMASK, &new, &old); /* set new mask */
ret = pthread_sigmask(SIG_BLOCK, &new, &old); /* blocking mask */
ret = pthread_sigmask(SIG_UNBLOCK, &new, &old); /* unblocking */
how 用来确定如何更改信号组。how 可以为以下值之一:
SIG_BLOCK。结果集是当前集合参数集的并集,向当前的信号掩码中添加 new,其中 new 表示要阻塞的信号组。
SIG_UNBLOCK。结果集是当前集合参数集的差集,从当前的信号掩码中删除 new,其中 new 表示要取消阻塞的信号组。
SIG_SETMASK。结果集是由参数集指向的集,将当前的信号掩码替换为 new,其中 new 表示新的信号掩码。
当 new 的值为 NULL 时,how 的值没有意义,线程的信号掩码不发生变化。要查询当前已阻塞的信号,请将 NULL 值赋给 new 参数。
除非 old 变量为 NULL,否则 old 指向用来存储以前的信号掩码的空间。
成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下情况,pthread_sigmask() 将失败并返回相应的值。EINVAL:未定义 how 的值。
Linux内核中有一个专门的函数集合来执行设置和修改信号掩码,它们放在kernel/signal.c中,其函数形式和功能如下:
总结
- 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知。也就是说,信号会随机发个该进程的一个线程。
- 每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有的线程共享的,这意味着尽管单个线程可以阻止某些信号,但当线程修改了与某个信号相关的处理行为后,所有的线程都共享这个处理行为的改变。这样如果一个线程选择忽略某个信号,而其他线程可以恢复信号的默认处理行为,或者为信号设置一个新的处理程序,从而可以撤销上述线程的信号选择。
- 当一个线程调用pthread_create() 创建新的线程时,此线程的信号掩码会被新创建的线程继承
posix对线程的调整的更多相关文章
- posix多线程--线程取消
1.三种取消状态Off 禁用取消Deferred 推迟取消:在下一个取消点执行取消Asynchronous 异步取消:可以随时执行取消 in ...
- POSIX多线程—线程基本概念
http://blog.csdn.net/livelylittlefish/article/details/7957007 作者:阿波链接:http://blog.csdn.net/livelylit ...
- posix thread线程
1. pthread线程通过调用你提供的某些函数开始.这个“线程函数”应该只有一个void*型参数,并返回系统的类型.2. 通过向pthread_create函数传递线程函数的地址和线程函数调用的参数 ...
- posix多线程--线程私有数据
1.当多个线程共享一个变量时,将该变量定义为静态或外部变量,使用互斥量确保共享变量的安全访问.如果每个线程都需要一个私有变量值,则该值成为线程的私有数据.程序创建一个键,每个线程独立地设定或得到自己的 ...
- Linux posix线程库总结
由于历史原因,2.5.x以前的linux对pthreads没有提供内核级的支持,所以在linux上的pthreads实现只能采用n:1的方式,也称为库实现. 线程的实现,经历了如下发展阶段: Linu ...
- Posix线程编程指南(4) 线程终止
线程终止方式 一般来说,Posix的线程终止有两种情况:正常终止和非正常终止.线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式:非正常终止是 ...
- Posix线程编程指南
Posix线程编程指南 Posix线程编程指南... 1 一线程创建与取消... 2 线程创建... 2 1.线程与进程... 2 2. 创建线程... 2 3. 线程创建属性... 2 4. 创建的 ...
- Posix线程编程指南(4)
Posix线程编程指南(4) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part4/ 线程终 ...
- [5]windows内核情景分析---进程线程
本篇主要讲述进程的启动过程.线程的调度与切换.进程挂靠 进程的启动过程: BOOL CreateProcess ( LPCTSTR lpApplicationName, ...
随机推荐
- 前端学习笔记之HTTP协议
阅读目录 一 HTTP协议简介 二 HTTP协议之请求Request 三 HTTP协议之响应Response 四 HTTP协议完整工作流程 五 HTTP协议关键性总结 六 自定义套接字分析HTTP协议 ...
- Spring Data JPA 关系映射(一对一,一对多,多对多 )
CascadeType.REMOVE 级联删除操作,删除当前实体时,与它有映射关系的实体也会跟着被删除.CascadeType.MERGE 级联更新(合并)操作,当Student中的数据改变,会相应地 ...
- css 基础-1
css 基础-1 一. HTML框架 (frameset) 属性: noresize(不可移动), border(边框线的大小), rows(分割成行), cols(分割列) ...
- Same Tree,判断两个二叉树是不是相同的树,结构相同,每个节点的值相同
算法分析:这道题很简单,利用递归即可. public class SameTree { public boolean isSameTree(TreeNode p, TreeNode q) { if(p ...
- JSP 文件上传
JSP 文件上传 JSP可以通过HTML的form表单上传文件到服务器. 文件类型可以是文本文件.二进制文件.图像文件等其他任何文档. 创建文件上传表单 接下来我们使用HTML标签来创建文件上传表单, ...
- myeclipse:web项目不能显示Web App Libraries
项目根路径下.classpath文件,加上 <classpathentry kind="con" path="org.eclipse.jst.j2ee.intern ...
- 基于Oracle的SQL优化(崔华著)-学习笔记
201704171025 01. 列rows记录的就是执行计划中每一个执行步骤所对应的Cardinality的值 列Cost(%CPU)记录的就是执行计划中的每一个执行步骤对应的成本 02. Comp ...
- JS object(对象)的学习汇总
Object(对象)是在所有的编程语言中都十分重要的一个概念,对于事物我们可以把他们看作是一个对象,而每一个事物都有自己的表示的属性和对于某一信息作出的相应的操作.而这些东西就变成了事物的属性和方法. ...
- SGU 156 Strange Graph 欧拉回路,思路,汉密尔顿回路 难度:3
http://acm.sgu.ru/problem.php?contest=0&problem=156 这道题有两种点 1. 度数>2 在团中的点,一定连接一个度数为2的点 2. 度数等 ...
- windows下搭建svn服务器及权限配置
服务器端VISUALSVN SERVER 3.3.1 下载地址 https://www.visualsvn.com/server/download/ 客户端TortoiseSVN 1.8.13下载地址 ...