精华内容
下载资源
问答
  • 以下属于进程的特性是
    万次阅读
    2017-10-09 19:54:52

    进程是具有一定独立功能的程序关于一个数据集合的一次运行活动。

    进程具有以下主要特性:
    (1)并发性: 可以与其它进程一道在宏观上同时向前推进。
    (2)动态性: 进程是执行中的程序。此外进程的动态性还体现在如下两个方面:首先,进程是动态产生、动态消亡的;其次,在进程的生存期内,其状态处于经常性的动态变化之中。
    (3)独立性: 进程是调度的基本单位,它可以获得处理机并参与并发执行。
    (4)交往性: 进程在运行过程中可能会与其它进程发生直接或间接的相互作用。
    (5)异步性: 每个进程都以其相对独立、不可预知的速度向前推进。
    (6)结构性: 每个进程有一个控制块PCB。

    进程和程序的相同点: 程序是构成进程的组成部分之一,一个进程存在的目的就是执行其所对应的程序,如果没有程序,进程就失去了其存在的意义。

    进程与程序的差别:

    (1)程序是静态的,而进程是动态的;

    (2)程序可以写在纸上或在某一存储介质上长期保存,而进程具有生存期,创建后存在,撤销后消亡;

    (3)一个程序可以对应多个进程,但一个进程只能对应一个程序;例如,一组学生在一个分时系统中做C语言实习,他们都需要使用C语言的编译程序对其源程序进行编译,为此每个学生都需要有一个进程,这些进程都运行C语言的编译程序。 另外,一个程序的多次执行也分别对应不同的进程。

    更多相关内容
  • fork创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的task_struct结构和pid,但却复制父进程其它所有的资源。例如,要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些...

    一、创建子进程的方法

    1、fork

    fork创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的task_struct结构和pid,但却复制父进程其它所有的资源。例如,要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方。所以,这一步所做的是复制。这样得到的子进程独立于父进程, 具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,共享内存等机制, 另外通过fork创建子进程,需要将上面描述的每种资源都复制一个副本。
    这样看来,fork是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程fork出一个子进程后,其子进程仅仅是为了调用exec执行另一个可执行文件,那么在fork过程中对于虚存空间的复制将是一个多余的过程。但由于现在Linux中是采取了copy-on-write(COW写时复制)技术,为了降低开销,fork最初并不会真的产生两个不同的拷贝,因为在那个时候,大量的数据其实完全是一样的。写时复制是在推迟真正的数据拷贝。若后来确实发生了写入,那意味着parent和child的数据不一致了,于是产生复制动作,每个进程拿到属于自己的那一份,这样就可以降低系统调用的开销。所以有了写时复制后呢,vfork的现实意义就不大了。
    fork()调用执行一次返回两个值,对于父进程,fork函数返回子程序的进程号,而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。
    在fork之后,子进程和父进程都会继续执行fork调用之后的指令。子进程是父进程的副本。它将获得父进程的数据空间,堆和栈的副本,这些都是副本,父子进程并不共享这部分的内存。也就是说,子进程对父进程中的同名变量进行修改并不会影响其在父进程中的值。但是父子进程又共享一些东西,简单说来就是程序的正文段。正文段存放着由cpu执行的机器指令,通常是read-only的。

    2、vfork

    vfork系统调用不同于fork,用vfork创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程。

    其次,子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。为了避免这些问题,需要确保一旦调用vfork(),子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用exit函数。子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续。

    但此处有一点要注意的是用vfork()创建的子进程必须显示调用exit()来结束,否则子进程将不能结束,而fork()则不存在这个情况。

    vfork也是在父进程中返回子进程的进程号,在子进程中返回0。
    用vfork创建子进程后,父进程会被阻塞直到子进程调用exec(exec,将一个新的可执行文件载入到地址空间并执行之。)或exit。vfork的好处是在子进程被创建后往往仅仅是为了调用exec执行另一个程序,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的
    ,因此通过vfork共享内存可以减少不必要的开销

    再次强调:在使用vfork()时,必须在子进程中调用exit()函数调用,否则会出现:__new_exitfn: Assertion `l != ((void *)0)’ failed 错误!而且,现在这个函数已经很少使用了!

    3、clone

    目前正在使用的,是fork的升级版。

    系统调用fork()和vfork()是无参数的,而clone()则带有参数。fork()是全部复制,vfork()是共享内存,而clone()是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的clone_flags决决定。
    fork不对父子进程的执行次序进行任何限制,fork返回后,子进程和父进程都从调用fork函数的下一条语句开始行,但父子进程运行顺序是不定的,它取决于内核的调度算法;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制;clone中由标志CLONE_VFORK来决定子进程在执行时父进程是阻塞还是运行,若没有设置该标志,则父子进程同时运行,设置了该标志,则父进程挂起,直到子进程结束为止。

    二,子进程的特性

    • 文件描述符共享,socket, pipe 等。
    • 子进程与父进程共享程序正文段。
    • 子进程拥有父进程的数据空间和堆、栈的副本,注意是副本,不是共享,写时复制。
    • 父进程和子进程将继续执行fork之后的程序代码。

    以下示例只是短暂演示,故无需回收子进程。

    示例1:输出信息

    $pid = pcntl_fork();
    if ($pid > 0) {
        echo "father" . PHP_EOL;
    } else if (0 == $pid) {
        echo "child" . PHP_EOL;
    } else {
        echo "fork失败" . PHP_EOL;
    }
    

    在这里插入图片描述
    可见子进程的输出信息也打印在当前会话窗口上了。stdout标准输出默认是打印到会话窗口,而输出到哪个窗口,要看该进程是由哪个会话创建的。
    在这里插入图片描述
    因此子进程和父进程的输出都会打印在同一个窗口上,即 pts/3 。

    示例2:父子进程都会继续向下执行

    $pid = pcntl_fork();
    if ($pid > 0) {
        echo "father" . PHP_EOL;
    } else if (0 == $pid) {
        echo "child" . PHP_EOL;
    } else {
        echo "fork失败" . PHP_EOL;
    }
    
    echo getmypid(), PHP_EOL;
    

    在这里插入图片描述
    示例3:子进程与父进程共享程序正文段

    $n = 1;
    
    $pid = pcntl_fork();
    if ($pid > 0) {
        echo "father" . PHP_EOL;
        $n += 1;
    } else if (0 == $pid) {
        echo "child" . PHP_EOL;
        $n += 2;
    } else {
        echo "fork失败" . PHP_EOL;
    }
    echo $n, PHP_EOL;
    

    在这里插入图片描述
    示例4:文件描述符共享,以socket为例。

    $socket = stream_socket_client("tcp://127.0.0.1:8888", $errno, $errstr, 30);
    
    $pid = pcntl_fork();
    if ($pid > 0) {
        stream_socket_sendto($socket, 'father msg');
        sleep(2);
    } else if (0 == $pid) {
        stream_socket_sendto($socket, 'child msg');
    } else {
        echo "fork失败" . PHP_EOL;
    }
    
    $str = stream_socket_recvfrom($socket, 8129);
    echo getmypid(), ' get msg: ', $str, PHP_EOL;
    
    sleep(10);
    

    server代码:https://gitee.com/phprao/socket/blob/master/server/socketServerEpoll.php
    在这里插入图片描述
    TCP服务端信息
    在这里插入图片描述

    socket共享,意味着一方的读,写,关闭都会影响父子关系中另一方的行为。
    服务端收到两条信息,因此会响应两条信息过来,stream_socket_recvfrom函数每次只会读取一条,看哪个进程先执行到这里。我们应用中绝大部分的通讯都是基于socket,所以这点需要注意。

    展开全文
  • 进程通信方法的特点以及使用场景

    千次阅读 2021-02-25 15:44:09
    文章目录前言一、共享存储二、消息传递三、管道...高级通信方法主要有以下三类。 一、共享存储   共享存储是指多个进程共享一块内存,是专门用来解决不同进程之间的通信问题的,由于是直接对内存进行数据传输操作


    前言

      进程通信是指进程之间的信息交换,根据传输数据的大小,可以把进程通信分为低级通信机制(效率低,例如信号量机制);高级通信机制(OS封装了细节,直接高效使用原语)

      进程通信是指进程之间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的。高级通信方法主要有以下三类。


    一、共享存储

      共享存储是指多个进程共享一块内存,是专门用来解决不同进程之间的通信问题的,由于是直接对内存进行数据传输操作,所以是速度最快的IPC(inter-process communication)方式,因为是共享内存,所以需要配合信号量机制实现同步。

    二、消息传递

    消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

      在消息传递系统中,进程间的数据交换是以格式化的消息(Message)为单位的。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。
      1)直接通信方式。发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。
      2)间接通信方式。发送进程把消息发送到某个中间实体,接收进程从中间实体取得消息。这种中间实体一般称为信箱,这种通信方式又称信箱通信方式

    三、管道通信

      所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又名pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接受管道输出的接收进程(即读进程)则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。
      1、无名管道,以下简称“管道”。管道具有以下两种局限性:1)管道为半双工的;2)管道只能在具有公共祖先的两个进程之间使用,通常,一个管道由一个进程创建,这个管道就能在父进程和子进程之间使用了。
      2、FIFO,即命名管道,与无名管道不同的是,其可以在不相关的程序之间交换数据。FIFO有两种用途:1)shell命令使用FIFO将数据从一条管道传送到另一条管道时,无须创建中间的临时文件;2)客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。

    四、几种IPC方法优缺点比较

    1、如果用户传递的信息较少,或者只是为了触发某些行为。信号是一种简洁有效的通信方式。但若是进程间要求传递的信息量较大或者存在数据交换的要求,就需要考虑别的通信方式了。

    2、无名管道有名管道的区别在于单向通信以及有关联的进程

    3、消息队列允许任意进程通过共享队列来进行进程间通信。并由系统调用函数来实现消息发送和接收之间的同步。从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用相对方便
      但是消息队列中信息的复制需要耗费CPU时间,不适宜信息量大或频繁操作的场合。

    4、消息队列与管道方式的区别在于,消息队列可以实现多对多,并需要在内存中实现,而管道可以在内存或磁盘上实现。

    5、共享内存无须复制,信息量大是其最大的优势。但是需要考虑同步问题

    转载内容:“几种IPC方法优缺点比较” 作者:wenmingxing 链接:https://www.jianshu.com/p/4989c35c9475 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • 系统事件非常多,总结恶意软件的特点,针对其特点进行针对 性的监控: 生成一些程序文件,并激活成为进程,想每次开机都能自动运 行,需要修改注册表。 使用ProcessMonitor监控进程活动 因此,重点关注以下事件: 壹...
  • 进程和子进程到底是如何执行的?

     20170724_父进程和子进程到底是如何执行的?


    这篇博客写的还不错。保留以供继续学习。

    问题:子进程父进程哪个先执行:【转】关于 fork 和父子进程的理解 + 【转】


    关于 fork 和父子进程的理解   (http://blog.163.com/kingore@126/blog/static/8540871920097300284862/)

    代码:
    #include <unistd.h> 
    #include <sys/types.h> 

    main () 

            pid_t pid; 
            pid=fork(); 

            if (pid < 0) 
                    printf("error in fork!"); 
            else if (pid == 0) 
                    printf("i am the child process, my process id is %d\n",getpid()); 
            else 
                    printf("i am the parent process, my process id is %d\n",getpid()); 
    }


    结果是 
    [root@localhost c]# ./a.out 
    i am the child process, my process id is 4286 
    i am the parent process, my process id is 4285 
    -------------------------------------------------------------
    Q: 我就想不到为什么两行都打印出来了,在我想来,不管pid是多少,都应该只有一行才对

    A: 这里的if和else不是以前理解的选择分支。fork后产生的子进程和父进程并行运行的.
    这种理解是不正确的。if 和 else 还是选择分支。 主要的原因是,fork() 函数调用一次,返回两次。两次返回的区别是:子进程的返回值是0,父进程返回值为新子进程的进程ID。

    --------------------------------------------------------------

    Q: 但是只有一个pid=fork(); 呀,fork()返回的第二次值在什么时候赋给pid呢

    A: pid这个变量是有两个的, 父进程一个, 子进程一个。

    ---------------------------------------------------------------

    要搞清楚fork的执行过程,就必须先讲清楚操作系统中的“进程(process)”概念。一个进程,主要包含三个元素: 

    o. 一个可以执行的程序; 
    o. 和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等); 
    o. 程序的执行上下文(execution context)。 

    不 妨简单理解为,一个进程表示的,就是一个可执行程序的一次执行过程中的一个状态。操作系统对进程的管理,典型的情况,是通过进程表完成的。进程表中的每一 个表项,记录的是当前操作系统中一个进程的情况。对于单 CPU的情况而言,每一特定时刻只有一个进程占用 CPU,但是系统中可能同时存在多个活动的(等待执行或继续执行的)进程。

    一个称为“程序计数器(program counter, pc)”的寄存器,指出当前占用 CPU的进程要执行的下一条指令的位置。 

    当 分给某个进程的 CPU时间已经用完,操作系统将该进程相关的寄存器的值,保存到该进程在进程表中对应的表项里面;把将要接替这个进程占用 CPU的那个进程的上下文,从进程表中读出,并更新相应的寄存器(这个过程称为“上下文交换(process context switch)”,实际的上下文交换需要涉及到更多的数据,那和fork无关,不再多说,主要要记住程序寄存器pc指出程序当前已经执行到哪里,是进程上 下文的重要内容,换出 CPU的进程要保存这个寄存器的值,换入CPU的进程,也要根据进程表中保存的本进程执行上下文信息,更新这个寄存器)。 

    好了,有这些概念打底,可以说fork了。当你的程序执行到下面的语句: 
    pid=fork(); 
    操 作系统创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序;上下文和数据,绝大部分就是原 进程(父进程)的拷贝,但它们是两个相互独立的进程!此时程序寄存器pc,在父、子进程的上下文中都声称,这个进程目前执行到fork调用即将返回(此时 子进程不占有CPU,子进程的pc不是真正保存在寄存器中,而是作为进程上下文保存在进程表中的对应表项内)。问题是怎么返回,在父子进程中就分道扬镳。 

    父进程继续执行,操作系统对fork的实现,使这个调用在父进程中返回刚刚创建的子进程的pid(一个正整数),所以下面的if语句中pid<0, pid==0的两个分支都不会执行。所以输出i am the parent process... 

    子 进程在之后的某个时候得到调度,它的上下文被换入,占据 CPU,操作系统对fork的实现,使得子进程中fork调用返回0。所以在这个进程(注意这不是父进程了哦,虽然是同一个程序,但是这是同一个程序的另 外一次执行,在操作系统中这次执行是由另外一个进程表示的,从执行的角度说和父进程相互独立)中pid=0。这个进程继续执行的过程中,if语句中 pid<0不满足,但是pid==0是true。所以输出i am the child process... 

    我想你比较困惑的就是,为什么看上去程序中互斥的两个分支都被执行了。在一个程序的一次执行中,这当然是不可能的;但是你看到的两行输出是来自两个进程,这两个进程来自同一个程序的两次执行。 

    我的天,不知道说明白了没……

    --------------------------------------------------------------------------------

    到底哪个进程执行在先,这个和操作系统的调度算法等等很多因素相关。我觉得理解上的困难,关键在于为什么会有两个输出,而不是谁先谁后。

    -------------------------------------------------------------------------------

    fork 之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互 相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,但只有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中 fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。 
    可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因。 
    至于那一个最先运行,可能与操作系统有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决。




    ****************************************************************************************************************************

    linux中fork函数及子进程父进程进程先后(http://blog.csdn.net/wu_zf/article/details/7640970)

    一、fork入门知识

         一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
        一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

         我们来看一个例子:

    [cpp]  view plain copy
    1. #include <unistd.h>  
    2. #include <stdio.h>   
    3. int main ()   
    4. {   
    5.     pid_t fpid; //fpid表示fork函数返回的值  
    6.     int count=0;  
    7.     fpid=fork();   
    8.     if (fpid < 0)   
    9.         printf("error in fork!");   
    10.     else if (fpid == 0) {  
    11.         printf("i am the child process, my process id is %d/n",getpid());   
    12.         printf("我是爹的儿子/n");//对某些人来说中文看着更直白。  
    13.         count++;  
    14.     }  
    15.     else {  
    16.         printf("i am the parent process, my process id is %d/n",getpid());   
    17.         printf("我是孩子他爹/n");  
    18.         count++;  
    19.     }  
    20.     printf("统计结果是: %d/n",count);  
    21.     return 0;  
    22. }  

         运行结果是:
        i am the child process, my process id is 5574
        我是爹的儿子
        统计结果是: 1
        i am the parent process, my process id is 5573
        我是孩子他爹
        统计结果是: 1

        在语句fpid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是if(fpid<0)……
        为什么两个进程的fpid不同呢,这与fork函数的特性有关。

        fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
        1)在父进程中,fork返回新创建子进程的进程ID;
        2)在子进程中,fork返回0;
        3)如果出现错误,fork返回一个负值;

        在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

        引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.
        fork出错可能有两种原因:
        1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
        2)系统内存不足,这时errno的值被设置为ENOMEM。
        创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
        每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。
       
     fork执行完毕后,出现两个进程,

        有人说两个进程的内容完全一样啊,怎么打印的结果不一样啊,那是因为判断条件的原因,上面列举的只是进程的代码和指令,还有变量啊。
        执行完fork后,进程1的变量为count=0,fpid!=0(父进程)。进程2的变量为count=0,fpid=0(子进程),这两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。可以说,我们就是通过fpid来识别和操作父子进程的。
        还有人可能疑惑为什么不是从#include处开始复制代码的,这是因为fork是把进程当前的情况拷贝一份,执行fork时,进程已经执行完了int count=0;fork只拷贝下一个要执行的代码到新的进程。

    二、fork进阶知识

        先看一份代码:

    [cpp]  view plain copy
    1. #include <unistd.h>  
    2. #include <stdio.h>  
    3. int main(void)  
    4. {  
    5.    int i=0;  
    6.    printf("i son/pa ppid pid  fpid/n");  
    7.    //ppid指当前进程的父进程pid  
    8.    //pid指当前进程的pid,  
    9.    //fpid指fork返回给当前进程的值  
    10.    for(i=0;i<2;i++){  
    11.        pid_t fpid=fork();  
    12.        if(fpid==0)  
    13.            printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
    14.        else  
    15.            printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
    16.    }  
    17.    return 0;  
    18. }  

        运行结果是:
        i son/pa ppid pid  fpid
        0 parent 2043 3224 3225
        0 child  3224 3225    0
        1 parent 2043 3224 3226
        1 parent 3224 3225 3227
        1 child     1 3227    0
        1 child     1 3226    0 

        这份代码比较有意思,我们来认真分析一下:
        第一步:在父进程中,指令执行到for循环中,i=0,接着执行fork,fork执行完后,系统中出现两个进程,分别是p3224和p3225(后面我都用pxxxx表示进程id为xxxx的进程)。可以看到父进程p3224的父进程是p2043,子进程p3225的父进程正好是p3224。我们用一个链表来表示这个关系:
        p2043->p3224->p3225 
        第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数在父进程中返向子进程id),代码内容为:

    [c-sharp]  view plain copy
    1. for(i=0;i<2;i++){  
    2.     pid_t fpid=fork();//执行完毕,i=0,fpid=3225  
    3.     if(fpid==0)  
    4.        printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
    5.     else  
    6.        printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
    7. }  
    8. return 0;  

        p3225(子进程)的变量为i=0,fpid=0(fork函数在子进程中返回0),代码内容为:

    [c-sharp]  view plain copy
    1. for(i=0;i<2;i++){  
    2.     pid_t fpid=fork();//执行完毕,i=0,fpid=0  
    3.     if(fpid==0)  
    4.        printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
    5.     else  
    6.        printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
    7. }  
    8. return 0;  

        所以打印出结果:
        0 parent 2043 3224 3225
        0 child  3224 3225    0

        第二步:假设父进程p3224先执行,当进入下一个循环时,i=1,接着执行fork,系统中又新增一个进程p3226,对于此时的父进程,p2043->p3224(当前进程)->p3226(被创建的子进程)。
        对于子进程p3225,执行完第一次循环后,i=1,接着执行fork,系统中新增一个进程p3227,对于此进程,p3224->p3225(当前进程)->p3227(被创建的子进程)。从输出可以看到p3225原来是p3224的子进程,现在变成p3227的父进程。父子是相对的,这个大家应该容易理解。只要当前进程执行了fork,该进程就变成了父进程了,就打印出了parent。
        所以打印出结果是:
        1 parent 2043 3224 3226
        1 parent 3224 3225 3227 

        第三步:第二步创建了两个进程p3226,p3227,这两个进程执行完printf函数后就结束了,因为这两个进程无法进入第三次循环,无法fork,该执行return 0;了,其他进程也是如此。
        以下是p3226,p3227打印出的结果:
        1 child     1 3227    0
        1 child     1 3226    0 

        细心的读者可能注意到p3226,p3227的父进程难道不该是p3224和p3225吗,怎么会是1呢?这里得讲到进程的创建和死亡的过程,在p3224和p3225执行完第二个循环后,main函数就该退出了,也即进程该死亡了,因为它已经做完所有事情了。p3224和p3225死亡后,p3226,p3227就没有父进程了,这在操作系统是不被允许的,所以p3226,p3227的父进程就被置为p1了,p1是永远不会死亡的,至于为什么,这里先不介绍,留到“三、fork高阶知识”讲。


        总结一下,这个程序执行的流程如下:

         这个程序最终产生了3个子进程,执行过6次printf()函数。
        我们再来看一份代码:

    [cpp]  view plain copy
    1. #include <unistd.h>  
    2. #include <stdio.h>  
    3. int main(void)  
    4. {  
    5.    int i=0;  
    6.    for(i=0;i<3;i++){  
    7.        pid_t fpid=fork();  
    8.        if(fpid==0)  
    9.            printf("son/n");  
    10.        else  
    11.            printf("father/n");  
    12.    }  
    13.    return 0;  
    14.   
    15. }  

         它的执行结果是:
        father
        son
        father
        father
        father
        father
        son
        son
        father
        son
        son
        son
        father
        son 

        这里就不做详细解释了,只做一个大概的分析。
        for        i=0         1           2
                  father     father     father
                                            son
                                son       father
                                            son
                   son       father     father
                                            son
                                son       father
                                            son
        其中每一行分别代表一个进程的运行打印结果。
        总结一下规律,对于这种N次循环的情况,执行printf函数的次数为2*(1+2+4+……+2N-1)次,创建的子进程数为1+2+4+……+2N-1个。

        (感谢gao_jiawei网友指出的错误,原本我的结论是“执行printf函数的次数为2*(1+2+4+……+2N)次,创建的子进程数为1+2+4+……+2”,这是错的)
        网上有人说N次循环产生2*(1+2+4+……+2N)个进程,这个说法是不对的,希望大家需要注意。

        数学推理见http://202.117.3.13/wordpress/?p=81(该博文的最后)。
       

        同时,大家如果想测一下一个程序中到底创建了几个子进程,最好的方法就是调用printf函数打印该进程的pid,也即调用printf("%d/n",getpid());或者通过printf("+/n");来判断产生了几个进程。

    有人想通过调用printf("+");来统计创建了几个进程,这是不妥当的。具体原因我来分析。
        老规矩,大家看一下下面的代码:

    [cpp]  view plain copy
    1. #include <unistd.h>  
    2. #include <stdio.h>  
    3. int main() {  
    4.     pid_t fpid;//fpid表示fork函数返回的值  
    5.     //printf("fork!");  
    6.     printf("fork!/n");  
    7.     fpid = fork();  
    8.     if (fpid < 0)  
    9.         printf("error in fork!");  
    10.     else if (fpid == 0)  
    11.         printf("I am the child process, my process id is %d/n", getpid());  
    12.     else  
    13.         printf("I am the parent process, my process id is %d/n", getpid());  
    14.     return 0;  
    15. }  

        执行结果如下:
        fork!
        I am the parent process, my process id is 3361
        I am the child process, my process id is 3362

               如果把语句printf("fork!/n"); 注释掉,执行printf("fork!");
        则新的程序的执行结果是:
        fork!I am the parent process, my process id is 3298
        fork!I am the child process, my process id is 3299 

        程序的唯一的区别就在于一个/n回车符号,为什么结果会相差这么大呢?
       
     这就跟printf的缓冲机制有关了,printf某些内容时,操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上。但是,只要看到有/n 则会立即刷新stdout,因此就马上能够打印了。
        运行了printf("fork!")后,“fork!”仅仅被放到了缓冲里,程序运行到fork时缓冲里面的“fork!”  被子进程复制过去了。因此在子进程度stdout缓冲里面就也有了fork! 。所以,你最终看到的会是fork!  被printf了2次!!!!
        而运行printf("fork! /n")后,“fork!”被立即打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有fork! 内容。因此你看到的结果会是fork! 被printf了1次!!!!
        所以说printf("+"); 不能正确 地反应进程的数量。
        大家看了这么多可能有点疲倦吧,不过我还得贴最后一份代码来进一步分析fork函数。

    [cpp]  view plain copy
    1. #include <stdio.h>  
    2. #include <unistd.h>  
    3. int main(int argc, char* argv[])  
    4. {  
    5.    fork();  
    6.    fork() && fork() || fork();  
    7.    fork();  
    8.    return 0;  
    9. }  

        问题是不算main这个进程自身,程序到底创建了多少个进程。
        为了解答这个问题,我们先做一下弊,先用程序验证一下,到此有多少个进程。

    [c-sharp]  view plain copy
    1. #include <stdio.h>  
    2. int main(int argc, char* argv[])  
    3. {  
    4.    fork();  
    5.    fork() && fork() || fork();  
    6.    fork();  
    7.    printf("+/n");  
    8. }  

        答案是总共20个进程,除去main进程,还有19个进程。
        我们再来仔细分析一下,为什么是还有19个进程。
        第一个fork和最后一个fork肯定是会执行的。
        主要在中间3个fork上,可以画一个图进行描述。
        这里就需要注意 && 和 || 运算符。
        A&&B,如果A=0,就没有必要继续执行&&B了;A非0,就需要继续执行&&B。
        A||B,如果A非0,就没有必要继续执行||B了,A=0,就需要继续执行||B。

        fork()对于父进程和子进程的返回值是不同的,按照上面的A&&B和A||B的分支进行画图,可以得出5个分支。

        

         加上前面的fork和最后的fork,总共4*5=20个进程,除去main主进程,就是19个进程了。

    三、fork高阶知识

            这一块我主要就fork函数讲一下操作系统进程的创建、死亡和调度等。因为时间和精力限制,我先写到这里,下次找个时间我争取把剩下的内容补齐。

    参考资料:

     

          http://blog.csdn.net/dog_in_yellow/archive/2008/01/13/2041079.aspx

          http://blog.chinaunix.net/u1/53053/showart_425189.html

          http://blog.csdn.net/saturnbj/archive/2009/06/19/4282639.aspx

          http://www.cppblog.com/zhangxu/archive/2007/12/02/37640.html

          http://www.qqread.com/linux/2010/03/y491043.html

          http://www.yuanma.org/data/2009/1103/article_3998.htm



    展开全文
  • 进程间通信

    千次阅读 多人点赞 2022-04-22 16:40:30
    了解进程间的通信方式,例如管道(匿名管道和命名管道)、共享内存、消息队列、信号量
  • 以下部分介绍进程的概念、进程状态及状态转换、描述进程属性的数据结构——进程控制块,以及对进程可实施的主要操作。 一、进程的定义 进程是具有一定独立功能的程序在某个数据集合上的一次运行活动,是系统进行资源...
  • linux中fork函数及子进程进程执行顺序

    千次阅读 多人点赞 2021-09-25 21:55:16
    fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程...
  • 操作系统之进程管理习题

    千次阅读 2021-12-11 22:19:54
    操作系统进程管理习题详解
  • Supervisor 可谓运维利器,使用 Supervisor 来管理进程,可以提高系统的高可用特性。 随着 Redis 越来越流行,越来越多的公司都使用上了 redis,因此 Redis 的进程管理就成了很多公司都需要面临的问题,本文介绍...
  • PCB(进程管理块)_百度百科

    千次阅读 2021-07-23 03:27:06
    PCB(进程管理块)语音编辑锁定上传视频为了描述控制进程的运行,系统中存放进程的管理和控制信息的数据结构称为进程控制块(PCB Process Control Block),它是进程实体的一部分,是操作系统中最重要的记录性数据结构。...
  • Linux进程管理

    千次阅读 多人点赞 2022-03-19 14:07:43
    关于进程,环境变量和程序地址空间的学习
  • 进程通信和线程详解

    千次阅读 2022-02-11 21:51:05
    2.6 进程通信 进程通信:指进程之间的信息交换 低级通信:进程间仅交换一些状态和少量数据。如:进程之间的互斥与同步
  • 山东科技大学学生课程设计 PAGE PAGE 27 设计1 进程调度算法的模拟 一设计目的 1通过编程实现进程调度算法的模拟了解进程调度的过程理解进程调度各方法的特点 二设计要求 1用语言来实现对n个进程采用不同调度算法的...
  • 【操作系统】第四话·进程和程序究竟有啥区别?

    千次阅读 多人点赞 2022-03-31 22:21:42
    ✅正确答案:A ✨✨✨我是分割线✨✨✨ 6.3 进程的组成 进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位,它由以下三部分组成:PCB、程序段、数据段。 1.什么是程序段? 程序段就是被进程...
  • Linux的进程调度算法简介

    千次阅读 2021-07-16 09:28:57
      进程调度的研究是整个操作系统理论的核心,在多进程的操作系统中,进程调度是一个全局性的、关键性的问题,它对系统的总体设计、系统的实现、功能设置以及各方面的性能都有着决定性的影响。进程运行需要各种各样...
  • 【Linux】进程间通信

    千次阅读 多人点赞 2022-05-02 18:34:17
    1. 进程间通信 1.1. 进程间通信的目的 1.2. 如何实现进程间通信 2. 管道通信 2.1. 匿名管道 2.1.1 创建匿名管道 2.1.2 . 深入理解匿名管道 2.2. 命名管道 2.2.1. 创建命名管道 3. system V 标准进程间通信 ...
  • windows进程 windows多进程编程

    千次阅读 2021-12-20 21:46:50
    这个进程好像是支持Window通用应用的进程,结束该进程后,所有的windows 通用应用(就是Metro应用)都会强行结束,翻译一下应该叫 “应用程序框架主机进程” adobe genuine software service—Adobe 正版软件服务 ...
  • 【操作系统】_7种进程调度算法

    千次阅读 2022-04-26 23:43:41
    书中一共描述了七种进程...先来先服务调度算法属于非剥夺方式。 从表面上看,这个方法对于所有进程都是公平的,并且一个进程的等待时间是可以预先估计的。但是从另一方面来说,这个方法并非公平,因为当一个大进程先到
  • 超级进程管家 v1.4.2

    2019-11-04 03:19:37
    超级进程管家是一款功能强大的、...具备了以下几个特性:1、监视进程的动态数据;2、颜色标记进程,让您一目了然;3、对有危害性的进程进行分析;4、定位查找进程的目标位置;5、自定义进程过滤,让您的系统更安全。
  • 【操作系统】第五话·进程的切换与进程控制

    千次阅读 多人点赞 2022-04-02 10:01:30
    进程在生命周期内,通常有以下5中状态,前3种是进程的基本状态: 1.运行态:进程正在处理机上运行;对于单处理机,同一时刻只有一个进程处于运行态。 2.就绪态:进程获得了除处理机以外的一切所需资源,一旦得到...
  • 详解进程同步

    千次阅读 2021-07-30 07:59:26
    进程和线程都是CPU的调度单元,所以进程同步和线程同步是一样的概念,文中不具体细分进程和线程 一、进程同步 在详解操作系统进程中,介绍过了并发进程,并发进程之间分为独立关系和交互关系,独立关系的进程分别在...
  • 进程的特征

    千次阅读 2020-04-17 10:37:44
    进程的特征: p36 进程和程序是两个截然不同的概念,除了进程具有程序所没有的PCB结构外,还具有 下面一些特征: 1. 动态性。 进程的实质是进程实体的执行过程,因此,动态性就是进程的最基本的 特征。动态性还表现...
  • Linux进程概念(精讲)

    千次阅读 多人点赞 2021-09-09 08:54:04
    文章目录基本概念描述进程-PCB组织进程查看进程通过系统调用获取进程的PID和PPID通过系统调用创建进程- fork初始进程状态运行状态-R浅度睡眠状态-S深度睡眠状态-D暂停状态-T僵尸状态-Z死亡状态-X僵尸进程僵尸进程的...
  • 特点: 孤儿进程会被系统进程收养,此时系统进程就会成为孤儿进程新的父进程,孤儿进程退出该进程会自动处理。 僵尸进程 : 子进程先于父进程退出,父进程又没有处理子进程的退出状态,此时子进程就会称为僵尸进程。...
  • 进程管理(一)--进程管理的基本概念

    千次阅读 2021-04-03 23:48:20
    对于内存管理告一段落,今天正式开始进入内存管理的章节,首先从基础学习,主要是包括进程线程基础概念篇,主要包括以下内容 为什么要引入进程的概念 进程的概念,进程和程序的联系和区别 进程控制块 进程的状态...
  • 非常详细的linux进程知识点!图文并茂
  • 进程间通信的方式总结(特点,以及code demo)

    千次阅读 多人点赞 2018-10-08 19:50:39
    进程间通信(IPC,InterProcess Communication):是指在不同进程之间传播或交换信息。 一、简单的进程间通信: 命令行:父进程通过exec函数创建子进程时可以附加一些数据。 环境变量:父进程通过exec函数创建...
  • 进程和线程的分配策略【杂记】

    千次阅读 2022-02-08 19:02:33
    进程和线程的分配策略【杂记】
  • 【Linux】进程详解一:进程概念

    千次阅读 多人点赞 2021-09-15 16:58:13
    进程状态详解6.1 查看进程的状态6.2 不同的进程状态6.3 孤儿进程6.4 僵尸进程6.5 进程状态的转化 前言 在上一讲 认识冯诺依曼体系&&初识LinuxOS 中已经学到了计算机的整体的体系结构和操作系统在计算机中起...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 329,559
精华内容 131,823
热门标签
关键字:

以下属于进程的特性是