Linux--进程组,会话,精灵进程(fork一次与fork两次的区别)

来源:互联网 发布:测试udp端口工具 编辑:程序博客网 时间:2024/05/16 18:02

Linux之进程组,作业,会话,精灵进程

 

1.进程组:

每个进程除了有一个进程ID之外,还属于一个进程组。进程组是一个或多个进程的集合。通常,它们与同一作业相关联,可以接收来自同一终端的各种信号。每个进程组有一个唯一的进程组ID。每个进程组都可以有一个组长进程。组长进程的标识是,其进程组ID等于其进程ID。

组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。

 

例:创建子进程,显示父子进程ID与组ID

 

需要用的函数:

获取当前进程id:getpid获取组id:getpgid或者getpgrp函数原型:#include<unistd.h>   pid_t getpgrp(void *);     //返回值:调用进程的进程组ID#include<unistd.h>   pid_t getpgid(pid_t pid);   //返回值:若成功则返回进程组ID,若出错则返回-1

我们会发现,进程id不同,但是他们同属于一个进程组,且组长id为父进程id。

setpgid可以设置进程ID与进程组ID。

#include<unistd.h>   intsetpgid(pid_t pid, pid_t pgid);  //返回值:成功则返回0,出错则返回-1

若想改变父进程组id为子进程id,只需要setgid(id,id)先将子进程组id设置成子进程id本身,再改变父进程组id(setgid(getpid(),getpid()))即可。

 

2.作业:

         一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成,Shell可以运行一个前台作业和任意多个后台作业,这称为作业控制。

         作业与进程组的区别:如果作业中的某个进程又创建了子进程,则子进程不属于作业。

 

3.会话

         会话是一个或多个进程组的集合。一个会话可以有一个控制终端。

         建立与控制终端连接的会话首进程被称为控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。

 

1  $proc1 | proc2 &

2  $proc3 | proc4 | proc5

其中proc1与proc2属于同一个后台进程组,proc3,proc4和proc5属于同一个前台进程组,

Shell本身属于一个单独的进程组。这些进程组的控制终端相同,它们同属于一个会话,当用户在控制终端输入特殊的控制键(如Ctrl+C,产生SIGINT,Ctrk+\,产生SIGQUIT,Ctrl+Z,产生SIGTSTP),内核发送相应的信号给前台进程组中的所有进程。

 

精灵进程(守护进程):

 

守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond等。下面我们用ps axj命令查看系统中的进程。

守护进程的运行应该是7*24小时的不间断运行。

 

用ps axj命令查看系统中的进程。参数a表示不仅列出当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。

 

凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。

PPID:bash。PGID:进程组id。SID:会话id。STAT:状态,其中s+表示前台sleep;s不带+表示后台sleep。

    在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表Kernel。init进程, 内核调用,负责内核启动后启动Linux系统。udevd负责维护/dev目录下的设备文件。acpid负责电源管理。syslogd负责维护/var/log下的日志文件。可以看出,守护进程通常采用以d结尾的名字,表示Daemon。

    Linux系统启动时会启动很多系统服务进程,例如“网络登录过程”的inetd,这些系统服务进程没有控制终端,不能直接和用户交互。其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)。

守护进程没有终端限制。让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程。

 

创建守护进程的函数:

#include<unistd.h>

pid_t setsid(void);

该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。

成功调用该函数的结果是:

1. 创建一个新的Session,当前进程成为SessionLeader,当前进程的id就是Session的id。

2. 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。

3. 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。


创建守护进程的步骤

1、在后台运行。调用fork,父进程退出(exit)。所有工作在子进程中进行,形式上脱离了控制终端。

     原因:1)如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止使得shell认为该命令已经执行完毕。2)保证子进程不是一个进程组的组长进程。

2、脱离控制终端,登录会话和进程组。调用setsid在子进程中创建一个新会话。

     setsid会导致:

     1)调用进程成为新会话的首进程。 2)调用进程成为一个进程组的组长进程 。3)调用进程没有控制终端。(再次fork一次,保证daemon进程,之后不会打开tty设备

3、将当前工作目录更改为根目录。

用chdir()函数进行,更改目录防止占用可卸载的文件系统,也可以换成其他路径。

4、调用umask将文件模式创建屏蔽字设置为0。

目的:防止继承的文件创建屏蔽字拒绝某权限,增加守护进程灵活性。

5、关闭不在需要的文件描述符。

继承的打开文件不会用到,浪费系统资源,无法卸载;getdtablesize();返回所有文件的文件描述符表的项数,即该进程打开的文件数目。

6、忽略SIGCHLD信号。

         忽略SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。signal(SIGCHLD,SIG_IGN);

7、禁止进程重新打开控制终端

        fork后进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:

        if(pid=fork())

       exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)

 

可是在翻阅资料的时候,我们会发现有些博客fork了两次?

第一次fork的作用是为了后面的setsid服务,因为调用setsid函数的进程不能是进程组组长,如果不fork出子进程,则此时的父进程是进程组组长,就无法调用setsid。当子进程调用完setsid函数之后,子进程是会话组长也是进程组组长,并且脱离了控制终端,此时,不管控制终端如何操作,新的进程都不会收到一些信号使得进程退出。

再次fork,终止父进程,保持子进程不是话首进程,从而保证后续不会在和其他终端关联。 第二次不是必须的,是可选的。


自己写一个简单的守护进程

 

1 0
原创粉丝点击