父子进程共享的东西

自父进程继承

  • 进程的资格(真实(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模块开发与结构解析》最后一章关于互斥锁的实现方案,一种是基于文件锁,一种是基于原子操作&信号量。
  • 记录锁的继承和释放
    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)

时间: 2024-08-27 08:24:06

父子进程共享的东西的相关文章

父子进程共享内存通信的三种方法

1.  mmap MAP_ANONYMOUS 在支持MAP_ANONYMOUS的系统上,直接用匿名共享内存即可, 2. mmap  /dev/zero 有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果. 3. shmget shmat shmctl shmget 是老式的system V 共享内存模式,很多系统都支持这种方法. 父子进程共享内存通信的三种方法

父子进程共享资源的关系

fork() 用来创建进程fork(void) 在linux中所有进程都是由init进程直接或间接创建 成功:在父进程中将返回子进程的PID:子进程返回0,以区别父进程 失败:父进程中返回-1 1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 5 int main(int argc,char *argv[]) 6 { 7 pid_t pid; 8 if((pid=fork())==

LINUX实现父子进程轮流修改文件的值

本例子是基于信号的同步机制实现父子进程轮流修改文件中的值. tatic volatile sig_atomic_t sigflag; static sigset_t newmask,oldmask,zeromask; static void sig_usr(int signo) { sigflag=1; } void TELL_WAIT(void) { if(signal(SIGUSR1,sig_usr)==SIG_ERR) perror("signal error"); if(sig

fork()父子进程文件描述符的关系

父子进程共享文件描述符:此说法,其实是父子进程共享 文件表项(父进程和子进程共享同一个file table entry) 由于子进程是父进程的拷贝,子进程会拷贝父进程的进程描述符中的文件描述符表,可以说继承父进程的文件描述字(files_struct中的struct file *fd_array[NR_OPEN_DEFAULT]的拷贝) 如果我们没有调用exec函数,则我们父子进程的代码段,堆栈,数据段都完全相同(因为是拷贝),所以此时我们的子进程可以使用fork()之前的fd值,虽然此时fd是

父子进程

fork 后子进程对父进程的继承 (1)为什么需要创建子进程? 每一个程序的运行都需要进程,创建子进程可以实现宏观上的并行 (2)什么时候需要创建子进程? 当父进程执行到某个阶段,或接收到某个事件后,需要创建一个独立的进程来协助其完成任务时,才需要调用fork创建一个新进程 (3)子进程对父进程的继承 1.为什么要继承父进程的相关资源,参考  http://blog.csdn.net/xlsernt_sina_com/article/details/31350829 摘选了两句话: 父进程创建子

父子进程间的交互

1.进程的创建与操作 任务描述: 在父进程中创建一个全局变量,一个局部变量,并赋予初始值,用fork函数创建子进程.在子进程中对父进程的变量进行自加操作,并且输出变量值,然后父进程睡眠一段时间 各进程结束前输出进程与父进程号,全局及局部变量值 相关知识: 由 fork 创建的新进程被称为子进程(child process).该函数被调用一次,但返回两次.两次返回的区别是子进程的返回值是 0,而父进程的返回值则是新子进程的进程 ID.将子进程 ID 返回给父进程的理由是:因为一个进程的子进程可以多

DLL中建立进程共享数据段需要注意的语法问题

默认情况下,所有的数据段都是进程独立的,每个进程有自己的一份.但有时候进程共享一个数据段也是有需要的,为此VC++编译连接器提供了专门的语法,下面是一个典型的例子,建立了一个名为Shared的共享数据段,该数据段里只有一个变量g_hwndOwner. ////////////////////////////////////////////// 进程共享区 /////////////////////////////////////// #pragma data_seg("Shared")

fork()函数 —— 父子进程资源问

fork()函数功能--创建新进程 1.父子进程有独立的数据段.堆.栈,共享代码段 Linux中每个进程都有4G的虚拟地址空间(独立的3G用户空间和共享的1G内核空间),fork()创建的子进程也不例外.子进程资源的由来: 1.1G内核空间既然是所有进程共享,因此fork()创建的子进程自然也将拥有: 2.3G的用户空间是从父进程进程而来. fork()创建子进程时继承了父进程的数据段.代码段.栈段.堆,注意从父进程继承来的是虚拟地址空间,同时也复制了页表(没有复制物理块).因此,此时父子进程拥

(待续)进程控制(四)---父子进程之间的复制

用户缓冲区: 由 fork函数创建的子进程会继承父进程的用户缓冲区.如果在父进程调用 fork 函数创建子进程之前缓冲区中仍然有数据在其中的话,那么子进程将会复制父进程的缓冲区. 文件共享: 由 fork 函数创建的子进程会继承父进程打开的所有文件描述符,而且父子进程相同的文件描述符是指向同一个文件表项(共享一个读写偏移指针).IPC(进程间通信)中的 无名管道 就是利用子进程会继承父进程的文件描述符这一特点来实现的.