进程派生——fork~~~

跌跌撞撞,unix也看到了进程管理,顿时觉得高大上了。然而面对第一个系统调用fork,这英语发音实在是蹩脚,差点就读成~~。

在unix里面,对于任何一个进程,都有一个唯一表示的进程ID(pid)当然除了进程ID这个标识之外还有很多其他属性:父进程ID(ppid),进程实际用户ID,进程有效用户ID,进程实际用户组ID,进程有效用户组ID。对于这些属性,可以使用下列函数获取

#include<unistd.h>

pid_t getpid(void);
pid_t getppid(void);
uid_t getuid(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);

值得注意的是,这些函数都没有返回出错。

函数fork

#include<unistd.h>

pid_t fork(void);

很奇特,它的返回值会有两个,对于子进程返回0,对于父进程,返回子进程ID,如果出错返回-1。

派生出来新的子进程会继承父进程的好多东西,什么实际用户ID啊,等等好多,如果你有一本unix编程的书籍,上面肯定会有这些,这里就不罗嗦了,这里关键探讨一下书里面一些比较难理解的地方。

#include<stdio.h>
#include<unistd.h>

//测试fork的用法

int main(int argc,char* argv[])
{
    printf("before fork~\n");
    pid_t pid=fork();
    if(pid == 0)   //fork函数返回0说明是执行子进程
    {
        printf("this is child\n");
    }
    else if(pid > 0)     //fork函数返回大于0 说明返回的是子进程的pid号
    {
        printf("this parent\n");
    }
    return 0;                    //两个进程的执行顺序是不一定的,
                                //这取决于系统的调度
}

我们来看看上面的程序,虽然代码少,但是大有乾坤。首先是printf,对于到标准输出流的IO而言,是行缓冲。所以在行缓冲满了,或者碰到\n刷新缓冲区的时候,缓冲区的数据才会被冲洗。然而fork函数出来的子进程是会继承父进程的缓冲区的。所以如果缓冲区里的数据没被刷新,这意味着子进程也会从父进程那里得到一份"before fork~",那么程序在执行的时候会很神奇的输出两个before
fork~,当然,我们上面的代码只会输出一次,因为它用\n冲刷了缓冲区。

还有一点是,如果程序执行的时候被重定向到了文件里,那么情况就有点不大一样了,因为这个时候是全缓冲,即使碰到\n也不会刷新缓冲区,这样子肯定可以看到两个before fork~!

还有一个情况是,对于父进程用open打开一个文件之后,fork出来的子进程是和父进程拥有统一个文件表的,这意味着他们可以改变对方的文件偏移量,就好像是用dup函数复制而来的一样!

最后,对于两个进程,他们的执行顺序是不一样的,这决定于系统的调度,,所以你多次执行这个程序,你会发现,每次执行的结果都不尽相同。如果想控制进程执行的顺序,就得使用wait函数或者后面的信号了。那个到时候再说~

那么,我们知道,我们一般派生出一个新的进程 都是为了执行不同的任务,所以一般fork子进程之后都会马上调用exec函数(以后会碰到)执行新的代码,所以为了提高效率,往往会用一个vfork的函数,vfork函数和fork函数的不同之处在于:fork函数会复制一份父进程的数据空间。而vfork不会。(简单讲就是fork之后的子进程不会改变父进程的变量值,但是vfork会),理解这句话的办法就是举个栗子~

#include<stdio.h>
#include<unistd.h>

int main(int argc,char* argv[])
{
    printf("before fork~\n");
    int test=1;
    pid_t pid=fork();
    if(0==pid)
    {
        printf("this is child\n");
        test++;
    }
    else if(pid>0)
    {
        printf("this is parent\n");
        printf("test=%d\n",test);
    }
}

我们可以看到,在子进程的代码片里,我们执行了test++语句,但是在父进程里输出的时候test还是等于1,这意味着子进程和父进程有各自的数据空间,两者的改变不会影响彼此,但是vfork就不会了,vfork的思想是我们信任程序fork之后就要使用exec执行其他程序了,所以没必要为子进程开辟一块空间来保存父进程的变量。所以vfork出来的子进程和父进程是共享统一块内存空间的。所以这个时候如果你在子进程里改变了父进程的变量值,会影响到父进程的值。

最后讲一下fork的情况,对于fork函数,系统也不会真的就开辟一块完整的空间来给子进程用以复制父进程的内容,而是才是写时复制(copy-on-write)的方法,就是父进程和子进程仍然共享数据段,栈,堆空间,只是系统会把这段空间的权限变为只读,如果父进程或者子进程试图修改某个空间,这个时候系统才会复制仅仅是那个空间的数据。以此来达到效率的提高。

