进程基础知识与进程函数详解,以及守护进程的创建过程

目录

一,进程基础知识

二,进程函数接口

1.创建进程

2.获取进程号

3.进程退出

4.回收进程资源

三,守护进程


一,进程基础知识

1.定义

    程序是静态的,是硬盘上的二进制文件。

    进程是一个独立的可调度的任务,是把程序搬运到了内存中,进程是动态的,是程序的一次执行过程,包含创建,调度,执行,消亡。

2.特点

    系统会为每一个进程分配0-4g的虚拟空间,0-3g(用户空间)是每个进程所独有的,3g-4g(内核空间)是所有进程共有的。

    CPU调度进程时会给进程分配时间片(几毫秒~十几毫秒),当时间片用完后,CPU再进行其他进程的调度,实现进程的轮转,从而实现多任务的操作。

3.进程段

Linux中的进程包含三个段:

“数据段”存放的是全局变量、常数以及动态数据分配的数据空间(如malloc函数取得的空间)等;

“正文段”存放的是程序中的代码;

“堆栈段”存放的是函数的返回地址、函数的参数以及程序中的局部变量。

4.进程分类

    交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,该类进程会立刻响应,典型的交互式进程有 -> shell命令进程、文本编辑器等

    批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。

    守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。

5.进程状态

(1)运行态R(TASK_RUNNING)

指正在被CPU运行或者就绪的状态。这样的进程被成为runnning进程。

(2)睡眠态(等待态)

可中断睡眠态S(TASK_INTERRUPTIBLE):处于等待状态中的进程,一旦被该进程等待的资源被释放,那么该进程就会进入运行状态。

不可中断睡眠态D(TASK_UNINTERRUPTIBLE):该状态的进程只能用wake_up()函数唤醒。

(3)暂停态T(TASK_STOPPED)

当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态;可向进程发送SIGCONT信号让进程转换到可运行状态。

(4)死亡态X:进程结束。

(5)僵尸态Z:进程已经终止运行,但还仍占用系统资源;要避免僵尸态的产生。

6.进程状态切换

    进程创建后,进程进入就绪态;当CPU调度到此进程时进入运行态,当时间片用完时,此进程会进入就绪态,如果此进程正在执行一些阻塞操作就会进行阻塞态,完成阻塞操作后又可以进入运行态,当进程结束时进入结束态。

二,进程函数接口

1.创建进程

 pid_t fork(void);

功能:从当前进程中创建一个子进程;

参数:无;

返回值:成功->在父进程中,返回的是大于0的子进程的进程号

               在子进程中,返回的是0

失败->返回-1并设置错误码。

        一个程序运行时,本身就是一个进程,该进程为父进程,当运行到fork函数时,如果创建子进程成功,就会返回一个0;fork函数之前的程序全部由父进程执行,fork函数之后的程序父进程和子进程都会运行,但是如果进行if判断,判断fork的返回值为0和大于0,就可以把后面的程序分为仅由子进程执行和仅由父进程执行的程序。

fork函数创建进程的特性:

(1)子进程几乎拷贝了父进程的全部内容,包括代码,数据,系统数据段中的PC值,栈中的数据,父进程中打开的文件等;父进程和子进程的PID与PPID是不相同的。

(2)父子进程有独立的地址空间,互不影响;在相应的进程中改变全局变量,静态变量时,都互不影响。

(3)若父进程先结束,子进程成为孤儿进程,被进程init收养,并且变为后台进程。

(4)若子进程先结束,父进程没有及时回收结束的子进程,子进程就会变成僵尸进程(要避免僵尸进程的产生)。

(5)fork函数的“写时拷贝”特性

        当子进程要修改全局变量的值时,会复制父进程的地址空间并开辟新空间;如果只是读取变量的值,子进程和父进程访问的是同一个地址空间;

    vfork函数先执行子进程再执行父进程,父子进程共享数据段。

    fork函数代码演示:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    //定义pid接收fork函数的返回值
    pid_t pid;
    pid = fork();

    //pid小于0,则创建失败
    if(pid < 0)
    {
        //perror打印错误信息
        perror("fork err");
        return -1;
    }
    else if(pid == 0) //pid等于0,则创建成功
    {
        printf("in child\n");
        while(1);
    }
    else
    {
        printf("in parent\n");
        while(1);
    }
    return 0;
}

    执行程序后,不能确定是父进程先运行还是子进程先运行,所以不能确定是先输出“in child”还是先输出“in parent”。

2.获取进程号

(1)获取当前进程的进程号

    pid_t getpid(void);

(2)获取当前进程的父进程号

    pid_t getppid(void);

