进程控制
Linux 中的进程包含3个段,分别为“数据段”、“代码段”和“堆栈段”。
· “数据段”存放的是全局变量、常数以及动态数据分配的数据空间(如malloc 函数取得的空间)等。
· “代码段”存放的是程序代码的数据。
· “堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量。
Linux 下的进程管理包括启动进程和调度进程,Linux 下启动一个进程有两种主要途径:手工启动和调度启动。调度进程包括对进程的中断操作、改变优先级、查看进程状态等。
1 /*execlp.c*/ 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 void main(void) 6 { 7 if(fork()==0) 8 { 9 /*调用execlp函数,这里相当于调用了“ps -ef”命令*/ 10 if(execlp("ps","ps","-ef",NULL)<0) 11 perror("execlp error!"); 12 } 13 }
exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit 系统之前要检查文件的打开情况,把文件缓冲区中的内容写回文件。
wait函数是用于使父进程(也就是调用wait的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。如果该父进程没有子进程或者他的子进程已经结束,则wait
就会立即返回。
waitpid的作用和wait一样,但它并不一定要等待第一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait功能,也能支持作业控制。实际上wait函数只是waitpid函
数的一个特例,在Linux内部实现wait函数时直接调用的就是waitpid函数。
1 /*waitpid.c*/ 2 #include <sys/types.h> 3 #include <sys/wait.h> 4 #include <unistd.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 void main(void) 8 { 9 pid_t pc,pp; 10 pc=fork(); 11 if(pc<0) 12 printf("Error fork.\n"); 13 /*子进程*/ 14 else if(pc==0) 15 { 16 /*子进程暂停5s*/ 17 sleep(5); 18 /*子进程正常退出*/ 19 exit(0); 20 } 21 /*父进程*/ 22 else 23 { 24 /*循环测试子进程是否退出*/ 25 do 26 { 27 /*调用waitpid,且父进程不阻塞*/ 28 pp=waitpid(pc,NULL,WNOHANG); 29 /*若子进程还未退出,则父进程暂停1s*/ 30 if(pp==0) 31 { 32 printf("The child process has not exited\n"); 33 sleep(1); 34 } 35 }while(pp==0); 36 /*若发现子进程退出,打印出相应情况*/ 37 if(pp==pc) 38 printf("child process exit,Get child %d\n",pp); 39 else 40 printf("some error occured.\n"); 41 } 42 }
守护进程,也就是通常所说的Daemon 进程,是Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事
件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的。一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进 程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。守护进程的名称通常以d结尾,比如sshd、xinetd、crond等。
编写守护进程:
1.创建子进程,父进程退出
pid=fork();
if(pid>0)
exit(0);
2.在子进程中创建新会话
· 进程组
进程组是一个或多个进程的集合。进程组由进程组ID 来惟一标识。除了进程号(PID)之外,进程组ID 也一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程ID 不会因组长进程的退出而受到影响。
· 会话期
会话组是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用。
· 让进程摆脱原会话的控制。
· 让进程摆脱原进程组的控制。
· 让进程摆脱原控制终端的控制。
setsid 函数能够使进程完全独立出来,从而脱离所有其他进程的控制。
3.改变当前目录为根目录
进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。
4.重设文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有一个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程
的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。
5.关闭文件描述符
6.处理SIGCHLD信号
处理
SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将
SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN); 这样,内核在子进程结束时不会产生僵尸进程。
该守护进程每隔10s在/tmp/dameon.log中写入一句话:
1 /*dameon.c创建守护进程*/ 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<fcntl.h> 6 #include<sys/types.h> 7 #include<unistd.h> 8 #include<sys/wait.h> 9 #define MAXFILE 65535 10 void main(void) 11 { 12 pid_t pc; 13 int i,fd,len; 14 char *buf="This is a Dameon\n"; 15 len =strlen(buf); 16 pc=fork(); //第一步 17 if(pc<0) 18 { 19 printf("error fork\n"); 20 exit(1); 21 } 22 else if(pc>0) 23 exit(0); 24 /*第二步*/ 25 setsid(); 26 /*第三步*/ 27 chdir("/"); 28 /*第四步*/ 29 umask(0); 30 for(i=0;i<MAXFILE;i++) 31 /*第五步*/ 32 close(i); 33 /*这时创建完守护进程,以下开始正式进入守护进程工作*/ 34 while(1) 35 { 36 if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0) 37 { 38 perror("open ERROR"); 39 exit(1); 40 } 41 write(fd, buf, len+1); 42 close(fd); 43 sleep(10); 44 } 45 }
利用库函数daemon()函数创建守护进程,其函数原型:
#include <unistd.h>
int daemon(int nochdir, int noclose);
nochdir:=0将当前目录更改至“/”
noclose:=0将标准输入、标准输出、标准错误重定向至“/dev/null”
返回值:成功:0 失败:-1