- 浏览: 271027 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
AndMacLinuXp:
试了下,不错!
printk内核调试 -
klose:
我引用你的文章,并做了简单的分析:这里贴出url:http:/ ...
linux系统调用fork, vfork, clone -
klose:
你上面提到的问题:free的问题。首先你可能疏忽了,stack ...
linux系统调用fork, vfork, clone -
qwe_rt:
HI ,非常nice的文章,在阅读过程中,我发现我的ubunt ...
linux手动添加开机启动的服务 -
suifeng:
谢谢分享, 受用中.
shell编程分支,循环
fork,vfork,clone都是linux的系统调用,用来创建子进程的(确切说vfork创造出来的是线程)。
先介绍下进程必须的4要点:
a.要有一段程序供该进程运行,就像一场戏剧要有一个剧本一样。该程序是可以被多个进程共享的,多场戏剧用一个剧本一样。
b.有起码的私有财产,就是进程专用的系统堆栈空间。
c.有“户口”,既操作系统所说的进程控制块,在linux中具体实现是task_struct
d.有独立的存储空间。
当一个进程缺少d条件时候,我们称其为线程。
1.fork 创造的子进程复制了父亲进程的资源,包括内存的内容task_struct内容(2个进程的pid不同)。这里是资源的复制不是指针的复制。下面的例子可以看出
[root@liumengli program]# cat testFork.c
#include"stdio.h"
int main() {
int count = 1;
int child;
if(!(child = fork())) { //开始创建子进程
printf("This is son, his count is: %d. and his pid is: %d\n", ++count, getpid());//子进程的内容
} else {
printf("This is father, his count is: %d, his pid is: %d\n", count, getpid());
}
}
[root@liumengli program]# gcc testFork.c -o testFork
[root@liumengli program]# ./testFork
This is son, his count is: 2. and his pid is: 3019
This is father, his count is: 1, his pid is: 3018
[root@liumengli program]#
从代码里面可以看出2者的pid不同,内存资源count是值得复制,子进程改变了count的值,而父进程中的count没有被改变。有人认为这样大批量的复制会导致执行效率过低。其实在复制过程中,子进程复制了父进程的task_struct,系统堆栈空间和页面表,这意味着上面的程序,我们没有执行count++前,其实子进程和父进程的count指向的是同一块内存。而当子进程改变了父进程的变量时候,会通过copy_on_write的手段为所涉及的页面建立一个新的副本。所以当我们执行++count后,这时候子进程才新建了一个页面复制原来页面的内容,基本资源的复制是必须的,而且是高效的。整体看上去就像是父进程的独立存储空间也复制了一遍。
其次,我们看到子进程和父进程直接没有互相干扰,明显2者资源都独立了。我们看下面程序
[root@liumengli program]# cat testFork.c
#include"stdio.h"
int main() {
int count = 1;
int child;
if(!(child = fork())) {
int i;
for(i = 0; i < 200; i++) {
printf("This is son, his count is: %d. and his pid is: %d\n", i, getpid());
}
} else {
printf("This is father, his count is: %d, his pid is: %d\n", count, getpid());
}
}
[root@liumengli program]# gcc testFork.c -o testFork
[root@liumengli program]# ./testFork
...
This is son, his count is: 46. and his pid is: 4092
This is son, his count is: 47. and his pid is: 4092
This is son, his count is: 48. and his pid is: 4092
This is son, his count is: 49. and his pid is: 4092
This is son, his count is: 50. and his pid is: 4092
This is father, his count is: 1, his pid is: 4091
[root@liumengli program]# This is son, his count is: 51. and his pid is: 4092
This is son, his count is: 52. and his pid is: 4092
...
(运气很衰,非要200多个才有效果,郁闷)从结果可以看出父子2个进程是同步运行的。这和下面的vfork有区别。
2.vfork创建出来的不是真正意义上的进程,而是一个线程,因为它缺少了我们上面提到的进程的四要素的第4项,独立的内存资源,看下面的程序
[root@liumengli program]# cat testVfork.c
#include "stdio.h"
int main() {
int count = 1;
int child;
printf("Before create son, the father's count is:%d\n", count);
if(!(child = vfork())) {
printf("This is son, his pid is: %d and the count is: %d\n", getpid(), ++count);
exit(1);
} else {
printf("After son, This is father, his pid is: %d and the count is: %d, and the child is: %d\n", getpid(), count, child);
}
}
[root@liumengli program]# gcc testVfork.c -o testVfork
[root@liumengli program]# ./testVfork
Before create son, the father's count is:1
This is son, his pid is: 4185 and the count is: 2
After son, This is father, his pid is: 4184 and the count is: 2, and the child is: 4185
[root@liumengli program]#
从运行结果可以看到vfork创建出的子进程(线程)共享了父进程的count变量,这一次是指针复制,2者的指针指向了同一个内存,所以子进程修改了count变量,父进程的 count变量同样受到了影响。另外由vfork创造出来的子进程还会导致父进程挂起,除非子进程exit或者execve才会唤起父进程,看下面程序:
[root@liumengli program]# cat testVfork.c
#include "stdio.h"
int main() {
int count = 1;
int child;
printf("Before create son, the father's count is:%d\n", count);
if(!(child = vfork())) {
int i;
for(i = 0; i < 100; i++) {
printf("This is son, The i is: %d\n", i);
if(i == 70)
exit(1);
}
printf("This is son, his pid is: %d and the count is: %d\n", getpid(), ++count);
exit(1);
} else {
printf("After son, This is father, his pid is: %d and the count is: %d, and the child is: %d\n", getpid(), count, child);
}
}
[root@liumengli program]# gcc testVfork.c -o testVfork
[root@liumengli program]# ./testVfork
...
This is son, The i is: 68
This is son, The i is: 69
This is son, The i is: 70
After son, This is father, his pid is: 4433 and the count is: 1, and the child is: 4434
[root@liumengli program]#
从这里就可以看到父进程总是等子进程执行完毕后才开始继续执行。
3.clone函数功能强大,带了众多参数,因此由他创建的进程要比前面2种方法要复杂。clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。先有必要说下这个函数的结构
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
这里fn是函数指针,我们知道进程的4要素,这个就是指向程序的指针,就是所谓的“剧本", child_stack明显是为子进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,低地址上放入了值,这个值就是进程控制块task_struct的值),flags就是标志用来描述你需要从父进程继承那些资源, arg就是传给子进程的参数)。下面是flags可以取的值
标志 含义
CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask
CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
CLONE_NEWNS 在新的namespace启动子进程,namespace描述了进程的文件hierarchy
CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
CLONE_PTRACE 若父进程被trace,子进程也被trace
CLONE_VFORK 父进程被挂起,直至子进程释放虚拟内存资源
CLONE_VM 子进程与父进程运行于相同的内存空间
CLONE_PID 子进程在创建时PID与父进程一致
CLONE_THREAD Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群
下面的例子是创建一个线程(子进程共享了父进程虚存空间,没有自己独立的虚存空间不能称其为进程)。父进程被挂起当子线程释放虚存资源后再继续执行。
[root@liumengli program]# cat test_clone.c
#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
[root@liumengli program]#
读者可以试试其它的资源继承方式。
评论
使用Valgrind查看内存使用:
==14022== HEAP SUMMARY:
==14022== in use at exit: 8,192 bytes in 1 blocks
==14022== total heap usage: 1 allocs, 0 frees, 8,192 bytes allocated
发表评论
-
linux下挂在windows共享文件夹
2012-05-06 12:53 887smbmount //nas/xxxx /mnt/nas -o ... -
进程组 对话期
2010-04-16 16:12 1411为了便于进程控制,于是就有了进程组,对话期的概念。 进 ... -
感谢你教会我如何提问-《提问的智慧》
2009-01-12 11:25 1456转自:http://linux.chinaunix ... -
linux手动添加开机启动的服务
2009-01-02 14:48 7201如果你只是想知道如何 ... -
让信号量来实现临界区控制
2008-12-09 16:14 1603前面我们谈到过管道,消息队列。我们可以使用他们可以解决了2个进 ... -
进程通行之报文消息队列
2008-12-04 17:39 1419报文和消息队列又是进 ... -
信号和信号处理
2008-11-26 16:09 3345信号同样是用于进程通 ... -
命名管道实现样例
2008-11-18 17:41 1288[root@liumengli name_pipe]# mkn ... -
linux中的管道实现和管道
2008-11-14 17:52 12983inux的shell中有个很常用的东西管道,很多人认为他是sh ... -
文件访问权限和安全
2008-10-28 18:02 1242打开文件,读取内容或 ... -
printk内核调试
2008-10-15 17:07 9452首先阐明一点,我调试的目的是为了学习,看看内核代码是如何运行, ... -
linux内核编译
2008-10-07 17:36 1968这是调试linux内核的必要步骤,虽然很麻烦,而且容易出错(所 ... -
linux文件系统
2008-10-06 11:08 1019文件系统是一个比较模糊的名词,文件也是一个比较模糊的名词。狭义 ... -
linux内核书籍下载
2008-09-25 11:47 2193学习内核代码之前,需要有以下一些基础: 1.linux系统管理 ... -
nanosleep函数
2008-09-24 17:36 3381int nanosleep(const struct ti ... -
linux进程调度政策
2008-09-23 11:45 1593进程调度政策就是调度系统种哪一个进程来CPU运行。这种调度分2 ... -
linux进程调度发生的时机
2008-09-19 18:03 4134所谓调度时机就是:什么时候会发生进程调度。在linux中,进程 ... -
SSH远程启动服务的问题
2008-09-18 12:32 4721SSH是我们最长用的远程连接linux服务器的工具了,安全 ... -
时钟中断
2008-09-17 16:57 3371时钟中断:是指在计算 ... -
wait4的实现
2008-09-17 15:38 3015不贴实现的代码了,很 ...
相关推荐
在Linux操作系统中,`fork()`、`vfork()` 和 `clone()` 都是用于创建新进程的函数,但它们在实现机制和使用场景上存在显著差异。 1. **fork()** `fork()` 是最传统的进程创建方法。它创建一个与父进程完全独立的新...
Linux 系统调用列表介绍 本文列出了大部分常见的 Linux 系统调用,并附有简要中文说明。 Linux 系统调用是操作系统提供给应用程序的接口,用于控制进程、文件系统、网络、设备等资源。以下是 Linux 系统调用的一个...
### Linux系统调用详解 #### 一、进程控制 Linux系统调用是操作系统与应用程序之间的桥梁,它们允许用户空间的应用程序请求内核执行特定的任务。下面详细介绍与进程控制相关的系统调用。 - **fork()**: 创建一个...
do_fork 函数被 sys_fork()、sys_clone() 和 sys_vfork() 等系统调用所调用,以实现进程的复制与创建。 #### 二、do_fork 函数的工作原理 do_fork() 函数主要负责创建一个新的进程,并对其进行初始化。该过程可以...
Linux系统调用是操作系统提供给应用程序的接口,允许用户空间的程序执行底层操作,如进程控制、文件操作、设备交互等。这份"Linux系统调用列表[定义].pdf"文档详细列举并解释了大量常见的Linux系统调用,对于软件...
在Linux系统编程中,`fork()`, `exit()` 和 `exec()` 这三个系统调用是构建多进程程序的关键组成部分。它们允许程序创建新的进程、结束当前进程以及替换当前进程的执行上下文,从而实现复杂的功能和服务。 ### `...
在本篇内容中,我们将详细探讨《系统调用表可以作为索引使用》这一主题的核心知识点,特别关注于Linux系统调用表的结构、功能以及如何作为索引工具来帮助程序员更好地理解和使用这些系统调用。 #### 一、系统调用的...
除了fork()函数外,Linux内核还提供了vfork()和clone()系统调用。vfork()函数与fork()函数类似,但它不允许子进程执行exec()函数,直到父进程调用wait()函数或者exit()函数。clone()函数则可以创建一个新的进程,...
### Linux系统调用函数知识点详解 #### 一、进程控制 **fork** - **功能**:创建一个新进程。 - **参数**:无。 - **返回值**:在父进程中返回子进程的PID,在子进程中返回0。 - **用途**:用于创建一个与父进程...
`sys_vfork()` 同样调用 `do_fork()`,但传递了 `CLONE_VFORK | CLONE_VM | SIGCHLD` 的组合标志。`CLONE_VFORK` 用于确保子进程在执行 `exec` 或退出之前不会运行;`CLONE_VM` 表明子进程将继承父进程的虚拟内存,...
整个进程创建过程可能涉及到如下函数:fork()/vfork()/_clone----------->clone()--------->do_fork()---------->copy_process() 进程的切换:进程切换又称为任务切换、上下文切换。它是一种行为,为了控制进程的...
4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核中的互斥操作 第5章 文件系统 ...
2. **进程管理**:包括进程创建(`fork`、`vfork`、`clone`)、进程间通信(管道、消息队列、共享内存、信号量、套接字等)、进程同步与互斥、进程调度策略等。这些概念对于编写多线程或多进程应用程序至关重要。 3...
在现代Linux系统中,`vfork()`通常被实现为`clone()`函数的一个特殊标志集,它仍然保留了地址空间共享的特点。 在实际开发中,由于`vfork()`的复杂性和潜在问题,一般推荐使用更安全、更稳定的`fork()`函数。然而,...
第4章进程与进程调度深入探讨了进程的四要素,进程创建、执行与消亡的过程,以及系统调用fork、vfork、clone、execve、exit、wait的使用。文档还分析了进程的调度与切换、强制性调度、系统调用nanosleep和pause、...
在Linux操作系统中,`fork()`函数是一个非常关键的系统调用,它被用来创建一个新的进程——也就是我们常说的子进程。这个过程被称为进程复制,因为子进程会继承父进程的大部分属性,包括打开的文件、内存空间、信号...
4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核中的互斥操作 第5章 文件...
当一个进程调用fork后,系统会复制当前进程(父进程)的大部分信息(PCB以及地址空间等)来创建一个新的进程(子进程)。子进程是父进程的副本,除了进程标识号(PID)和资源统计信息(如CPU使用时间等)之外,几乎...