精华内容
下载资源
问答
  • fork()函数用于从已存在的进程创建一个新进程。新进程称为子进程,而园进程称为父进程。使用fork()函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程的上下文、代码段、进程...
  • Linux中fork()函数创建进程

    千次阅读 2019-05-09 22:05:09
    Linux系统中学习fork函数创建进程前言一.准备工作二.任务三.感想 前言    最近学习到操作系统原理中的进程同步的知识点时,为了加深对进程的了解,就实践了一下在Linux系统中fork()函数的使用。 一.准备工作 ...

    Linux系统中学习fork函数创建进程

    前言

       最近学习到操作系统原理中的进程同步的知识点时,为了加深对进程的了解,就实践了一下在Linux系统中fork()函数的使用。

    一.准备工作

       装有Linux系统的电脑
       以下过程都默认对Linux系统的基本命令有一定的了解,像cd,mkdir 等命令都比较熟悉,然后基本上会用vim编辑器

    Linux系统版本不限,虚拟机中也行!

    二.任务

    任务1:

       编写一段程序,使用系统调用fork()创建两个进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程输出不同的内容。试观察记录屏幕上的显示结果,并分析原因。
       以下的显示结果中 wzm 为我的用户名!
       1.打开终端,切换到root用户
        sudo su
       2.新建一个文件夹命名为process,用来存放学习过程中用到的文件
        mkdir process
       3.新建一个文件夹命名为test1,存放任务1用到的C程序文件
        mkdir test1
       4.建一个C程序 test1.c
        vim test1.c

    上述过程中我的文件夹已存在,我就直接使用cd命令切换进去的!!!
       5.向test1.c中写入

    在这里插入图片描述
    代码段如下

     #include<unistd.h>                                                                                                                                       
      2 #include<stdio.h>
      3 
      4 int main(){
      5      pid_t fpid1,fpid2;  //fpid表示fork函数返回的值                                                                 
      6      fpid1=fork(); //进程1                                                
      7      if(fpid1 < 0){
      8          printf("error in fork!\n");
      9      }else if(fpid1 == 0){
     10          printf("I am the child1 process, my process id is %d\n", getpid());
     11      }else{
     12          printf("I am the parent1 process, my process id is %d\n", getpid());
     13 
     14      }
     15 
     16 
     17      fpid2=fork(); //进程2
     18      if(fpid2 < 0){
     19          printf("error in fork!\n");
     20      }else if(fpid2 == 0){
     21          printf("I am the child2 process, my process id is %d\n", getpid());
     22 
     23      }else{
     24          printf("I am the parent2 process, my process id is %d\n", getpid());
     25 
     26      }
     27 
     28      return 0;
     29 }

       6.编译运行
       gcc test1.c
       ./a.out

       运行结果如下:
    在这里插入图片描述
       不难发现上述程序执行之后,进程2中的语句被输出了两遍,分析原因如下:
       fork()函数:一个现有进程通过调用fork()函数可以创建一个新进程
       fork()函数被调用一次,能够返回两次,返回值有三种:
       1.进程创建出现错误,返回一个负值;
       2.在子进程中,返回值为0;
       3.在父进程中,返回值为新创建的子进程的ID;
       因此可以通过返回值来判断当前进程是父进程还是子进程。

       而关于多次运行出现的顺序不同则是因为:在调用fork()函数之后,父进程与子进程之间的执行顺序是不确定的,这个取决于内核的调度算法。
    而将test1.c的内容改为如下:显示的数目又将不一样在这里插入图片描述
      因此在创建了多个新进程的情况下,一定要注意是只让父进程创建新进程还是之前的父进程和子进程都创建新进程!!!

    任务2:

       修改上述程序,每一个进程循环显示一句话。子进程显示’daughter …‘及’son …’,父进程显示’parent …’,观察结果,分析原因。
       1.打开终端,切换到root用户,切换到process目录下
       sudo su
       cd /home/wzm/process
       2.新建文件夹test2,并在其中建test2.c
       mkdir test2
       cd /home/wzm/process/test2
       vim test2.c
       3.向test2.c中写入在这里插入图片描述

      1 #include<unistd.h>                                                                                                                                       
      2 #include<stdio.h>
      3 
      4 int main(){
      5      pid_t fpid1,fpid2;  //fpid表示fork函数返回的值                                                                 
      6      fpid1=fork(); //子进程1                                                
      7      if(fpid1 < 0){
      8          printf("error in fork!\n");
      9      }else if(fpid1 == 0){
     10          printf("I am the son process, my process id is %d\n", getpid());
     11      }else{
     12          fpid2=fork(); //子进程2
     13          if(fpid2 < 0){
     14              printf("error in fork!\n");
     15          }else if(fpid2 == 0){
     16              printf("I am the daughter process, my process id is %d\n", getpid());
     17          }else{
     18              printf("I am the parent process, my process id is %d\n", getpid());
     19          }
     20      }
     21 
     22      return 0;
     23 }
    

       4.编译运行
       gcc test2.c
       ./a.out

       运行结果如下:

    在这里插入图片描述
       不难发现父程序与子程序之间的执行顺序仍然是不确定的
       任务二的原理与任务一的原理很相似

    任务3:

       再调用exec()用新的程序替换该子进程的内容,并利用wait()来控制进程执行顺序。调用exit()使子进程结束。
       1.打开终端,切换到root用户,切换到process目录下
       sudo su
       cd /home/wzm/process
       2.新建文件夹test3,并在其中建test2.c
       mkdir test3
       cd /home/wzm/process/test3
       vim test3.c
       3.向test3.c中写入在这里插入图片描述

      1 #include<stdio.h>                                                                                                                                        
      2 #include<unistd.h>
      3 #include<stdlib.h>
      4 #include<sys/types.h>
      5 #include<sys/wait.h>
      6 void main(){
      7     int pid;
      8     pid=fork();
      9     switch(pid){
     10         case -1:{
     11                     //表示创建失败
     12                     printf("error in fork!\n");
     13                     exit(1);
     14                 }
     15         case 0:{
     16                    //子进程
     17                    execl("/bin/date","date",NULL);
     18                    printf("error in execl");
     19                    exit(1);
     20                }
     21         default:{
     22                     //父进程
     23                     wait(NULL);
     24                     printf("command date completed!\n");
     25                     exit(0);
     26                 }
     27     }
     28 }

       4.编译运行
       gcc test3.c
       ./a.out

       运行结果如下:

    在这里插入图片描述
       exec是一组函数的统称,在这里我用的是execl()函数,exec()函数用来替代fork()函数新创建的子进程执行。
       执行成功的话就不会返回,进程结束;
       执行失败的话会返回错误信息,继续执行后面的代码,并且之后必须的用exit()函数来让子进程退出。

       这里我用来替代子进程的程序的功能是执行命令date,在Linux系统中date命令只是简简单单地用来显示目前的时间信息。

    三.感想

       作为一个Linux系统新手,刚开始看fork()函数时也是很懵逼的,也是看了好多博客才逐渐明白的,上面的解释可能写得不是很清楚,自己Linux系统的学习之路还很遥远,但是慢慢来慢慢练,一天了解几个命令总会用熟的。最后很感谢那些将自己的心得体会分享的博客主们!!!

    展开全文
  • 主要介绍了使用C语言的fork()函数Linux创建进程的实例讲解,fork在父进程下创建出的子进程可以与父进程一起来多进程运行同一个程序,需要的朋友可以参考下
  • 本文将介绍如何使用 fork/vfork 系统调用来创建进程并使用 exec 族函数在新进程中执行任务。 fork 系统调用 要创建一个进程,最基本的系统调用是 fork: # include pid_t fork(void); pid_t vfork(void); 调用 ...
  • fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的...
  • linux进程创建函数简介

    千次阅读 2018-05-19 16:53:00
    原文地址:https://blog.csdn.net/u013013553/article/details/42031793 Linux创建进程和执行所创建的进程分为2个阶段。 第一个阶段是创建。父进程首先复制子进程,所复制出来的子进程拥有自己的任务结构体和...

       原文地址:https://blog.csdn.net/u013013553/article/details/42031793   

        Linux将创建进程和执行所创建的进程分为2个阶段。

        第一个阶段是创建。父进程首先复制子进程,所复制出来的子进程拥有自己的任务结构体和系统堆栈,除此之外所有资源都与父进程共享。Linux提供两种方式复制子进程:一个是fork(),另外一个是clone()。fork()函数复制时将父进程的所以资源都通过复制数据结构进行了复制,然后传递给子进程,所以fork()函数不带参数;clone()函数则是将部分父进程的资源的数据结构进行复制,复制哪些资源是可选择的,这个可以通过参数设定,所以clone()函数带参数,没有复制的资源可以通过指针共享给子进程。

        

    Clone()函数的声明如下:

    int clone(int (*fn)(void *), void*child_stack, int flags, void *arg)

    其中,fn为函数指针,此指针指向一个函数体,即想要创建进程的静态程序;

    child_stack为给子进程分配系统堆栈的指针;

    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线程标准,子进程与父进程共享相同的线程群

    fork()可以看成是完全版的clone(),而clone()克隆的只是fork()的一部分。

       

            第二个阶段就是所创建进程的执行。子进程创建完后一般都会走自己的路。Linux为了子进程能做自己的事特意提供了一个系统调用execve(),用以执行一个可执行程序的映像,这个映像以文件形式存在(这句话其实就是说用execve()可以调用一个可执行程序,因为这个可执行程序就在磁盘上,所有是以文件形式存在的,而映像是说已经编译链接好了的,只要调入内存就可以执行,一般为二进制文件)。vfork创建的子进程要先于父进程执行,子进程执行时,父进程处于挂起状态,子进程执行完,唤醒父进程。


    展开全文
  • Linux中,创建一个新进程的唯一方法是由某个已存在的进程调用 fork 或 vfork 函数,被创建的新进程称为子进程(child process),已存在的进程称为父进程(father process)。 1. fork函数基础 #include <...

    在Linux中,创建一个新进程的唯一方法是由某个已存在的进程调用 fork 或 vfork 函数,被创建的新进程称为子进程(child process),已存在的进程称为父进程(father process)。

    1. fork 函数基础

    fork 函数实质是一个系统调用,作用是创建一个新的进程,当一个进程调用它,完成后就出现两个几乎一模一样的进程, 其中由 fork 创建的新进程被称为子进程,原来的进程称为父进程。子进程是父进程的一个拷贝,即子进程从父进程得到了数据段和堆栈段的拷贝,这些需要分配新的内存;对于只读的代码段,通常使用共享内存的方式访问。

    用户通常在有如下需求的时候使用 fork 函数:

    • 一个进程希望复制自身,使得父子进程能同时执行不同段的代码,通常来说这种应用会涉及网络服务:父进程等待远端的一个请求或者应答,当收到这个请求或者应答的时候调用 fork 创建一个子进程来完成处理,而自己继续等待远端的请求或者应答。
    • 进程想执行另外一个程序, 例如在 Shell 中调用用户所生成的应用程序。

    fork 函数的标准调用格式:

    #include <unistd.h>
    pid_t fork(void);
    

    fork 函数没有参数,被调用一次,但返回两次:

    • 对于父进程而言:函数的返回值是子进程的进程标识符,因为一个进程的子进程可以多于一个,所以没有一个函数使一个进程可以获得其所有子进程的进程标识符,必须通过这种方式来收集。
    • 对于子进程而言:函数的返回值是0,一个进程只有一个父进程,所以子进程总是可以调用 getppid 获得其父进程的进程标识符,所以在这里不需要返回父进程的进程标识符。
    • 如果出错:返回值为“-1”。

    可以通过 fork 函数的返回值来分辨父进程和子进程。

    2. 子进程和父进程共享的数据空间

    当 fork 函数返回后,子进程和父进程都从调用 fork 函数的下一条语句开始执行,但是父进程或子进程哪个先执行是随机的,取决于具体的调度算法,如果需要确定让其中一个先运行,可以使用 sleep 等函数让其中一个“休眠”一段时间,但是这个时间长度是不确定的。

    通常来说,fork 所创建的子进程将会从父进程中拷贝父进程的数据空间、堆和堆栈,并且和父进程一起共享正文段,子进程所拷贝的仅仅是一个副本,和父进程的相应部分是完全独立的。

    3. 子进程和父进程共享的文件

    调用 fork 函数之后子进程会复制父进程的相应内存空间,父进程中所有打开的文件描述符也会被复制到子进程中,此时父进程和子进程的每个打开的文件描述符会共享同一个文件表项。

    父进程和子进程共享文件的示意图。

    在这里插入图片描述
    fork 所创建的子进程和父进程共享同一个文件的偏移量,此时如果父进程和子进程同时对同一个文件进行写操作且没有任何形式的同步操作,会出现写文件的混乱。

    4. 创建多个子进程

    在 Linux 中可以使用 fork 函数创建多个子进程,以下为一段使用 while 循环调用 fork 函数无限制创建子进程的代码。

    #include <unistd.h>
    int main(int agrc,char *argv[])
    {
        while(1)
        {
            fork();
        }
    }
    

    如果运行以上代码对应的可执行文件,由于无限制的创建进程导致硬件资源被分配光,Linux 会很快进入“死机状态”。

    展开全文
  • LinuxLinux进程创建与管理

    万次阅读 多人点赞 2018-07-27 19:21:29
    Linux系统中,除了系统启动之后的第一个进程由系统来创建,其余的进程都必须由已存在的进程创建,新创建进程叫做子进程,而创建进程进程叫做父进程。那个在系统启动及完成初始化之后,Linux自动创建进程...

    在Linux系统中,除了系统启动之后的第一个进程由系统来创建,其余的进程都必须由已存在的进程来创建,新创建的进程叫做子进程,而创建子进程的进程叫做父进程。那个在系统启动及完成初始化之后,Linux自动创建的进程叫做根进程。根进程是Linux中所有进程的祖宗,其余进程都是根进程的子孙。具有同一个父进程的进程叫做兄弟进程。

    Linux进程创建的过程示意图如下所示:

     

    子进程的创建

    在Linux中,父进程以分裂的方式来创建子进程,创建一个子进程的系统调用叫做fork()。

    系统调用fork()

    为了在一个进程中分裂出子进程,Linux提供了一个系统调用fork()。这里所说的分裂,实际上是一种复制。因为在系统中表示一个进程的实体是进程控制块,创建新进程的主要工作就是要创建一个新控制块,而创建一个新控制块最简单的方法就是复制。

    当然,这里的复制并不是完全复制,因为父进程控制块中某些项的内容必须按照子进程的特性来修改,例如进程的标识、状态等。另外,子进程控制块还必须要有表示自己父进程的域和私有空间,例如数据空间、用户堆栈等。下面的两张图就表示父进程和相对应的子进程的内存映射:

    稍微介绍一下进程地址空间:

    进程的数据区也就是未初始化的数据(.bss)、已初始化的数据(.data);进程的栈区也就是进程的用户栈和堆;进程程序代码就是进程的程序文件(.text);除此之外还有进程的系统堆栈区。可见下面的这张更详细的介绍图:

    例子:

    #include <stdio.h>
    
    int count1=0;
    int main(void)
    {
        int pid;
        int count2=0;
        count1++;
        count2++;
        printf("count1=%d,count2=%d\n",count1,count2);
    
        pid=fork();
        count1++;
        count2++;
        printf("count1=%d,count2=%d\n",count1,count2);
        printf("pid=%d\n"pid);
    
        return 0;
    }

    程序运行结果为:

    由此可知:

    • 函数fork()却是分裂出了两个进程:因为自函数fork()之后执行了两遍之后的代码(先子进程一次,后父进程一次)。同时,这也证明了父进程和子进程运行的是同一个程序,也正是这个理由,系统并未在内存中给子进程配置独立的程序运行空间,而只是简单地将程序指针指向父进程的代码;
    • 两个进程具有各自的数据区和用户堆栈,在函数fork()生成子进程时,将父进程数据区和用户堆栈的内容分别复制给了子进程。同时,接下来的内容,父进程和子进程都是对自己的数据区和堆栈中的内容进行修改运算了。

    其实,在父进程中调用fork()之后会产生两种结果:一种为分裂子进程失败,另一种就是分裂子进程成功。如果fork()失败,则返回-1,;否则会出现父进程和子进程两个进程,在子进程中fork()返回0,在父进程中fork()返回子进程的ID。系统调用fork()工作流程示意图如下:

    也就是说,在fork()函数之前需要确认内核中有足够的资源来完成。如果资源满足要求,则内核slab分配器在相应的缓冲区中构造子进程的进程控制块,并将父进程控制块中的全部成员都复制到子进程的控制块,然后再把子进程控制块必须的私有数据改成子进程的数据。当fork()返回到用户空间之前,向子进程的内核栈中压入返回值0,而向父进程内核堆栈压入子进程的pid。最后进行一次进程调度,决定是运行子进程还是父进程。

    显然,为了能够使子程序和父程序执行不同的代码,在fork()之后应该根据fork()的返回值使用分支结构来组成程序代码。例如:

    #include <stdio.h>
    #include <sys/types.h>
    
    int main(void)
    {
        pid_t pid;
    
        pid = fork();
        if(pid < 0){
            ...                //打印fork()失败信息
        }else if(pid == 0){
            ...                //子进程代码
        }else{
            ...                //父进程代码
        }
    
        return 0;
    }

    在代码中获得当前进程pid的函数为:getpid();

    在代码中获得当前进程父进程pid的函数为:getppid()。

    这里需要注明一点:父子进程的调度的顺序是由调度器决定的,与进程的创建顺序无关。

    关于子进程的时间片

    与UCOSIII不同,Linux在大多数情况下是按时间片来进行进程调度的。当某一个进程所拥有的时间片用完之后,内核会立即剥夺它的运行权,停止它的执行。那么当子进程被创建出来之后,这个子进程的初始时间片应该是多大呢?

    Linux规定,如果父进程只是如之前那样简单地创建了一个子进程,那么系统会将父进程剩余的时间片分成两份,一份留给父进程,另一份作为子进程的时间片。因为之前的这种简单方式下,父子进程使用的是同一个代码,还没有成为两个真正的各自独立的进程,所以没有资格享用两份时间片。

     

    与进程相关的系统调用

    函数execv()

    为了在程序运行中能够加载并运行一个可执行文件,Linux提供了系统调用execv()。其原型为:

    int execv(const char* path, char* const argv[]);

    其中,参数path为可执行文件路径,argv[]为命令行参数。

    如果一个进程调用了execv(),那么该函数便会把函数参数path所指定的可执行文件加载到进程的用户内存空间,并覆盖掉原文件,然后便运行这个新加载的可执行文件。

    在实际应用中,通常调用execv()的都是子进程。人们之所以创建一个子进程,其目的就是执行一个与父进程代码不同的程序,而系统调用execv()就是子进程执行一个新程序的手段之一。子进程调用execv()之后,系统会立即为子进程加载可执行文件分配私有程序内存空间,从此子进程也成为一个真正的进程。

    如果说子进程是父进程的“儿子”,那么子进程在调用execv()之前,它所具有的单独用户堆栈和数据区也仅相当于它的私有“房间”;但因它还没有自己的“住房”,因此也只能寄住在“父亲”家,而不能“自立门户”,尽管它有自己的“户口”(进程控制块)。

    调用execv()后,父进程与子进程存储结构的示意图如下:

    与上文刚fork()的内存映像图相比,刚调用fork()之后,父子共同使用同一块程序代码;而调用execv()之后,子程序拥有了自己的程序代码区。

    例子:

    #include <stdio.h>
    #include <sys/types.h>
    
    int main(void)
    {
        pid_t pid;
    
        if(!(pid=fork())){
            execv("./hello.o",NULL);
        }else {
            printf("my pif is %d\n", getpid());
        }
    
        return 0;
    }

    而可执行文件./hello.o可以编写一个.c程序再进行编译获得。

    函数execv()其实是Linux的exec函数族成员之一,该函数族一共有5个函数和1个系统调用,分别是:

    int execl(const char* path, const char* arg, ...);
    int execlp(const char* file, const char* arg, ...);
    int execle(const char* path, const char* arg, ..., char* const envp[]);
    int execv(const char* path, const char* argv[]);
    int execvp(const char* file, const char* argv[]);
    int execve(const char* path, const char* argv[], char* const envp[]);

    其中,只有execv()是真正意义上的系统调用,其他的都是在此基础上经过包装的库函数。

    execv函数族的作用是,根据指定的文件名找到可执行文件,并将其关联到调用exec族函数的进程,从而使进程执行该可执行文件。简单地说,就是用execv族函数加载的程序文件代替该进程原来的程序文件。

    与一般的函数不同,exec族函数执行成功后一般不会返回调用点,因为它运行了一个新的程序,进程的代码段、数据段和堆栈等都已经被新的数据所取代,只留下进程ID等一些表面信息仍保持原样,虽然还是旧的躯壳,但是其实质内容已经全部变化了。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

    系统调用wait()

    虽然子进程调用函数execv()之后拥有自己的内存空间,称为一个真正的进程,但由于子进程毕竟由父进程所创建,所以按照计算机技术中谁创建谁负责销毁的惯例,父进程需要在子进程结束之后释放子进程所占用的系统资源。

    为实现上述目标,当子进程运行结束后,系统会向该子进程的父进程发出一个信息,请求父进程释放子进程所占用的系统资源。但是,父进程并没有准确的把握一定结束于子进程结束之后,那么为了保证完成为子进程释放资源的任务,父进程应该调用系统调用wait()。

    如果一个进程调用了系统调用wait(),那么进程就立即进入等待状态(也叫阻塞状态),一直等到系统为本进程发送一个消息。在处理父进程与子进程的关系上,那就是在等待某个子进程已经退出的信息;如果父进程得到了这个信息,父进程就会在处理子进程的“后事”之后才会继续运行。

    也就是说,wait()函数功能是:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

    如果父进程先于子进程结束进程,则子进程会因为失去父进程而成为“孤儿进程”。在Linux中,如果一个进程变成了“孤儿进程”,那么这个进程将以系统在初始化时创建的init进程为父进程。也就是说,Linux中的所有“孤儿进程”以init进程为“养父”,init进程负责将“孤儿进程”结束后的资源释放任务。

    这里区分一下僵尸进程和孤儿进程:

    • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作;
    • 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程(也就是进程为中止状态,僵死状态)。

    Linix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

    参考文章:孤儿进程与僵尸进程[总结]

    系统调用exit()

    系统调用exit()用来终结一个进程,通常这个系统调用会在一些与进程控制有关的系统调用中被调用。

    如果一个进程调用exit(),那么这个进程会立即退出运行,并负责释放被中止进程除了进程控制块之外的各种内核数据结构。这种只剩下“身份证”的进程叫做“僵尸进程”,其进程控制块域state的值为TASK_ZOMBLE。

    一个因调用exec函数族函数而执行新的可执行文件的子进程,当进程执行结束时,便会执行系统调用exit()而使自己成为一个“僵尸进程”,这也正是父进程要负责销毁子进程的进程控制块的原因。

    另外,exit()执行到最后,将调用进程调度函数schedule()进行一次进程调度。

    这里区分一下exit()和_exit()函数:

    exit()定义在stdlib.h文件,_exit()定义在unistd.h文件中。同时两者的步骤也是不一样的:

    具体体现在哪里呢?

    在Linux标准库中,有一套称为高级I/O函数,例如我们所熟知的printf、fopen、fread、fwrite都在此列,它们也被称为缓冲I/O。其特征是对应每一个打开的文件,都存在一个缓冲区,在每次读文件时会多读若干条记录,这样下次读文件时就可以直接从内存的缓冲区去读。在每次写文件时也会先写入缓冲区,当缓冲区写满,或者我们fflush()手动的刷新缓冲区,或者遇到\n、EOF这样的结束符,才会把对应的数据从缓冲区写入到文件中。这样的好处是大大增加的文件的读写的速度,因为我们都知道磁盘上的读写速度是很慢的,但这也为我们编程带来了一点麻烦,例如有些数据,我们认为已经写入了文件,但实际上它们很可能存在在缓冲区中。

    也就是说,_exit()函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核的各种数据结构;exit()函数则在这些基础上做了一些小动作,在执行退出之前还加了若干道工序。exit()函数与_exit()函数的最大区别在于exit()函数在调用exit  系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件。也就是图中的“清理I/O缓冲”。

    参考文章:exit函数和_exit函数的区别

    系统调用vfork()

    vfork()是Linux提供的另一个用来生成一个子进程的系统调用。

    与fork()不同,vfork()并不把父进程全部复制到子进程中,而只是用用赋值指针的方法使子进程与父进程的资源实现共享。由于函数vfork()生成的子进程与父进程共享同一块内存空间,因此实质上vfork()创建的是一个线程,但习惯上人们还是叫它子进程。

    用vfork()创建子进程且调用execve()之后,子进程的内存映像如下所示:

    这里区分一下fork()与vfork():

    • fork():子进程拷贝父进程的数据段,代码段 。vfork():子进程与父进程共享数据段 ;
    • fork():父子进程的执行次序不确定 。vfork():保证子进程先运行,在调用execve()或exit()之前,与父进程数据是共享的,在它调用execve()或exit()之后,父进程才可能被调度运行。

    注意:由于vfork()保证子进程先运行,在它调用execve()或exit()之后,父进程才可能被调度运行。如果在调用这两个函数之前,子进程依赖于父进程的进一步动作,则会导致死锁。 

     

    内核中的进程和线程

    操作系统是一个先于其他程序运行的程序,那么当系统中除了操作系统之外没有其他的进程时怎么办?同时,系统中的所有用户进程必须由父进程来创建,那么“祖宗进程”是什么呢?

    前一个问题由Linux的进程0来解决,后一个问题由Linux的进程1来解决。其实,前者是一个内核进程,而后者先是一个内核进程,后来又变为用户进程。

    内核线程及其创建

    Linux实现线程的机制非常独特,它没有另外定义线程的数据结构和调度算法,只不过在创建时不为其分配单独的内存空间。如果线程运行在用户空间,那么它就是用户线程;如果运行在内核空间,那么它就是内核线程。并且,无论是线程还是进程,Linux都用同一个调度器对它们进行调度。

    但是Linux却单独提供了一个用于创建内核线程的函数kernel_thread()。该函数的原型如下:

    int kernel_thread(int (* fn)(void *), void* arg, unsigned long flags);

    其中,fn指向线程代码;arg为线程代码所需的入口参数。

    内核线程周期性执行,通常用来完成一些需要对内核幕后完成的任务,例如磁盘高速缓存的刷新、网络连接的维护和页面的交换等,所以它们也叫做内核任务。

    进程0

    计算机启动之后,首先进行Linux的引导和加载,在Linux内核加载之后,初始化函数start_kernel()会立即创建一个内核线程。因为Linux对线程和进程没有太严格的区分,所以就把这个由初始化函数创建的线程叫做进程0。

    进程0的执行代码时内核函数cpu_idel(),该函数中只有一条hlt(暂停)指令;也就是说,这个进程什么工作也没做,所以也叫做空闲进程。该空闲进程的PCB叫做init_task(),当系统没有可运行的其它进程时,调度器才会选择进程0来运行。

    进程1

    进程1也叫做init进程,它是内核初始化时创建的第2个内核线程,其运行代码为内核函数init()。

    该函数首先创建kswapd()等4个与内存管理有关的内核线程。接下来,在内核的init()中调用execve()系统调用装入另一个需要在用户空间运行的init函数代码,于是进程1就变成一个普通的进程。它是用户空间的第一个进程,所以它就成了其他用户进程的根进程。

    只要系统,init进程就永不中止,它负责创建和监控操作系统外层所有进程的活动。

    守护进程

    守护进程是不受终端控制并在后台运行的进程。Linux使用了很多守护进程,在后台做一些经常性的注入定期进行页交换之类的例行工作。

    守护进程一般可以通过以下方式启动:

    • 在系统启动时由启动脚本启动,这些启动脚本通常放在/etc/rc.d目录下;
    • 利用inetd超级服务器启动,大部分网络服务都是这样启动的,如ftp、telnet等;
    • 由cron定时启动以及在终端用nohup启动的进程也是守护进程。

     

    展开全文
  • linux进程创建常用函数

    千次阅读 2012-12-12 21:53:42
    获取进程ID #include #include pid_t getpid(void) //获取本进程ID pid_t getppid(void) //获取父进程ID ...创建进程 ...//功能:创建进程 ...父进程中,fork返回新创建的子进程的PID //2.子进程中,f
  • 主要介绍了深入解读Linux进程函数fork(),vfork(),execX(),分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
  • Linux创建进程

    万次阅读 2018-11-03 22:17:55
    创建进程Linux的下是由父进程来完成的,创建完成的新进程是子进程。子进程有三种执行顺序的可能性: 父进程和子进程并发执行; 子进程先执行,父进程等待子进程执行完毕。 父进程先执行,子进程后执行。 另外...
  • 掌握创建进程的方法 实验内容 11.子进程和父进程的创建 ? ?2.编写附件中的程序实例 ? ?3.撰写实验报告 实验原理 1原型 #include <unistd.h> pid_t fork(void; 在linux中fork函数时非常重要的函数它从已存在进程中...
  • Linux进程创建Fork()函数分析

    千次阅读 2017-11-02 08:25:49
    一个进程包括代码、数据、进程控制块、堆栈等资源。进程在运行时还有自身的状态,这个...Linux下,用于创建进程函数是fork()函数。fork()函数创建的“子进程是父进程的一个完整拷贝”,也就是说子进程和父进程基本
  • linux系统如何使用fork函数创建进程

    万次阅读 多人点赞 2017-09-16 15:48:00
    linux系统中创建进程有两种方式:一是由操作系统创建,二是由父进程创建进程(通常为子进程)。系统调用函数fork()是创建一个新进程的唯一方式,fork()函数Linux系统中一个比较特殊的函数,其一次调用会有两...
  • fork函数Linux下一个近乎专有的C语言函数,因为使用时需要调用unistd.h这个头文件,这里我们就在Linux环境下举例讲解C语言的fork()函数创建进程的用法,需要的朋友可以参考下
  • Linux创建进程的fork()函数

    千次阅读 2016-06-30 23:49:22
    今天总结一下Linux进程创建... 在Linux中,fork函数的功能就是在一个进程创建一个新的进程,当前调用fork函数进程就是产生的新进程的父进程,新进程在一下也称为子进程。在新进程生成之后就会在系统中开始执行。
  • Linux创建进程的三种方式及特点

    万次阅读 多人点赞 2017-08-12 14:39:06
    linux源码中这三个调用的执行过程是执行fork(),vfork(),clone()时,通过一个系统调用表映射到sys_fork(),sys_vfork(),sys_clone(),再在这三个函数中去调用do_fork()去做具体的创建进程工作。  fork   fork...
  • 这是我们操作系统课的一个实验 一、实验目的: ...进行Linux(可选)下的创建进程实验并提供截图和源代码 三、实验准备 首先学习了一下fork()的用法和特点,这几点是要注意的: 1、进...
  • Linux 创建目录函数mkdir

    千次阅读 2021-02-25 18:15:57
    一、Linux 创建目录函数mkdir的mode设置问题 函数原型: #include <sys/stat.h> int mkdir(const char *path, mode_t mode); 参数: path是目录名 mode是目录权限 返回值: 返回0 表示成功, 返回 -1...
  • mkdir函数用于创建目录。格式如下:#include<sys>#include<sys>#include<unistd>...这个新创建目录的用户ID被设置为调用进程的有效用户ID,其组则为父目录的组ID或者进程的有效组ID。 若调用成功,mkdir将更新该目录的s
  • 进程概述 进程:就是进行中的程序 程序:存放指令的程序文件,存放在磁盘上,固定不变的,保存着指令的有序集合。 程序执行过程:将程序从硬盘导入到内存,内存上分为代码区、静态变量区、堆栈区等等 文本区:...
  • linux创建进程fork的方法步骤

    千次阅读 2016-12-07 18:40:46
    fork创建进程 函数原型如下 #include// 必须引入头文件,使用fork函数的时候,必须包含这个头文件,否则,系统找不到fork函数 pid_t fork(void); //void代表没有任何形式参数 父进程与子进程 1.掌握概念,什么...
  • 要了解子进程,了解fork首先要知道什么是进程,具体参见博客linux 什么是进程进程状态转换、 我首先来看一个程序代码 #include&amp;amp;amp;amp;lt;stdlib.h&amp;amp;amp;amp;gt; #include&amp;...
  • linux C 进程 创建进程 详解

    千次阅读 2017-11-25 21:46:55
    首先介绍一下进程创建方法,fork与vfork函数 fork函数  一个进程调用fork()函数后,系统先给新的进程分配资源,包括代码、数据和分配给进程的资源,然后把原来的进程的所有值都复制到新的新进程中,只有...
  • 五.linux进程之exec族函数进程关系

    千次阅读 2019-04-27 16:38:24
    type=note 目录 一、exec族函数及实战 1、exec族函数包含如下函数: 2、函数使用示例: 二、进程状态和system函数 1、进程的5种状态 2、进程各种状态之间的转换图 3、system...
  • Linux创建进程的三种方式

    千次阅读 2019-02-21 14:19:57
    linux源码中这三个调用的执行过程是执行fork(),vfork(),clone()时,通过一个系统调用表映射到sys_fork(),sys_vfork(),sys_clone(),再在这三个函数中去调用do_fork()去做具体的创建进程工作。  fork   fork...
  • linux进程创造 - 创建进程API及过程

    千次阅读 2016-11-20 00:56:20
    1. 创建进程函数API1.1 创建进程fork()fork的翻译为“叉子,分叉”,其实在unix编程中,我们来创建进程的时候是深有体会的,感觉创建一个进程就像是走到了一个岔路口,父进程和子进程在叉路口分道扬镳,所以我想这...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 238,502
精华内容 95,400
关键字:

创建进程的函数linux

linux 订阅