Linux 的多进程

一.进程的概念

  什么是进程?进程这个概念是针对系统而言,对于我们来说,我们面对的概念是程序。当输入指令执行一个程序的时候,系统将启动一个进程。

  进程顾名思义就是正在进行的程序,或者说是正在运行的程序,

  Linux 下一个进程在内存中有三部分数据: "代码段“ 、“堆栈段”和“数据段”。

1.代码段

  代码段就是存放了程序代码

2.堆栈段

  存放的是程序的返回地址、程序的参数以及程序的局部变量。

3.数据段

  存放的是程序的全局变量,常数以及动态数据分配的数据空间(比如用 new 函数分配的空间)

4.注意事项

  如果系统同时运行多个相同的程序,它们的“代码段”是相同的,但是“堆栈段” 和 “数据段” 是不同的(相同的程序,处理的数据同)

二.进程的编号

1.查看进程

  (1)ps 查看当前终端的进程,ps 是 Process Status 的缩写。
在这里插入图片描述
  (2)ps -ef 查看系统的全部进程
在这里插入图片描述
  (3) ps -ef|more 查看系统的全部进程,结果分页显示

  (4)ps-ef|grep 服务端 :查看系统全部的进程,然后从结果集中过滤出包含“服务端”这个词的结果。

在这里插入图片描述

2.一些常见的标识

  UID : 启动进程的操作系统用户;UID,是用户身份证明(User Identification)的缩写。

  PID : 进程编号;Process Identification 的缩写

  PPID : 进程的父进程的编号;Parent Process Identification

  C :CPU 使用的资源百分比

  STIME:进程启动的时间 ;S——start

  TTY:进程所属的终端;是系统进程的话,就是一个问号“?”

  终端是一种字符型设备。它有多种类型,通常使用tty来简称各种类型的终端设备。tty是Teletype的缩写。Teletype是最早出现的一种终端 设备,很象电传打字机(或者说就是),是由Teletype公司生产的。设备名放在特殊文件目录/dev/下。

  TIME:使用掉的CPU的时间

  CMD:执行的是什么指令;command

3.getpid 库函数

  getpid 库函数的功能是获取本程序运行时进程的编号。

  (1)函数的声明:

pid_t getpid();

  函数没有参数,返回值是进程的编号,pid_t 就是 typedef int pid_t。

  (2)函数的代码

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  printf("本程序的进程编号是:%d\n",getpid());
  sleep(30);    //是为了方便查看进程
  }

  1)执行程序
在这里插入图片描述

  2)查看正在执行的程序

在这里插入图片描述

4.注意细节

  (1)进程的编号是系统动态分配的,相同的程序在不同的时间执行,进程的编号是不同的。
在这里插入图片描述
  getpid 这个程序在上文的执行时的编号是 77173,到了这里执行时,编号是77192

  (2)进程的编号会循环使用,但是在同一时间,进程的编号是唯一的,也就是说:不管任何时间,系统不可能存在两个编号相同的进程。

在这里插入图片描述

三.多进程

  fork 在英文中的意思是“分叉”,在一个进程中,如果使用了 fork 函数,就产生了另一进程,于是进程就“分叉” 了,所以这个名字取得很形象。有了多进程就可以提高CPU的利用率。
在这里插入图片描述

1.函数声明

pid_t fork();

  fork 函数用于产生一个新的进程,函数的返回值 pid_t 是一个整数,在父进程中,返回值是子进程;在子进程中,返回值是0。

2.函数代码

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  printf("本程序的进程编号是“:%d\n",getpid());
  sleep(40);
  int ipid=fork();
  printf("fork ok\n");
  sleep(1);    //等待进程的生成

  printf("pid=%d\n",ipid);

  if( ipid!=0)  printf("父进程的编号是:%d\n",getpid());
  else  printf("子进程的编号是:%d\n",getpid());

  sleep(30);   //方便查看
  }

(1)运行效果:

在这里插入图片描述
(2)查进程编号
在这里插入图片描述
  这里确实是生成了两个进程,一个是 77750,另一个是77753,其中 77753 是 77750 的子进程。

3.运行结果分析

在这里插入图片描述

  (1)这里的 “fork ok” 为什么打印了两次?看看代码只有一行啊。

 printf("fork ok\n");

  (2)为什么父进程和子进程编号都打印了?这里可是 if else 语句,不应该只是打印其中一个进程的编号吗?

 if( ipid!=0)  printf("父进程的编号是:%d\n",getpid());
  else  printf("子进程的编号是:%d\n",getpid());

  这到底发生了什么,调用了 fork() 函数 ,因为 fork() 函数创建了一个新的进程(子进程)与原来的进程(父进程)一模一样。还记得前面的影分身之术吗?复制了一模一样的。

在这里插入图片描述
  或者说,像下面这个图一样。当程序执行到 fork 函数的时候,就分叉(创建了一个子进程),这两个进程都是要走完剩下的代码的,如果正常运行。
