学习笔记4

来源:互联网 发布:怎么安装mysql数据库 编辑:程序博客网 时间:2024/05/21 04:02

###########################################################################
2013-12-11
进程组
进程组是一个或多个进程的集合。每个进程组有一个唯一的进程组ID。
getgrp函数
#include <sys/types.h>
#include <unistd.h>
pid_t getpgrp(void);
返回调用进程的进程组ID。每个进程组有一个组长进程。组长进程的标识是,其进程组ID等于其进程ID。进程组组长可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中有一个进程存在,则该进程组就存在,与其组长进程是否终止无关。从进程组创建开始到其中最后一个进程离开为止的时间区间称为进程组的生命期。

setpgid函数
#include <sys/types.h>
#include <unistd.h>
int setpgid(pid_t pid,pid_t pgid);
将pid进程的进程组ID设置成pgid,如果这两个参数相等,则由pid指定的进程变成进程组组长。一个进程只能为它自己或它的子进程设置进程组ID。在它的子进程执行了exec后,它就不再能改变子进程的进程组ID了。如果pid是0,则使用调用者的进程ID。如果pgid是0,则由pid指定的进程ID被用作为进程组ID。

对话期
对话期(session)是一个或多个进程组的集合。
setsid函数
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
作用是建立一个新对话期,如果调用此函数的进程不是一个进程组组长,则此函数创建一个新对话期,结果为:
(1)此进程变成该新对话期的对话期首进程(session leader,对话期首进程是创建该对话期的进程)。此进程是该对话期中的唯一进程。
(2)此进程成为一个新进程组的组长进程。新进程组ID是此调用进程的进程ID。
(3)此进程没有控制终端。如果在调用setsid之前此进程有一个控制终端,那么这种联系也被解除。
如果此调用进程已经是一个进程组的组长,则返回出错。

对话期和进程组的一些特性:
(1)一个对话期可以有一个单独的控制终端。这通常是我们在其上登录的终端设备(终端登录情况)或伪终端设备(网络登录情况)。
(2)建立与控制终端连接的对话期首进程,被称之为控制进程。
(3)一个对话期中的几个进程组可被分成一个前台进程组以及一个或几个后台进程组。
(4)如果一个对话期有一个控制终端,则它有一个前台进程组,其他进程组为后台进程组。
(5)无论何时键入中断键(常常是DELETE或Ctrl-C)或退出键(常常是Ctrl-\),就会造成将中断信号或退出信号送至前台进程组的所有进程。
(6)如果终端界面检测到调制解调器已经脱开连连,则将挂断信号送至控制进程(对话期首进程)

tcgetpgrp和tcsetpgrp函数
#include <sys/types.h>
#include <unistd.h>
pid_t tcgetpgrp(int filedes);  //成功则返回前台进程组ID,出错-1
int tcsetpgrp(int filedes,pid_t pgrpid);  //成功返回0,出错-1
函数tcgetpgrp返回前台进程组ID,它与在filedes上打开的终端相关。如果进程有一个控制终端,则该进程可以调用tcsetpgrp将前台进程组ID设置为pgrpid,pgrpid应该是同一对话期中的一个进程组ID,filedes必须引用该对话期的控制终端。它们一般由作业控制shell调用。只有定义了_POSIX_JOB_CONTROL,这两个函数才被定义,否则出错。

