20北大软微华为实验班——守护进程

实验目的:在Linux下使用C语言实现一个守护进程。

一、守护进程的特点

  1. 程序启动后一直在后台运行,即使退出终端,程序也保持运行的状态。
  2. 用于执行特定的系统任务。
  3. 很多守护进程在系统引导的时候启动,并一直运行直到系统关闭。也有的守护进程在需要时启动,完成任务后自动结束。

二、创建守护进程

在这里插入图片描述
按回车进入命令模式——按i进入插入模式——写入代码
代码:(作用:循环任务,不断向文件中输入内容)
20北大软微华为实验班——守护进程_第1张图片
——按esc键进入一般模式——输入== :wq==(w表示保存q表示退出)保存退出——编译运行:20北大软微华为实验班——守护进程_第2张图片
——查看程序运行结果
20北大软微华为实验班——守护进程_第3张图片
再打开一个终端窗口,查看进程的信息:在这里插入图片描述
20北大软微华为实验班——守护进程_第4张图片
关闭第一个终端
在这里插入图片描述
这说明testDaemon还不是一个守护进程。
改造程序:

#include
#include
#include
#include
#include
#include
#include
#include

void init_daemon()
{
    int pid;
    int i;
    pid=fork();//首次调用fork:父进程直接退出,貌似要“脱胎换骨搞独立”
    if(pid<0)
        exit(1);//exit(0):正常运行程序并退出程序;exit(1):非正常运行导致退出程序;
    else if(pid>0)
        exit(0);
    setsid();//调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。同时与控制终端脱离。
    pid=fork();//结束第一进程(组长),第二子进程继续(第二子进程不再是会话组长),目的是禁止进程重新打开控制终端。
    if(pid>0)
        exit(0); /
    else if(pid<0)
        exit(1);
    for(i=0;i<NOFILE;i++)
        close(i);//关闭打开的文件描述符,进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸载以及可以引起无法预料的其他错误。
    chdir("/tmp");//改变当前工作目录。脱离当前工作目录,改变到一个新的目录
    umask(0);//重设文件的权限掩码,为了防止继续沿用从父进程继承下来的掩码内容。
    return;
}

void main()
{
    FILE *fp;
    time_t t;
    printf("pid = %d\n", getpid());
    init_daemon();
    while(1)
    {
        sleep(6);
        fp=fopen("hello2.log","a");
        if(fp>=0)
        {
            time(&t);
            fprintf(fp,"current time is:%s\n",asctime(localtime(&t)));
            fclose(fp);
        }
    }
    return;
}

20北大软微华为实验班——守护进程_第5张图片
20北大软微华为实验班——守护进程_第6张图片
思考

当前守护进程的父进程是谁? - 当前进程是不是成了孤儿进程,父进程是1号进程?

关联的终端是哪一个?- 待分析

三、实验分析

1. Linux下terminal、shell、bash是什么,他们之间的关系是什么?
  • 终端(terminal):提供一个输入输出环境。对于Linux来说,终端是一个字符的命令交互界面,实现对计算机的控制。
linux的终端分类 地址
物理终端 /dev/console
伪终端(远程网络终端 、图形下的终端) /dev/pts/#(数字)
虚拟终端 /dev/tty#
串行终端 /dev/ttys#
  • shell:全称一个命令解释器,是Linux内核的一个外壳,负责外界与Linux内核交互。== shell接收命令==,对内核解释执行,在返回用户或应用程序。 当打开一个终端时,操作系统会将terminal和shell关联起来,在terminal中输入命令,由shell解释命令。
  • bash:每打开一个terminal,就会多一个进程,进程名为bash。全称:Bourne Again Shell,bash是Bourne shell的扩展,bash是shell的一种,可以看成是shell++。
2. 操作系统中进程与程序的关系?
  • 进程=进程控制块(PCB)+代码段(编译后形成的、可在CPU上执行的程序代码)+数据段(程序运行时需要的数据,可以是原始数据也可以是程序执行中产生的中间或最终数据)+堆栈段(程序运行时动态分配的内存)
  • 数据段=只读数据段(常量)+已初始化数据段(全局变量、静态变量)+未初始化数据段bss(不分配内存,因为都为0,只有一些标记信息)
程序 进程
静态(完成某任务的指令集合) 动态(一次执行过程)
永久生命周期 暂时生命周期
- 有独立地址空间和执行状态
- 是CPU、内存、资源分配基本单位
可对应多个进程 只对应一个程序
3. 进程控制块(PCB)包括哪些内容,进程控制块的作用是什么?
  • PCB(processing control block),一种数据结构,与进程一一对应。作用:== 管理进程==,,记录进程信息,是进程存在唯一标识。。
  • Linux中用结构体task_struct描述PCB
  • PCB=进程id、用户id、组id+进程状态+上下文切换CPU寄存器值+控制终端信息+工作目录+文件描述符+…
4. 程序代码中的fork函数是什么功能
  • Linux进程管理相关函数
