这里接着讲do_fork的第一步——copy_process
这里是具体的调用
- p = copy_process(clone_flags, stack_start, regs, stack_size,
-
child_tidptr, NULL, trace);
接着看copy_process的代码,copy_process的代码比较长,这里先介绍一下主题流程,再将代码一段一段进行分析:
1、检查clone_flags参数,clone_flages参数参数如下:
- #define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
-
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
-
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
-
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
-
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
-
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
-
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
-
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
-
#define CLONE_THREAD 0x00010000 /* Same thread group? */
-
#define CLONE_NEWNS 0x00020000 /* New namespace group? */
-
#define CLONE_SYSVSEM 0x00040000 /* share system V SEM_UNDO semantics */
-
#define CLONE_SETTLS 0x00080000 /* create a new TLS for the child */
-
#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */
-
#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */
-
#define CLONE_DETACHED 0x00400000 /* Unused, ignored */
-
#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */
-
#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */
-
/* 0x02000000 was previously the unused CLONE_STOPPED (Start in stopped state)
-
and is now available for re-use. */
-
#define CLONE_NEWUTS 0x04000000 /* New utsname group? */
-
#define CLONE_NEWIPC 0x08000000 /* New ipcs */
-
#define CLONE_NEWUSER 0x10000000 /* New user namespace */
-
#define CLONE_NEWPID 0x20000000 /* New pid namespace */
-
#define CLONE_NEWNET 0x40000000 /* New network namespace */
-
#define CLONE_IO 0x80000000 /* Clone io context */
参数比较多,clone_flags是这些参数的组合,这个检查的作用就是防止无效的组合进入。
- if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
-
return ERR_PTR(-EINVAL);
-
-
/*
-
* Thread groups must share signals as well, and detached threads
-
* can only be started up within the thread group.
-
*/
-
if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
-
return ERR_PTR(-EINVAL);
-
-
/*
-
* Shared signal handlers imply shared VM. By way of the above,
-
* thread groups also imply shared VM. Blocking this case allows
-
* for various simplifications in other code.
-
*/
-
if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
-
return ERR_PTR(-EINVAL);
- if ((clone_flags & CLONE_PARENT) &&
- current->signal->flags & SIGNAL_UNKILLABLE)
- return ERR_PTR(-EINVAL);
以上有三种组合是不通过的:
1)CLONE_NEWNS 和 CLONE_FS组合,一方面需要创建一个新的命名空间,同时又要与父进程公用文件系统信息,这显然是不成立的,不同的命名空间就是用来分隔系统资源的,这样资源跨命名空间的情况是不允许的。
2)CLONE_THREADE设置了,但是没有设置CLONE_SIGHAND:在用CLONE_THREAD创建一个线程时,必须用CLONE_SIGHAND激活信号共享
3)只有在父子进程之间共享虚拟地址空间(CLONE_VM)时,才能提供共享的信号处理程序
信号相关的内容后面会详细讲解的。
4)防止产生init进程的兄弟,如果当前的cloner是init进程(即是current->signal->flags设置了SIGNAL_UNKILLABLE),如果同时设置了CLONE_PARENT(新进程的parent设置为cloner的parent),那么这样生成的进程将是init进程的兄弟,由于在退出的时候没有父进程来对他进行回收,这样的进程会形成僵尸进程,所以要避免这样的进程产生。
PS:
还有一个问题就是ERR_PTR(-EINVAL),copy_process函数定义的返回结果是task_struct的指针,而EINVAL是一个整数,这里用ERR_PTR进行了转化
- static inline void * __must_check ERR_PTR(long error)
-
{
-
return (void *) error;
-
}
ERR_PTR将EINVAL转化成void指针进行返回,这样就可以了。
再看看ERR_PTR函数,前面有个__must_check,这个是什么呢,看看具体的代码
- #if __GNUC_MINOR__ >= 4
-
#define __must_check __attribute__((warn_unused_result))
-
#endif
这里定义了宏__must_check,是利用gcc编译器的_attribute_给函数加上了属性,这个属性的作用是: 让编译器检查所有调用者是否都检查函数的结果。这确保调用者适当地检验函数结果,从而能够适当地处理错误。
也就是说,调用者在拿到结果后必须要进行检查是否返回了正确的结构,否则编译器会给出提示信息。
2、dup_task_struct
dup_task_struct的主要工作是生成新进程的task_struct结构,主要完成复制task_struct的工作,后面会详细分析这个函数的。
3、检查资源限制
- if (atomic_read(&p->real_cred->user->processes) >=
-
task_rlimit(p, RLIMIT_NPROC)) {
-
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
-
p->real_cred->user != INIT_USER)
-
goto bad_fork_free;
-
}
4、完成一些初始化工作
5、sched_fork:完成调度相关的设置,将这个task分配给CPU
- sched_fork(p, clone_flags);
6、复制共享进程的的各个部分
7、设置各个ID以及进程关系,等等
阅读(3305) | 评论(0) | 转发(2) |