进程的基本属性:进程ID、父进程ID、进程组ID、会话和控制终端

摘要:本文主要介绍进程的基本属性,基本属性包括:进程ID、父进程ID、进程组ID、会话和控制终端.

进程基本属性

1.进程ID(PID)

函数定义:
     #include <sys/types.h>
     #include <unistd.h>
     pid_t getpid(void);
函数说明:
    每个进程都有一个非负整型表示的唯一进程ID(PID).好比如我们的身份证一样,每个人的身份证号是唯一的.因为进程ID标示符总是唯一的,常将其用来做其他标示符的一部分以保证其唯一性,进程ID(PID)是无法在用户层修改的.
    在Linux系统中,PID为0 的进程通常是调度进程,常常被称为交换进程,也是第一个系统进程.第一个用户进程是init进程,其PID为1.
    在应用编程中,调用getpid()函数可以获得当前进程的PID,此函数没有参数,如果执行成功返回当前进程的PID,失败返回-1,出错原因存储于errno.
例子1:打印自己的进程ID(PID).
#include <unistd.h>
#include <stdio.h>
int main()
{
        pid_t pid; //pid_t 其实是int
        pid = getpid();
        printf("the current program's pid is %d\n",pid);
        while(1);
        return 0;
}

运行后使用“ps u”命令查看对照一下.

2.父进程ID(PPID)

函数定义:
     #include <sys/types.h>
     #include <unistd.h>
     pid_t getppid(void);
函数说明:
    任何进程(除init进程)都是由另一个进程创建,该进程称为被创建进程的父进程,被创建的进程称为子进程,父进程ID无法在用户层修改.父进程的进程ID即为子进程的父进程ID(PPID).
    用户可以通过调用getppid()函数来获得当前进程的父进程ID(PPID).此函数没有参数,如果执行成功返回当前进程的父进程ID(PPID),失败返回-1,出错原因存储于errno.
例子1:打印自己的父进程PPID.
#include <unistd.h>
#include <stdio.h>
int main()
{
        pid_t ppid; //pid_t 其实是int
        ppid = getppid();
        printf("the current program's ppid is %d\n",ppid);
        while(1);
        return 0;
}

运行后使用“ps u”命令查看对照一下.

3.进程组ID(process group ID PGID)

函数定义:
    #include <unistd.h>
    int setpgid(pid_t pid, pid_t pgid);
    pid_t getpgid(pid_t pid);
    pid_t getpgrp(void);                 /* POSIX.1 version */
    pid_t getpgrp(pid_t pid);            /* BSD version */
    int setpgrp(void);                   /* System V version */
    int setpgrp(pid_t pid, pid_t pgid);  /* BSD version */
函数说明:
    在Linux系统中,每个用户都有用户ID(UID)和用户组ID(GUID).同样,进程也拥有自己的进程ID(PID)和进程组ID(PGID). 进程组是一个或多个进程的集合;他们与同一作业相关联.每个进程组都有唯一的进程组ID(PGID),进程组ID(PGID)可以在用户层修改.比如,将某个进程添加到另一个进程组,就是使用setpgid()函数修改其进程组ID.
    用户可以通过调用getpgid()函数来获得当前进程的进程组ID(PGID).若此参数为0表示获取当前进程的进程组ID,如果执行成功返回当前进程的进程组ID(PGID),失败返回-1,出错原因存储于errno. 建议使用POSIX.1规定中的无参数getprgp()函数替代getpgid(pid)函数.
    进程组ID(PGID)也可以通过函数getpgrp()获得.通过fork()函数产生的子进程会继承它的父进程的进程组ID(PGID).
    每个进程组都可以有一个组长进程,组长进程的进程组ID等于其进程ID.但组长进程可以先退出,即只要在某个进程组中有一个进程存在,则该进程组就存在,与其组长进程是否存在无关.进程组的最后进程可以退出或转移到其他组.
    可以将某个进程加入到某个进程组中,调用系统函数setpgid().其第一个参数为欲修改进程组ID(PGID)的进程ID(PID),第二参数为新的进程组ID(PGID),如果这两个参数相等,则由pid指定的进程变为该进程组组长;如果pid为0,则使用调用者的进程ID(即修改当前进程的进程组ID(PGID为指定的pgid));如果pgid是0,则由pid指定的进程ID(PID),用做进程组ID(PGID)(即:pid所指进程作为进程组的组长进程).
    一个进程只能为自己或子进程设置进程组ID(PGID),如果在它的子进程中调用了exec()等系列函数,就不再能改变该子进程的进程组ID(PGID).
#include <unistd.h>
#include <stdio.h>
int main()
{
        int i;
        printf("\t pid \tppid \t pgid\n");
        printf("parent:\t%d\t%d\t%d\n",getpid(),getppid(),getpgid(0));
        for(i=0; i<2; i++)
        {
                if(fork()==0)
                {
                        printf("child:\t%d\t%d\t%d\n",getpid(),getppid(),getpgid(0));
                }
        }
        sleep(500);
        return 0;

}

输出:

4.会话(session)

函数定义:
    #include <unistd.h>
    pid_t getsid(pid_t pid);
    pid_t setsid(void);
