一、clone函数
1.1、函数原型
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
头文件:
#define _GNU_SOURCE
#include <sched.h>
函数作用:clone函数允许用户指定子进程继承或者拷贝父进程的某些特定资源,如信号量、文件描述符、文件系统、内存等内容。
函数参数:
1)fn 新创建进程的入口函数;
2)clid_stack 子进程栈空间的起始地址;
3)arg 传递给子进程的参数,需要和CLONE_SETTLS一起使用
4)ptid 存储父进程的pid,需要和CLONE_PARENT_SETTID一起使用
5)tls (Thread Local Storage) descriptor,需要和CLONE_SETTLS一起使用
6)ctid 存储子进程的pid,需要和CLONE_CHILD_SETTID选项一起使用
3)flag 创建进程的属性
返回值:
成功返回子进程的ID号,失败返回-1.
1.2、flag各参数的含义和用法,这里只介绍了部分参数的含义,其它参数的含义请参考clone(2)
- CLONE_FILES
父进程和子进程共享相同的文件描述符表,例如在父进程中创建或者改变了某个文件描述符,也会影响子进程中该文件描述符,反过来也成立;如果这个选项没有被打开,那么子进程只会继承父进程中文件描述符的拷贝,父进程中对相应文件描述符的改变不影响子进程,反之也成立; - CLONE_FS
子进程和父进程共享相同的文件系统信息,包含根文件系统、当前工作目录、unmask。 - CLONE_IO
父子进程共享相同的IO环境; - CLONE_NEWIPC
- CLONE_NEWNET
- CLONE_NEWNS
- CLONE_NEWPID
- CLONE_NEWUTS
- CLONE_PARENT
这个选项表示新创建的子进程的父进程不是调用者,而是调用者的父进程,新的子进程和它的调用者拥有相同的父进程; - CLONE_PARENT_SETTID
存储父进程的pid到clone函数的ptid中; - CLONE_PID(Linux 2.0 to 2.5.15)
Linux 2.5.16之后该选项被弃用; - CLONE_PTRACE
若父进程被trace,子进程也会被trace。 - CLONE_SETTLS
- CLONE_SIGHAND
子进程和父进程共享相同的table of signal handlers。 - CLONE_STOPPED
这个选项原意是在创建子进程时将子进程停止运行,类似于子进程接收到了SIGSTOP,在收到SIGCONT信号后重新恢复;
这个选项在linx2.6.25版本中被弃用,在2.6.38版本中被移除,从linux4.6开始这个位被CLONE_NEWCGROUP替代; - CLONE_SYSVSEM(linux2.5.10以后)
父进程和子进程共享相同的信号量; - CLONE_THREAD
- CLONE_UNTRACED
- CLONE_VFORK
父进程会一直挂起直到子进程结束,使用这个选项的作用和vfork类似; - CLONE_VM
这个选项可以使父进程和子进程运行在相同的内存中,如果没有设置这个选项,子进程和父进程运行在不同的内存空间。
二、clone和fork的区别
1)clone允许新创建的进程共享调用它的进程(通常是父进程)的部分资源,比如内存空间、文件描述符表、信号量等,具体需要共享那些内容可以通过clone的flag参数设置;
2)fork函数创建的子进程可以看做是父进程的完全拷贝,而clone则是有选择性的拷贝;
三、示例代码:
#define _GNU_SOURCE //注意,这个宏必须在最前面,否则编译会报错
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/select.h>
#include <stdlib.h>
int value = 0;
int child_progress(void *arg)
{
while(1)
{
printf("child,value = %d\r\n",value);
value ++;
sleep(1);
if(value == 5)break;
}
return 0;
}
void main(int argc,char *argv[])
{
int ret = -1;
char *stack = NULL;
pid_t tid = 0;
stack = malloc(4096);
if(NULL == stack)
{
printf("malloc fail]\r\n");
return;
}
//子进程继承父进程的数据空间/在子进程结束后运行/将子进程的id存储到tid变量中
int mask = CLONE_VM|CLONE_VFORK|CLONE_CHILD_SETTID;
ret = clone(child_progress,stack+4096,mask,NULL,NULL,NULL,&tid);//栈地址向下增长,因此起始地址为stack+4096
if(ret < 0)
{
printf("clone error\r\n");
return;
}
printf("clone sucess,pid:%d %d\r\n",ret,tid);
while(1)
{
printf("father,value = %d\r\n",value);
value ++;
sleep(1);
}
}
运行结果: