unix/linux编程的一些问题.

1.如何避免僵尸进程,这个问题在写多进程网络程序经常会遇到.

  • 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起.
  • 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用waitpid回收.
  • 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号
  • 连续fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收估计还要自己做。

2.UNIX或Linux系统对信号是否有排队机制,这个问题来自bbs.chinaunix.net的一个帖子,最开始的时候是因为把信号和僵尸进程的概念搞错了,对waitpid的作用没有弄楚造成了一些理解上的失误.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

void sig_child(int signo)
{ pid_t pid;
int stat;
while( (pid = waitpid(-1,&stat,WNOHANG)) > 0)
{
printf("child %d exit/n",pid);
sleep(5);//这应该导致信号丢失吧?
}
return;
}

void child_func()
{
return;
}

int main()
{
pid_t pid;
int i = 0;

signal(SIGCHLD,sig_child);
for(;i < 5;i ++)
{
if( (pid = fork()) == 0)
{
child_func();
printf("child function finished/n");
exit(0);
}
else if(pid > 0)
{
continue;
}
else
{
printf("fork failed/n");
exit(1);
}
}
return 0;
}

输出是:

linux:~/test # gcc -o waitpid waitpid.c
linux:~/test # ./waitpid
child function finished
child 7449 exit
child function finished
child 7450 exit
child function finished
child 7454 exit
child function finished
child 7455 exit
child function finished
child 7459 exit

对于SIGCHILD信号(属于不可靠信号,不支持信号排队.),按理说应该有信号丢失,可为什么还是输出5次呢?这实际上没有把握清楚waitpid(-1,&stat,WNOHANG)函数的作用.它是用来检查并回收僵尸进程的,在这个程序里信号确实会丢失的.但waitpid函数不是由SIGCHILD信号驱动的.

3.sigsuspend函数的问题,对于原子操作的理解.

Unix提供了等待信号的系统调用,sigsuspend就是其中一个,在www.chinaunix.net上曾经讨论过一个关于该系统调用的问题,这里也做一下解疑。

CU网友讨论的问题的核心就是到底sigsuspend先返回还是signal handler先返回。这个问题Stevens在《Unix环境高级编程》一书中是如是回答的“If a signal is caught and if the signal handler returns, then sigsuspend returns and the signal mask of the process is set to its value before the call to sigsuspend.”,由于sigsuspend是原子操作,所以这句给人的感觉就是先调用signal handler先返回,然后sigsuspend再返回。但其第一个例子这么讲又说不通,看下面的代码:
CU上讨论该问题起于中的该例子:

#include <stdio.h>

#include "ourhdr.h"

static void        sig_int(int);

int main(void)
{
   sigset_t   newmask, oldmask, zeromask;

   if (signal(SIGINT, sig_int) == SIG_ERR)
      err_sys("signal(SIGINT) error");

   sigemptyset(&zeromask);

   sigemptyset(&newmask);
   sigaddset(&newmask, SIGINT);
   /* block SIGINT and save current signal mask */
   if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
      err_sys("SIG_BLOCK error");

   /* critical region of code */
   pr_mask("in critical region: ");

   /* allow all signals and pause */
   if (sigsuspend(&zeromask) != -1)
      err_sys("sigsuspend error");
   pr_mask("after return from sigsuspend: ");

   /* reset signal mask which unblocks SIGINT */
   if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
      err_sys("SIG_SETMASK error");

   /* and continue processing ... */ 
   exit(0);
}

static void sig_int(int signo) {
   pr_mask("/nin sig_int: ");
   return;
}

结果:
$a.out
in critical region: SIGINT
^C
in sig_int: SIGINT
after return from sigsuspend: SIGINT

如果按照sig_handler先返回,那么SIGINT是不该被打印出来的,因为那时屏蔽字还没有恢复,所有信号都是不阻塞的。那么是Stevens说错了么?当然没有,只是Stevens没有说请在sigsuspend的原子操作中到底做了什么?
sigsuspend的整个原子操作过程为:
(1) 设置新的mask阻塞当前进程;
(2) 收到信号,恢复原先mask;
(3) 调用该进程设置的信号处理函数;
(4) 待信号处理函数返回后,sigsuspend返回。
大致就是上面这个过程,原来signal handler是原子操作的一部分,而且是在恢复屏蔽字后执行的,所以上面的例子是没有问题的

4.如何知道服务器端/客户端是否断开连接?

5.如果write的字节数>socket发送缓冲区,tcp/udp做何处理?

  • 对于udp协议:

      如果应用进程写一个大于套接口发送缓冲区大小的数据报,内核将返回一个   EMSGSIZE错误.

  • 对于tcp协议:

对于阻塞I/O: 将会阻塞write系统调用直到数据完全copy到套接口发送缓冲区,在这种情况下一般只考虑是否有错误发生,不需要考虑写入了多少
对于非阻塞I/O: 在设定的发送时间范围内能发多少发多少,返回发送成功的字节数. 但是得注意返回错误的特定情况.在实际应用中,情况如下:
一般是用setsockopt函数设置发送阻塞的时间,然后调用send()发送数据,当超出这个时间,send函数会返回已发送的数据大小, 但是请注意此时缓存中可能还有些数据没有发送到网络上.
那么当在应用层再一次调用send函数时,就会报告经典的错误:
Resource temporarily unavailable
那么如果是阻塞情况,send函数会一直等到所有应用层的数据全部发送完毕再返回.

 


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值