唯有前进值得敬仰

---等得越久,相聚时越幸福
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

linux socket编程基础4

Posted on 2010-07-13 19:39  绿豆芽33  阅读(504)  评论(0编辑  收藏  举报

进程与信号这部分内容在linux 网络编程经常用到,如服务器守护进程,在此先概括一下基本知识。

一、进程

1.创建进程

 #include<unistd.h>

 pid_t fork(void);

父进程中,函数fork返回子进程的进程号,应该是一个正整数;子进程中,函数fork返回0,调用失败,返回-1。

2.创建新进程的必要处

 进程需要执行一个新的任务,而它自己同时执行另一个任务;例如,在网络程序中,父进程完成接收客户机连接的任务,

而子进程完成处理客户机请求的任务。

 一个进程需要执行另一个程序,它首先创建一个子进程,然后将子进程的正文改变为所有执行的程序。Shell进程就是以

这种方式执行用户输入的命令的。在这种场合下,子进程需要调用exec来改变正文部分。该系统调用有6种不同的形式,不

同的是参数。int execve,execl,execlp,execle,execv,execvp.

3.用户标识号

 两个难以区分的用户号:实际用户标识号与有效用户标识号。前者标识运行进程的用户,后者用于给新创建的文件赋所有权,

检查文件的存取权限和检查通过系统调用kill向进程发送信号的许可权限。

二、信号

1.捕获信号

 进程通过设置信号的处理函数来捕获一个信号,sigaction设置信号的处理函数。

 #include<signal.h>

 int sigaction(int signum,const struct sigaction*act,struct sigaction*oldact);

signum是除了SIGKILL和SIGSTOP外的任何信号,成功执行返回0,否则-1.

struct sigaction{

  void (*sa_handler)(int);//指定信号的动作,默认时为SIG_DFL,忽略为SIG_IGN,自定义时设置为用户的信号处理函数名

  sigset_t sa_mask;//哪些信号将被屏蔽

  int sa_flags;//

  void (*sa_restore)(void);//

};

处理一个信号的过程中,可能接受新的信号,可以选择性的屏蔽某些信号的处理,linux提供的函数有:

int sigprocmask(int how,const sigset_t*set,sigset_t*oldset);//改变当前被屏蔽的信号集

int sigpending(sigset_t *set);//检查当前未处理的信号,将他们的屏蔽码存储在set中

int sigsuspend(const sigset_t*mask);//暂时将进程的屏蔽信号集替换为参数mask的值,然后暂停进程,等待信号,函数返回之前,回复原来的屏蔽信号集。

提供的控制信号集的函数:

int sigemptyset(sigset_t *set);//清空信号集

int sigfillset(sigset_t *set);//填满信号集

int sigaddset(sigset_t *set,int signum);//向信号集中添加一个信号

int sigdelset(sigset_t *set,int signum);//删一个

int sigismember(const sigset_t *set,int signum);//是否属于信号集

向进程发信号函数:int kill(pid_t pid,int sig);//切莫望文生义

三、进程终止exit函数

linux系统中的进程执行exit来终止,系统释放进程的资源,删除进程的上下文,但是保留它的进程表项,向它的父进程发送信号SIGCHLD,此时进程处于Zombie状态。如何处理进程的死亡问题呢?

看一个例子:

int main()

{ int i;

 for(i=0;i<5;i++)

  if(fork()==0)

    exit(0);

 for(;;){}

}

程序运行时,执行ps x检查,可知:

上面标志“Z+”的就是僵尸进程(zombie)。不信可以通过 cat /proc/21191/status查看,里面有State项。

  如何处理SIGCHLD信号来消除僵尸子进程呢?

1.忽略SIGCHLD信号

struct sigaction act;

act.sa_handler=SIG_IGN;

sigemptyset(&act.sa_mask);

act.sa_flags=0;

sigaction(SIGCHLD,&act,NULL); 

当进程忽略该信号之后,内核将清除子进程的进程表项。这种做法近限于linux,有些unix不清除。

2.调用函数wait或waitpid等待子进程终止然后清除进程表项。

pid_t wait(int *statloc);//等待任何一个子进程终止;返回终止的子进程号,参数存储子进程的终止状态。

pid_t waitpid(pid_t pid,int *statloc,int option);//等待特定的子进程终止,option=WNOHANG时,如果没有终止的子进程,函数立即返回,不阻塞。

为了处理所有终止的子进程,可以循环的调用这两个函数:

while((pid=wait(&status))>0){

  printf("child %d died,exit code %d\n",pid,status);

}

这种处理方法使得父进程在所有子进程终止之前,不能再处理其他任务,所有一般在父进程没有其他任务时才使用这种方法。

3.捕获SIGCHLD信号——最理想的解决方案

在自己的函数中处理这个信号,如下;

void sigchld_handler(int signo)

{  pid_t pid;int status;

  while((pid=waitpid(-1,&status,WNOHANG))>0){}//不能用wait

  return;

}

void main(){

...

struct sigaction act;

act.sa_handler=sigchld_handler;

sigemptyset(&act.sa_mask);

act.sa_flags=0;

if(sigaction(SIGCHLD,&act,NULL)<0){

  perror("sigaction error\n");

  exit(1);

}

...

不影响父进程处理其他任务。

4.调用fork两次

父进程调用fork,然后子进程中再次调用函数fork,而子进程调用函数exit终止。

pid=fork();

if(pid==0){

  for(i=0;i<5;i++)

  if(fork()==0){printf("chile %d \n",getpid());

    sleep(1);

    exit(0);

  }//if

exit(0);

}//if

for(;;);