目录
亲缘关系
亲缘关系主要体现于父子进程,子进程父进程创建,代码继承于父进程,父进程负责回收,子进程诞生至结束父进程全程参与,这种称为强亲缘关系。
系统开机后(字符Linux系统无UI)如何创建第一个终端:
终端子进程:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
printf("son %d ,parent %d\n",getpid(),getppid());
while(1)
sleep(1);
}
前台才能用命令,当前这个zhong是前台所以终端不能用命令了,终端子进程默认情况下也是唯一的前台进程。
或者是有加号的是前台。
进程组关系
进程组是一种系统管理单位,进程管理器进行组划分,组转化转义,便于系统管理多进程。
一个进程组是由一个组长进程N个组员进程构成。进程组长的唯一标识,pid=pgid。
终端子进程被创建均为组长进程。
普通进程的生命周期随着使用时长持续。
进程组中直到最后一个进程终止或转移,进程组为空,系统会释放进程组。
就近原则,组长进程创建子进程,都会默认归纳到同组,成为组员进程。
进程组的成员可以转移,变为其他组成员,进程组概念与亲缘概念没有必然联系。
大多数系统不允许组长变更。
getpgrp();返回进程组id
setpgid(pid_t pid,pid_t pid);//创建进程组或转移进程组。
创建进程,组长不允许使用,组员进程可以set(getpid(),getpid()),组员申请组,组id是自己id。
转移进程,组长无法转移,setpid(3000,5000);//3000转移到5000这个组。目标组得存在,并且对目标组有权限才能转移成功。
转移到别的组代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<3;i++)
{
pid=fork();
if(pid==0)break;
}
if(pid>0){
printf("parent %d,group %d\n",getpid(),getpgrp());
while(wait(NULL)>0);
}
else if(pid==0){
printf("child %d,group %d,i %d\n",getpid(),getpgrp(),i);
if(i==2){
sleep(5);
printf("create group\n");
setpgid(getpid(),getpid());
printf("child pid %d,group %d,i %d\n",getpid(),getpid(),i);
}
while(1)sleep(1);
}
else{
printf("fork fail\n");
exit(0);
}
return 0;
}
会话关系
一个终端下可能有终端子进程和其他终端进程构成,为了便于管理这些终端进程,使用会话关系管理。
会话由一个会话发起者和若干个会话参与者构成。会话发起者标志pid=gid=sid;
会话发起者结束后按组杀死参与者,杀死终端子进程的那一组。
getsid(getpid())//返回当前进程会话id
setsid()//创建新会话(创建组->创建会话)
终端子进程无法脱离终端必然被杀死。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<3;i++){
pid=fork();
if(pid==0)break;
}
if(pid>0){
printf("parent %d,group %d\n",getpid(),getpgrp());
while(wait(NULL)>0);
}
else if(pid==0){
printf("child %d,group %d,i %d,sid %d\n",getpid(),getpgrp(),i,getsid(getpid()));
while(1)sleep(1);
}
else{
printf("fork fail\n");
exit(0);
}
return 0;
}
一组都杀没了。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int i;
for(i=0;i<3;i++){
pid=fork();
if(pid==0)break;
}
if(pid>0){
printf("parent %d,group %d\n",getpid(),getpgrp());
while(wait(NULL)>0);
}
else if(pid==0){
printf("child %d,group %d,i %d,sid %d\n",getpid(),getpgrp(),i,getsid(getpid()));
if(i==2){
sleep(5);
printf("create group\n");
setsid();
printf("child pid %d,group %d,i %d,sid %d\n",getpid(),getpid(),i,getsid(getpid()));
}
while(1)sleep(1);
}
else{
printf("fork fail\n");
exit(0);
}
return 0;
}
关闭后6258躲过一劫。
孤儿态进程
父进程先于子进程退出,子进程失去管理,变为孤儿进程。
进程变为孤儿,父进程变更变为托管进程。所有孤儿进程结束后托管进程(upstart user可视化进程,在ubuntu14.04的版本为1init进程后续版本是init的子进程)负责处理这些僵尸进程,避免内存泄漏。
托管进程不干预孤儿进程执行,只是负责回收。
孤儿进程残留影响新进程的创建。孤儿进程的危害是弹性的,取决于孤儿进程的工作,如果孤儿进程持续申请系统资源,危害较大。
(错误信息的抛出:假如fork()失败返回-1,系统会有个errno记录,假如error=3,perror("fork error")找这个3在系统这个错误文件里找到对应错误的原因->STDERR_FIENO->使用用户自定义语句和错误信息进行拼接。)
孤儿进程是后台进程,由于父进程也没有了所以也不会出现像上面被一组杀死这种情况。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid;
pid=fork();
if(pid>0){
exit(0);
}
else if(pid==0){
while(1){
sleep(6);
printf("家人们谁懂啊,孤儿了\n");
}
}
else{
exit(0);
}
return 0;
}
这里能看出是后台进程。
这里能看见由于我们是老版本所以1号进程是托管进程。
怎么早发现早处理孤儿进程:
1.利用管道的特性处置孤儿进程,读端关闭,写端杀死。
2.kill(parent_pid,0)//测试进程是否存活。pthread_kill(tid,0)//测试线程是否存活。
要是多组的话那就很麻烦。
守护进程
守护进程又叫精灵进程,是典型的后台服务程序,其实也是孤儿进程。
守护进程的生命周期比普通进程要长,守护进程的生命周期随系统持续,开机启动,关机结束。
后台的守护进程主要职能,为主体(系统)提供持续的服务与支持。
后台服务程序,不能持续占用系统资源,会对系统造成很大的负担,所以它的工作模式都是低开销的。(抵销工作:间隔执行,定时执行,条件触发)
后台服务程序,不允许访问前台1资源。
守护进程的开发标准流程:
1.父进程创建子进程父进程退出
2.守护进程创建新会话脱离原有控制终端
3.关闭无用的描述符(STDIN_FILENO,STDOUT_FILENO)(STDERR_FILENO标准输出结果是打印在前台的,用dup2()文件描述符重定向,将错误信息重定向到特定文件中)
4.修改进程umask变为0002(修改进程掩码,避免跨平台执行出现权限异常问题)
5.修改进程的工作目录,改为根目录。(就像用u盘,将关键数据拷贝到目标主机,将服务器工作路径改为目标主机的某个路径)
6.执行守护进程的代码(任务)(低销)
7.守护进程的退出处理,资源释放等等任务。
然后配合开机启动就完成了。
开机启动:脚本队列在root下/etc/init.d/#脚本池
shell 脚本简单说明:
先看一下我们shell的版本。
再给一个执行权限。
如果是root用户下 sudo chmod 0775 jiaoben
然后./jiaoben就完事了。
#!/usr/bin/bash
#设置版本
ps aux
ls -l
date
编写一个开机启动守护进程:
开机启动脚本:
1.去找现有的脚本拷贝启动块信息,到自定义脚本中,适当修改。
2.赋予脚本执行权限
3.将自定义脚本剪切到脚本池
sudo update-rc.d shellname start 99 2.#设置启动
sudo update-rc.d shellname remove #取消启动
开机启动脚本:
#!/bin/bash
### BEGIN INIT INFO
# Provides: shouhuprocess
# Required-Start: $network
# Short-Description: one process
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
cd "/home/zzj/shouhu/"
./shouhuprocess
sudo chmod 0775 myshell
mv myshell /etc/init.d
sudo update-rc.d shellname start 99 2.#设置启动
守护进程:
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<time.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<signal.h>
int process_run(){
int timefd;
time_t tim;
char buf[1024];
bzero(buf,1024);
timefd=open("time.log",O_CREAT|O_RDWR,0664);
if(timefd==-1){
perror("time.log faild\n");
exit(0);
}
while(1){
tim=time(NULL);
ctime_r(&tim,buf);
write(timefd,buf,strlen(buf));
bzero(buf,1024);
sleep(3);
}
return 0;
}
int createprocess()
{
int errfd;
errfd=open("allerr",O_CREAT|O_RDWR,0664);
if(errfd==-1){
perror("allerr create faild\n");
exit(0);
}
pid_t pid;
pid=fork();
if(pid>0){
exit(0);
}
else if(pid==0){
//创建会话
setsid();
//关闭无用描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
//文件重定向
dup2(errfd,STDERR_FILENO);
//修改进程掩码
umask(0002);
//修改工作路径
chdir("./");
//执行守护进程任务
process_run();
//退出处理资源释放
}
else{
perror("fork fail\n");
exit(0);
}
return 0;
}
int main()
{
createprocess();
return 0;
}