###########################################################################
2013-12-12
作业控制
作业控制允许一个终端上起动多个作业(进程组),控制哪一个作业可以存取该终端,以及哪些作业在后台运行。用户可以在前台或后台启动一个作业,一个作业只是几个进程的集合。
有必要提及一下的是例子:
$ cat > temp.foo &
cat后面不跟文件名时是从标准输入读,输出到标准输出,> temp.foo又将标准输出重定向到temp.foo文件中。而&是指将这个进程放到后台运行,因为后台作业试图读终端,终端驱动程序检测到这种情况,所以发送了一个特定信号SIGTTIN给后台作业,而这会使得后台作业停止并通知用户。用户可以用fg命令将后台停止作业转至前台运行。这样shell将此作业转为前台进程组(tcsetpgrp),并将继续信号(SIGCONT)送给该进程组。因为现在该作业在前台进程组中,所以它可以读控制终端。
注意,说进程与终端进程组ID(TPGID列)相关联是用词不当。进程并没有终端进程控制组。进程属于一个进程组,而进程组属于一个对话期。对话期可能有,也可能没有控制终端。如果它确有一个控制终端,则此终端设备知道其前台进程的进程组ID。这一值可以用tcsetpgrp函数在终端驱动程序中设置。前台进程组ID是终端的一个属性,而不是进程的属性。取自终端设备驱动程序的该值是ps在TPGID列中打印的值。如果ps发现此对话期没有控制终端,则它在该列打印-1。
僵尸进程:先于父进程终止,但是父进程没有对其进行善后处理(获取终止子进程有关信息,释放它仍占有的资源)。消灭僵尸进程的唯一方法是终止其父进程。
孤儿进程:该进程的父进程先于自身终止。其特点是PPID=1(init进程的ID)。一个孤儿进程可以自成孤儿进程组。
孤儿进程组:该组中的每个成员的父进程要么是该组的一个成员,要么不是该组所属成员。【即终端(为会话期所有)无法获知进程组里任一进程的进程状态】,反过来说,如果该组中有一个进程,其父进程属于同一会话的另一个组,那这个进程组就不是孤儿进程组了。
获取进程ID:getpid();
获取父进程ID:getppid();
获取进程组ID:getpgid(0); 或 getpgrp();
获取前台进程ID:tcgetpgrp(0);

###########################################################################
2013-12-13
信号
一般对信号进行的操作有下面三种:
(1)忽略此信号。大多数信号都可以使用这种方式来处理,但有两种信号不能被忽略:SIGKILL和SIGSTOP。原因是:它们向超极用户提供一种使进程终止或停止的可靠方法。另外如果忽略由硬件产生的信号(例如非法存储访问或除以0),则进程的行为是未定义的。
(2)捕捉信号。
为了做到这一点要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户希望对这种事件进行的处理。比如说SIGCHLD的捕捉函数一般可以调用waitpid以取得该子进程的进程ID以及它的终止状态。
(3)执行系统默认动作。大多数信号的系统默认动作是终止该进程。(UNIX信号表参见apue表10-1)
在系统默认动作中,有一项是“终止w/core”表示在进程当前工作目录的core文件中复制了该进程的存储图像(该文件名是为core)。大多数UNIX调试程序都使用core文件以检查进程在终止时的状态。

signal函数
#include <signal.h>
void (*signal(int signo,void (*func)(int)))(int);
这玩意有必要解释一下,首先signal是这个函数的函数名,它带有一个int参数signo和一个函数指针func(这个函数指针指向一个返回void的带一个int型参数的函数),而signal这个函数本身的返回值也是一个函数指针(这个函数指针指向一个返回void的带一个int型参数的函数)。
另外也可以写成:
typedef void Sigfunc(int);  //可以看成void==Sigfunc(int),因此Sigfunc表示参数为一个参数为int型,返回值为void的函数
Sigfunc *signal(int signo,Sigfunc *func);

需要区分的是:
void (*func())(int);  //声明一个函数func,参数为空,返回值为函数指针(指向返回值为void,参数为int的函数)
void (*func)(int);   //声明一个函数指针(指向返回值为void,参数为int的函数)
简单实例:
void func0(int i)
{
 printf("func0:i=%d\n",i);
}
void (*func1(int j))(int)
{
 printf("func1:j=%d\n",j);
 return func0;
}
int main(void)
{
 int i=0,j=1;
 void (*func2)(int);
 func2=func1(j);
 func2(i);
 return 0;
}

继续signal函数,其中当func的值为SIG_IGN时,则向内核表示忽略此信号(SIGKILL和SIGSTOP不能忽略)。如果是SIG_DFL,则表示接到信号后的动作是系统默认动作。当指定为函数地址时,则捕捉此信号,func指向的函数称为信号捕捉函数或信号处理程序。
几个宏的定义:
#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())1
个人认为对应于sinal原型应该改为:
#define SIG_ERR (void (*)(int))-1
#define SIG_DFL (void (*)(int))0
#define SIG_IGN (void (*)(int))1
当执行一个程序时,所有信号的状态都是系统默认或忽略。通常所有信号都被设置为系统默认动作,除非调用exec的进程忽略该信号。exec函数将原先设置要捕捉的信号都更改为默认动作,其他信号的状态则不变。(因为执行一个新程序后,信号捕捉函灵敏的地址很可能在所执行的新程序文件中已无意义)。

0 0