线程不可中断睡眠与异步IO

jack_leiwin 2014-03-25 12:24:38
各位大神,你们好
最近写了一个简单的程序,pthread多线程并发,每个线程会读取一个文件中的一个文件地址的数据,大约有300个线程同时读取的时候,有几个线程进入了不可中断睡眠状态,最后只剩下几个大约5~6个,此时cpu利用率0,内存空间利用率不变,请问这是怎么回事儿啊?就剩下那么几个,这个读写的磁盘还不能够为他们服务吗?
1.我想问的问题就是为什么他们会进入不可中断睡眠?
2.有人说用异步I/O,异步IO可以通过设定信号和处理函数,或者线程回调函数来避免不可中断睡眠?请问这又是为什么?
一般的read函数读取时争夺资源,异步读取也是争夺资源,为什么同步read会进入不可中断睡眠,异步的不会呢?

谢谢!
...全文
395 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
jack_leiwin 2014-03-31
  • 打赏
  • 举报
回复
lucky-lucky 2014-03-31
  • 打赏
  • 举报
回复
引用 8 楼 jack_leiwin 的回复:
[quote=引用 7 楼 p569354158 的回复:] 疑问就是磁盘不响应读取?
磁盘一直在响应读取,但是它读写只能够供一个线程来用,考虑哲学家就餐问题,筷子被别人拿走了,等等别人把筷子放下了我再吃。[/quote] 循环读文件,这里面不会存在死锁吧,虽然最后的3~5个不可中断睡眠状态线程都在竞争磁盘,但是磁盘最终会按照优先级一个一个满足,是资源饥饿想象??不理解,这里为什么会有死锁? 前辈?[/quote] 按照我的理解,这里面不可能死锁,而读不到数据应该是资源饥饿状态。最重要的还是要看源码如何实现,如下,普通文件系统的读取函数实现:

static ssize_t uart_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)
{
    int ret = 0;

    struct my_dev *dev = filp->private_data; /*获得设备结构体指针*/

    if (NULL == dev)
    {
        printk(KERN_ERR "dev is NULL\n");

        return -ENXIO;
    }

	/*
	 * 读取函数假若获取不到信号量,若为不可中断读取,该函数一直不返回,若为可中断函数该函数
	 * 会立即返回,若系统调用为慢速系统调用,则根据实际需求返回后会重启系统调用。
	 * 第一个线程到这里来执行的时候,它首先获得了信号量,进入后续的代码并从磁盘读取数据。
	 * 之后的线程陆续到这里,全部卡在这里,它们一直在等待信号量,并没有尝试读取磁盘。
	 * 至于你说的死锁,我觉得好像不会出现。
	 * 另外进入读取函数内部可能会出现睡眠,因为使用DMA读取数据的时候是CPU是等待状态。
	 */
    if (down_interruptible(&dev->sem))
    {
        return -ERESTARTSYS;
    }
	/*add your code at here*/
    up(&dev->sem);

    return ret;
}
这是我的理解,打错无需笑话
jack_leiwin 2014-03-30
  • 打赏
  • 举报
回复
引用 7 楼 p569354158 的回复:
疑问就是磁盘不响应读取?
磁盘一直在响应读取,但是它读写只能够供一个线程来用,考虑哲学家就餐问题,筷子被别人拿走了,等等别人把筷子放下了我再吃。[/quote] 循环读文件,这里面不会存在死锁吧,虽然最后的3~5个不可中断睡眠状态线程都在竞争磁盘,但是磁盘最终会按照优先级一个一个满足,是资源饥饿想象??不理解,这里为什么会有死锁? 前辈?
lucky-lucky 2014-03-29
  • 打赏
  • 举报
回复
引用 6 楼 jack_leiwin 的回复:
[quote=引用 4 楼 p569354158 的回复:] 1.我想问的问题就是为什么他们会进入不可中断睡眠? 这与你打开的文件有关联,你打开的设备或文件系统实现时为了避免资源竞争内部添加了信号量,一般的实现,默认在open时为O_ONBLOCK类型,它们不会进入睡眠状态,除非你指定为了O_BLOCK,你在调用read系统调用的时候会进入睡眠状态,在你上面的代码当中因为线程函数未使用循环,所以在默认情况下,299个线程会依次全部退出(read返回-EAGAIN),只有一个线程会真正的读取。你所说的线程进入了不可中断睡眠除非你指定了O_BLOCK。 2.有人说用异步I/O,异步IO可以通过设定信号和处理函数,或者线程回调函数来避免不可中断睡眠?请问这又是为什么? 异步IO也有很多方式来实现,poll、select、信号驱动IO、aio系列。 这些异步IO的实现本质上是在内核里面创建了线程来等待数据的到来,然后再发送到用户空间,调用这些函数会立即返回,它们不会因为没有数据而进入睡眠状态,当有数据到来时,内核可以通过信号通知要求获取数据的进程,或者使用aio实时扩展。 一般的read函数读取时争夺资源,异步读取也是争夺资源,为什么同步read会进入不可中断睡眠,异步的不会呢? read当读不到数据的时候可能会阻塞,考虑你从网络上读取数据,调用recvfrom系统调用,该函数不会立即返回,而是等待有人向UDP端口发送数据后再返回。 而异步确保了系统调用会立刻返回,让你干其它的事情,等数据到来了会通知你。 下面是一个信号驱动IO的例子

