歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

守護進程XINETD與SOCKET程序的含義

1.Xinetd提供被動式的超級服務,也就是服務程序是被使用端所啟動,平時則無須存在。例如,ftp, telnetd, pop3,imap, auth等等,這些服務沒有人使用時,無須啟動。此外,xinetd將socket轉換成stdin/stdout,因而使得網路服務程式設計大大簡化,您可以只用printf及fgets便可完成處理很復雜的網路協定。

2.一個簡單的服務程序 goodie

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
char *cmds[]={
"help",
"say",
"hello",
"bye",
"exit",
NULL
};
int getcmd(char *cmd)
{
int n=0;
while (cmds[n]!=NULL) {
if (strncasecmp(cmd,cmds[n],strlen(cmds[n]))==0) return n;
n++;
}
return -1;
}
void main(void)
{
char buf[1024];
int ok;
printf("Welcome to goodie service! ");
fflush(stdout);
ok=0;
do {
while (fgets(buf,1023,stdin)==NULL);
switch (getcmd(buf)) {
case -1: printf("Unknown command! "); break;
case 0:
      printf("How may I help you, sir? ");
      int j=0;
      while(cmds[j++]!=NULL)printf(” %s\t\t“,cmds[j-1]);
      break;
case 1: printf("I will say %s",&buf[3]); break;
case 2: printf("How're you doing today? "); break;
case 3: printf("Si ya, mate! "); ok=1; break;
case 4: printf("Go ahead! "); ok=1; break;
}
fflush(stdout);
} while (!ok);
}

3.配置文件

在/etc/services 中加入如下項

goodie 12345/tcp

服務名是 goodie 服務端口是 12345 服務類型是 TCP

在/etc/xinetd.d目錄下新檢服務文件 goodie

輸入內容:

service goodie
{
  socket_type=stream
  protocal   =tcp
  wait      =no
  server    = /{goodie dir}/goodir
  disable   =no #初始化為開啟服務監聽
}

4.啟動服務

先停止服務

killall xinetd

啟動服務

/usr/sbin/xinetd -f /etc/xinetd.conf

5.開啟終端連接

telnet localhost 12345

6.xinetd 的工作原理

通過配置xinetd,然後查看相應的套接字和進程,可以看出,xinetd是這樣工作的(針對tcp服務)

A.啟動時讀取/etc/xinetd.conf文件並為文件中指定的所有服務創建相應的套接字(流或數據報),xinetd能處理的服務的數目依賴於所創建的套接字數目。每個新創建的套接字都被加入到select調用所用到的描述符集中。

B.對每一個套接字調用bind,綁定服務端口(/etc/services中定義),端口號通過調用getservbyname獲得。

C.所有套接字建立後,調用select等待它們變為可讀,當tcp套接字上有數據報到來時變為可讀。xinetd在大部分時間阻塞在select的調用處;

D.xinetd守護進程fork,由子進程處理服務請求;子進程關閉除了要處理的套接字之外的所有描述字,子進程三次調用dup2,把套接字描述字復制到0、1、2,然後關閉原套接字;以後程序對套接字操作就是對0、1、2進行操作;子進程exec執行相應的服務器程序,並將配置文件中的參數傳遞。

E. 因為tcp服務器通常設置nowait標記,表示xinetd在該套接字上再次選擇之前,必須等待在該套接字上服務的子進程終止。所以,父進程中的fork返回時,把子進程的進程號記錄下來,這樣,在子進程終止時,父進程可以用waitpid的返回值查知是那一個子進程;父進程用FD_CLR宏關閉select使用的描述字集中與這個套接字對應的位,以便不對該套接字select;當子進程終止時,父進程收到一個SIGCHLD信號,父進程的信號處理程序得到終止子進程的進程號,父進程通過打開描述字集中對應的位恢復對該套接字的select。

7.重新實現的XINETD超級守護程序

A.程序利用上面的原理在思路上重新實現了超級守護進程。

B.程序在一些地方只是概念性的代碼,完全實現還有待補充。

C.程序能演示XINETD 的功能,但是在注釋有BUG 和NOTE 的地方還需改進

