一、进程间关系
1.进程组(Process Group)
1)是一个或多个进程的集合,通常,这个集合与同一个作业相关联,可以接受同一终端的各种信号。
2)每一个进程组都有一个组长进程,其进程组ID等于其进程组ID。
3)当一个进程组中还有一个进程存在,就算终止了这个组长进程,这个进程组依然存在。
4)我们将一些进程归为进程组的一个重要原因是我们可以将信号发送给一个进程组,进程组中的所有进程都会收到该信号。
ps:
a:列出当前以及其他所有用户的进程。
x:列出所有有(无)控制终端的进程。
j:列出所有与作业控制相关的信息。
//PGID为进程所在的进程组的ID
//SID是会话ID
//每个进程都有父进程,而所有的进程以init进程为根,形成一个树状结构
2.作业(Job)
1)shell分前后台来控制的不是进程而是作业或进程组。
2)一个shell可以运行一个前台作业或多个后台作业,一个前台作业可以由多个进程组成。
3)在前台新起一个作业,shell被提到了后台,但是前台进程退出,shell又会被提到前台,再次等待用户输入命令。
注:作业与进程组的区别?
如果作业中的某个进程又创建了一个子进程,则子进程不属于作业,但属于进程组。进程组范围更广。倘若此时进程退出,shell就被提到了前台,子进程被放到后台执行,因为这个新起的子进程不属于作业。
//ctrl+c杀掉的不是进程而是作业。
//jobs:查看当前所有作业
//fg 1 :将一号作业放到前台,在按ctrl+z则向所有前台进程发送SIGTSTP,默认动作是使进
程停止,cat继续以在后台作业的形式存在。
//bg 1 :将一号作业放到后台。可以将某个停止的作业在后台继续运行。
//后台作业可以写。
4)与作业控制有关的信号
3.会话(Session)
1)概念:一个或多个进程组的集合。
2)一个会话可以有一个控制终端。
3)建立与控制终端连接的会话首进程称为控制进程。
4)一个会话会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。
5)一个会话中应包括控制进程(会话首进程),一个前台进程组和任意多个后台进程组。
6)会话主要是针对一个终端建立的。当我们打开多个终端窗口时,实际上就创建了多个终端会话。
7)无论何时键入中断键,都会将中断信号发送至前台进程组的所有进程
——————————————————————————————————————————————————————
注:
1.每打开一个终端,就新建一个会话。
2.-bash代表当前终端是从网络中登陆的。
3.bash解释器。
二、守护进程
1.概念
守护进程也叫精灵进程,是运行在后台的一种特殊进程,独立于控制终端。Linux中的大多数服务器就是用守护进程实现的。如ftp服务器、ssh服务器、httpd。守护进程也会完成许多系统任务。
Linux系统启动时,会启动很多的系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其他进程都是在用户登录或运行时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响。一直运行。
大多数守护进程都以超级用户特权运行,所有守护进程都没有控制终端,其终端名设置为问号
2.查看守护进程
ps axj | more
//more类似cat,以一页一页的形式展示,按空格键到下一页。
1)TPGID:前台进程组ID,只要是-1,就说明是守护进程,是没有控制终端的进程。
2)COMMAND:命令的名称和参数,一般[]括起来的是内核线程,没有用户空间代码,没有程序文件名和命令行,通常名字采用以k开头,表示kernel。
3)udevd:用于维护设备文件,acpid负责电源管理,syslogd负责维护日志文件。
4)守护进程通常采用以d结尾的名字,表示Daemon。
注:守护进程本身也是孤儿进程,守护进程自成进程组,自成会话。0号进程是所有进程的祖先。
3.创建守护进程
1)调用umask将文件模式创建屏蔽字设置为0。
2)调用fork,然后使父进程exit。这样做有两个好处。第一,保证了子进程获得了一个新的进程ID,肯定不是一个进程组的组长进程。第二,当父进程退出时,shell认为这条命令执行完毕,shell被提到了前台,子进程进入后台运行。
3)调用setsid创建一个新会话,使调用进程成为新会话的首进程,并成为一个新进程的组长进程,没有控制终端。
4)将当前工作目录更改为根目录。因为原本的守护进程在系统在引导之前是一直存在的。
5)关闭不在需要的文件描述符,文件描述符也是一种资源,况且守护进程也不需要控制终端。
#include <unistd.h>
pid_t setsid(void);
//函数调用成功,返回新创建的Session的id,出错返回-1。
//注:在调用这个函数之前,当前进程不允许为进程组的组长进程
实现代码:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
void mydaemon(void)
{
int fd0;
pid_t pid;
struct sigaction sa;
umask(0);
if((pid=fork())<0)
{
perror("fork");
return;
}
else if(pid>0)
{
exit(0);
}
setsid(); //创建一个新会话。
sa.sa_handler=SIG_IGN;//当接收到SIGCHLD信号时,默认处理动作为忽略。
sigemptyset(&sa.sa_mask);
sa.sa_flags=0;
if(sigaction(SIGCHLD,&sa,NULL)<0) //使注册子进程退出忽略信号
{
return;
}
if(chdir("/"<0)) //将子进程的目录改为根目录
{
printf("child dir error");
return;
}
close(0);
close(1);
close(2);
//fd0=open("/dev/null",O_RDWR);
//dup2(fd0,1);
//dup2(fd0,2);
//关闭文件描述符,或者重定向到/dev/null
}
int main()
{
mydaemon();
while(1)
{
sleep(1);
}
//守护进程一直运行。main程序进入了休眠状态。
}
注:也可以使用系统函数daemon来创建一个精灵进程。