• 进程互斥和fork


    自父进程继承

    • 进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs))
    • 环境(environment)
    • 堆栈
    • 内存
    • 打开文件的描述符(注意对应的文件的位置也是和文件一起由父子进程共享的)
    • 执行时关闭(close-on-exec) 标志 (译者注:close-on-exec标志可通过fnctl()对文件描 述符设置,POSIX.1要求所有目录流都必须在exec函数调用时关闭。更详细说明, 参见《UNIX环境高级编程》 W. R. Stevens, 1993, 尤晋元等译(以下简称《高级编程》), 3.13节和8.9节)
    • 信号(signal)控制设定
    • 信号量(semaphore)
    • nice值 (译者注:nice值由nice函数设定,该值表示进程的优先级, 数值越小,优先级越高)
    • 进程调度类别(scheduler class) (译者注:进程调度类别指进程在系统中被调度时所属的类别,不同类别有不同优先级,根据进程调度类别和nice值,进程调度程序可计算出每个进程的全局优先级(Global process prority),优先级高的进程优先执行)
    • 进程组号
    • 对话期ID(Session ID) (《高级编程》:进程所属的对话期 (session)ID, 一个对话期包括一个或多个进程组, 更详细说明参见《高级编程》 9.5节)
    • 当前工作目录
    • 根目录 (根目录不一定是操作系统的“/”,它可由chroot函数改变)
    • 文件方式创建屏蔽字(file mode creation mask (umask)) (《高级编程》:创建新文件的缺省屏蔽字)
    • 资源限制
    • 控制终端
    • 所有互斥锁、读写锁和条件变量(同事考虑到多线程环境)

    子进程所独有

    • 进程号
    • 不同的父进程号(译者注: 即子进程的父进程号与父进程的父进程号不同, 父进程号可由getppid函数得到)
    • 自己的文件描述符和目录流的拷贝(译者注: 目录流由opendir函数创建,因其为顺序读取,顾称“目录流”)
    • 子进程不继承父进程的进程,正文(text), 数据和其它锁定内存(memory locks) (译者注:锁定内存指被锁定的虚拟内存页,锁定后, 不允许内核将其在必要时换出(page out), 详细说明参见《The GNU C Library Reference Manual》 2.2版, 1999, 3.4.2节)
    • 在tms结构中的系统时间(译者注:tms结构可由times函数获得, 它保存四个数据用于记录进程使用中央处理器 (CPU:Central Processing Unit)的时间,包括:用户时间,系统时间, 用户各子进程合计时间,系统各子进程合计时间)
    • 资源使用(resource utilizations)设定为0
    • 阻塞信号集初始化为空集(译者注:原文此处不明确, 译文根据fork函数手册页稍做修改)
    • 不继承由timer_create函数创建的计时器
    • 不继承异步输入和输出
    • 文件锁不继承,具体原因见参考文献。

    随想杂谈

    • 假设fork是在父进程有多个线程时发生的,要考虑如下问题:

      子进程通过继承整个地址空间的副本,从而父进程哪里继承了所有互斥量,读写锁和条件状态。如果父进程包含多个线程,子进程在 fork 返回以后,如果紧接着不是马上调用 exec 的话,就需要清理锁状态。 在子进程内部只存在一个线程,它是由父进程调用 fork 返回以后,如果父进程中的线程占有锁,子进程同样占有这些锁,问题就出在子进程同样占有这些锁——但子进程不包含这些占有锁的线程副本,所以子进程没有办法知道它占有的那些锁并且需要释放哪些锁。如果子进程从 fork 返回以后马上调用某个 exec 函数,就可以避免这样的问题。这种情况下,老的地址空间被丢弃,所以锁的状态无关紧要。

      当然,不用exec,也可以使用pthread_atfork,在调用fork之前,线程先获取进程中所有锁,在调用fork后分别在父子进程中释放这些锁,从而父子可以重新获取和释放这些锁资源。

    • pthread接口也提供了进程范围的共享互斥量,这是需要设置互斥类型为PTHREAD_PROCESS_SHARED,因为是进程间共享,要求mutex init时候,使用共享的内存区域中分配的互斥量。进程间互斥,还推荐阅读《nginx模块开发与结构解析》最后一章关于互斥锁的实现方案,一种是基于文件锁,一种是基于原子(本质和互斥同质)操作&信号量。建议集合accept_mutex锁的实现了解不同互斥锁的原理!
    • 记录锁的继承和释放
      1、锁与进程和文件两方面
      a、当一个进程终止时,它所建立的锁全部释放;(即 进程退出,文件锁自动释放)
      b、任何时候关闭一个描述符时,则该进程通过这一描述符可以引用的文件上的任何一把锁都释放。(即 关闭文件,文件锁自动释放)

      情况一::
      fd1 = open(pathname,....);
      read_lock(fd1,....);
      fd2 = dup(fd1);
      close(fd2); //此时,在close(fd2)后,在fd1上加的锁被释放。
      情况二::
      fd1 = open(pathname,....);
      read_lock(fd1,....);
      fd2 = open(pathname,...);
      close(fd2); //此时,在close(fd2)后,在fd1上加的锁被释放。

      2、由fork产生的子进程不继承父进程所设置的锁。(文件锁不能被继承)
      这意味着,若一个进程得到一把锁,然后调用fork,那么对于父进程获得的锁而言,子进程被视为另外一个进程,对于从父进程处继承过来的任一描述符,子进程需要调用fcntl才能获得它自己的锁。
      (这其实也是锁的本意,文件锁的作用是阻止多个进程同时写一个文件或区域,如果子进程继承父进程的锁,则父、子就可以同时写同一个文件。这显然是不对的。)

      3、在执行exec后,新程序可以继承原执行程序的锁。(EXEC文件锁被继承)
      执行exec后,其实是用当前进程的进程实体替换原进程的进程实体。原进程被杀掉(但保留了 进程ID即 PID)

  • 相关阅读:
    第四次作业—四则运算
    第四次作业—代码规范
    【欢迎来怼】事后诸葛亮会议
    软件工程——第七次作业
    第17次Scrum会议(10/29)【欢迎来怼】
    软件工程——第六次作业
    第10次Scrum会议(10/22)【欢迎来怼】
    软件工程——第五次作业
    欢迎来怼——第四次Scrum会议
    软件工程——第四次作业(3)
  • 原文地址:https://www.cnblogs.com/zhaoyl/p/4056771.html
Copyright © 2020-2023  润新知