您的位置:首页 > 运维架构 > Linux

linux高编之信号守护进程

2013-10-14 22:23 183 查看
linux系统启动时会有很多系统服务进程,它们没有控制终端,不能直接和用户交互,在用户登录或运行程序时创建,在程序运行结束或用户注销时终止,这样的进程叫做守护进程。在linux终端下可用命令ps axj查看:



其中TPGID为-1的列就是系统中的守护进程。守护进程是很有用的,比如Internet服务器,Web服务器进程等等,都是运行在后台。守护进程运行在后台,类似于Windows中的系统服务。

2、创建守护进程

创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。

#include <unistd.h>

pid_t setsid(void);

该函数调用成功时返回新创建的Session的id(其实也就是当前进程的id),出错返回-1。注意,调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进程组的Leader也很容易,只要先fork再调用setsid就行了。fork创建的子进程和父进程在同一个进程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调用setsid就不会有问题了。

下面就写一个最简单的守护进程:

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

void daemonize(void)
{
	pid_t pid;

	if((pid=fork())<0){
		perror("fork");
		exit(1);
	}else if(pid!=0)	//如果是父进程就退出
		exit(0);
	setsid();		//在子进程中创建守护进程
		
	if(chdir("/")<0){	//改变守护进程工作路径
		perror("chdir");
		exit(1);
	}

	close(0);		//关闭文件描述符
	open("/dev/null",O_RDWR);
	dup2(0,1);		//将文件描述符1、2都重定向到0,然后再将其定向到/dev/null	
	dup2(0,2);
	
}
int main(void)
{
	daemonize();		//调用创建守护进程函数
	while(1);

	return 0;
}


运行程序之后查看,a.out已经变为了守护进程了:





之前写过一个服务器端与客户端通信的函数,现在学了守护进程了,我们可以将服务器端改为守护进程,这样服务器端就可以一直在后台运行了,其实很简单,就是在服务器端的函数中用fork创建一个进程,然后在子进程中调用我们刚才写的创建守护进程的函数就可以了,程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <signal.h>

#define MAXLINE 80

void daemonize(void)
{
	pid_t pid;

	if((pid=fork())<0){
		perror("fork");
		exit(1);
	}else if(pid!=0)
		exit(0);
	setsid();
	if(chdir("/")<0){
		perror("chdir");
		exit(1);
	}

	close(0);
	open("/dev/null",O_RDWR);
	dup2(0,1);
	dup2(0,2);
	signal(SIGCHLD,SIG_IGN);
	
}

int main(void)
{
	struct sockaddr_in servaddr,cliaddr;
	socklen_t cliaddr_len;
	int listenfd,connfd;
	int p,length=0;
	char *ip;
	char *port=NULL;
	FILE *fp_config;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	int i,n;
	pid_t pid;

	fp_config=fopen("config.txt","r");
	if(fp_config<0){
		printf("open file error!\n");
		return 1;
	}

	getline(&port,&length,fp_config);
	for(i=0;i<strlen(port);i++){
		if(port[i]=='\n')
			port[i]='\0';
	}
	p=atoi(port);

	daemonize();		//看这里,调用创建守护进程函数
	listenfd=socket(AF_INET,SOCK_STREAM,0);

	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr.sin_port=htons(p);

	bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	listen(listenfd,20);
	
	
	while(1){
		cliaddr_len=sizeof(cliaddr);
		connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
		n=read(connfd,buf,MAXLINE);
		for(i=0;i<n;i++)
			buf[i]=toupper(buf[i]);
		write(connfd,buf,n);
		close(connfd);
	}
}


这里有个注意的小地方,调用守护进程函数之后不能再次用文件描述符了,因为我在守护进程函数中已经把文件描述符重定向到/dev/null中了,若要再次使用,要重新定向。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: