man clone

CLONE(2)
Linux Programmer's Manual


名字:

clone —— __clone2,用于创建子进程

概述:

#define _GNU_SOURCE

#include <sched.h>

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...
                 /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );


描述:

clone()创建了一个新进程,行为上和fork(2)类似,它事实上是基于底层clone系统调用(在下文中称为sys_clone)的库函数。后文有对sys_clone的描述。

和fork(2)不同,这些系统调用允许子进程共享当前进程(即父进程)的部分执行上下文,例如内存空间、文件描述符表、信号处理函数。


clone()的主要用途是实现线程:多线程控制一个运行时并发访问内存空间的程序。

当子进程被clone()创建之后,它执行fn(arg)函数。(这和fork(2)不一样,fork(2)的情况下,子进程会从父进程调用fork的点继续运行)。参数fn是一个在子进程执行初期调用的函数指针,参数arg将传递给fn函数。

当fn(arg)函数返回时,子进程将终结,fn返回的整型值是子进程的退出码,子进程也可能通过调用exit(2)或者获取一个关键的信号之后显式地被终结。

参数child_stack指明了子进程的栈的位置,因为父子进程可能共享内存,子进程不可能在和父进程一样的栈中执行,因此,父进程必须为子进程建立内存空间并将指针传递给clone()。栈在所有的运行linux的处理器(处理HP PA处理器)都是向下生长(向低地址)的,因此child_stack通常为子栈分配的内存空间的最大地址处。

flags的低字节包含了子进程死亡的时候发送给父进程的信号,如果信号指定了除了SIGCHLD之外的任何位,父进程在通过wait(2)等待子进程时,必须指定__WALL或者__WCLONE选项。如果没有指定信号,父进程在子进程终结的时候不会收到信号。

flags必须通过0或者下述常量按位或,从而指明父进程和子进程之间共享的内容。


CLONE_CHILD_CLEARTID(2.5.49内核之后)

在子进程存在时,清除子线程在内存空间的ctid位置的ID,这个地址可被set_tid_address(2)系统调用改变,这被线程库调用。

CLONE_CHILD_SETTID(2.5.49内核之后)

保存子线程在其内存ctid位置的ID。

CLONE_FILES

如果它被置位,父子进程共享文件描述符表,父进程或者子进程创建的任何文件描述符在另一个进程中也是合法的。类似的,如果一个进程关闭了文件描述符或者改变了它的标志位(使用fcntl(2) F_SETFD操作),另一个进程也被影响。

如果没有被置位,子进程将继承在父进程调用clone()时的所有文件描述符(子进程中复制的文件描述符指向父进程中对应的同样的文件描述符指向的那个文件)。后来父进程或者子进程对这些文件描述符的开关操作、改变标志位的操作都不会影响到另一个进程。

CLONE_FS

如果CLONE_FS被置位,父子进程会共享同样的文件系统信息。包括文件系统的根目录、当前工作目录和umask,父进程或者子进程的chroot、chdir或者umask操作会影响到另一个进程。

如果CLONE_FS没有置位,子进程工作在父进程调用clone()时的文件系统的拷贝之上。任何一个进程以后调用chroot、chdir、umask都不会影响到另一个。

CLONE_IO(2.6.25以后)

如果CLONE_IO被置位,新进程和父进程共享IO上下文。如果该标志位没有置位,新的进程有自己的IO上下文。

CLONE_NEWIPC(2.6.19之后)

如果CLONE_NEWIPC被置位,将创建一个新的IPC命名空间;如果没有,进程和父进程在同一个IPC命名空间中。这个标志位用于容器的实现。

IPC命名空间包括System V IPC对象的标识符集(这些对象通过msgctl、semctl和shmctl创建)。IPC命名空间中创建的对象对该空间中所有其他进程可见,但是对其它IPC命名空间中的进程不可见。

当IPC命名空间被销毁时(例如,当该命名空间中的最后一个进程终结),该命名空间中所有的IPC对象都自动被销毁。

使用这个标志位还需要:内核配置为CONFIG_SYSVIPC和CONFIG_IPC_NS,并且进程拥有特权级(CAP_SYS_ADMIN)。这个标志位不可以和CLONE_SYSVSEM同时置位。

CLONE_NEWNET(2.6.24之后)

(该标志位还没有实现,可能要到2.6.28以上才实现)

如果CLONE_NEWNET被置位,将创建新的network命名空间;否则,进程将和父进程在一个网络命名空间中。这个标志位用于容器的实现。

网络命名空间提供了独立的网络栈的视图(网络设备接口,IPV4、IPV6协议栈,IP路由表,防火墙规则,/proc/nhet和/sys/class/net目录树,socket等等)。一个物理网络设备只能存在在一个网络命名空间,而一个虚拟网络设备对(“veth”)提供了和管道类似的抽象,用于创建不同命名空间之间的通道,也可以创建对另一个命名空间中的物理网络设备的网桥。

