Linux进程租_学习笔记

Linux进程组

每个进程都有一个进程组号(PGID)

  • 进程组:一个或多个进程的集合(集合中的进程并不孤立)
  • 进程组中的进程通常存在父子关系,兄弟关系或功能相近

进程组可方便进程管理(如:同时杀死多个进程,发送一个信号给多个进程)

  • 每个进程必定属于一个进程组,也只能属于一个进程组
  • 进程除了有PID外,还有PGID(唯一,但可变)
  • 每个进程组有一个进程组长,进程组长的PID和PGID相同
  • pid_t getpgid(pid_t pid);        //获取指定进程的组标识
  • int setpgid(pid_t pid, pid_t pgid);        //设置进程的组标识
    • pid == pgid,将pid指定的进程设为组长
    • pid == 0 设置当前进程的组标识
    • pgid == 0 则将pid作为组标识

获取调用进程进程组id(getpgrp)

每个进程组都有唯一的进程组ID(整数,也可以存放在pid_t类型中)。进程组由进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性之一。
getpgrp: 获得进程组 id, 即领头进程的 pid
#include <unistd.h>
pid_t getpgrp(void);
//返回值;调用进程的进程组ID


#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>





int main(void)

{

    int pid = 0;

    int i = 0;

    

    printf("parent = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    

    while( i < 5 )

    {

        if( (pid = fork()) > 0 )

        {

            printf("new: %d\n", pid);

        }

        else if( pid == 0 )

        {

            sleep(1);

            printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

            sleep(60);

            printf("last -- pgid = %d\n", getpgrp());

            break;

        }

        else

        {

            printf("fork error...\n");

        }

        

        i++;

    }

    

    if( pid )

    {

        sleep(60);

    }

   

    return 0;

}

深入理解进程组

  • 进程组长终止,进程组依然存在(进程组长仅用于创建新进程组)
  • 父进程创建子进程后立即通过setpgid()改变其组标识(PGID)
  • 同时,子进程能需要通过setpgid() 改变自身组标识(PGID)
  • 子进程调用exec()
    • 父进程无法通过setpid()改变其组标识(PGID)
    • 只能自身通过setpgid()改变其组标识()
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
    int pid = 0;
    
    printf("parrent = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
    
    if( (pid = fork()) > 0 )
    {
        int r = setpgid(pid, pid);
        printf("new: %d, r = %d\n", pid, r);
    }
    else if( pid == 0 )
    {
        setpgid(pid, pid);
        sleep(1);
        printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());
    }
    else
    {
        printf("fork error...\n");
    }  
    
    sleep(60);
   
    return 0;
}
如果pid是0,则使用调用者的进程ID。
另外,如果pgid是0,则由pid指定的进程ID被用作为进程组ID。(如setpgid(0, 0),将调用进程设置为进程组组长,进程组id为调用进程的进程号)
setpgid(0, 0),将调用进程设置为进程组组长,进程组id为调用进程的进程号

子进程调用exec代码示例:

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>



int main(void)

{
    sleep(5);


    printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    

    printf("hello world\n");

    

    sleep(30); 

    return 0;

}



#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>



int main(void)

{

    int pid = 0;

    

    printf("parrent = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    

    if( (pid = fork()) > 0 )

    {

        int r = 0;

	sleep(1);

	r = setpgid(pid, pid);

        printf("new: %d, r = %d\n", pid, r);

    }

    else if( pid == 0 )

    {

	char* out = "./helloworld.out";

	char* const ps_argv[] = {out, NULL};

	char* const ps_envp[] = {"PATH=/bin:/usr/bin",NULL};

	execve(out, ps_argv, ps_envp);

 //       setpgid(pid, pid);

 //       sleep(1);

 //       printf("child = %d, ppid = %d, pgid = %d\n", getpid(), getppid(), getpgrp());

    }

    else

    {

        printf("fork error...\n");

    }  

    

    sleep(60);

   

    return 0;

}

一个进程只能为它自己或它的子进程设置进程组ID在它的子进程调用了exec后,它就不再能改变该子进程的进程组ID。

Linux会话(session)

  • 用户通过终端登录系统后会产生一个会话
  • 会话是一个或多个进程组的集合
  • 每个会话有一个会话标识(SID)
    • 终端登录后的第一个进程成为会话首进程,通常是一个shell/bash
    • 对于会话首进程(session leader),其PID与SID相等
  • 通常情况下,会话与一个终端(控制终端)相关联用于执行输入输出操作
    • 会话首进程建立与控制终端的连接(会话首进程又叫控制进程)
    • 会话中的进程组可分为
      • 前台进程组:可接收控制终端中的输入,也可输出数据到控制终端
      • 后台进程组:所有进程后台运行,无法接受终端的输入,但可输出数据到终端

每个会话最多只有1个前台进程组,可以有多个后台进程组

问题:在终端中输入命令后,发生了什么?

  • 当命令行(shell)运行命令后创建一个新的进程组
  • 如果运行的命令中有多个子命令则创建多个进程(处于新建的进程组中)
  • 命令不带&
    • shell 将新建的进程组设置为前台进程组,并将自己暂时设置为后台进程组
  • 命令带&
    • shell 将新建的进程组设置为后台进程组,自己依旧是前台进程组

什么是终端进程组标识(TPGID) ?

  • 标识进程是否处于一个和终端相关的进程组中
  • 前台进程组:TPGID == PGID
  • 后台进程组:TPGID != PGID
  • 若进程和任何终端无关:TPGID == -1

Linux 会话接口

  • #include <unistd.h>
  • pid_t getsid(pid_t pid)        //获取指定进程的SID,(pid == 0)->当前进程
  • pid_t setsid(void)                //调用进程不能是进程组长
    • 创建新会话, SID == PID,调用进程成为会话首进程
    • 创建新进程组,PGID == PID,调用进程成为进程组长
    • 调用进程没有控制终端,若调用前关联了控制终端,调用后与控制终端断链


#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>



int main(void)

{

    int pid = 0;

    int i = 0;

    

    if( (pid = fork()) > 0 )

    {

        printf("parrent = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));

        printf("new: %d\n", pid);

    }

    else if( pid == 0 )

    {

        setsid();

        sleep(3);

        printf("child = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));

    }

    else

    {

        printf("fork error...\n");

    }  

    

    sleep(120);

    

    return 0;

}

最后能输出新会话语句的原因是因为printf会把stdout挂在到同一个终端中!
  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值