系统调用fork笔记

fork函数的原型是这样的:

1 pid_t fork(void); 

它实际上是一个系统调用,被包装在unistd.h中
由fork创建的新进程称为子进程,创建子进程的进程叫做父进程.子进程拥有与父进程一模一样的数据,从fork()语句开始分化.
它的返回值类型pid_t是一个内容为int的宏,在sys/types.h中声明.子进程返回0,父进程中返回子进程的pid(可以在子进程中调用getpid()得到,它同样被包装在unistd.h中).出错返回-1.出错原因可能是当前进程数超过限定或内存不足以新建一个进程.
除了init进程外,每一个进程都有一个父进程.init进程没有父进程,可以说,所有进程都是init进程fork出来的.

下面这个程序演示了fork函数.

 1 #include <assert.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <stdio.h>
 5
 6 int main(void)
 7 {
 8         pid_t childpid = fork(); /* 从这一行开始,子进程开始 */
 9         if (childpid == -1) /* 返回值为-1 -- 出错 */
10                 printf("出错啦!\n");
11         else if (childpid == 0) /* 返回值为0 -- 该进程为子进程 */
12                 printf("我是子进程!我的pid为%x\n", getpid());
13         else if (childpid > 1) /*
14                                 * 返回值为正数 -- 该进程为父进程
15                                 * 如果pid为1, 则表明父进程为init进程
16                                 */
17                 printf("我是父进程!我儿子的pid为%x\n", childpid);
18         else /* 异常 */
19                 assert(0);
20         return 0;
21 }        
/tmp louis$ gcc fork1.c -o f
/tmp louis$ ./f
我是父进程!我儿子的pid为1bf0
我是子进程!我的pid为1bf0
/tmp louis$ ./f
我是父进程!我儿子的pid为1bf9
我是子进程!我的pid为1bf9
/tmp louis$ ./f
我是父进程!我儿子的pid为1bfb
我是子进程!我的pid为1bfb
/tmp louis$ ./f
我是父进程!我儿子的pid为1bfd
我是子进程!我的pid为1bfd
/tmp louis$ 

再看一个例子:

 1 #include <assert.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <stdio.h>
 5
 6 int main(void)
 7 {
 8         printf("%x: Hello, world!我只会显示一次!\n", getpid());
 9         pid_t childpid = fork();
10         if (childpid == -1)
11                 printf("出错啦!\n");
12         else if (childpid == 0)
13                 printf("%x: Hello, world!我们父子会各自输出一次\n", getpid());
14         else if (childpid > 1)
15                 printf("%x: Hello, world!我们父子会各自输出一次\n", getpid());
16         else /* 异常 */
17                 assert(0);
18         printf("%x: Hello, world!这次我们父子都会输出这条信息\n", getpid());
19         wait();
20         return 0;
21 } 
/tmp louis$ gcc fork2.c -o f2
/tmp louis$ ./f2
1c3e: Hello, world!我只会显示一次!
1c3e: Hello, world!我们父子会各自输出一次
1c3e: Hello, world!这次我们父子都会输出这条信息
1c3f: Hello, world!我们父子会各自输出一次
1c3f: Hello, world!这次我们父子都会输出这条信息
/tmp louis$ ./f2
1c40: Hello, world!我只会显示一次!
1c40: Hello, world!我们父子会各自输出一次
1c40: Hello, world!这次我们父子都会输出这条信息
1c41: Hello, world!我们父子会各自输出一次
1c41: Hello, world!这次我们父子都会输出这条信息
/tmp louis$  

倒数第3行的wait()调用的意思是:如果该进程拥有子进程且子进程还在运行,那么等待子进程结束后再继续执行之后的语句.
如果不加上这个调用,父进程就有可能先于子进程结束.这意味着,该父进程fork出的子进程没有了父进程,成了"孤儿"进程.这时,init进程会立刻"领养"该"孤儿"进程,成为"孤儿"进程的父进程.然后,被领养的"孤儿"进程结束运行后,内存中还保留这相关信息.这时,它就会变成"僵尸"进程,占用着宝贵的资源.
 
再看最后一个例子吧.这个例子说明了父子进程之间的数据资源并非共享.

 1 #include <assert.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <stdio.h>
 5
 6 int main(void)
 7 {
 8         int i = 0;
 9         pid_t childpid = fork();
10         if (childpid == -1)
11                 printf("出错啦!\n");
12         else if (childpid == 0) {
13                 i = 1;
14                 printf("子进程%x: i = %d\n", getpid(), i);
15         } else if (childpid > 1) {
16                 i += 1;
17                 printf("父进程%x: i = %d\n", getpid(), i);
18         } else
19                 assert(0);
20         printf("%x: i = %d\n", getpid(), i);
21         wait();
22         return 0;
23 } 
/tmp louis$ gcc fork3.c -o f3
/tmp louis$ ./f3
父进程1c88: i = 1
1c88: i = 1
子进程1c89: i = 1
1c89: i = 1
/tmp louis$ ./f3
父进程1c8a: i = 1
1c8a: i = 1
子进程1c8b: i = 1
1c8b: i = 1
/tmp louis$  

子进程和父进程是完全不同的两个进程,父子进程要分别占用不同的资源。
 
最后出一道练习题:下面的代码执行后会有什么效果呢?