当网络命名空间被释放(例如,命名空间中最后一个进程终结),物理网络设备就被移到初始的网络命名空间(而不是这个进程的父进程的)。

使用这个标志位还需要:内核配置为CONFIG_NET_NS,进程有特权级(CAP_SYS_ADMIN)。

CLONE_NEWNS(2.6.19之后)

以新的命名空间开始子进程。

每一个进程都在一个命名空间之中,进程的命名空间是描述进程看到的文件目录的数据。在CLONE_NEWNS没有置位的时候fork或者clone,子进程和父进程在同一个命名空间中。系统调用mount和umount改变父进程的命名空间,也因此影响所有的在这个命名空间中的进程,但是不影响不同命名空间中的进程。

如果CLONE_NEWNS置位,子进程会在新的命名空间中开始,新命名空间被初始化为父进程命名空间的拷贝。

只有有特权级的进程(有CAP_SYS_ADMIN)才可以指定CLONE_NEWNS标志位,不允许CLONE_NEWNS和CLONE_FS同时标记。

CLONE_NEWPID(2.6.24之后)

如果CLONE_NEWPID被置位,将在新的PID命名空间中创建进程;反之,子进程和父进程在同样的PID命名空间中。这个标志位用于实现容器。

PID命名空间为PID提供了独立的环境:新的命名空间中的PID从1开始,它是一个单独的系统,对fork、vfork或者clone的调用会产生拥有命名空间内独一无二的PID的进程。

在新的命名空间中的第一个进程(通过CLONE_NEWPID标志位创建的进程)的PID为1,是这个命名空间中的init进程;这个命名空间中的孤儿进程会被这个进程收养而不是被真正的init。和传统的init进程不一样,这个PID命名空间中的init进程会终结,如果终结了,命名空间中所有的进程也终结了。

PID命名空间形成了一个层次结构,当新的PID命名空间创建后,这个命名空间中的进程对创建了这个命名空间的命名空间可见(对上可见);类似的,如果父PID命名空间自己就是另一个PID命名空间的子空间,那么父子PID命名空间的进程都对祖父PID命名空间可见。相反的,子PID命名空间中的进程看不到父命名空间的进程。命名空间层次结构的存在意味着每个进程可能拥有多个PID:每个可见它的命名空间都有一个PID,这些PID都对于对应的命名空间独一无二(对getpid的调用总返回这个进程存在的命名空间的PID)。

创建了新的命名空间之后,子空间改变根目录并在/proc挂载新的procfs实例是很有用的,这样使得ps能共正确工作。(如果CLONE_NEWNS被指为,就没必要改变根目录了:新的procfs实例将被直接挂载到/proc上)。

使用这个标志位还需要:内核配置为CONFIG_PIS_NS,进程有特权级(CAP_SYS_ADMIN),该标志位不能和CLONE_THREAD同时置位。

CLONE_NEWUTS(2.6.19之后)

如果CLONE_NEWUTS被置位,将在新的UTS命名空间中创建进程,新的命名空间的标识符将复制父进程的UTS命名空间的标识符;反之,进程将和父进程在一个UTS命名空间中。这个标志位被用于实现容器。

UTS命名空间时uname返回的标识符集,其中,域名和host名可以通过setdomainname和sethostname分别改变,对UTS命名空间中标识符的改变对该命名空间中所有的其他进程都可见,但是对其他UTS命名空间中的进程不可见。

使用该标志位还需要:内核配置为CONFIG_UTS_NS,进程拥有特权级(CAP_SYS_ADMIN)。

CLONE_PARENT(2.3.12以后)

如果CLONE_PARENT被置位,那么子进程的父进程将和调用进程一样;反之,子进程的父进程就是调用进程。

注意当子进程终结的时候,getppid返回的父进程会收到信号,因此如果CLONE_PARENT被置位,调用进程的父进程而不是调用进程本身,会收到信号。

CLONE_PARENT_SETTID(2.5.49之后)

保存子线程在父子内存空间ptid位置的ID。

CLONE_PID(废弃)

CLONE_PTRACE

如果CLONE_PTRACE被置位并且调用进程被trace,那么子进程也会被trace。

CLONE_SETTLS(2.5.32之后)

新的TLS(Thread Local Strorage)描述符是newtls参数。

CLONE_SIGHAND

如果CLONE_SIGHAN被置位,父子进程将共享信号处理函数表。如果父进程或者子进程调用了sigaction来改变对应信号的行为,这个行为在另一个进程也会改变。然而,调用进程和子进程仍然有分离的信号mask和pending信号集。因此,其中一个可能会通过sigprocmask来阻塞或解除阻塞信号,并不影响其他进程。

从2.6.0开始,如果使用了该标志位,必须使用CLONE_vM。

CLONE_STOPPED(2.6.0之后)

