linux之多进程

1. 进程的基本概念

2. ps查看进程

  • ps -ef 查看当前所有进程
  • ps 查看当前中断的进程
  • ps -ef | grep book 查看当前所有进程。从结果中过滤出包含"book"的记录

3. getpid库函数

获取程序运行时进程的编号
pid_t getpid();

4. fork

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

子进程拷贝了父进程的堆栈段和数据段,在父进程中定义的变量,子进程中会复制一个副本,fork之后,子进程对变量的操作不会影响父进程,父进程对变量的操作不会影响子进程。

5. 多进程的应用

6. 僵尸进程

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源。

一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)

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

解决方法:
如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。

signal(SIGCHLD, SIG_IGN);//忽略子进程退出的信号,避免产生僵尸进程
/*
 *  程序名:demo48.cpp,此程序演示采用freecplus框架的CTcpServer类实现socket通信的服务端。
 *  作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_freecplus.h"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

//父进程退出调用代码
void FathEXIT(int sig)
{
  if(sig > 0){//把信号屏蔽掉
    signal(sig, SIG_IGN);
    signal(SIGINT, SIG_IGN);
    signal(SIGTREM, SIG_IGN);
    printf("catching the signal(%d).\n", sig);
  }

  kill(0, 15);//通知子进程,退出 发送给当前进程所属进程组里的所有进程

  printf("父进程退出!\n");

  exit(0);
}

void ChldEXIT(int sig)
{
  if(sig > 0){
    signal(sig, SIG_IGN);
    signal(SIGINT, SIG_IGN);
    signal(SIGTREM, SIG_IGN);
    printf("catching the signal(%d).\n", sig);
  }

  printf("子进程退出!\n");

  exit(0);
}

int main(int argc,char *argv[])
{
  //关闭全部的信号
  for(int ii=0; ii<100; ++ii)
  {
    signal(ii, SIG_IGN);
  }
  //设置信号,在shell状态下可用"kill + 进程号"正常终止线程
  signal(SIGINT, FathExit);//ctrl+C
  signal(SIGTERM, FathExit);//kill


  signal(SIGCHLD, SIG_IGN);//忽略子进程退出的信号,避免产生僵尸进程
  //如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
  if (argc!=2)
  {
    printf("Using:./demo48 port\nExample:./demo48 5005\n\n"); return -1;
  }

  CTcpServer TcpServer;   // 创建服务端对象。
 
  if (TcpServer.InitServer(atoi(argv[1]))==false) // 初始化TcpServer的通信端口。
  {
    printf("TcpServer.InitServer(%s) failed.\n",argv[1]); return -1;
  }
 
  while(true)
  {
    if (TcpServer.Accept()==false)   // 等待客户端连接。
    {
      printf("TcpServer.Accept() failed.\n"); return -1;
    }
    if(fork() > 0)
    {
        TcpServer.CloseClient();//关闭父进程的客户端socket
        continue;//核心代码
    }
    //子进程设置信号
    signal(SIGINT, FathExit);//ctrl+C
    signal(SIGTERM, FathExit);//kill

    TcpServer.CloseListen();//关闭子进程的监听socket
    
    printf("client(%s)connected\n",TcpServer.GetIP());
 
    char strbuffer[1024];  // 存放数据的缓冲区。
 
    while (true)
    {
      memset(strbuffer,0,sizeof(strbuffer));
      //if (TcpServer.Read(strbuffer,300)==false) break; // 接收客户端发过来的请求报文。
      if (TcpServer.Read(strbuffer,10)==false) break; // 接收客户端发过来的请求报文。
      printf("receve:%s\n",strbuffer);
 
      strcat(strbuffer,"ok");      // 在客户端的报文后加上"ok"。
      printf("send:%s\n",strbuffer);
      if (TcpServer.Write(strbuffer)==false) break;     // 向客户端回应报文。
    }
 
    printf("client disconnect\n");    // 程序直接退出,析构函数会释放资源。
    exit(0);
  }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值