当多线程进程调用fork创建子进程时,Pthreads指定只有那个调用fork的线程在子进程内存在(表示子进程中只有调用线程这个线程)。尽管当从fork调用返回时,只有调用线程在子进程中存在,所有其他的Pthreads线程状态仍保留为与调用fork时相同的状态。在子进程中,线程拥有与在父进程内相同的状态。它拥有相同的互斥量,同样的线程私有数据键值等。尽管当调用fork时在同步对象上等待的任何线程不再等待,所有的互斥量和条件变量仍然存在(因为其他线程不在子进程存在,所以他们怎么能等待呢?)。

注:fork调用不会影响互斥量的状态。如果它在父进程中被锁住,则它在子进程中被锁!

如果一个互斥量在fork调用时被锁,则它在子进程中仍然被锁。因为一个加锁的互斥量被锁住它的线程拥有,只有锁住互斥量的线程是调用fork的那个线程时,互斥量可以在子进程中被开锁。这是重要的如果当你调用fork时,另外的线程把一个互斥量锁住,则你将失去对该互斥量和由该互斥量控制的任何数据的存取。

因为没有调用线程私有数据销毁和清除处理函数,你可能需要担心存储泄漏问题。

1.fork处理器

int pthread_atfork(void (*prepare)(void),void (*parent)(void),void(*child)(void));

Pthreads增加了pthread_atfork ”fork处理器”机制以允许你的代码越过fork调用保护数据和不变量。这与atexit有点类似,后者在一个进程终止时允许程序执行清除操作。使用pthread_atfork,你需要提供三个独立的处理函数地址。prepare fork处理器在父进程调用fork之前调用,parent fork处理器在fork执行后在父进程内被调用,child fork处理器在fork执行后在子进程内被调用。

通常,pthread fork处理器以正确的顺序锁住所有的由相关代码,使用的互斥量以阻止死锁的发生。调用fork的线程将在prepare fork处理器中阻塞直到它锁住了所有的互斥量后,这就保证了其他线程不能锁住某个互斥量或修改子进程可能需要的数据。parent fork处理器只需要开锁所有互斥量即可,以允许父进程和所有线程继续正常工作。

child fork处理器经常可以与parent fork处理器一样;但是有时需要重置程序或库的状态。例如:如果使用daemon线程在后台执行函数,你或者需要记录那些线程不再存在的事实,或者在子进程内创建新线程来执行同样的函数。你可能需要重置计数器,释放堆存储等。

/** atfork.c** Demonstrate the use of "fork handlers" to protect data* invariants across a fork.*/
#include <sys/types.h>
#include <pthread.h>
#include <sys/wait.h>
#include "errors.h"pid_t self_pid;                         /* pid of current process */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/** This routine will be called prior to executing the fork,* within the parent process.*/
void fork_prepare (void)
{int status;/** Lock the mutex in the parent before creating the child,* to ensure that no other thread can lock it (or change any* associated shared state) until after the fork completes.*/status = pthread_mutex_lock (&mutex);if (status != 0)err_abort (status, "Lock in prepare handler");printf("fork_prepare\n");
}/** This routine will be called after executing the fork, within* the parent process*/
void fork_parent (void)
{int status;/** Unlock the mutex in the parent after the child has been created.*/status = pthread_mutex_unlock (&mutex);if (status != 0)err_abort (status, "Unlock in parent handler");printf("fork_parent\n");
}/** This routine will be called after executing the fork, within* the child process*/
void fork_child (void)
{int status;/** Update the file scope "self_pid" within the child process, and unlock* the mutex.*/self_pid = getpid ();status = pthread_mutex_unlock (&mutex);if (status != 0)err_abort (status, "Unlock in child handler");printf("fork_child: self_pid = %d\n",self_pid);
}/** Thread start routine, which will fork a new child process.*/
void *thread_routine (void *arg)
{pid_t child_pid;int status;child_pid = fork ();if (child_pid == (pid_t)-1)errno_abort ("Fork");/** Lock the mutex -- without the atfork handlers, the mutex will remain* locked in the child process and this lock attempt will hang (or fail* with EDEADLK) in the child.*/status = pthread_mutex_lock (&mutex);if (status != 0)err_abort (status, "Lock in child");printf("After fork\n");status = pthread_mutex_unlock (&mutex);if (status != 0)err_abort (status, "Unlock in child");printf ("After fork: %d (%d)\n", child_pid, self_pid);if (child_pid != 0) {if ((pid_t)-1 == waitpid (child_pid, (int*)0, 0))errno_abort ("Wait for child");}return NULL;
}int main (int argc, char *argv[])
{pthread_t fork_thread;int atfork_flag = 1;int status;if (argc > 1)atfork_flag = atoi (argv[1]);if (atfork_flag) {status = pthread_atfork (fork_prepare, fork_parent, fork_child);if (status != 0)err_abort (status, "Register fork handlers");}self_pid = getpid ();printf("main self_pid = %d\n",self_pid);status = pthread_mutex_lock (&mutex);if (status != 0)err_abort (status, "Lock mutex");/** Create a thread while the mutex is locked. It will fork a process,* which (without atfork handlers) will run with the mutex locked.*/status = pthread_create (&fork_thread, NULL, thread_routine, NULL);if (status != 0)err_abort (status, "Create thread");printf("before sleep\n");sleep (5);printf("after sleep\n");status = pthread_mutex_unlock (&mutex);if (status != 0)err_abort (status, "Unlock mutex");printf("main unlock\n");status = pthread_join (fork_thread, NULL);if (status != 0)err_abort (status, "Join thread");printf("huangcheng \n");return 0;
}