在这里插入图片描述

  子进程和父进程使用相同的代码段;子进程拷贝了父进程的堆栈段和数据段。子进程一旦可是运行,它就复制了父进程的一切数据,然后各自运行,相互之间没有影响。

  也就是说从下面这段代码之后,

int ipid = fork();

这个程序就有两个进程了。两个进程都会执行,剩下的代码,所以才会有上面的运行结果。

四.fork() 函数的返回值

  fork() 函数对返回值做了特别地处理,调用 fork 函数之后,在子进程的中 fork() 的返回值是 0 。在父进程中 fork() 的返回值仍是原来进程的编号(父进程)。也就是说这个函数会返回两个值。

  我们就可以通过 fork() 的返回值来区分父进程和子进程,然执行不同的代码

1.子进程和父进程执行不同代码示例

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void fatherFun()
{
  printf("我是老子,我喜欢孩子他娘\n");
}

void childFun()
{
  printf("我是孩子,我喜欢西施\n");
}

int main()
{
  if( fork() >0 )
    {
       printf("这是父进程,将调用fatherFun()\n");
            fatherFun();
    }
  else
    {
       printf("这是子进程,将调用childFun()\n");
       childFun();
    }

   sleep(2);
   printf("我执行完\n");
   sleep(2);
}

运行效果:

在这里插入图片描述

2.父子进程分别操作变量

  在上文已经提到,子进程拷贝了父进程的堆栈段和数据段,也就是说:在父进程中定义的变量子程序中会复制一份副本,fork 之后,父子进程对变量的操作,不会影响到彼此。

  (1)测试代码

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  int a=520;
  if( fork()>0 )
    {
       a=521;
       printf("父进程:我将521赋值给 a,a=%d\n",a);
    }
   
  else
    {
       a=250;
       printf("子进程:我将250赋值给 a,a=%d\n",a);
    }
    
    sleep(1);
}

  (2)运行效果

在这里插入图片描述

五.Linux 常识补充

  在Linux系统中运行任何一个命令,比如 ls ,当运行一个命令时,系统会生成一个子进程来运行 ls 这个命令对应的程序。

  用 ps -ef|more 来查看,Linux系统有两个最原始的进程,
在这里插入图片描述

然后由进程 2 去生成其他的进程
在这里插入图片描述

六.fork() 生成多个子进程

1.由父进程生成 10 个子进程

1.1 错误代码

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  int pid[10];
  printf("本程序的编号是:%d\n\n",getpid());
 
  for(int ii=0;ii<10;ii++)
    {
       if( fork()==0)
 
         {
           printf("我是第%d个子进程,pid[%d]=%d\n",ii,ii,getpid());
         }
     }

  sleep(1);
}

1.2 运行结果

在这里插入图片描述

1.3 错误分析

  为什么会这样呢?我只是想生成10个子进程而已,但是这不止10个了。我先将这程序的流程图画出来就知道了。

在这里插入图片描述

  从代码和流程图可以知道,当 fork()>0 时,程序会执行下一次循环,再生成一个子进程。当 fork()==0 时,程序会打印生成一个子进程,然后执行下一次循环。也就是说不打印生成子进程程序会执行下一次循环,在生成一个子进程;打印的话,也会执行下一次循环,再生成一个子进程。一次循环不止生成一个子程序。他们两个重复了。

1.4 改进方法

  (1)解决方法就是让其中一个生成了一个子进程就跳出整个循环,不要参与其中。改进的代码如下,当 fork() ==0 时打印了一次后就马上跳出整个循环,不要再执行下一次循环再生成一个子进程了。当 fork()>0 时,不打印,马上执行下一次循环,生成一个子进程。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  int pid[10];
  printf("本程序的编号是:%d\n\n",getpid());
 
  for(int ii=0;ii<10;ii++)
    {
       if( fork()==0)
 
         {
           printf("我是第%d个子进程,pid[%d]=%d\n",ii,ii,getpid());
           return 0;    //跳出整个循环
         }
     }
   continue;    //fork() >0,执行下一次循环,生成一个子进程
  sleep(1);
}

(2)运行结果:
在这里插入图片描述

(3)用 ps -ef|grep 10个 查看进程编号,及子进程是由哪个进程生成的。

在这里插入图片描述

1.5 子进程的生成顺序

  为什么子进程的生成顺序不一样,因为 fork() 生成子进程的时间太短了,有的子进程生成的时间点几乎是一致的,哪个先跑完(跑得更快)剩下的代码都不一定的。

  只要在生成了一个子进程后,就设置一个等待的时间,让这个子进程先跑完就可以解决了。
在这里插入图片描述

  运行结果:
在这里插入图片描述

2.由子进程生成多代子进程

(1)代码

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
  for (int ii =0;ii<10;ii++)
    {
       if(fork()>0)   return 0;
       printf("第%d 代,编号为:%d\n",ii+1,getpid());
    }
  sleep(100);
}
~              

(2)运行效果

在这里插入图片描述

(3)用命令 ps -ef|grep 查看
在这里插入图片描述
为什么这里不能看到10个子进程对应的父进程?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>