linux在UBoot执行run_command("bootcmd",0)之后内核启动。然后执行 init/main.c 中的 start_kernel 函数进行一些初始化,如中断RAM 磁盘。start_kernel最后调用rest_init.
/* We need to finalize in a non-__init function or else race conditions * between the root thread and the init thread may cause start_kernel to * be reaped by free_initmem before the root thread has proceeded to cpu_idle. * gcc-3.4 accidentally inlines this function, so use noinline. */ static noinline void __init_refok rest_init(void)__releases(kernel_lock) { int pid; kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); unlock_kernel(); /** The boot idle thread must execute schedule() * at least once to get things moving: */ init_idle_bootup_task(current); rcu_scheduler_starting(); preempt_enable_no_resched(); schedule();.}//调度器接管理了。
static int __init kernel_init(void * unused){ ....... init_post();
}
/* This is a non __init function. Force it to be noinline otherwise gcc * makes it inline to init() and it becomes part of init.text section */ static noinline int init_post(void)__releases(kernel_lock) { /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); free_initmem(); unlock_kernel(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) printk(KERN_WARNING "Warning: unable to open an initial console.\n"); (void) sys_dup(0); (void) sys_dup(0); current->signal->flags |= SIGNAL_UNKILLABLE; if (ramdisk_execute_command) { run_init_process(ramdisk_execute_command); printk(KERN_WARNING "Failed to execute %s\n", ramdisk_execute_command); } /** We try each of these until one succeeds. * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */ .....run_init_process("/sbin/init"); run_init_process("/etc/init"); run_init_process("/bin/init"); run_init_process("/bin/sh"); panic("No init found. Try passing init= option to kernel."); }
从上面代码可以看到:start_kernel->rest_init->kernel_init->init_post->run_init_process
其中run_init_process就是执行文件系统中的一些用户空间进程,从而若要使内核启动后执行我们想要的进程,可以修改init_post这个函数,若修改run_init_process所执行的文件,或添加一些要执行的文件。
当内核启动init进程后(kernel_thread函数执行),在用户空间的进程的产生方式只有fork、vfork、clone等这几个函数来产生,它们是以复制的方式而不是新创建一个。fork()函数经常与exec系列函数一起用,fork之后立即调用exec,执行一个可执行文件,
fork,vfork它们是通过clone实现的,clone中的一些参数可以细化新进程复制产生的方式,而前两个函数是对这些参数进行了实例化。但在内核中它们都是通过do_fork()实现的。 看P118深入理解Linux内核。
Sys_arm.c (arch\arm\kernel)
用户空间fork()系统调用包含的头文件
#include<unistd.h>/*#包含<unistd.h>*/
#include<sys/types.h>/*#包含<sys/types.h>*/
pid_t fork(void)//pid_t 是一个,其实质是int 被定义在#include<sys/types.h中
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
出错返回错误信息如下: EAGAIN:达到进程数上限. ENOMEM:没有足够空间给一个新进程分配.
两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本,并继承用户代码、组代码、环境变量、已打开的文件代码、工作目录等。COW技术使得只有其中一个进程修改时才拷贝。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。所以在移植代码的时候我们不应该对此作出任何的假设。
但子进程不会继承文件的锁定和未处理的信号。
——————————————————————————————————————————————————————————
用户空间的CLONE
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
<pre name="code" class="objc">extern int clone (int (*__fn) (void *__arg), void *__child_stack,
int __flags, void *__arg, ...) __THROW;
/* Unshare the specified resources. */
extern int unshare (int __flags) __THROW;
这里fn是函数指针, arg就是传给子进程的参数,当这个函数返回时,子进程终止。 child_stack明显是为子进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,低地址上放入了值,这个值就是进程控制块task_struct的值),flags就是标志用来描述你需要从父进程继承那些资源
/* Cloning flags. */
# 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 shared. */进程间共享信号处理程序
# define CLONE_PTRACE 0x00002000 /* Set if tracing continues on the child. */
# 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 /* Set to add to same thread group. */
# define CLONE_NEWNS 0x00020000 /* Set to create new namespace. */
# define CLONE_SYSVSEM 0x00040000 /* Set to shared SVID SEM_UNDO semantics. */
# define CLONE_SETTLS 0x00080000 /* Set TLS info. */
# define CLONE_PARENT_SETTID 0x00100000 /* Store TID in userlevel buffer before MM copy. */
# define CLONE_CHILD_CLEARTID 0x00200000 /* Register exit futex and memory location to clear. */
# define CLONE_DETACHED 0x00400000 /* Create clone detached. */
# define CLONE_UNTRACED 0x00800000 /* Set if the tracing process can't force CLONE_PTRACE on this clone. */
# define CLONE_CHILD_SETTID 0x01000000 /* Store TID in userlevel buffer in the child. */
# 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 I/O context. */
转载的例子。
#include "stdio.h"
#include "sched.h"
#include "signal.h"
#define FIBER_STACK 8192
int a;
void * stack;
int do_something(){
printf("This is son, the pid is:%d, the a is: %d/n", getpid(), ++a);
free(stack); //这里我也不清楚,如果这里不释放,不知道子线程死亡后,该内存是否会释放,知情者可以告诉下,谢谢
exit(1);
}
int main() {
void * stack;
a = 1;
stack = malloc(FIBER_STACK);//为子进程申请系统堆栈
if(!stack) {
printf("The stack failed/n");
exit(0);
}
printf("creating son thread!!!/n");
clone(&do_something, (char *)stack + FIBER_STACK, CLONE_VM|CLONE_VFORK, 0);//创建子线程
printf("This is father, my pid is: %d, the a is: %d/n", getpid(), a);
exit(1);
}
[root@liumengli program]# gcc test_clone.c -o test_clone
[root@liumengli program]# ./test_clone
creating son thread!!!
This is son, the pid is:7326, the a is: 2
This is father, my pid is: 7325, the a is: 2
http://blog.csdn.net/caianye/article/details/5947282