Linux进程的创建函数fork()及其fork内核实现解析
#include <unistd.h>pid_t fork(void);
/proc/sys/kernel/sched_child_runs_first
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <wait.h>int g_int = 1;//数据段的全局变量int main(){int local_int = 1;//栈上的局部变量int *malloc_int = malloc(sizeof(int));//通过malloc动态分配在堆上的变量*malloc_int = 1;pid_t pid = fork();if(pid == 0) /*子进程*/{local_int = 0;g_int = 0;*malloc_int = 0;fprintf(stderr,"[CHILD ] child change local global malloc value to 0\n");free(malloc_int);sleep(10);fprintf(stderr,"[CHILD ] child exit\n");exit(0);}else if(pid < 0){printf("fork failed (%s)",strerror(errno));return 1;}fprintf(stderr,"[PARENT] wait child exit\n");waitpid(pid,NULL,0);fprintf(stderr,"[PARENT] child have exit\n");printf("[PARENT] g_int = %d\n",g_int);printf("[PARENT] local_int = %d\n",local_int);printf("[PARENT] malloc_int = %d\n",local_int);free(malloc_int);return 0;}
[PARENT] wait child exit[CHILD ] child change local global malloc value to 0[CHILD ] child exit[PARENT] child have exit[PARENT] g_int = 1[PARENT] local_int = 1[PARENT] malloc_int = 1