19~32       函数fork_prepare是prepare处理器。在创建子进程前,它将在父进程内被fork调用。该函数改变的任何状态(特别是被锁住的互斥量)将被拷贝进子进程。fork_prepare函数锁住程序的互斥量。
38~49       函数fork_parent是parent处理器。在创建子进程后,它将在父进程内被fork调用。总的来说,一个parent处理器应该取消在parent处理器中做的处理,以便父进程能正常继续。fork_parent函数解锁fork_prepare锁住的互斥量。
55~68       函数fork_child是child处理器。它将在子进程被fork调用。在大多数情况下,child处理器需要执行在fork_parent处理器做过的处理,解锁状态以便子进程能继续运行。它可能也需要执行附加的清除操作,例如,fork_child锁住self_pid变量为子进程的pid,同时解锁进程互斥量。
73~100     在创建子进程以后,它将继续执行thread_routine代码,thread_routine函数解锁互斥量。当运行fork处理器时,fork调用将被阻塞(当prepare处理器锁住互斥量时)直到互斥量可用。没有fork处理器,线程将在主函数解锁互斥量前调用fork,并且线程将在这个点上在子进程挂起。
117~130  主程序在创建将调用fork的线程之前锁住互斥量。然后,它睡眠若干秒以保证当互斥量被锁住时,线程能够调用fork,然后解锁互斥量。运用pthread_routine的线程将总是在父进程中成功,因为它将简单的阻塞直到主程序释放锁。
运行结果:

main self_pid = 5564
before sleep
after sleep
main unlock
fork_prepare
fork_parent
After fork
After fork: 5568 (5564)
fork_child: self_pid = 5568
After fork
After fork: 0 (5568)
huangcheng

2.exec

exec函数没有因为引入线程受到很多影响。exec函数的功能是消除当前程序的环境并且用一个新程序代替它。对exec的调用,将很快的终止进程内除调用exec的线程外的所有线程。他们不执行清除处理器或线程私有数据destructors——线程只是简单的停止存在。

所有的同步对象也消失了,除了 pshared互斥量(使用PTHREAD_PROCESS_SHARED属性值创建的互斥量)和pshared条件变量。因为只要共享内存被一些进程映射,后者仍然是有用的。然而,你应该解锁当前进程可能锁住的任何pshared互斥量——系统不会为你解锁它们。

3.进程结束

在一个非线程程序中,对于exit函数的显示调用和从程序主函数返回有一样的效果,指进程退出。Pthreads增加了pthread_exit函数,该函数能在进程继续运行的同时导致单个线程的退出。

在一个多线程程序中,主函数是“进程主线程的启动函数”。尽管从任何其他线程的启动函数返回就像调用pthread_exit终止线程一样,但是从主函数返回将终止整个进程。与进程相关的所有内存(和线程)将消失。线程不会执行清除处理器或线程私有数据destructors函数。调用exit具有同样的效果。

当你不想使用起始线程或让它等待其他线程结束时,可以通过调用pthread_exit而非返回或者调用exit退出主函数。从主函数中调用pthread_exit将在不影响进程内其他线程的前提下终止起始线程,允许他们继续和正常完成。

转载于:https://www.cnblogs.com/jiangu66/archive/2013/05/10/3071801.html

