摘要:本文主要介绍进程的基本属性,基本属性包括:进程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 显示控制终端的进程组和会话
————————————————
版权声明:本文为CSDN博主「FMsunyh」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010006102/article/details/39958613