函数 说明
fork() 在已存在的进程在创建子进程,子进程返回0,父进程返回pid,可由getpid()查看
exec() 在一个进程中启动另一个程序
exit() 结束进程
wait() 阻塞自己,若进程的子进程已结束则彻底销毁,若没有这样的子进程则一直阻塞
sleep() 让进程挂起指定时间
getpid() 获得当前进程的id
getppid 获得当前进程的父进程的id
5. 进程与进程之间有什么关系(父子关系、兄弟关系…),我们在终端执行hello world程序时,他的父进程是谁?

hello world程序代码:

#include 
#include 
#include 
#include 

int main(void)
{
    pid_t childpid;//pid_t 为进程号类型
    int status;
    childpid = fork(); //fork()创建一个子进程,父进程返回子进程的pid,传递给变量chidpid
    if (-1 == childpid){//fork()创建一个子进程错误,返回-1
        perror("fork()");//perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。
        exit(EXIT_FAILURE);//exit()结束进程,EXIT_FAILURE异常退出
    }
    else if(0 == childpid)//子进程fork()返回0,即以下代码子进程执行
    {
        puts("In child process");
        sleep(10);//子进程阻塞10s
        printf("\tchild pid = %d\n", getpid());//输出子进程id
        printf("\tchild ppid = %d\n", getppid());//输出父进程id
        exit(EXIT_SUCCESS);//安全退出
    }else{//fork()在父进程处返回子进程id,即以下代码父进程执行
        waitpid(childpid, &status, 0);//waitpid()等待直到子进程结束或有信号到来,返回子进程的结束状态值    
        puts("in parent");
        printf("\tparent pid = %d\n", getpid());
        printf("\tparent ppid = %d\n", getppid());
        printf("\tchild process exited with status %d \n", status);
    }
    exit(EXIT_SUCCESS);
}

结果:
20北大软微华为实验班——守护进程_第7张图片
结论:终端执行hello world程序时,他的父进程是终端。

6. 什么是僵尸进程,什么是孤儿进程?

(1)僵尸进程

  • 子进程先于父进程结束,子进程的PCB需要父进程释放,但父进程没有释放,这个子进程称为僵尸进程。僵尸进程实际上是已经死掉的进程。
  • 除了在进程列表中还有位置,僵尸进程不占任何内存。
  • 大量僵尸进程会消耗系统资源->系统崩溃。∴要避免僵尸进程。
    若父进程不结束且释放,则僵尸进程一直存在;若父进程结束,进程号为1的进程(eg.init进程),来清除。
  • 程序模拟:(子进程退出后,父进程没有调用wait释放子进程资源,子进程->僵尸进程)
#include 
#include 
#include 
#include 

int main(){
   pid_t id = fork();//创建一个子进程并取得返回值
   if(id < 0){//创建子进程错误
      perror("fork");//打印 fork+错误信息
      return 1;
   }
   //父进程执行
   else if(id > 0){//创建子进程成功
      printf("parent [%d] is sleeping\n",getppid());//打印终端进程id
      sleep(500);//父进程挂起500s
   }
   //子进程执行
   else{
      printf("child [%d] is begining\n",getpid());
      sleep(2);
      exit(1);//子进程结束。exit(0):正常运行程序并退出程序;exit(1):非正常运行导致退出程序;
   }
   return 0;
}

20北大软微华为实验班——守护进程_第8张图片
注:defunct和zombie都是指僵尸进程。

(2)孤儿进程

  • 父进程执行完或终止后,子进程仍然运行,这种子进程即孤儿进程。
  • 孤儿进程由pid==1的进程(eg.init进程)收养,对它们完成状态收集工作。
#include 
#include 
#include 
#include 

int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    if (pid == 0)
    {
        printf("I am the child process.\n");
        printf("pid: %d\tppid:%d\n",getpid(),getppid());//父进程还在运行,ppid=210
        printf("I will sleep 50 seconds.\n");
        sleep(50);//子进程挂起50s
        printf("pid: %d\tppid:%d\n",getpid(),getppid());//父进程已经结束,ppid=1
        printf("child process is exited.\n");
    }
    else
    {
        printf("I am father process.\n");
        sleep(10);//父进程挂起10s
        printf("father process is exited.\n");//父进程先于子进程结束
    }
    return 0;
}

20北大软微华为实验班——守护进程_第9张图片
20北大软微华为实验班——守护进程_第10张图片

7. 信号是什么?如何杀死一个进程?

(1)信号

  • 又称软件中断。事件发生时通知打断进程的正常执行过程。
  • == 也是进程通信的一种方式==,一个进程可以向另一个进程发信号。
    (2)杀死一个进程
  • 使用kill命令:通过向进程发指定信号来结束相应进程。
kill <pid> #kill命令发终止信号给进程,进程捕获到信号->清理并释放资源->退出
kill [号码选项] pid

最后:感谢软微张老师的知识分享,学到不少知识!

你可能感兴趣的:(linux,操作系统)