#include <unistd.h>
int main(void)
{
        while (1)
                fork();
        return 0;
} 

大家想不出来可千万别自己去尝试呀,呵呵.

系统调用fork笔记

时间: 2024-10-14 22:18:19

系统调用fork笔记的相关文章

linux系统调用fork()总结(二)

一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈.内存信息.打开的文件描述符.信号控制设置.进程优先级.进程组号.当前工作目录.根目录.资源限制.控制终端等. 子进程与父进程的区别在于: 1.父进程设置的锁,子进程不继承(因为如果是排它锁,被继承的话,矛盾了) 2.各自的进程ID和父进程ID不同 3.子进程的未决告警被清除: 4.子进程的未决信号集设置为空集. 二,fork系统调用 包含头文件 <sys/types.h> 和 &

系统调用fork()、vfork()以及clone()

一.宏观实现 以前介绍过fork()和clone()的区别,下面介绍一下两者在程序接口上的不同: pid_t fork(void); int __clone(int(*fn)(void *arg), void * child_stack, int flags, void *args) 系统调用__clone()的主要用途是创建一个线程,这个线程可以是内核线程,也可以是用户线程.创建用户空间线程时,可以给定子线程用户空间堆栈的位置,还可以指定子进程运行的起点.同时,也可以用__clone()创建进

Linux 系统调用 —— fork 内核源码剖析

系统调用流程简述 fork() 函数是系统调用对应的 API,这个系统调用会触发一个int 0x80 的中断: 当用户态进程调用 fork() 时,先将 eax(寄存器) 的值置为 2(即 __NR_fork 系统调用号): ? 执行 int $0x80,cpu 进入内核态: ? 执行 SAVE_ALL,保存所有寄存器到当前进程内核栈中: ? 进入 sys_call,将 eax 的值压栈,根据系统调用号查找 system_call_table ,调用对应的函数: ? 函数返回,执行 RESTOR

系统调用学习笔记

开始学内核的时候,一定会讲从ring3到ring0的调用,但是网上很多的文章讲的模棱两可,这次记录下我对系统调用的研究........ 一个线程由用户态进入内核态的途径有3种典型的方式: 1.  主动通过int 2e(软中断自陷方式)或sysenter指令(快速系统调用方式)调用系统服务函数,主动进入内核 2.  发生异常,被迫进入内核 3.  发生硬件中断,被迫进入内核 现在的cpu调用api进入内核都是通过sysenter指令(AMD的处理器是syscall)进入到ring0,系统在启动的时

fork系统调用(转载)

(1) fork系统调用说明 fork系统调用用于从已存在进程中创建一个新进程,新进程称为子进程,而原进程称为父进程.fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程的进程号,而子进程中的返回值则返回 0.因此,可以通过返回值来判定该进程是父进程还是子进程. 使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文.进程堆栈.内存信息.打开的文件描述符.信号控制设定.进程优先级.进程组号.当前工作目录.根

linux: fork系统调用实现剖析

首先了解一下程序和进程的概念: 程序:程序是完成特定任务的一系列指令集合. 进程:从用户角度来看,进程是程序的一次执行过程.从系统的角度看,进程是操作系统分配内存和cpu等资源的基本单位,进程是资源分配的最小单位.每一个进程都有自己独立的地址空间与执行状态,像unix这样的多任务操作系统能够让许多程序同时运行,每一个运行着的程序就构成了一个进程. 进程数据结构: 进程的静态描述:由pcb.有关程序段和该程序段所操作的数据结构集. 进程控制块:描述进程情况及控制运行所需要的全部信息 代码段:是进程

操作系统概念学习笔记 9 线程

操作系统概念学习笔记 9 线程 概述 单个进程可以包括多个控制线程. 线程 --一种CPU利用的基本单元,它是形成多线程计算机的基础. 线程是CPU使用的基本单元,它由线程ID.程序计数器.寄存器集合和栈组成.它与属于统一进程的其他线程共享代码段.数据段和其他操作系统资源. 一个传统重量级的进程只有单个控制线程,如果进程有多个控制线程,那么能同时做多个任务. 单线程与多线程 动机 一个应用程序通常是作为一个具有多个控制线程的独立进程实现的.如一个忙碌的网页服务器如果有多个(或数千个)客户并发访问

[转载]【Linux学习笔记】Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)

在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入汇编在CS术语上叫做inline assembly.本文的笔记试图说明Inline Assembly的基本语法规则和用法(建议英文阅读能力较强的同学直接阅读本文参考资料中推荐的技术文章 ^_^).        注意:由于gcc采用AT&T风格的汇编语法(与Intel Syntax相对应,二者的区别参见这里),因此,本文涉及到的汇编代码均以AT&T Syntax为准. 1. 基本语法规则    

Linux上机笔记(2)

在VI下编译运行C++ vi  1.cpp   (创建cpp文件名) i     (进入insert模式开始编辑) 输入完成代码后按Esc 键 退出 然后按 Shift +:输入wq   (保存并退出) g++  1.cpp  -o  1   (编译1.cpp文件,编译后的可运行文件名为1) ./1     运行结果 系统调用 fork()   系统每调用一次会产生一个新的进程 getpid()  该系统调用进程返回本进程的pid getppid()   该系统调用进程返回本进程的父进程的pid