注:在父进程中要获取子进程的pid,只需要用printf输出pid;

代码演示:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork();
    if(pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if(pid == 0)
    {
        //获取子进程的pid和父进程的pid
        printf("in child:childpid:%d parentpid:%d\n",getpid(),getppid());
    }
    else
    {
        //获取子进程的pid和父进程的pid
        printf("in parent:childpid:%d parentpid:%d\n",pid,getpid());
    }
    return 0;
}

3.进程退出

(1)void exit(int status);

功能:结束进程并刷新缓存

(2)void _exit(int status);

功能:结束进程,不刷新缓存

参数:status->一个整型参数,用来传递进程结束时的状态。

           status为0时->正常结束;

           其他数值表示进程非正常结束。

思考:exit和return的区别是什么?

    exit不管是在子函数还是在主函数中,都可以结束进程;

    return是当子函数中有return时返回到主函数中子函数的调用位置,并不结束进程。

exit演示代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int fun()
{
    printf("in fun\n");
    exit(0);//结束进程,刷新内存
    return 0;
}

int main(int argc, char const *argv[])
{
    fun();
    
    printf("hello\n");
    while(1);
    return 0;
}

执行结果为:只输出in fun,因为先调用含有exit的函数,运行完exit后,整个进程直接退出,不会运行main函数中后面的程序。

4.回收进程资源

(1)pid_t wait(int *status);

功能:回收子进程资源;

参数:status->子进程的退出状态。

返回值:成功->回收的子进程的进程号;

              失败->-1;

        父进程一旦调用了wait,就会立即阻塞自己,如果wait收集到了僵尸子进程,则会把它销毁并返回,如果没有僵尸进程,wait就会一直阻塞,直到收集到僵尸进程。

        参数status用来保存子进程退出时的状态,不需要保存时,可将status设定为NULL。

(2)pid_t waitpid(pid_t pid,int *status,int options);

功能:回收子进程资源

参数:pid:等待的子进程识别码;一般使用-1,表示等待任意子进程。

pid < -1时,等待进程组号为pid绝对值的子进程;

pid = 0时,等待进程组号与目前进程相同的子进程;

pid > 0时,等待进程号为pid的子进程。

status->子进程退出时的状态。

options0->阻塞(通常使用),

                WNOHANG->非阻塞。

返回值:成功->被关闭的子进程的进程号;

               失败->-1。

wait函数代码演示:

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

int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork();
    if(pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if(pid == 0)
    {
        printf("in child\n");
        exit(5);//exit()函数中的status是多少,宏WEXITSTATUS(s)就输出多少
    }
    else
    {
        int s;
        wait(&s);//回收子进程资源,阻塞父进程,先让子进程运行
        printf("%d\n",s);
        printf("%d\n",WIFEXITED(s));//输出不为0,执行宏WEXITSTATUS
        printf("%d\n",WEXITSTATUS(s));//获取子进程exit()返回的结束代码
        printf("in parent\n");
    }
    return 0;
}

执行结果为:

顺序输出以下内容:

父进程阻塞,去运行子进程,输出“int child”;

打印s,1280,代表子进程退出时的状态

1

5

in parent

三,守护进程

1.特点:守护进程是后台进程,它的生命周期比较长,从系统启动时开启,在系统关闭时结束;它是脱离控制终端且周期执行的进程。

2.创建守护进程的步骤:

(1)使用fork函数,创建子进程,让父进程退出,使子进程成为孤儿进程(后台进程)

(2)使用setsid函数,在子进程中创建新会话,让子进程成为会话组组长,让子进程完全脱离终端

(3)使用chdir函数,改变进程运行路径为根目录,因为守护进程的路径不能被删除或卸载;

(4)使用umask函数,重设文件权限掩码,目的是增大守护进程的权限

(5)使用close函数关闭文件描述符

代码演示:​​​​​​​

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    pid_t pid;
    if((pid = fork()) < 0)
    {
    perror("fork err");
    return -1;
    }else if(pid == 0)
{
    int i;
    setsid(); //在子进程中创建会话
    chdir("/"); //改变当前路径为根目录
    umask(0); //重设文件权限掩码
    for(i = 0;i < 3;i++)
    {
        close(i); //关闭文件描述符
    }
    //守护进程文件路径
    int fd = open("/tmp/error.log",O_WRONLY|O_CREAT|O_APPEND,0777);
    while(1)
    {
        write(fd,"hello",5);
        sleep(1);
    }
}
else
{
    exit(0);
}
    return 0;
}

  执行程序后,可以在目录/temp下找到error.log文件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值