上次讲了关于线程的一些操作,这次讲一下进程,进程是程序运行的真正实例,就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; }