为了账号安全,请及时绑定邮箱和手机立即绑定

请问关于select函数可否等待多个信号量?如何实现?

/ 猿问

请问关于select函数可否等待多个信号量?如何实现?

守候你守候我 2019-09-20 19:14:56

select函数可否等待多个信号量?如何实现


查看完整描述

4 回答

?
梵蒂冈之花

使用select函数的部分代码如下:
//设置超时时间
timeval *ptimeval = new timeval;
ptimeval.tv_sec = 60;
ptimeval.tv_usec = 10;

m_Exit = FALSE;

while( m_Exit != TRUE)
{
select( maxfds, &readfds, &writefds, &exceptfds,ptimeval);
cout << “ time is out…”<< endl;
);
现象:第一次可以等待60秒后,退出Select函数,但是第二次进入Select函数后,瞬间就会退出,根本不会等待60秒,屏幕上“time is out"不间断的出现

原因:调用select之后,readfds的fd_count值由1变为0,所以瞬间返回,每次将readfds的fd_count值设为1,既每次用FD_SET来重置读集合,则功能正常实现

int sockfd;
fd_set fdR;
struct timeval timeout = ..;
...
for(;;) {
FD_ZERO(&fdR);
FD_SET(sockfd, &fdR);
switch (select(sockfd + 1, &fdR, NULL, &timeout)) {
case -1:
error handled by u;
case 0:
timeout hanled by u;
default:
if (FD_ISSET(sockfd)) {
now u read or recv something;
/* if sockfd is father and
server socket, u can now
accept() */
}
}
}


查看完整回答
反对 回复 2019-09-22
?
牧羊人nacy

在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv,recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。这是就需要用到非阻塞的编程方式,使用selcet函数就可以实现非阻塞编程。selcet函数是一个轮循函数,即当循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。Select的函数格式:intselect(intmaxfdp,fd_set*readfds,fd_set*writefds,fd_set*errorfds,structtimeval*timeout);select函数有5个参数第一个是所有文件节点的最大值加1,如果我有三个文件节点1、4、6,那第一个参数就为7(6+1)第二个是可读文件节点集,类型为fd_set。通过FD_ZERO(&readfd);初始化节点集;然后通过FD_SET(fd,&readfd);把需要监听是否可读的节点加入节点集第三个是可写文件节点集中,类型为fd_set。操作方法和第二个参数一样。第四个参数是检查节点错误集。第五个参数是超时参数,类型为structtimeval,然后可以设置超时时间,分别可设置秒timeout.tv_sec和微秒timeout.tv_usec。然后调用select函数,用FD_ISSET()函数判断节点是否可读写。返回值不为0表示可读写,为0表示不可读写。select函数的返回值为是一个整数,表示有几个节点可读写。先说明两个结构体:第一,structfd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set*),将一个给定的文件描述符加入集合之中FD_SET(int,fd_set*),将一个给定的文件描述符从集合中删除FD_CLR(int,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int,fd_set*)。第二,structtimeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。具体解释select的参数:intmaxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。fd_set*errorfds同上面两个参数的意图,用来监视文件错误异常。structtimeval*timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。返回值:返回状态发生变化的描述符总数。负值:select错误正值:某些文件可读写或出错0:等待超时,没有可读写或错误的文件




查看完整回答
反对 回复 2019-09-22
?
九州编程

REPEATS 决定总共生产的次数 (可以自己修改)
CONSUMER_SPEED 决定消费的速度 (越大越慢,可以自己修改)
PRODUCER_SPEED 决定生产的速度 (越大越慢,可以自己修改)

我的例子里,生产者生产一个随机数。另外消费速度比生产速度慢,所以可以看到输出中,+++ (生产者) 开头的出现的比--- (消费者)多,当生产者结束后,就只有 --- 打印了。

对这个程序由什么问题,可以baidu hi我。在linux/unix下用 gcc 编译。

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/stat.h>

#define REPEATS (10) /* count of production/consumption */

#define MAX_BUFFER_SIZE (8)

typedef struct
{
int bottom;
int top;

int data[MAX_BUFFER_SIZE];
} STRUCT_BUFFER;

STRUCT_BUFFER * pBuffer = NULL;

/* Define speed of consumer/producer, change them as u like */
#define PRODUCER_SPEED (1) /* 1/sec */
#define CONSUMER_SPEED (2) /* 1/2sec */

int sem_consume; /* consumer sem */
int sem_produce; /* producer sem */
int shm_buffer; /* shared buffer */

#define FLAG (IPC_CREAT | S_IRWXU)

/* Init semphores & shared buffer */
void init()
{
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;

shm_buffer = shmget(0x1111, sizeof(STRUCT_BUFFER), FLAG);
pBuffer = shmat(shm_buffer, 0, 0);
memset(pBuffer, 0, sizeof(STRUCT_BUFFER));

sem_consume = semget(0x2222, 1, FLAG);
arg.val = 0;
if (semctl(sem_consume, 0, SETVAL, arg) < 0)
{
perror("Consumer");
exit(1);
}

sem_produce = semget(0x3333, 1, FLAG);
arg.val = MAX_BUFFER_SIZE;
if (semctl(sem_produce, 0, SETVAL, arg) < 0)
{
perror("Producer");
exit(1);
}
}

/* destroy semphores & shared buffer */
void deinit()
{
shmctl(shm_buffer, IPC_RMID, NULL);
semctl(sem_consume, 0, IPC_RMID);
semctl(sem_produce, 0, IPC_RMID);
}

int main()
{
int pid, i;
struct sembuf sbuf;

init();

printf("Start fork...\n");
pid = fork();

if (pid > 0)
{
/* parent process, consumer */
for (i = 0; i < REPEATS; i++)
{
/* Try decrementing 1 from consumer */
sbuf.sem_num=0;
sbuf.sem_op=-1;
sbuf.sem_flg=0;
semop(sem_consume, &sbuf, 1);

/* OK */
printf("Consumer get %6d\n", pBuffer->data[pBuffer->bottom]);
pBuffer->bottom = (pBuffer->bottom+1)%MAX_BUFFER_SIZE;

/* Try incrementing 1 to producer */
sbuf.sem_op = 1;
semop(sem_produce, &sbuf, 1);

sleep(CONSUMER_SPEED);
}
wait(0);
shmdt(pBuffer);
}
else if (pid == 0)
{
srand(time(NULL));
/* child process, producer */
for (i = 0; i < REPEATS; i++)
{
/* Try decrementing 1 from producer */
sbuf.sem_num=0;
sbuf.sem_op=-1;
sbuf.sem_flg=0;
semop(sem_produce, &sbuf, 1);

/* OK */
pBuffer->data[pBuffer->top] = (rand()%1000)*1000 + i + 1;
printf("Producer put %6d\n", pBuffer->data[pBuffer->top]);
pBuffer->top = (pBuffer->top+1)%MAX_BUFFER_SIZE;

/* Try incrementing 1 to consumer */
sbuf.sem_op = 1;
semop(sem_consume, &sbuf, 1);

sleep(PRODUCER_SPEED);
}
shmdt(pBuffer);
exit(0);
}

deinit();
return 0;
}


查看完整回答
反对 回复 2019-09-22
?
慕的地6264312

可以,但是不是同时响应。是分先后的。
假如有任务A,B,C,优先级是1,2,3。任务A,B,等待任务C产生的信号量D.那么是任务A进入运行态,等A运行完了B再运行。
当然这里你不能设置A等待信号量清除。得让B去清。

查看完整回答
反对 回复 2019-09-22

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信