1.进程组
每个进程除了有⼀一个进程ID之外,还属于⼀一个进程组。进程组是⼀一个或多个进程的集合。每个进程组都可以有⼀一个组长进程。组长进程的标识是,其进程组ID等于
其进程ID。
2.作业
Shell分前后台来控制的不是进程⽽而是作业(Job)或者进程组(Process Group)。⼀一个前台作业可以由多个进程组成,⼀一个后台也可以由多个进程组成,Shell可以运⾏行⼀一个前台
作业和任意多个后台作业,这称为作业控制。
作业与进程组的区别:如果作业中的某个进程又创建了⼦子进程,则⼦子进程不属于作业。
3.会话
会话(Session)是⼀一个或多个进程组的集合。
一个会话可以有⼀一个控制终端。
一个会话中的⼏几个进程组可被分为⼀一个前台进程组以及⼀一个或多个后台进程组。所以⼀一个
会话中,应该包括控制进程(会话⾸首进程),⼀一个前台进程组和任意后台进程组。
守护进程
守护进程也可以叫做精灵进程。是运⾏行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程通俗讲就是操作系统中不随终端和会话的关闭而结束,一般在根目录下。
通常使用命令ps axj | grep ‘d$’查看操作系统中的守护进程,他们的进程id,进程组id,会话id都是自己,独立于任何终端,不受终端影响,通常网络中的某些服务就是采用守护进程。
怎么设置守护进程?
1.调用系统函数daemon( int nochdir, int noclose);
如果缺省为daemon(0,0)创建出来的守护进程,通常在根目录下,关闭了读,写和错误。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
daemon(0,0);
while(1);
}
2.调用setsid(void),注意该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调
⽤用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进 程组的Leader也很容易,只要先fork再调用setsid就行了。
步骤:
(1)调用umask将文件模式创建屏蔽字设置为0
(2)父进程fork出子进程,然后子进程调用setsid,父进程直接退出(保证了子进程不是一个进程组的组长),
(3)调用setsid创建一个新的会话(调用成功会使调用进程成为新会话的首进程,并且成为一个进程组的组长进程,调用进程没有控制终端)
(4)将当前工作目录更改为根目录
(5)关闭不在需要的文件描述符
(6)忽略SIGCHLD信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void mydaemon()
{
pid_t pid;
pid = fork();
umask(0);
if(pid > 0)
{
printf("i am father\n");
exit(0);
}
else
{
setsid();
close(0);
close(1);
close(2);
chdir("/");
signal(SIGCHLD,SIG_IGN);
}
}
int main()
{
mydaemon();
while(1)
{}
return 0;
}
这样就创建完成了,可以通过查看ls /proc/pid,这里的pid指的是运行的进程号。里面可以产看cwd默认路径与exe执行路径。
创建守护进程的时候fork一次和fork两次两者有什么区别,就要先知道第一次fork和第二次fork都起到了什么作用:
(1)调用一次fork的作用:
就是为了后面的setsid服务,因为调用setsid函数的进程不能是进程组组长,如果不fork出子进程,则此时的父进程是进程组组长,就无法调用setsid。当子进程调用完setsid函数之后,子进程是会话组长也是进程组组长,并且脱离了控制终端,此时,不管控制终端如何操作,新的进程都不会收到一些信号使得进程退出。
(2)第二次fork的作用:
虽然当前关闭了和终端的联系,但是后期可能会误操作打开了终端。
只有会话首进程能打开终端设备,也就是再fork一次,再把父进程退出,再次fork的子进程作为守护进程继续运行,保证了该精灵进程不是对话期的首进程,二次fork更加安全。