函数说明:
    会话是一个或多个进程的集合.系统调用函数getsid()用来获取某个进程的会话ID(SID).
    如果pid是0,返回调用进程的会话SID,一般说来,改制等于进程组ID(PGID).如果pid并不属于调用者所在的会话,则调用者就无法获取SID.
    某个进程的会话ID也是可以修改的,调用函数setsid()用来创建一个新的会话.
    如果调用进程已经是一个进程组的组长,则此函数返回错误.如果不是,则返回一个新的会话.
(1)该进程变成新会话首进程,会话首进程是创建该会话的进程,此时,该进程是新会话唯一的进程.
(2)该进程成为一个新的进程组的组长进程.新的进程组ID(PGID)是该调用进程的PID.
(3)该进程没有控制终端.如果在调用setsid()之前该进程就有一个控制终端,那么这种联系也会中断.

 

图1 进程组合会话的进程安排

5.控制终端(controlling terminal)

函数定义:
    #include <unistd.h>
    pid_t tcgetpgrp(int fd);
    int tcsetpgrp(int fd, pid_t pgrp);
函数说明:
    会话和进程组的关系:
(1)一个会话可以有一个控制终端,建立于控制终端相连接的会话首进程被称为控制进程.
(2)一个会话中的几个进程组可被分为一个前台进程组和几个后台进程组,如果一个会话有一个控制终端,则他有一个前台进程组.
(3)无论何时键入终端的中断键,都会将中断信ID发送给前台进程组的所有会话;无论何时键入终端的退出键,都会将退出信ID发送给前台进程组的所有会话.

如果终端监测到调制解调器(或网络)已经断开连接,则将挂断信ID发送给控制进程(会话首进程).
    调用函数tcgetgrpt()获取与打开的终端相关联的前台进程组的进程组ID.
    调用函数tcsetgrpt()设置某个进程组是前台进程还是后台进程组.
    如果进程有一个控制终端,则将前台进程组ID设置为pgrp,pgrp的值应该在同一会话中的一个进程组的ID,fd为控制终端的文件描述符.
    如果调用tcsetpgrp()函数的是会话中的后台进程组的进程,则该进程不会阻塞,或者忽略SIGTTOU信号.信号SIGTTOU将会发送给该进程组的所有进程.
    当fd是指向进程的控制终端,函数tcgetpgrp()返回终端的前台进程组的进程组ID,该ID值是一个大于1且没有使用的值.如果fd指向的不是进程的终端,则函数返回-1,并设置errno指示出错.
 
图2 显示控制终端的进程组和会话

笔者:个人能力有限,只是学习参考...读者若发现文中错误,敬请提出.
-- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙筑高台,静下心来,慢慢地沉淀---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,这是一个创建守护进程,使用 fork 创建子进程,之后 kill 子进程,让进程可以通过信号感知子进程退出事件的 C++ 代码示例: ```c++ #include <unistd.h> #include <signal.h> #include <sys/stat.h> #include <sys/types.h> #include <iostream> // 子进程退出信号处理函数 void child_exit_handler(int signum) { std::cout << "Child process exited." << std::endl; } int main() { // 创建守护进程 pid_t pid = fork(); if (pid < 0) { std::cerr << "Failed to fork." << std::endl; return -1; } if (pid > 0) { // 进程 std::cout << "Daemon process started with PID " << pid << "." << std::endl; // 等待子进程退出 signal(SIGCHLD, child_exit_handler); pause(); std::cout << "Daemon process exited." << std::endl; return 0; } // 子进程 umask(0); // 创建新的会话 pid_t sid = setsid(); if (sid < 0) { std::cerr << "Failed to create new session." << std::endl; return -1; } // 关闭标准输入输出 close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 执行子进程任务 while (true) { std::cout << "Child process is running." << std::endl; sleep(1); } return 0; } ``` 在该代码中,首先使用 `fork()` 创建新的进程。如果返回值为负数,则表示创建进程失败;如果返回值为正数,则表示是进程,需要等待子进程退出;如果返回值为 0,则表示是子进程,需要执行子进程任务。 在进程中,使用 `signal()` 函数注册子进程退出信号处理函数,然后调用 `pause()` 函数使进程挂起,等待信号的到来。当子进程退出时,会发送 `SIGCHLD` 信号给进程进程接收到信号后会调用注册的信号处理函数。 在子进程中,首先调用 `umask()` 函数设置文件权限掩码,然后调用 `setsid()` 函数创建新的会话,成为新的进程长,并与原来的控制终端脱离关系。然后关闭标准输入输出和错误输出,执行子进程任务。 如果要让进程杀死子进程,可以使用 `kill()` 函数发送信号给子进程。例如,在进程中添加以下代码: ```c++ kill(pid, SIGKILL); ``` 其中,`pid` 是子进程进程 ID,`SIGKILL` 是要发送的信号,表示杀死进程。当进程执行到这行代码时,会向子进程发送 `SIGKILL` 信号,杀死子进程。 当子进程被杀死时,会发送 `SIGTERM` 信号给进程进程也会调用注册的信号处理函数。在该函数中,可以添加一些代码来处理子进程被杀死的情况。例如,可以在函数中输出一条日志,告知用户子进程被杀死了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

且漫CN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值