Linux学习之旅(15)-----文件I/O(续)

阻塞和非阻塞

阻塞和非阻塞这是指的的对象实际上是进程。由于多个进程在并发执行共享系统资源,致使它们在运行过程中呈现出间断性的运行规律,所以进程在其生命周期中可能会具有多种状态。一般而言,每个进程只至少对应以下三种转态的其中一种:

(1)就绪状态(Ready):是指进程已处于准备好运行的状态,即进程已分配到除CPU以外的所有必要资源,只要拿到CPU就可以马上执行。如果系统中存在许多处于“就绪状态”的进程,通常将它们按一定的策略(如优先级策略)排成一个队列,称该队列为就绪队列。

(2)执行状态(Running):是指进程已获得CPU,其程序正在执行的状态。对任何一个时刻而言,在单机处理机系统中,只有一个进程处于执行状态,而在多处理机系统中,则可以有多个进程处于执行状态。

(3)阻塞状态(Block):是指正在执行的进程由于发生了某些事情(如:I/O请求、申请缓冲区失败等)暂时无法继续运行的状态,即进程的执行受到阻塞。此时,为了提高操作系统的效率,操作系统会将处理机分配给另一个就绪进程,而让受阻的进程处于暂停状态,一般将这种暂停状态称为阻塞状态。通常系统会将处于阻塞状态下的进程也排成一个队列,称为阻塞队列。实际上在一般的操作系统中,会按阻塞的原因不同,而设置多个阻塞队列,以提高操作系统的效率。

三种状态的转换

程序:从终端上读取信息(阻塞)

在写这个程序之前,首先要知道如何从一个终端上读写信息。向终端中写信息很简单,直接printf(),就可以直接将信息输出到信息。这是为什么那?在上一篇文章()中说过,一个程序在运行时会默认打开三个流(标准输入,标准输出,标准错误),那么在程序运行时如果遇到printf()这样操作那么就会向标准输出中写,这样输入的内容就会显示在屏幕上。同时,如果程序需要读数据,那么就读入标准输入流中。所有我们可以直接在标准输入中读取终端的信息,将它写在标准输出流中,就会显示在屏幕上了。

STDIN_FILENO     0  //标准输入流文件描述符
STDOUT_FILENO    1  //标准输出流文件描述符
STDERR_FILENO    2  //标准错误流文件描述符
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    //要保证开的缓冲区是可以装下输入的全部字符,不然就会出错。
    char buf[100];
    int len=read(STDIN_FILENO,buf,100);
    if(len<0)
    {   
        perror("read");
        exit(1);
    }   
    write(STDOUT_FILENO,buf,len);
    return 0;
}

如果运行了程序而不输入数据,那么程序就会一直等待,这就被称为阻塞。

通过ps -aux命令,我们查看一下这个程序的运行状态。(S为睡眠状态)

如果在终端上一直没有内容输入,那终端就会一直在哪里被阻塞,什么也不能干?这可是非常不好的,那如何解决这个问题那,那就需要在文件被打开是给文件指定O_NONBLOCK标志。这样如果终端没有数据读,read/write就是直接返回-1,同时将错误类型设置为EWOULDBLOCK或者EAGAIN(意思为“再试一次“)。如果应用到服务器上这种方式被称为”轮询模型(Poll)“(就是服务器按顺序将所有的客户端都询问一遍)。

程序:修改刚才的代码,使用O_NONBLOCK标志。

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    int fileDes=open("/dev/tty",O_RDWR|O_NONBLOCK);
    if(fileDes<0)
    {   
        perror("/dev/tty");
    }   
    char buf[10];
    int i=0;
    for(i=0;i<5;++i)
    {   
        int len=read(fileDes,buf,10);
        //如果没有检测到用户输入,就等一秒。
        if(len<0&&errno==EWOULDBLOCK)
        {
            printf("Please Inout:\n");
            sleep(1);
            continue;
        }
        else if(len<0)
        {
            perror("Error Tip:");
        }
        else
        {
            write(STDOUT_FILENO,buf,len);
            break;
        }
    }
    if(i==5)
    {
        char buf[]="Time out\n";
        write(STDOUT_FILENO,buf,sizeof(buf));
    }
    close(fileDes);
    return 0;
}

