Linux&C++(11):创建一个进程

Linux的0、1和2号进程

整个linux系统全部的进程是一个树形结构。

0号进程(系统进程)是所有进程的祖先,它创建了1号和2号进程。

(相当于是我们世界的时间线)

1号进程(systemd)负责执行内核的初始化工作和进行系统配置。

2号进程(kthreadd)负责所有内核线程的调度和管理。

如何创建一个子进程

pid_t fork(void);

使用fork函数,使用fork函数创建的进程称为子进程,子进程和父进程共同执行fork函数及fork函数之后的代码。

可以实现父子进程运行不同的代码吗?

可以,因为fork函数虽然被调用一次,但是却返回两次值,一次是子类的,默认是0,一次是父类进程,默认是子类的进程号。

        执行逻辑是,如果返回了值,就先执行后面的代码,执行完后,再接收第二个返回值,再次执行后面的代码。

        依据这个逻辑,可以利用if -else实现两种代码功能,如果fork返回值为0就是子类时,执行一种情况,如果是非0时就是父类,执行另一种情况。

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
  int bh=8;
  string message="轻舟已过万重山。";

  pid_t pid=fork();

  if (pid>0)
  { // 父进程将执行这段代码。
    sleep(1);
    cout << "父:pid=" << pid << endl;
    cout << "父:我是父亲" << bh << "号:" << message << endl;
  }
  else
  { // 子进程将执行这段代码。
    bh=3; message="两岸猿声啼不住。";
    cout << "子:pid=" << pid << endl;
    cout << "子:我是儿子" << bh << "号:" << message << endl;

因为父进程比子进程慢了1s,所以是先返回子类,然后再返回父类。

父进程和子进程的进程号相同吗?

不同,通过es - pf|grep demo可以知道。

其中,上面是父进程,下面是子进程, 第3列是父进程编号,可知子进程的父进程31895和上面相同。但归根还是不同的进程。

子进程和父进程是共享变量吗?

是共享变量,但是子进程如果将变量进行了修改,那么就不是共享变量了,此时父进程并没有受到子进程改变变量影响。因为共享的变量只是虚拟地址相同,但是物理地址不同。

僵尸进程

父进程先关闭子进程后关闭

        如果父进程比子进程先退出,子进程将被1号进程托管(这也是一种让程序在后台运行的方法)。在父进程后面加一个return 0,子进程while循环,父进程关闭后,子进程被1号进程托管。

子进程先关闭父进程后关闭

        如果子进程比父进程先退出,而父进程没有处理子进程退出的信息,那么,子进程将成为僵尸进程。

由图可知,如果父进程先走了,那么子进程就会成孤儿,查看进程就会发现<defunct>,不可用。

使用top查看当前性能也可知道有一个僵尸进程

僵尸进程的危害

因为CPU会为每一个进程都分配数据结构,如果存在僵尸进程,那么就会导致资源不会被释放。

更重要的是,僵尸进程占用了进程号,如果在系统繁忙的时候,有可能会因为没有足够的进程号而不会创建新的进程。

如何避免僵尸进程

子进程退出的时候,内核会向父进程发头SIGCHLD信号,如果父进程用signal(SIGCHLD,SIG_IGN)通知内核,表示自己对子进程的退出不感兴趣,那么子进程退出后会立即释放数据结构。

Linux使用信号

对于单进程,使用信号很简单,给程序发送信号,让服务程序退出。

如何杀死多进程?

        在多进程的服务程序中,如果子进程收到退出信号,子进程自行退出,如果父进程收到退出信号,则应该先向全部的子进程发送退出信号,然后自己再退出。

测试代码:

#include <iostream>
#include <unistd.h>
#include <signal.h>
using  namespace std;

void FathEXIT(int sig);  // 父进程的信号处理函数。
void ChldEXIT(int sig);  // 子进程的信号处理函数。

int main()
{
  // 忽略全部的信号,不希望被打扰。
  for (int ii=1;ii<=64;ii++) signal(ii,SIG_IGN);

  // 设置信号,在shell状态下可用 "kill 进程号" 或 "Ctrl+c" 正常终止些进程
  // 但请不要用 "kill -9 +进程号" 强行终止
  signal(SIGTERM,FathEXIT); signal(SIGINT,FathEXIT);  // SIGTERM 15 SIGINT 2

  while (true)
  {
    if (fork()>0) // 父进程的流程。
    {
      sleep(5); continue;
    }
    else          // 子进程的流程。
    {
      // 子进程需要重新设置信号。
      signal(SIGTERM,ChldEXIT);   // 子进程的退出函数与父进程不一样。
      signal(SIGINT ,SIG_IGN);    // 子进程不需要捕获SIGINT信号。

      while (true)
      {
        cout << "子进程" << getpid() << "正在运行中。\n"; sleep(3); continue;
      }
    }
  }
}

// 父进程的信号处理函数。
void FathEXIT(int sig)
{
  // 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断。
  signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);

  cout << "父进程退出,sig=" << sig << endl;

  kill(0,SIGTERM);     // 向全部的子进程发送15的信号,通知它们退出。

  // 在这里增加释放资源的代码(全局的资源)。

  exit(0);
}

// 子进程的信号处理函数。
void ChldEXIT(int sig)
{
  // 以下代码是为了防止信号处理函数在执行的过程中再次被信号中断。
  signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);

  cout << "子进程" << getpid() << "退出,sig=" << sig << endl;

  // 在这里增加释放资源的代码(只释放子进程的资源)。

  exit(0);
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值