/*
 * asynctest.c: use async notification to read stdin
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
 
int gotdata=0;
void sighandler(int signo)
{
    if (signo==SIGIO)
        gotdata++;
    return;
}
 
char buffer[4096];
 
int main(int argc, char **argv)
{
    int count;
    struct sigaction action;
 
    memset(&action, 0, sizeof(action));
    action.sa_handler = sighandler;
    action.sa_flags = 0;
 
    sigaction(SIGIO, &action, NULL);
 
    fcntl(STDIN_FILENO, F_SETOWN, getpid());
    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);
 
    while(1) {
        /* this only returns if a signal arrives */
        sleep(86400); /* one day */
        if (!gotdata)
            continue;
        count=read(0, buffer, 4096);
        /* buggy: if avail data is more than 4kbytes... */
        write(1,buffer,count);
        gotdata=0;
    }
}
另外给你一个aio_read的例子

#include <sys/types.h>
#include <aio.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
#include <string.h>
#include <stdio.h>
 
using namespace std;
 
const int SIZE_TO_READ = 100;
 
int main()
{
    // open the file
    int file = open("blah.txt", O_RDONLY, 0);
 
    if (file == -1)
    {
        perror("Unable to open file!");
        return 1;
    }
 
    // create the buffer
    char* buffer = new char[SIZE_TO_READ];
 
    // create the control block structure
    aiocb cb;
 
    memset(&cb, 0, sizeof(aiocb));
    cb.aio_nbytes = SIZE_TO_READ;
    cb.aio_fildes = file;
    cb.aio_offset = 0;
    cb.aio_buf = buffer;
 
    // read!
    if (aio_read(&cb) == -1)
    {
        cout << "Unable to create request!" << endl;
        close(file);
    }
 
    cout << "Request enqueued!" << endl;
 
    // wait until the request has finished
    while(aio_error(&cb) == EINPROGRESS)
    {
        cout << "Working..." << endl;
    }
 
    // success?
    int numBytes = aio_return(&cb);
 
    if (numBytes != -1)
    {
        cout << "Success!readed " << numBytes << " bytes" << endl;
        cout << buffer << endl;
    }
    else
    {
        cout << "Error!" << endl;
    }
 
    // now clean up
    delete[] buffer;
    close(file);
 
    return 0;
}
    
疑问就是磁盘不响应读取?[/quote] 磁盘一直在响应读取,但是它读写只能够供一个线程来用,考虑哲学家就餐问题,筷子被别人拿走了,等等别人把筷子放下了我再吃。
jack_leiwin 2014-03-29
  • 打赏
  • 举报
回复
引用 4 楼 p569354158 的回复:
1.我想问的问题就是为什么他们会进入不可中断睡眠? 这与你打开的文件有关联,你打开的设备或文件系统实现时为了避免资源竞争内部添加了信号量,一般的实现,默认在open时为O_ONBLOCK类型,它们不会进入睡眠状态,除非你指定为了O_BLOCK,你在调用read系统调用的时候会进入睡眠状态,在你上面的代码当中因为线程函数未使用循环,所以在默认情况下,299个线程会依次全部退出(read返回-EAGAIN),只有一个线程会真正的读取。你所说的线程进入了不可中断睡眠除非你指定了O_BLOCK。 2.有人说用异步I/O,异步IO可以通过设定信号和处理函数,或者线程回调函数来避免不可中断睡眠?请问这又是为什么? 异步IO也有很多方式来实现,poll、select、信号驱动IO、aio系列。 这些异步IO的实现本质上是在内核里面创建了线程来等待数据的到来,然后再发送到用户空间,调用这些函数会立即返回,它们不会因为没有数据而进入睡眠状态,当有数据到来时,内核可以通过信号通知要求获取数据的进程,或者使用aio实时扩展。 一般的read函数读取时争夺资源,异步读取也是争夺资源,为什么同步read会进入不可中断睡眠,异步的不会呢? read当读不到数据的时候可能会阻塞,考虑你从网络上读取数据,调用recvfrom系统调用,该函数不会立即返回,而是等待有人向UDP端口发送数据后再返回。 而异步确保了系统调用会立刻返回,让你干其它的事情,等数据到来了会通知你。 下面是一个信号驱动IO的例子