/*如果是写时拷贝, 那么无论是初始页表, 还是拷贝的页表, 都设置了写保护*后面无论父子进程, 修改页表对应位置的内存时, 都会触发page fault*/if (is_cow_mapping(vm_flags)) {ptep_set_wrprotect(src_mm, addr, src_pte);//设置为写保护pte = pte_wrprotect(pte);}

struct task_struct {...struct files_struct *files;...}
static int copy_files(unsigned long clone_flags,struct task_struct *tsk){struct files_struct *oldf, *newf;int error = 0;oldf = current->files;//获取父进程的文件结构体if (!oldf)goto out;/*创建线程和vfork, 都不用复制父进程的文件描述符, 增加引用计数即可*/if (clone_flags & CLONE_FILES) {atomic_inc(&oldf->count);goto out;}/*对于fork而言, 需要复制父进程的文件描述符*/newf = dup_fd(oldf, &error); //复制一份文件描述符if (!newf)goto out;tsk->files = newf;error = 0;out:return error;}
struct files_struct *dup_fd(struct files_struct *oldf,int *errorp){struct files_struct *newf;struct file **old_fds, **new_fds;int open_files, size, i;struct fdtable *old_fdt, *new_fdt;*errorp = -ENOMEM;newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);if (!newf)goto out;
struct files_struct {atomic_t count;struct fdtable __rcu *fdt;struct fdtable fdtab;spinlock_t file_lock ____cacheline_aligned_in_smp;int next_fd;struct embedded_fd_set close_on_exec_init;struct embedded_fd_set open_fds_init;struct file __rcu * fd_array[NR_OPEN_DEFAULT];};struct fdtable //文件描述符表{unsigned int max_fds;struct file __rcu **fd; /* current fd array */fd_set *close_on_exec;fd_set *open_fds;struct rcu_head rcu;struct fdtable *next;};struct embedded_fd_set {unsigned long fds_bits[1];};
atomic_set(&newf->count, 1);spin_lock_init(&newf->file_lock);newf->next_fd = 0;new_fdt = &newf->fdtab;new_fdt->max_fds = NR_OPEN_DEFAULT;new_fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init;new_fdt->open_fds = (fd_set *)&newf->open_fds_init;new_fdt->fd = &newf->fd_array[0];new_fdt->next = NULL;

spin_lock(&oldf->file_lock);old_fdt = files_fdtable(oldf);open_files = count_open_files(old_fdt);/*如果父进程打开文件的个数超过NR_OPEN_DEFAULT*/while (unlikely(open_files > new_fdt->max_fds)) {spin_unlock(&oldf->file_lock); /* 如果不是自带的fdtable而是曾经分配的fdtable, 则需要先释放*/if (new_fdt != &newf->fdtab)__free_fdtable(new_fdt);/*创建新的fdtable*/new_fdt = alloc_fdtable(open_files - 1);if (!new_fdt) {*errorp = -ENOMEM;goto out_release;}/*如果超出了系统限制, 则返回EMFILE*/if (unlikely(new_fdt->max_fds < open_files)) {__free_fdtable(new_fdt);*errorp = -EMFILE;goto out_release;}spin_lock(&oldf->file_lock);old_fdt = files_fdtable(oldf);open_files = count_open_files(old_fdt);}
old_fds = old_fdt->fd;/*父进程的struct file 指针数组*/- new_fds = new_fdt->fd; /*子进程的struct file 指针数组*/
- /* 拷贝打开文件位图 */
- memcpy(new_fdt->open_fds->fds_bits,old_fdt->open_fds->fds_bits, open_files/8);
- /* 拷贝 close_on_exec位图 */
- memcpy(new_fdt->close_on_exec->fds_bits,old_fdt->close_on_exec->fds_bits, open_files/8);
- for (i = open_files; i != 0; i--) {
- struct file *f = *old_fds++;
- if (f) {
- get_file(f); /* f对应的文件的引用计数加1 */
- } else {
- FD_CLR(open_files - i, new_fdt->open_fds);
- }
- /* 子进程的struct file类型指针, *指向和父进程相同的struct file 结构体*/
- rcu_assign_pointer(*new_fds++, f);
- }
- spin_unlock(&oldf->file_lock);/* compute the remainder to be cleared */
- size = (new_fdt->max_fds - open_files) * sizeof(struct file *);
- /*将尚未分配到的struct file结构的指针清零*/
- memset(new_fds, 0, size);/*将尚未分配到的位图区域清零*/
- if (new_fdt->max_fds > open_files) {
- int left = (new_fdt->max_fds-open_files)/8;
- int start = open_files / (8 * sizeof(unsigned long));
memset(&new_fdt->open_fds->fds_bits[start], 0, left);memset(&new_fdt->close_on_exec->fds_bits[start], 0, left);}rcu_assign_pointer(newf->fdt, new_fdt);return newf;out_release:kmem_cache_free(files_cachep, newf);out:return NULL;}

#include<stdio.h>#include <stdlib.h>#include <unistd.h>int glob = 88 ;int main(void) {int var;var = 88;pid_t pid;if ((pid = vfork()) < 0) {printf("vfork error");exit(-1);} else if (pid == 0) { /* 子进程 */var++;glob++;return 0;}printf("pid=%d, glob=%d, var=%d\n",getpid(), glob, var);return 0;}
Linux进程的创建函数fork()及其fork内核实现解析的更多相关文章
- Linux进程的创建函数fork()及其fork内核实现解析【转】
转自:http://www.cnblogs.com/zengyiwen/p/5755193.html 进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进 ...
- linux 进程的创建
1. 进程号: 每个进程在被初始化的时候,系统都会为其分配一个唯一标识的进程id,称为进程号: 进程号的类型为pid_t,通过getpid()和getppid()可以获取当前进程号和当前进程的父进程的 ...
- linux进程学习-创建新进程
init进程将系统启动后,init将成为此后所有进程的祖先,此后的进程都是直接或间接从init进程“复制”而来.完成该“复制”功能的函数有fork()和clone()等. 一个进程(父进程)调用for ...
- linux进程解析--进程的创建
通常我们在代码中调用fork()来创建一个进程或者调用pthread_create()来创建一个线程,创建一个进程需要为其分配内存资源,文件资源,时间片资源等,在这里来描述一下linux进程的创建过程 ...
- Linux进程-进程的创建
今天学习了Linux的进程创建的基本原理,是基于0.11版本核心的.下面对其作一下简单的总结. 一.Linux进程在内存中的相关资源 很容易理解,Linux进程的创建过程就是内存中进程相关资源产生 ...
- Linux 进程,线程,线程池
在linux内核,线程与进程的区别很小,或者说内核并没有真正所谓单独的线程的概念,进程的创建函数是fork,而线程的创建是通过clone实现的. 而clone与fork都是调用do_fork(),差异 ...
- 撸代码--linux进程通信(基于共享内存)
1.实现亲缘关系进程的通信,父写子读 思路分析:1)首先我们须要创建一个共享内存. 2)父子进程的创建要用到fork函数.fork函数创建后,两个进程分别独立的执行. 3)父进程完毕写的内容.同一时候 ...
- Operating System-Process(1)什么是进程&&进程的创建(Creation)&&进程的终止(Termination)&&进程的状态(State)
本文阐述操作系统的核心概念之一:进程(Process),主要内容: 什么是进程 进程的创建(Creation) 进程的终止(Termination) 进程的状态(State) 一.什么是进程 1.1 ...
- 通过fork函数创建进程的跟踪,分析linux内核进程的创建
作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验过程 1.打开gdb, ...
随机推荐
- [剑指Offer] 58.对称的二叉树
题目描述 请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. [思路]递归,关键是isSame函数中的最后一句 /* struct Tree ...
- RT-thread组件初始化代码分析
RT-thread提供了组件化功能,具体实现是在components/init文件夹下components.c文件中实现的.应用组件化功能首先在rtconfig.h中添加宏定义#define RT_U ...
- [WC2005]友好的生物
description 洛谷 求 \[max_{1\le i<j\le n}\{\sum_{s=1}^{k-1}(C_s-|D_{is}-D_{js}|)-(C_k-|D_{ik}-D_{jk} ...
- HashMap & SparseArray & ArrayMap 简单说明
HashMap 使用有限一维拉链数组存储结构,鉴于所用Entry结构{key, value, nextExtry},Key的hash值用于取余获得所属的数组行下标,通过链表方式顺序存放所有余数相同的各 ...
- 背景建模技术(二):BgsLibrary的框架、背景建模的37种算法性能分析、背景建模技术的挑战
背景建模技术(二):BgsLibrary的框架.背景建模的37种算法性能分析.背景建模技术的挑战 1.基于MFC的BgsLibrary软件下载 下载地址:http://download.csdn.ne ...
- Educational Codeforces Round 6 A
A. Professor GukiZ's Robot time limit per test 0.5 seconds memory limit per test 256 megabytes input ...
- bnuoj25660 Two Famous Companies
题目链接:https://www.bnuoj.com/v3/problem_show.php?pid=25660 这个二分真的是烧脑QAQ,想了一晚上才懂了一个大概. 首先,整体思路是二分,直观上感受 ...
- oracle xml操作
/*=====================生成\修改xml========================= */ --xmlelement多个标签层级 SELECT XMLELEMENT(& ...
- 「模板」「讲解」Treap名次树
Treap实现名次树 前言 学平衡树的过程可以说是相当艰难.浏览Blog的过程中看到大量指针版平衡树,不擅长指针操作的我已经接近崩溃.于是,我想着一定要写一篇非指针实现的Treap的Blog. 具体如 ...
- nginx与php-fpm通讯方式
nginx和php-fpm的通信方式有两种,一种是tcp socket的方式,一种是unix socke方式. tcp sockettcp socket的优点是可以跨服务器,当nginx和php-fp ...