Linux进程详解(多进程、孤儿、僵尸、守护进程实现)

文章目录

      • 前言
      • 一.进程标识
      • 二.多进程的创建
      • 三.孤儿进程
      • 四.僵尸进程
      • 五.守护进程

前言

程序是静态的,进程是动态的,我们将进程运行起来后,能够通过一些linux指令 如 topps 等,来查看进程的动态。


一.进程标识

  1. getpid() 获取本进程的id
  2. getppid() 获取父进程id
  3. getuid() 获取user(使用者)id
void test0()
{
    cout << "本进程ID:" << getpid() << endl;
    cout << "父进程ID:" << getppid() << endl;
    cout << "进程所属的用户ID" << getuid() << endl;
    cout << "进程所属的组ID" << getgid() << endl;
}

二.多进程的创建

  1. int system(const char* command)
    相当于在终端执行 command 语句
void test1()
{
    system("ls -l"); //相当于在终端执行shell语言 ls -l
    system("clear"); //相当于在终端执行shell语言 clear
}
  1. pid_t fork(void)
    返回值pid_t
  • 0则表示为子进程
  • -1表示创建进程失败
  • 父进程的返回值是子进程的进程号

可能会有疑问:
明明只有一段代码,为什么有两个进程可以执行?
在进行fork操作之后,fork以下的代码相当于有两个进程同时执行, 也就是有两个 有着不同pid值的test2()函数, 分别执行以下的代码。

fork调用子进程的过程中值得注意的一点:子进程会继承父进程的文件句柄
尽管在父进程中已经将fd进行了close, 但是子进程中不会相互收到影响

void test2()
{
    int fd = open("file", O_RDWR | O_CREAT, 0755);
    ERROR_CHECK(fd, -1, "open");
    pid_t pid = fork();
    if (pid < 0)
        cout << "fork ERROR" << endl;
    else if (pid == 0) //子进程
    {
        sleep(3);
        cout << "this is child process" << endl;
        char buff[100] = {0};
        read(fd, buff, sizeof(buff));
        cout << "buff:" << buff << endl;
        close(fd); 
    }
    else //父进程
    {
        cout << "this is parent process" << endl;
        close(fd);
    }
}
  1. int execl(const char *__path, const char *__arg, ...)

execl 的工作原理与 fork 并不相同,fork是复制一份进程,而execl则是拉取另一份进程对本进程进行覆盖。
注意:execl 语句以下将不再执行,因为已经被execl拉取的程序覆盖。

例如,从main函数中execl add程序计算 1 + 2;
main.cpp:

int main()
{
    execl("./add", "add", "1", "2", NULL);
    cout << "this is main function" << endl;
    return 0;
}

add.cpp:

int main(int argc, char* argv[])
{
    ARGS_CHECK(argc, 3);
    cout << atoi(argv[1]) + atoi(argv[2]) << endl;    
    return 0;
}

三.孤儿进程

父进程提前退出,子进程仍在运行的进程叫做孤儿进程。
孤儿进程的状态为 R(运行),其PPID(父进程) 为 init(祖先进程),也就是说当父进程提前退出时,祖先进程会来接管孤儿进程。

孤儿进程是没有危害的,init祖先进程会帮助我们处理。

void test3()
{
    pid_t pid = fork();
    if (pid < 0)
        cout << "fork ERROR" << endl;
    else if (pid == 0) //子进程
    {
        cout << "this is child process" << endl;
        sleep(10);
    }
    else //父进程
    {
        cout << "this is parent process" << endl;
    }
}

待父进程退出后,查看进程的状态,可以看到,12665号进程的父进程已经变为1号进程,也就是init始祖进程。
在这里插入图片描述

四.僵尸进程

子进程先退出,父进程仍在运行,那么叫子进程为僵尸进程。
此时子进程的进程状态为 Z(zombie僵尸),因为没有父进程为其进行清理子进程的环境

此时在父进程中最好使用 wait(NULL) , 表示等待所有的子进程并清理环境

void test4()
{
    pid_t pid = fork();
    if (pid < 0)
        cout << "fork ERROR" << endl;
    else if (pid == 0) //子进程
    {
        cout << "this is child process" << endl;
    }
    else //父进程
    {
        wait(NULL);//等待所有的子进程,来帮助清理子进程,注意必须放在父进程的一开头
        cout << "this is parent process" << endl;
        sleep(30);
    }
}

父进程中未设置wait函数,运行程序:
发现出现僵尸进程!
在这里插入图片描述
父进程中设置wait函数,运行程序:
程序中并没有出现僵尸进程!只有仍在运行的父进程!
在这里插入图片描述


五.守护进程

守护进程是一种特殊的后台程序,它独立于终端环境,不受终端的控制与影响,会一直运行到系统关闭。同时,守护进程也属于孤儿进程。
守护进程的创建有五个步骤:

  1. 终结父进程,这样子进程才会不受控制
  2. 创建新的会话组: setsid()
  3. 改变工作目录: chdir("/")
  4. 修改掩码:umask(0)
  5. 关闭文件描述符; close(fd)
    具体可参考 :百度百科

实例:创建守护进程,向系统日志中写30s的信息

void test5()
{
    if (fork() > 0)
        exit(0);
    setsid();
    chdir("/");
    umask(0);
    for (int i = 0; i < 64; i++)
        close(i);
    int cnt = 0;
    while (cnt < 10)
    {
        syslog(LOG_INFO, "this is %d message", cnt);
        cnt ++;
        sleep(1);
    }   
}

程序运行后,守护进程启动,状态为S(sleep)。
在这里插入图片描述
同时守护进程不断往日志中写数据。
Linux进程详解(多进程、孤儿、僵尸、守护进程实现)_第1张图片
当数据写完时,守护进程也结束。
在这里插入图片描述

你可能感兴趣的:(Linux)