/*
 * asynctest.c: use async notification to read stdin
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
 
int gotdata=0;
void sighandler(int signo)
{
    if (signo==SIGIO)
        gotdata++;
    return;
}
 
char buffer[4096];
 
int main(int argc, char **argv)
{
    int count;
    struct sigaction action;
 
    memset(&action, 0, sizeof(action));
    action.sa_handler = sighandler;
    action.sa_flags = 0;
 
    sigaction(SIGIO, &action, NULL);
 
    fcntl(STDIN_FILENO, F_SETOWN, getpid());
    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);
 
    while(1) {
        /* this only returns if a signal arrives */
        sleep(86400); /* one day */
        if (!gotdata)
            continue;
        count=read(0, buffer, 4096);
        /* buggy: if avail data is more than 4kbytes... */
        write(1,buffer,count);
        gotdata=0;
    }
}
另外给你一个aio_read的例子

#include <sys/types.h>
#include <aio.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
#include <string.h>
#include <stdio.h>
 
using namespace std;
 
const int SIZE_TO_READ = 100;
 
int main()
{
    // open the file
    int file = open("blah.txt", O_RDONLY, 0);
 
    if (file == -1)
    {
        perror("Unable to open file!");
        return 1;
    }
 
    // create the buffer
    char* buffer = new char[SIZE_TO_READ];
 
    // create the control block structure
    aiocb cb;
 
    memset(&cb, 0, sizeof(aiocb));
    cb.aio_nbytes = SIZE_TO_READ;
    cb.aio_fildes = file;
    cb.aio_offset = 0;
    cb.aio_buf = buffer;
 
    // read!
    if (aio_read(&cb) == -1)
    {
        cout << "Unable to create request!" << endl;
        close(file);
    }
 
    cout << "Request enqueued!" << endl;
 
    // wait until the request has finished
    while(aio_error(&cb) == EINPROGRESS)
    {
        cout << "Working..." << endl;
    }
 
    // success?
    int numBytes = aio_return(&cb);
 
    if (numBytes != -1)
    {
        cout << "Success!readed " << numBytes << " bytes" << endl;
        cout << buffer << endl;
    }
    else
    {
        cout << "Error!" << endl;
    }
 
    // now clean up
    delete[] buffer;
    close(file);
 
    return 0;
}
    
疑问就是磁盘不响应读取?
jack_leiwin 2014-03-29
  • 打赏
  • 举报
回复
引用
一般的read函数读取时争夺资源,异步读取也是争夺资源,为什么同步read会进入不可中断睡眠,异步的不会呢? read当读不到数据的时候可能会阻塞,考虑你从网络上读取数据,调用recvfrom系统调用,该函数不会立即返回,而是等待有人向UDP端口发送数据后再返回。而异步确保了系统调用会立刻返回,让你干其它的事情,等数据到来了会通知你。
前辈,你好,你的回答然我学到对了很多! 其实,我的那个多线程程序中,每个线程会有一个ifstream对象,各自调用read读取数据,每个文件使用默认方式打开。如果读不到数据的话,按照您的说法应该是会阻塞,直到每个线程各自都读到数据。但是最后就剩下几个线程了,这几个线程处于不可中断睡眠状态,如果说是因为资源竞争,那么最后这几个线程,磁盘应该可以通过请求队列一个一个满足,最终各自的都会有数据读取,然后成功返回,他们一直等待难道是就这几个线程的读取请求磁盘不予相应?还是有别的原因了,比如说线程即使有数据读取也没有被唤醒? 异步读取中,发出读操作后返回继续执行下面的步骤,在需要数据的地方通过循环判断aio_error(&cb) == EINPROGRESS,来判断时候读取完成,但是会不会出现和上面的情况,就是磁盘一直不响应读取请求,然后一直循环判断,虽然线程不处于不可中断睡眠,但是线程一直也无法向前执行,这也是一个问题,难道这个不可能发生吗? 谢谢
lucky-lucky 2014-03-29
  • 打赏
  • 举报
