Linux系统下读取目录操作及可重入函数介绍

2022-03-18 20:37:52 浏览数 (1)

在写文章之前,分享一下今晚看伟东山老师的直播收获心得。我自身是一个小菜鸟,第一次听QEMU模拟器软件,不过听完老师的介绍感觉这功能好强大,感觉都不用买硬件了来做实验的(不过还是建议买开发板来做实验,比较有感觉,因为它还是不能模拟出特别先进的芯片,以及无法模拟出类似于GPU等复杂的硬件,而且搞底层软件开发的,还是要有开发板来支持的;当然,当你手头不是宽裕的时候,这个时候QEMU还是可以派上一定的作用了,至少可以测试一般的外设功能的,还是很强大的,我自己也在摸索使用),这里有兴趣的小伙伴可以看这个教程--------http://wiki.100ask.org/100ask_imx6ul_qemu。同时也非常期待伟老师后期录制的新教学视频。

一、读取目录下的子文件:

1、在Linux系统下,打开和读取目录下的子文件主要是用opendir与readdir函数来操作的,我们解析一下这两个函数的原型:

a、opendir函数:

代码语言:javascript复制
 #include <sys/types.h>
 #include <dirent.h>
 DIR *opendir(const char *name);           //文件指针
 DIR *fdopendir(int fd);

注:opendir打开一个目录后得到一个DIR类型的指针给readdir使用。

b、readdir函数:

代码语言:javascript复制
#include <dirent.h>
struct dirent *readdir(DIR *dirp);                       //结构体指针,DIR *dirp是一个目录
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

注:1)readdir函数调用一次就会返回一个struct dirent类型的指针,这个指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(所谓目录项就是目录中的一个子文件)。

2)readdir调用一次只能读出一个目录项,要想读出目录中所有的目录项必须多次调用readdir函数。readdir函数内部户记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。

2、下面就来用代码来做一个简单的试验,我们来判断它的文件类型,是普通文件还是不是普通文件,下面是我是在 /mnt/hgfs/day

目录进行操作的,里面有8个子文件:

代码示例:

代码语言:javascript复制
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char **argv)
{
DIR *pDir = NULL;
struct dirent * pEnt = NULL;
unsigned int cnt = 0;
if (argc != 2)
{
    printf("usage: %s dirnamen", argv[0]);//这个argv[0]表示传进来的文件目录名
    return -1;
}
pDir = opendir(argv[1]);
if (NULL == pDir)
{
    perror("opendir");
    return -1;
}
while (1)
{
    pEnt = readdir(pDir);
    if(pEnt != NULL)
    {
        // 还有子文件,在此处理子文件
        printf("name:[%s]   ,", pEnt->d_name);
        cnt  ;
        if (pEnt->d_type == DT_REG)
        {
            printf("是普通文件n");
        }
        else
        {
            printf("不是普通文件n");
        }
    }
    else
    {
        break;
    }
};
printf("总文件数为:%dn", cnt);

return 0;
}

演示效果:

小结:这里也可以去尝试测试一下其他一些类型文件方法是一样的。

二、可重入函数介绍:

1、在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果有一个函数不幸被设计成为这样:那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。这样的函数是不安全的函数,也叫不可重入函数。相反,肯定有一个安全的函数,这个安全的函数又叫可重入函数那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括 static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。如果确实需要访问全局变量(包括 static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。

编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。 说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

示例:假设 Exam 是 int 型全局变量,函数 Squre_Exam 返回 Exam 平方值。那么如下函数不具有可重入性。

代码语言:javascript复制
  int Exam = 0;  //全局变量
  unsigned int example( int para )  
  {   

     unsigned int temp; 

     Exam = para; // (**)  

      temp = Square_Exam( ); 

        return temp;  
 }  

此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使 Exam 赋与另一个不同的 para 值,所以当控制重新回到 “temp = Square_Exam( )” 后,计算出的temp很可能不是预想中的结果。此函数应如下改进。

代码语言:javascript复制
 int Exam = 0;  

 unsigned int example( int para )   

 {  

unsigned int temp;

[申请信号量操作] //(1)  加锁  

Exam = para;  

temp = Square_Exam( );  

[释放信号量操作] //     解锁   

return temp; 

 }  

申请不到“信号量”,说明另外的进程正处于给 Exam 赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。

保证函数的可重入性的方法:

1)在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量);

2)对于要使用的全局变量要加以保护(如采取关中断、信号量等互斥方法),这样构成的函数就一定是一个可重入的函数。

3)注意定义可重入函数,函数内不能含有全局变量及static变量,不能使用malloc、free

注明:上面的readdir函数和我们前面接触的一些函数是不同的,首先readdir函数直接返回了一个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址。多次调用readdir其实readir内部并不会重复申请内存而是使用第一次调用readdir时分配的那个内存。这个设计方法是readdir不可重入的关键。readdir在多次调用时是有关联的,这个关联也标明readdir函数是不可重入的。

三、总结:

明天继续分享双链表操作的文章学习,今晚听伟老师的直播课收获还真蛮大的,更加明确了自己要走的方向,在这里也非常感谢老师的分享。

0 人点赞