简单的 daemon process 的实现,主要的步骤是:fork -> setsid -> umusk -> chdir -> close fds -> openlog -> 服务程序!
server.c
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <string.h> #include <syslog.h> #include <fcntl.h> #include <sys/types.h> #include <time.h> #include <unistd.h> #include <sys/select.h> #include <netinet/in.h> #include <sys/socket.h> #define BUF_LEN 1024 #define SERV_PORT 60000 #define FD_SIZE 100 #define MAX_BACK 100 #define MAXFD 64 //extern int daemon_proc; int daemon_init( char * name, int f ) //!> 注意第一个参数:服务程序名称, facility { int i; pid_t pid; if( ( pid = fork() ) < 0 ) //!> fork 一个进程 { printf("fork error\n"); return -1; } else if( pid ) { _exit( 0 ); } if( setsid() < 0 ) //!> 新建一个会话组,成为leader { printf( "setsid error\n" ); return -1; } signal( SIGHUP, SIG_IGN ); //!> 注意要忽略挂起信号 //!> 因为上面的是会话头进程,下面是要被exit的,这时候发安出SIGHUP信号,为了不影响新的进程,那么必须忽略 if( ( pid = fork() ) < 0 ) //!> 再次fork( 为了不能自动获得终端 ) { printf("2 fork error\n"); return -1; } else if( pid ) { _exit( 0 ); //!> 会话leader退出 } //daemon_proc = 1; chdir( "/" ); //!> 改变目录 for( i = 0; i < MAXFD; i++ ) //!> 关闭所有的文件描述符( 安全性 ) { close( i ); } open( "/dev/null", O_RDONLY ); //!> 也是安全性问题 open( "/dev/null", O_WRONLY ); open( "/dev/null", O_RDWR ); openlog( name, LOG_PID, f ); //!> 产生log记录 return 0; } int main( int argc, char ** argv ) { time_t ticks; char buf[50]; int listenfd, connfd; int addrlen, len; struct sockaddr_in servaddr, chiaddr; daemon_init( argv[0], 0 ); //!> 设置为daemon程序 if( (listenfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("Create socket Error : %d\n", errno ); exit(EXIT_FAILURE ); } //!> 下面是接口信息 bzero( &servaddr, sizeof( servaddr ) ); //memset( &servaddr, 0, sizeof( servaddr ) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr =htonl( INADDR_ANY ); servaddr.sin_port = htons( SERV_PORT ); if( bind( listenfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) == -1 ) { printf("%d\n", listenfd); printf("bind error\n"); exit( 1 ); } if( listen(listenfd, MAX_BACK ) == -1 ) { printf( "listen error\n" ); exit( 1 ); } addrlen = len =sizeof( chiaddr ); FILE * f; while( 1 ) { len = addrlen; if( (connfd = accept( listenfd, (struct sockaddr*)&chiaddr, &len ) ) == -1) { //!> accept 返回的还是套接字 printf("Accept Error : %d\n", errno ); continue; } memset( buf, 0, sizeof( buf ) ); ticks = time( NULL ); sprintf( buf, "%s", ctime( &ticks ) ); //!> 时间server buf[strlen(buf)] = '\0'; /* f = fopen( "/home/pengtao/桌面/朝花朝拾、夕有新花/data_time 后台进程server/t", "w+" ); if( f ) { fwrite( buf, sizeof( buf ), 1, f ); fclose( f ); } */ write( connfd, buf, strlen( buf ) ); close( connfd ); } return 0; }
我们现在想想,为什么需要fork两次呢??????我们需要知道下面的知识!
守护进程、孤儿进程的关系:
首先:Deamon进程(守护进程)就是一个孤儿进程!!!
所以我们需要构造孤儿进程!
也就是:守护进程创建是特意而为的,创建的方式确实是让它 fork 出来的进程成为孤儿进程。
守护进程创建时是刻意让父进程结束执行(也就是代码中的fork两次后,将第一个子进程kill,那么由子进程创建ok的子进程就成为了orphan进程(孤儿进程)),让子进程被 init 接管,目的是不让守护进程有任何的 control terminal。然后还要调用 setsid 使它成为一个单独的 session 中的进程且只有这一个进程,除此之外还要关闭所有的文件描述符,将文件描述符 0, 1, 2 全部指向 /dev/null 保证它不会将信息打印到终端,不会读取用户输入。
Client.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/select.h> #define MAXLINE 1024 #define SERV_PORT 60000 //!> void send_and_recv( int connfd ) { char buf[50]; memset( buf, 0, sizeof( buf ) ); read( connfd, buf, sizeof( buf ) ); puts( buf ); } int main( int argc, char ** argv ) { //!> char * SERV_IP = "10.30.97.188"; char buf[MAXLINE]; int connfd; struct sockaddr_in servaddr; if( argc !=2 ) { printf("Input server ip !\n"); exit(EXIT_FAILURE ); } //!> 建立套接字 if( ( connfd= socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("Socket Error...\n" , errno ); exit(EXIT_FAILURE ); } //!> 套接字信息 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET, argv[1],&servaddr.sin_addr); //!> 链接server if( connect(connfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) < 0) { printf("Connect error..\n"); exit(EXIT_FAILURE); } //!> //!> send and recv send_and_recv( connfd ); //!> close(connfd ); return 0; }
gcc -o client client.c
./server
./client
简单DEMO而已~~~