借鉴其他文章,没有抄袭之意,只是为了方便记忆
什么是进程:进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随这资源的分配和释放,可以认为时一个程序的一次执行过程。
进程的五种状态:初始态,就绪态,运行态,挂起态,终止态。
进程切换:
分时操作系统中 是所有的进程通过时间片轮转的方式占用硬件资源。
实时操作系统中 按照实际情况来优先级高的先用硬件资源。(vxwork ucos )
区别:system() exec()都可以执行进程外的命令,system是在原进程上开辟一个新的进程,但是exec是用新的进程(命令)覆盖原有的进程。
进程创建:
ps -ajx| grep " " 命令的选项
ppid 父进程号 pid 进程号 command 进程名称
函数 pid_t fork(void)
所有writeable的东西都会复制,包括堆,栈,数据段,未初始化数据段,打开的文件描述符,信号安装过的handler,共享库,ipc(共享内存,消息队列,信号量)。
创建子进程方式:fork()创建子进程和父进程,空间独立,互不干扰。
vfork() 创建子进程和父进程,共用一个空间,子进程是优先进程。
wait:阻塞等待子进程退出,回收子进程残留资源,获取子进程退出原因
wait和waitpid调用一次只能清理一个子进程,应该使用循环
进程退出:main中的return;
main中的隐式返回 exit, _Eciy, _exit;
异常退出:调用Abort信号,产生SIGABRT信号(ctrl+c),其他(段错误)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main ()
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("error\n");
exit(1);
}else if(pid == 0)
{
printf("child[%d], parent is%d\n", getpid(), getppid());
exit(0);
}else if (pid > 0)
{
wait(NULL);
printf("parent %d\n", getpid());
exit(0);
}
return 0;
}
特殊进程:
孤儿进程:父进程先于子进程退出,子进程被init进程收养。(无害)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//孤儿进程
int main ()
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("error\n");
exit(1);
}else if(pid == 0)
{
sleep(1);
printf("child[%d], parent is%d\n", getpid(), getppid());
exit(0);
}else if (pid > 0)
{
printf("parent %d\n", getpid());
exit(0);
}
return 0;
}
僵尸进程:子进程退出时没有通知父进程,父进程未能把子进程所占用的进程PID等资源进行回收。(有害)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//僵尸进程
int main ()
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("error\n");
exit(1);
}else if(pid == 0)
{
printf("child[%d], parent is%d\n", getpid(), getppid());
exit(0);
}else if (pid > 0)
{
sleep(10);
printf("parent %d\n", getpid());
exit(0);
}
return 0;
}
守护进程:一种运行在后台的一种特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。(日志文件 时间函数 time 和前台脱离关系)
#include <stdio.h>
#include <unistd.h> //fork
#include <stdlib.h> //exit
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
int main ()
{
umask(0); //防止子进程继承的文件模式权限不够
pid_t pid = fork();
if(pid > 0)
{
exit(0);
}
setsid();//进程组,调用进程没有控制的终端,有联系也会切断
pid = fork();
if(pid > 0)
{
exit(0);
}
close(0); //关闭不需要的文件描述符,或者重定义到“/dev/null”中,
// “/dev/null”是一个无底洞,专门用来处理需要被丢弃的东西。
close(1);
close(2);
//chdir("./");//根目录
signal(SIGINT, SIG_IGN);//忽略SIGCHLD信号
int fd = open("a.txt", O_RDWR | O_CREAT, 0777);
while(1)
{
time_t t1;
time(&t1);
write(fd, "abc ", 4);
// lseek(fd, 2, SEEK_CUR);//当前位置
write(fd, ctime(&t1), strlen(ctime(&t1)));
sleep(5);
}
close(1);
return 0;
}
守护进程为什么要fork两次?
第一次fork:第一次fork的作用是让Shell认为这条命令已经终止,不用挂在终端输入上;在一个是为了后面的setsid服务,因为调用setsid函数的进程不能是进程组的组长。所以,到了这里子进程便成了一个新会话组的组长。
第二次fork:第二次不是必须的,主要目的是:放置进程再次打开一个控制终端,因为打开一个控制终端的前台条件是该进程必须是会话组长,再次fork,子进程ID!=sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。