回复
1.我想问的问题就是为什么他们会进入不可中断睡眠? 这与你打开的文件有关联,你打开的设备或文件系统实现时为了避免资源竞争内部添加了信号量,一般的实现,默认在open时为O_ONBLOCK类型,它们不会进入睡眠状态,除非你指定为了O_BLOCK,你在调用read系统调用的时候会进入睡眠状态,在你上面的代码当中因为线程函数未使用循环,所以在默认情况下,299个线程会依次全部退出(read返回-EAGAIN),只有一个线程会真正的读取。你所说的线程进入了不可中断睡眠除非你指定了O_BLOCK。 2.有人说用异步I/O,异步IO可以通过设定信号和处理函数,或者线程回调函数来避免不可中断睡眠?请问这又是为什么? 异步IO也有很多方式来实现,poll、select、信号驱动IO、aio系列。 这些异步IO的实现本质上是在内核里面创建了线程来等待数据的到来,然后再发送到用户空间,调用这些函数会立即返回,它们不会因为没有数据而进入睡眠状态,当有数据到来时,内核可以通过信号通知要求获取数据的进程,或者使用aio实时扩展。 一般的read函数读取时争夺资源,异步读取也是争夺资源,为什么同步read会进入不可中断睡眠,异步的不会呢? read当读不到数据的时候可能会阻塞,考虑你从网络上读取数据,调用recvfrom系统调用,该函数不会立即返回,而是等待有人向UDP端口发送数据后再返回。 而异步确保了系统调用会立刻返回,让你干其它的事情,等数据到来了会通知你。 下面是一个信号驱动IO的例子

/*
 * asynctest.c: use async notification to read stdin
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
 
int gotdata=0;
void sighandler(int signo)
{
    if (signo==SIGIO)
        gotdata++;
    return;
}
 
char buffer[4096];
 
int main(int argc, char **argv)
{
    int count;
    struct sigaction action;
 
    memset(&action, 0, sizeof(action));
    action.sa_handler = sighandler;
    action.sa_flags = 0;
 
    sigaction(SIGIO, &action, NULL);
 
    fcntl(STDIN_FILENO, F_SETOWN, getpid());
    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);
 
    while(1) {
        /* this only returns if a signal arrives */
        sleep(86400); /* one day */
        if (!gotdata)
            continue;
        count=read(0, buffer, 4096);
        /* buggy: if avail data is more than 4kbytes... */
        write(1,buffer,count);
        gotdata=0;
    }
}
另外给你一个aio_read的例子

#include <sys/types.h>
#include <aio.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
#include <string.h>
#include <stdio.h>
 
using namespace std;
 
const int SIZE_TO_READ = 100;
 
int main()
{
    // open the file
    int file = open("blah.txt", O_RDONLY, 0);
 
    if (file == -1)
    {
        perror("Unable to open file!");
        return 1;
    }
 
    // create the buffer
    char* buffer = new char[SIZE_TO_READ];
 
    // create the control block structure
    aiocb cb;
 
    memset(&cb, 0, sizeof(aiocb));
    cb.aio_nbytes = SIZE_TO_READ;
    cb.aio_fildes = file;
    cb.aio_offset = 0;
    cb.aio_buf = buffer;
 
    // read!
    if (aio_read(&cb) == -1)
    {
        cout << "Unable to create request!" << endl;
        close(file);
    }
 
    cout << "Request enqueued!" << endl;
 
    // wait until the request has finished
    while(aio_error(&cb) == EINPROGRESS)
    {
        cout << "Working..." << endl;
    }
 
    // success?
    int numBytes = aio_return(&cb);
 
    if (numBytes != -1)
    {
        cout << "Success!readed " << numBytes << " bytes" << endl;
        cout << buffer << endl;
    }
    else
    {
        cout << "Error!" << endl;
    }
 
    // now clean up
    delete[] buffer;
    close(file);
 
    return 0;
}
    
jack_leiwin 2014-03-26
  • 打赏
  • 举报
回复
大牛都到哪儿去了?
jack_leiwin 2014-03-25
  • 打赏
  • 举报
回复
引用 1 楼 linlan999 的回复:
上代码 -------
涉及到的代码很多,贴出来很乱 简单描述就是
void* pfunc(void *p)
{
     inputfile.read(100000000);
   ....   .... 
}
for(int i=0;i<300;i++)
{   ... ....
    pthread_create(,,pfunc,..);
}
linlan999 2014-03-25
  • 打赏
  • 举报
回复
上代码 -------

23,116

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