时间: 2024-10-12 06:50:59

进程派生——fork~~~的相关文章

模拟shell ( 进程函数:fork(),execvp(),wait() )

shell是一个管理进程和运行进程的程序,下面我们就通过模拟一个shell程序这个实例来更好地认识认识在Linux/Unix系统中,进程的创建和结束,以及父子进程之间的一些关系.接下来先贴上源代码的中命令的读取部分: numargs=0; while(numargs<MAXARGS){ printf("Arg[%d]?",numargs); if(fgets(argbuf,ARGLEN,stdin)&&*argbuf!='\n') arglist[numargs+

进程创建fork 、getpid 、getppid

创建一个进程: /******************** *功能:创建一个新进程,通过复制当前进程(和父进程一样),执行的位置都一样 *pid_t:一般是16位的有符号的整型数,不够可以typedef * 返回值:非0 :“在父进程中”--->返回子进程编号 * 0 :”在子进程中“--->返回0 * -1 :失败,并设置errno * ****************/ pid_t fork(void); 父子进程区别:fork的返回值不一样 pid不同 ppid不同 未决信号(未响应的信

Python进程分支fork和exec详解

在python中,任务并发一种方式是通过进程分支来实现的.在linux系统在,通过fork()方法来实现进程分支. fork()调用后会创建一个新的子进程,这个子进程是原父进程的副本.子进程可以独立父进程外运行. fork()是一个很特殊的方法,一次调用,两次返回. fork()它会返回2个值,一个值为0,表示在子进程返回;另外一个值为非0,表示在父进程中返回子进程ID. 以下只能在linux中运行,不能在window下运行. 进程分支fork() 实例如下: #!/usr/bin/python

linux c学习笔记----进程创建(fork,wait,waitpid)

1.pid_t fork(); (1)当一个进程调用了fork 以后,系统会创建一个子进程.这个子进程和父进程不同的地方只有他的进程ID 和父进程ID,其他的都是一样.就象符进程克隆(clone)自己一样. (2)为了区分父进程和子进程,我们必须跟踪fork 的返回值. 当fork 掉用失败的时候(内存不足或者是用户的最大进程数已到)fork 返回-1,否则fork 的返回值有重要的作用.对于父进程fork 返回子进程的ID,而对于fork 子进程返回0.我 们就是根据这个返回值来区分父子进程的

进程控制fork vfork

主要函数: fork 用于创建一个新进程 exit 用于终止进程 exec 用于执行一个程序 wait 将父进程挂起,等待子进程结束 getpid 获取当前进程的进程ID nice 改变进程的优先级 --------------------------------- 孤儿进程: 如果一个子进程的父进程先于子进程结束,子进程就成为一个孤儿进程,他由init进程收养,成为init进程的子进程. #include <stdio.h> #include <stdlib.h> #includ

进程控制fork与vfork

1. 进程标识符 在前面进程描述一章节里已经介绍过进程的两个基本标识符pid和ppid,现在将详细介绍进程的其他标识符. 每个进程都有非负的整形表示唯一的进程ID.一个进程终止后,其进程ID就可以再次使用了.如下是一个典型进程的ID及其类型和功能. 进程名:swapper (交换进程),进程ID:0,类型:系统进程,作用:它是内核的一部分,不执行磁盘上的程序,是调度进程. 进程名:init(init进程),进程ID:1,类型:用户进程 ,作用:永远不会终止,启动系统,读取系统初始化的文件. 进程

进程创建fork函数的调试[1]

1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 #include<stdlib.h> 5 int main() 6 { 7 int count=0; 8 pid_t pid;//此时只有一个进程 9 pid=fork();//此时创建了两个进程 10 if(pid<0) 11 { 12 printf("error in fork!"); 13 ex

Linux进程管理——fork()和写时复制

写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: ·      为子进程的页表分配页面 ·      为子进程的页分配页面 ·      初始化子进程的页表 ·      把父进程的页复制到子进程相应的页中 创建一个地址空间的这种方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容.在大多数情况下,这样做常常是毫无意义的,因为许多

修改linux用户的可以打开的最大文件数和进程数(fork: retry: Resource temporarily unavailable)

今天,jenkins发布应用时,遇到一个报错,如下: /etc/profile:fork: retry: Resource temporarily unavailable Google了一下,网上大家大部分说原因可能是,用户的进程数或者文件打开数,达到了限制.按照这个思路,我去查问题,发现用户的进程数为200多,文件打开数为1300多 而通过ulimit–a,看到 openfiles                      (-n) 1024 maxuser processes