D.這是一個總結性的資料,參考了“利用INETD實現UDP守護進程“和“www.douzhe.com“上的文章。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
#include <sys/ioctl.h>
//為每一個服務分配一個包含socket,及路徑的信息結構。
struct param{
 int  sock;   //BIND的套接字
 char path[256]; //服務程序 路徑
 pid_t pid ;   //CHILD 進程ID
 struct param *next;
}
fd_set readfds;
struct param *phead,*ptail,*p;
/*
響應子進程結束的信號函數;
如果服務中wait=yes;
則bind socket 必須等到子進程結束才能監聽下一個請求
*/
void signal_handler(int signum)
{
 pid_t pid;
 pid= waitpid(-1,NULL,WUNTRACED);
 for(p=phead;p!=NULL;p=p->next)
   if(p->pid==pid)
{
     //FD_SET(p->sock,&readfds);
     printf("child exit pid= %d\n",pid);
     break;
    }
 signal(SIGCHLD,signal_handler);
}
int main(int argc,char *argv[])
{
 int sock,ss;
 int nret;
 int sock_len;
 struct sockaddr_in addr;
 struct sockaddr_in addr_clt;
 FILE *file;
 fd_set testfds;
 sock_len=sizeof(addr);

 p=(struct param*)malloc(sizeof(struct param));
 ptail=p;
 phead=NULL;

 p->sock=socket(AF_INET,SOCK_STREAM,0);
   // file=fopen("/etc/xinetd.conf","r");
   // while(1)//省略從"/etc/xinetd.conf"文件中讀每一個服務並啟動
 {
 addr.sin_family=AF_INET;
 addr.sin_addr.s_addr=htonl(INADDR_ANY);
 addr.sin_port=htons(9734);//從“/etc/service”文件中讀服務的端口號 getservbyname;
 bind(p->sock,(struct sockaddr *)&addr,sock_len);
 listen(p->sock,5);
  FD_ZERO(&readfds);
  FD_SET(p->sock,&readfds);

  strcpy(p->path,"~/socket/");//省略從"/etc/xinetd.conf"文件中讀每一個服務的路徑
  if(phead==NULL)
  {
   p->next=NULL;
   phead=ptail=p;
   printf("phead==null\n");
  }
 else
  {
   ptail->next=p;
   ptail=p;
   ptail->next=NULL;
  }
  printf("path=%s,sock=%d\n",phead->path,phead->sock);
}

 while(1)
{
 int fd;
 int clientfd;
 int n;
 pid_t pid;
 int flag=0;
 testfds=readfds;

  nret=select(FD_SETSIZE,&testfds,NULL,NULL,NULL);
  //if(nret<0){perror(strerror(errno));exit(5);}// bug 1:如果子進程END,會顯示“interrupt system call”錯誤。

  for(fd=0;fd<FD_SETSIZE;fd++)
  if(FD_ISSET(fd,&readfds))
   { //bug 2:第一次接收到串“sock addr=3.190.0.64,client sock=4”,以後用LOOPBACK 連接返回為"127.0.0.1"
clientfd=accept(fd,(struct sockaddr*)&addr_clt,&n);
    printf("sock addr=%s,client sock=%d\n",inet_ntoa(addr_clt.sin_addr),clientfd);
     pid=fork();
    switch(pid)
{
case -1:
  perror("error");
  exit(5);
    case 0:

  //dup2(clientfd,stdin);
     //dup2(clientfd,stdout);
     //dup2(clientfd,stderr);
     //close(clientfd);
  for(p=phead;p!=NULL;p=p->next)
  if(p->sock==fd)
      { printf("child runing\n");
  //execve(); //note 1:未調用SERVER PATH(FILE)
  }
      sleep(1);
  exit(5);
    default:// >0
  close(clientfd);
  if(flag) //note 2: 由service name 的wait 標志決定
  FD_CLR(fd,&readfds);
      for(p=phead;p!=NULL;p=p->next)
   if(p->sock==fd)
    {
         p->pid=pid;
         printf("sock:%d, child pid=%d\n",p->sock,p->pid);
    }
      signal(SIGCHLD,signal_handler);//note 3:放到開始,還是這兒呢?
     break;
}
   }

}
}

Copyright © Linux教程網 All Rights Reserved