posix多线程有感--线程高级编程(线程和fork,exec)相关推荐

  1. LinuxC高级编程——线程间同步

    LinuxC高级编程--线程间同步 宗旨:技术的学习是有限的,分享的精神是无限的. 1. 互斥锁mutex 多个线程同时访问共享数据时可能会冲突.对于多线程的程序,访问冲突的问题是很普遍的,解决的办法 ...

  2. LinuxC高级编程——线程

    LinuxC高级编程--线程 宗旨:技术的学习是有限的,分享的精神是无限的. 一.线程基础 main函数和信号处理函数是同一个进程地址空间中的多个控制流程,多线程也是如 此,但是比信号处理函数更加灵活 ...

  3. posix多线程有感--线程高级编程(条件变量)

    1.初始化条件变量pthread_cond_init int pthread_cond_init(pthread_cond_t *cv,const pthread_condattr_t *cattr) ...

  4. UNIX环境高级编程——线程同步之条件变量以及属性

    条件变量变量也是出自POSIX线程标准,另一种线程同步机制.主要用来等待某个条件的发生.可以用来同步同一进程中的各个线程.当然如果一个条件变量存放在多个进程共享的某个内存区中,那么还可以通过条件变量来 ...

  5. unix环境高级编程-线程(2)

    线程终止: 如果进程中的任意线程调用了exit._Exit或者_exit,那么整个进程就会终止,与此类似,如果默认的动作是终止进程,那么发送到线程的信号就会终止整个进程 单个进程可以通过三种方式退出, ...

  6. UNIX环境高级编程——线程

    线程包含了表示进程内执行环境必需的信息,其中包括进程中标示线程的线程ID.一组寄存器值.栈.调度优先级和策略.信号屏蔽字.errno变量以及线程私有数据. 进程的所有信息对该进程的所有线程都是共享的, ...

  7. 线程基本编程——线程函数大全

    索引: 1.创建线程pthread_create 2.等待线程结束pthread_join 3.分离线程pthread_detach 4.创建线程键pthread_key_create 5.删除线程键 ...

  8. java 多线程 任务队列_Java并发编程线程池任务队列

    类ThreadPoolExecutor最常使用的构造方法是: ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliv ...

  9. posix多线程有感--API

    一.头文件 #include <pthread.h> 二.编译选项 -lpthread 三.结构体 pthread_t pthread_attr_t pthread_barrier_t p ...

最新文章

  1. java string 常用方法_String类的12个常用方法
  2. 使用 python 处理 nc 数据
  3. [蛋蛋四格漫画]-贺沪江日语四周年版庆
  4. VSCode摸鱼插件,让工作更轻松
  5. 嫡权法赋权法_赋权法_
  6. texlive 2022安装与使用
  7. android 时钟翻转,旋转时钟游戏
  8. Mysql表和数据的复制操作
  9. 驱动程序是如何驱动硬件的?
  10. 怎样把计算机网络共享给手机,怎么把电脑网络共享给手机
  11. 黑客到底有多黑-黑客群体构成,技术起源概述
  12. 16.04Ubuntu桌面版搭建
  13. mysql利用cpu率高_MySQL CPU 使用率高的原因和解决方法
  14. 【C语言你真的学会了吗】C语言深度剖析(1)【关键字深度剖析】
  15. matlab 在2k屏幕,如何将4k显示器的分辨率调整为2k,并将2k分辨率用于4k显示器
  16. 资深SRE带你看阿里云香港故障
  17. linux美化桌面,Linux_设置动态壁纸来美化Ubuntu桌面,我们知道你想拥有一个有格调 - phpStudy...
  18. 电子杂志设计制作手册
  19. 简述力法计算弹性固定无铰拱的原理_实用建筑结构静力计算手册的目录
  20. 虚拟化技术 — 虚拟机磁盘

热门文章

  1. ViewState的使用
  2. 帝国cms栏目忘记设置为终极栏目怎么办?
  3. 【原创】大叔问题定位分享(33)oozie提交任务报错ArithmeticException: / by zero
  4. distinct 只针对一个字段
  5. 01012_计算机常用单词
  6. Tips-Windows 10【多桌面视窗】操作
  7. 查询oracle中所有用户信息
  8. Python 之路 Day5 - 常用模块学习
  9. elasticsearch 运行原理
  10. 转:TestLink1.9.3测试用例:Excel转换XML工具二实现代码