如果CLONE_STOPPED被置位,子进程会初始为stop(仿佛被发送了SIGSTOP信号),必须通过SIGCONT信号来resume。

从2.6.25开始,这个标志位不推荐使用。你可能永远不会用到它。

CLONE_SYSVSEM(2.5.10之后)

如果CLONE_SYSVSEM被置位,子进程和父进程共享System V信号量列表;反之,子进程有独立的列表并初始化为空。

CLONE_THREAD(2.4.0之后)

如果CLONE_THREAD被置位,子进程会放到和调用进程同样的线程组里。为了使得后文更加可读,术语“线程”指的是同一个线程组里面的进程。

线程组在2.4内核之后被加入用以支持POSIX线程的概念,也就是共享一个PID的一组线程,其内部这些共享的PID被称作线程组的标识符(TGID),自从2.4内核开始,getpid返回调用者的TGID。

线程组中的线程可以通过他们的线程ID来区分(TID),新线程的TID可以通过调用clone的返回值来获取,也可以通过gettid来获取线程的TID。

当不使用CLNOE_THREAD来调用clone的时候,新的线程被放在新的线程组中,其TGID和线程的TID一样,该线程是该线程组的头。

使用CLONE_THREAD创建的线程和调用clone的进程拥有相同的父进程(和CLONE_PARENT类似),因此getppid返回值和线程组中所有的线程一样。当CLONE_THREAD线程终结,通过clone创建这个进程的进程并没有被发送一个SIGHLD(或者其他终止)信号,这个线程的状态也不可以通过wait来获取。(这个线程被废弃了)。

线程组中所有线程都终止后,该线程组的父线程将被发送一个SIGHLD(或者其他终止)信号。

如果线程组中任何线程执行execve,那么线程组中除了头之外的所有线程都被终止,新的程序将在线程组头中执行。

如果线程组中任何线程通过fork创建子进程,其他线程组中的任何线程都能对这个子线程调用wait。

自2.5.35开始,如果这个标志位被置位,那么CLONE_SIGHAND也必须被置位。

线程组可以作为一个整体使用kill信号,也可以指定某一个线程(例如TID)使用tgkill信号。

信号的响应和处理是基于进程的:如果发送一个未处理的信号到一个线程,那么它会影响到(终止、停止、继续或者忽略)线程组中所有的成员。

每个线程有自己的信号屏蔽位,可以通过sigprocmask设置,但是信号可以通过下述之一来挂起:对全部进程(例如,发送到任意一个线程组成员)使用kill的时候,或者对某一个线程使用tgkill的时候。调用sigpending返回一组信号集,它包括对全部线程挂起的信号和对父进程挂起的信号。

如果kill信号被发送到线程组,同时线程组有一组信号处理程序,那么仅仅有一个处理程序会被调用,它从没有阻止该信号的线程组成员的处理程序中任意选取。如果线程组中多个线程使用sigwaitinfo来等待同一个信号,内核会从这些线程中选择任意一个来接受kill信号。

CLONE_UNTRACED(2.5.46内核之后)

如果CLONE_UNTRACED被置位,被trace的进程不能强制使用CLONE_PTRACE来跟踪子进程。

CLONE_VFORK

如果CLONE_VFORK被置位,父进程的执行将被暂停,直到子进程通过execve或者_exit释放了它的虚拟内存资源。(和vfork一样)

如果CLONE_VFORK没有被置位,那么父子进程在调用之后都处于可调度状态,应用程序不可以期待他们以特定的顺序来执行。

CLONE_VM

如果CLONE_VM被置位,父子进程在同一个内存空间中运行。特别的,父进程或者子进程执行内存写操作对另一个进程可见,推而广之,父进程或者子进程通过mmap或者munmap进行内存映射或者解除映射也会影响到另一个进程。

如果CLONE_VM没有被置位,子进程运行在父进程调用clone时的内存空间的独立的拷贝中。任意一个进程的内存写或者文件映射/解除映射操作都不会影响到另一个。

sys_clone

sys_clone系统调用的响应和fork很接近,子进程会从父进程系统调用的那个点继续执行。因此,sys_clone只需要标志位和child_stack参数,它和clone的参数意义一样。(注意参数的顺序和clone不一样)

sys_clone的另一个不同在于child_stack参数可以为0,这样的话COW保证了当任何一个进程修改了内核栈的时候子进程获得独立的内核栈的拷贝。这时,为了操作正确,CLONE_VM选项不应该指定。

在2.4及更早版本中,clone是不带ptid、tls和ctid参数的。

返回值

如果成功,子进程的线程ID(TID)会被返回到调用者线程的执行期。

如果失败,会返回-1,没有子线程被创建,errno会被置位。


(注:

fork的返回值和clone的不一样,对fork来说,如果成功,子进程的PID会返回给父进程,0会返回给子进程;如果失败,-1返回给父进程,子进程没有创建,errno会被置位)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值