上次讲了关于线程的一些操作,这次讲一下进程,进程是程序运行的真正实例,就linux而言,进程结构可以分为三部分,代码段、数据段和堆栈段。余下的就不进行科普了
进程的创建通过fork来搞定,由于创建的进程会完美copy父进程的数据,数据段和堆栈段(说copy有些不好,毕竟现在操作系统实现了一种“写时复制”的机制,只有子进程修改了某些内容时,操作系统才会开辟空间把修改的内容从父进程copy过来,想一想就知道主要很省空间),所以调用一次fork会返回两次(堆栈段一样,可以看成父子进程执行的深度一样),父进程返回儿子的pid,子进程返回0,然后通过下面的操作就可以实现父子进程执行不同的操作。
pid = fork();
if(0 > fork){
printf("Create error\n");
exit(0);
}
else if(0 == fork){
printf("Parent process");
//do
exit(0)
}
printf("child process!");
//do
1、孤儿进程:爸比死掉的进程,如果创建的子进程的父进程在子进程结束之前挂掉了,那么子进程就成为孤儿进程,被init进程收养,孤儿进程实质上没什么危害。
2、僵尸进程:爸比还在,儿子挂掉了,但是爸比没有给儿子收尸(通过wait或者是waitpid函数),儿子就变成僵尸了。这里有一个小知识,子进程执行完了,按道理而言他的一切都应该被销毁,不应该留下一些什么。但是实际上一个进程执行完了,操作系统是会回收他的资源,但是保留了一个数据结构Zombie,记录这个进程是什么状态,怎么挂掉的,就像是某人挂了会留下一具尸体,然后验尸官可以得到你的死因。如果是仅仅占用些资源没啥大不了的,关键是僵尸进程还把进程号pid一拿着(死不瞑目啊),而系统的进程号又是有限的,僵尸进程多了,系统便不能创建新的进程了。通过上面所讲,可以知道为什么子进程挂了,父进程还是能够得到他的状态信息,通过上面的两个函数可以回收Zombie这部分资源。要不然只有等父进程挂了,由init进程收养,init会定期清理僵尸进程。
在linux下使用ps命令,看到-Z标志可以判定该进程是一个僵尸进程
3、守护进程:终端写多了就知道,一般开一个进程,打开了一个窗口,把窗口关了,进程也就结束了。而守护进程则是脱离于终端并且在在后台运行的进程,创建守护进程如下;
①创建子进程,父进程退出
②在子进程中创建新的会话——setsid()
使用系统函数setsid()建立新会话,并担任会话组的组长,让进程摆脱原会话、原进程组、原控制终端的控制,使进程完全独立出来,摆脱其它进程控制。
③改变当前工作目录为根目录:子进程进程父进程的当前工作目录,在实际运行中会对以后的的使用造成麻烦
④重设文件掩码:继承了父进程的文件掩码,对于有些操作不方便
⑤关闭文件描述符:对于继承自父进程子进程不需要的文件,关掉节约系统资源
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#define MAXFILE 65535
int main(void)
{
pid_t pid;
int i, fd, len;
char buf[100] = "Daemon process write!\n";
len = strlen(buf);
pid = fork();
if(0 > pid){
sleep(10);
printf("error");
}else if(0 < pid){
printf("Parent process!");
sleep(2);
exit(0);
}
printf("a daemon process!");
sleep(2);
//create a daemon process
setsid(); //1建立新会话
chdir("/"); //2改变工作目录为根目录
umask(0); //3重设文件权限掩码
for(i = 0; i < MAXFILE; ++i)
close(i); //4关闭文件描述符
while(1){
if(fd = open("//home/sakura/Desktop/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600) <0 ){
perror("open");
exit(1);
}
write(fd, buf, len + 1);
close(fd);
sleep(10);
}
printf("Over");
sleep(10);
return 0;
}