我想做一个简单的程序,父进程创建一些子进程;在孩子暂停()之前,他们通知父亲过程。 子进程运行正常,但是父进行select等,否则child已经写入套接字;错误在哪里?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
typedef struct{
pid_t pid;
int sockfd;
}Child;
void err_exit(char* str)
{
perror(str);
exit(EXIT_FAILURE);
}
int convert_int(char* str)
{
int v;
char*p;
errno = 0;
v = strtol(str,&p,0);
if(errno != 0 || *p != '\0')
err_exit("errno");
return v;
}
void child_job(pid_t pid,int sockfd)
{
int v = write(sockfd,"1",1);
if(v == -1)
err_exit("write");
printf("process %d in pause()\n",pid);
pause();
}
int main(int argc, char* argv[])
{
int nsel;
fd_set masterset;
int n_child,i;
int sockfd[2];
pid_t pid;
Child* c = NULL;
if(argc != 2)
err_exit("usage: <awake2> #children\n");
FD_ZERO(&masterset);
n_child = convert_int(argv[1]);
c = malloc(n_child*sizeof(Child));
if(c == NULL)
err_exit("malloc");
for(i = 0; i <n_child; i++){
if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)) < 0) { //create socket between child and father
perror("errore in socketpair");
exit(1);
}
if ((pid = fork()) > 0) {
if (close(sockfd[1]) == -1) { //father process closes sockfd[1]
perror("errore in close");
exit(1);
}
c[i].pid = pid;
c[i].sockfd = sockfd[0];
FD_SET(c[i].sockfd, &masterset);
}
else if(!pid)
child_job(getpid(),c[i].sockfd);
}
for(;;){
if ((nsel = select(n_child+1, &masterset, NULL, NULL, NULL)) < 0) {
perror("errore in bind");
exit(1);
}
int i;
for(i = 0; i <n_child; i++){
if(FD_ISSET(c[i].sockfd, &masterset)) {
printf("changed fd\n");
}
}
}
}
答案 0 :(得分:1)
有一件事是错误的,你将c[i].sockfd
传递给child_job()
。在父进程中,它被设置为对中的第一个套接字fd,但在子进程中调用child_job()
,其中c
永远不会被设置为任何内容。您正在传递malloc
内存的原始内容。将其更改为child_job(getpid(), sockfd[1]);
,您就会越来越近。
另一件事是select
的第一个参数可能太低了。 n_child
是子项的数量,但您需要在此处传递一个数字,该数字大于集合中的最高文件描述符。例如,使用参数1
运行程序,以便创建1个子项。它可能以文件描述符0,1和2打开,因此套接字对将是文件描述符3和4. 3进入fd_set
,但是第一个选择的参数是1 + 1 = 2。选择忽略你的fd 3,因为它高于限制。
要解决此问题,请在int maxfd;
附近创建一个新变量fd_set
,并在-1
集合时将其初始化为FD_ZERO
,并在每次调用{{1}后将其初始化为FD_SET
更新它:
if( [whatever fd you just gave to FD_SET] > maxfd)
maxfd = [whatever fd you just gave to FD_SET];
并使用maxfd+1
作为第一个参数调用select。
(或者可以切换到poll
)
这应该让你足够远,你的第一个选择呼叫工作。之后,您会发现更多问题。
您传递给选择的fd_set
将被修改(这就是您之后可以对其进行FD_ISSET
测试的原因)。如果你回到循环的顶部并再次传递它而不重新初始化它,select将不再查看所有文件描述符,只是在第一次调用中准备好的文件描述符。要解决此问题,请在选择调用之前再创建一个fd_set
并将主服务器复制到其中,并且永远不要将主服务器传递给select。 (或者您可以通过扫描子表每次从头开始重建集。)
如果你从select中得到一个可读的fd,你应该在再次调用select之前阅读它,否则你只是在一个&#34;吃CPU调用select over over&#34;循环。