lseek

这个函数的作用是移动鼠标的位置和fseek()的功能是一样的。

off_t lseek(int fileDes,off_t offset,int whencn);

参数说明:

(1)fileDes:文件描述符。

(2)offset:偏移量。(相对相对位置的偏移量)

(3)whencn:相对位置。SEEK_SET(文件起始位置),SEEK_CUR(当前鼠标位置),SEEK_END(文件末尾位置)

(4)返回值:成功返回当前鼠标位置,失败返回-1.

程序:利用lseek()函数开辟一个4096K的文件。

使用lseek开辟的空间必须进行一次写操作,负责不会生效。

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
int main()
{
    //注意文件的权限位。
    umask(0);
    int fileDes=open("testFile1",O_RDWR|O_CREAT|O_EXCL,0777);
    if(fileDes<0)
    {
        perror("testFile1");
    }
    int off=lseek(fileDes,0x1000,SEEK_SET);
    //使用lseek开辟的空间必须进行一次写操作,负责不会生效。
    char buf[]="";
    int len=write(fileDes,buf,sizeof(buf));
    if(len<0)
    {
        perror("write");
    }
    close(fileDes);

    return 0;
}

fcntl

有细心的同学可能注意到在第二次对终端操作的时候,我们没有使用STDIN_FILENO,而是打开/dev/tty文件,这是为什么那?因为文件的标志是要在文件打开时被赋予的,而STDIN_FILENO在程序一开始就自动打开了,所以我们需要使用/dev/tty(表示当前终端)。哪有什么办法可以给打开的文件设置权限位吗?就是使用fnctl()函数。

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);

fcntl()函数位可变参函数。

int fcntl(fileDes,F_GETFL); //获取文件的标识位
int fcntl(fileDes,F_SETFL); //设置文件的标识位

程序:修改非阻塞终端读写函数(使用fcntl()函数)

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    //int fileDes=open("/dev/tty",O_RDWR|O_NONBLOCK);
    int fileDes=fcntl(STDIN_FILENO,F_GETFL);
    fileDes|=O_NONBLOCK;
    if(fcntl(STDIN_FILENO,F_SETFL,fileDes)==-1)
    {
        perror("STNIN_FILENNO");
        exit(1);
    }
    char buf[10];
    int i=0;
    for(i=0;i<5;++i)
    {
        //int len=read(fileDes,buf,10);
        int len =read(STDIN_FILENO,buf,10);
        //如果没有检测到用户输入,就等一秒。
        if(len<0&&errno==EWOULDBLOCK)
        {
            printf("Please Inout:\n");
            sleep(1);
            continue;
        }
        else if(len<0)
        {
            perror("Error Tip:");
        }
        else
        {
            write(STDOUT_FILENO,buf,len);
            break;
        }
    }
    if(i==5)
    {
        char buf[]="Time out\n";
        write(STDOUT_FILENO,buf,sizeof(buf));
    }
    close(fileDes);
    return 0;
}

ioctl

用来获取、设置设备的物理信息。

程序:获取当前设备的屏幕分辨率。

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/fb.h>

int main()
{
    //显示器对应的设备文件为fb0
    int fileDes;
    struct fb_var_screeninfo screen_info;
    fileDes=open("/dev/fb0",O_RDWR);
    ioctl(fileDes,FBIOGET_VSCREENINFO,&screen_info);
    printf("%d*%d\n",screen_info.xres,screen_info.yres);
    return 0;
}

800*600为系统默认屏幕(字符界面)分辨率,而非图像化界面的分辨率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值