組み込みIOプログラミング

序文:

1. IO プログラミング: データとファイルの間で相互に通信するプロセス。IO プログラミングは、ファイルの入出力操作です。 

2. IO プログラミングは、標準 IO とファイル IO に分かれています。

両者の違いを簡単に紹介しましょう。

        標準 IO: ASIC で定義された I/O 操作用の一連の関数。C ライブラリ操作アプリケーション層をサポートする C 言語ライブラリを指します。高度な IO の一種です。主な操作対象は通常のファイルです。システムのオーバーヘッドを軽減するため、標準I/O使用時にユーザー領域にバッファを作成し、読み書き時にバッファを動作させ、その後適切なタイミングでシステムコールにより実空間にアクセスすることで負荷を軽減します。システムコールの数。

        ファイル I/O: ファイル IO は POSIX (Portable System Interface) によって定義されており、UNIX や Linux などの多くのシステムが POSIX 標準をサポートしています。ファイル IO は通常、デバイス ファイルなどの特殊なファイルを対象とします。ただし、ファイル IO は C ライブラリをサポートしていません。 

 

3. Linux ではすべてがファイルであり、ファイルは次の 7 つのカテゴリに分類できます。

  普通文件          -
    目录文件        d
    管道文件        p 
    字符设备        c
    块设备          b
    链接文件        l 
    套接字          s 

1. Linux標準I/Oプログラミング

1. 標準 IO コア ---- ストリーム

1.1 まず、ストリームとは何かについて説明します。

        標準 IO の中心となるオブジェクトはストリームです。標準 IO を使用してファイルを開くと、ファイルを記述する FIFE 構造が自動的に作成され、この構造をストリームと呼びます。標準 I/O は主に「ストリーム」上で動作します。

struct _IO_FILE {
   int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */
 #define _IO_file_flags _flags
 
   /* The following pointers correspond to the C++ streambuf protocol. */
   /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
   char* _IO_read_ptr;   /* Current read pointer */
   char* _IO_read_end;   /* End of get area. */
   char* _IO_read_base;  /* Start of putback+get area. */
   char* _IO_write_base; /* Start of put area. */
   char* _IO_write_ptr;  /* Current put pointer. */
   char* _IO_write_end;  /* End of put area. */
   char* _IO_buf_base;   /* Start of reserve area. */
   char* _IO_buf_end;    /* End of reserve area. */
   /* The following fields are used to support backing up and undo. */
   char *_IO_save_base; /* Pointer to start of non-current get area. */
   char *_IO_backup_base;  /* Pointer to first valid character of backup area */
   char *_IO_save_end; /* Pointer to end of non-current get area. */
 
   struct _IO_marker *_markers;
   struct _IO_FILE *_chain;
1.2. Linux スタートアップファイル

    Linux には、プログラムを実行するたびに自動的に
                                                 ファイル ポインタ を開く
    標準入力 (ファイル) 標準入力
    (ファイル) 標準入力 (ファイル) 標準
    出力 (ファイル) 標準出力画面 標準エラー出力 (ファイル) 標準エラー出力画面 の3 つの特殊ファイルがあります。

2. 標準 IO バッファリング

標準 IO バッファリングには、フル バッファリング、ライン バッファリング、バッファリングなしの 3 つのタイプがあります。

        バッファーがいっぱいです: バッファーがいっぱいであるか、fflush を使用してリフレッシュします。

        行の折り返し: バッファがいっぱいであるか、fflush を使用してフラッシュされているか、または \n が発生しました。

        バッファなし: 配列をバッファに書き込みます。書き込まれた各文字はすぐにフラッシュされます。

3. ストリーム操作

        ストリーム操作には、開く、閉じる、読み取りと書き込み、位置決めなどが含まれます。

3.1 ファイルを開く:
       #include <stdio.h>
            //打开文件
            FILE *fopen(const char *pathname, const char *mode);
            参数:
                pathname:文件路径
                mode:打开方式
                   r    只读的方式打开文件,文件必须存在。
                   r+   读写的方式打开文件,文件必须存在。
                   w    只写的方式打开文件,文件不存在则创建,存在则清空
                   w+   读写的方式打开文件,文件不存在则创建,存在则清空  
                   a    追加只写方式打开文件,文件不存在则创建,存在则追加写入  
                   a+   追加读写方式打开文件,文件不存在则创建,存在则追加写入  
                   
            返回值:
                成功返回文件指针,失败返回 NULL ,并且设置错误号。
3.2 ファイルの読み取りと書き込み

3.2.1 フォーマットされた読み取りと書き込み:

格式化输出: 

#include <stdio.h>
            int printf(const char *format, ...);
            //向stream指向的文件中输出数据
            int fprintf(FILE *stream, const char *format, ...);
            //向内存中输出数据,因为没有指定输出数据的大小,容易造成内存泄漏,因此不安全
            int sprintf(char *str, const char *format, ...);
            //向内存中输出数据,指定了输出数据大小,因此类型安全   
            int snprintf(char *str, size_t size, const char *format, ...);
            参数:
                str:内存首地址
                size:内存的大小
                format:数据的格式
                变参:要输入的数据
格式化输入:

 #include <stdio.h>
            //从标准输入读取数据
            int scanf(const char *format, ...);
            //从steam指定的文件中输入读取数据
            int fscanf(FILE *stream, const char *format, ...);           
            //从指定的内存中输入数据
            int sscanf(const char *str, const char *format, ...);
3.2.2 フォーマットされていない入出力

非格式化的输入输出主要依赖于库函数的调用:

1>按字符读写:
            
                #include <stdio.h>               
                int fgetc(FILE *stream);//从steam指定的文件中读取一个字符,是一个函数   
                int getc(FILE *stream);//从steam指定的文件中读取一个字符,是一个宏  
                int getchar(void); //从标准输入读取一个字符    
                参数:
                    stream:文件指针
                返回值:
                    成功一个字符(int),出错或者到达文件末尾EOF。

                #include <stdio.h>       
                int fputc(int c, FILE *stream); //向stream指向的文件中输入一个字符,是一个函数             
                int putc(int c, FILE *stream); //向stream指向的文件中输入一个字符,是一个宏 
                int putchar(int c); //向标准输出输出一个字符 
                参数:
                    c:要输入的字符
                    stream:文件指针

2>按行读写:
                #include <stdio.h>
               //向stream指向文件中读取一行数据,保存到s指向的内存中
               //会将\n 一起读入到内存中
               //最多只能读size -1 个字符,在末尾主动添加 \0
                char *fgets(char *s, int size, FILE *stream);
                参数:
                    s:内存首地址
                    size:读取字符的个数
                    stream:文件指针
                返回值:
                    成功返回内存首地址,失败或者读到文件末尾返回NULL

                // 默认向键盘获得一行数据,保存到s指向的内存中,因为没有限制内存长度,
               // 容易造成内存泄漏
               char *gets(char *s);            
                #include <stdio.h>
                //向stream指向文件中写入一行数据
                int fputs(const char *s, FILE *stream);
                参数:
                    s:内存首地址
                    stream:文件指针  
                //默认向标准输出打印一行数据,保存到s指向的内存中,因为没有限制内存长度
                // 容易造成内存泄漏
                int puts(const char *s);  

3>按快读写:

                #include <stdio.h>
                //按块读取
                size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
                参数:
                    ptr:内存首地址
                    size:读取数据对象的大小
                    nmemeb:读取的个数
                    stream:文件指针 
                返回值:
                    实际读取到的个数,失败返回0
                                
                //按块写入
                size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
               参数:
                    ptr:内存首地址
                    size:写入数据对象的大小
                    nmemeb:写入的个数
                    stream:文件指针 
                返回值:
                    实际写入到的个数,失败返回0 






3.3 ファイルストリームの位置付け

 文件指针的偏移和定位
    文件指针的偏移
            #include <stdio.h>
            //设置文件指针的偏移
            int fseek(FILE *stream, long offset, int whence);
               参数:
                stream:文件指针
                offset:相对于whence的偏移量
                whence:偏移的起始位置
                    SEEK_SET, 文件的起始位置
                    SEEK_CUR, 文件指针当前的位置
                    SEEK_END, 文件的末尾
                返回值:成功返回0,失败返回-1 ;
    文件指针的定位
            //定位文件指针的当前位置
            long ftell(FILE *stream);
            参数:
                stream:文件指针
            返回值:成功返回文件指针当前位置,失败 -1 ;
3.4 ファイルを閉じます。
   #include <stdio.h>
            //关闭打开的文件
            int fclose(FILE *stream);
3.5 時刻取得
获取标准时间:
            #include <time.h>
            time_t time(time_t *tloc);
            参数:
                tloc:存放标准时间的地址         
            返回值:成功返回标准时间,失败返回-1 ,并且设置错误号。    
    获得字符串时间
            char *ctime(const time_t *timep);
            参数:
                timep:标准时间 
            返回值:成功返回字符串时间指针,失败NULL  
    获得当地的时间
            struct tm *localtime(const time_t *timep);
            参数:
                timep:标准时间             
            返回值:成功结构体指针,失败返回NULL。
               struct tm {
                   int tm_sec;    /* Seconds (0-60) 秒数*/
                   int tm_min;    /* Minutes (0-59) 分钟*/
                   int tm_hour;   /* Hours (0-23) 小时*/
                   int tm_mday;   /* Day of the month (1-31)日期 */
                   int tm_mon;    /* Month (0-11) 实际月份 =tm_mon+1  */
                   int tm_year;   /* Year - 1900 实际年份=tm_year+1900 */
                   int tm_wday;   /* Day of the week (0-6, Sunday = 0)一周中的第几天 */
                   int tm_yday;   /* Day in the year (0-365, 1 Jan = 0)一年中的第几天 */
                   int tm_isdst;  /* Daylight saving time 冬令时间或者夏令时间*/
               };
    将当地时间转换成字符串时间。
    
            char *asctime(const struct tm *tm);
            参数:
                tm:当地时间结构体地址                
            返回值:成功返回字符串时间指针,失败NULL

2. Linux ファイル I/O プログラミング

1. ファイル記述子

1.1 ファイル記述子の概要:

Linux オペレーティング システムはファイルの概念に基づいており、ファイルは文字シーケンスで構成される情報媒体であるため、I/O デバイスをファイルとして扱うことができます。したがって、ディスク上の通常のファイルと対話するために使用されるのと同じシステム コールで、I/O デバイスを直接使用できます。カーネルは、特定のファイルを区別して参照するためにファイル記述子を使用する必要があります。

ファイル記述子には次のような特徴があります。


         1- 文件描述符是一个非负整数
        2- 对于内核而言所有打开的文件都由文件描述符引用
        3- 当打开或者创建文件的时候,内核会向进程返回一个最小的没有被占用的整数。作为文件描述符。
        4- 当读写文件的时候,需要用到open、create返回该文件描述符
1.2 ファイル記述子のコピーとリダイレクト
#include <unistd.h>

     int dup(int oldfd);
      参数:
          oldfd:要拷贝的文件描述符
      返回值:成功返回新的文件描述符,失败-1 。
            
    文件描述符的重定向(捆绑)
            int dup2(int oldfd, int newfd);
            参数:
                oldfd,newfd:文件描述符 
                
            返回值:成功返回newfd的值,失败返回-1 ;
        注意:
            在dup2之前关闭newfd描述的文件

2. 電源を入れると自動的に起動するプログラム用の 3 つのファイル。

したがって、ファイル記述子を 3 から順にソートしてファイルを作成します。

3. 仮想ファイルシステム

 Linux システムの成功における重要な要素の 1 つは、他のオペレーティング システムと調和して共存できる能力です。Linux ファイル システムは、仮想ファイル オペレーティング システム (VFS) とさまざまな特定のファイル システムの 2 層アーキテクチャで構成されています。

VFS は、さまざまな特定のファイル システムの共通部分を抽出して、システム カーネルの一部である抽象化レイヤーを形成します。VFS は、基盤となるファイル システムの実装の詳細や相違点からユーザーを保護します。

次のコマンドを使用して、システムがどのファイル システムをサポートしているかを確認できます。

cat /proc/filesystems

 Linux システムの VFS の場所:

4. ファイル IO 操作

4.1 ファイルioを開く
打开文件
            #include <sys/types.h>
            #include <sys/stat.h>
            #include <fcntl.h>
			
            int open(const char *pathname, int flags);
            int open(const char *pathname, int flags, mode_t mode);
            
            参数:
                pathname:文件路径
                flags:打开方式
                    O_RDONLY :只读
                    O_WRONLY :只写
                    O_RDWR   :读写        这三个参数互斥

            -------------------------------------------------
                    O_CREAT :如果文件不存在则创建,并使用第三个参数设置权限
                    O_EXCL  :在使用O_CREAT创建文件的时候,如果文件已经存在,则返回错误信息,
                            可以用来检测文件是否存在
                    O_APPEND:以追加方式打开文件
                    O_TRUNC :如果文件已经存在,那么打开文件时先删除文件原有的数据。                   
                    
                    
                mode:创建文件的权限
                
                mode = mode & (~uamsk)
            返回值:成功返回文件描述符,失败返回-1
            
            r:       O_RDONLY
            r+:     O_RDWR
            
            w:       O_WRONLY|O_CREAT|O_TRUNC,0666
            w+:      O_RDWR|O_CREAT|O_TRUNC,0666
            
            a:      O_WRONLY|O_CREAT|O_APPEND,0666
            a+:     O_RDWR|O_CREAT|O_APPEND,0666
4.2 ファイルIOの読み書き
1>读:
            #include <unistd.h>

            ssize_t read(int fd, void *buf, size_t count);
            参数:
                fd:文件描述符
                buf:内存首地址
                count:实际读取的个数,最多只能读buf-1 个字符
            返回值:成功返回实际读取到的个数,到达文件末尾返回0,
                    失败返回-1 ,并且设置错误号。                   
2>写:
            #include <unistd.h>

            ssize_t write(int fd, const void *buf, size_t count);
            参数:
                fd:文件描述符
                buf:内存首地址
                count:实际写入的个数,最多只能读buf-1 个字符 
            返回值:成功返回实际写入到的个数,失败返回-1 ,并且设置错误号。
4.3 ファイルを閉じる
  #include <unistd.h>
  int close(int fd);
4.4 ファイルの配置
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);
    参数:
    fd:文件描述符
        offset:相对于whence的偏移量
        whence:偏移的起始位置
        SEEK_SET, 文件的起始位置
        SEEK_CUR, 文件指针当前的位置
        SEEK_END, 文件的末尾               
 返回值:成功返回偏移量,失败返回-1 

5 ファイルロック

ファイルが共有され、複数のファイルが同じファイル上で動作する場合、リソース競合の問題が発生することがありますが、Linux では通常、ファイルをロックすることでこの問題を解決します。

1>fcntl函数:可以施加建议性锁,强制性锁,还有记录锁 。
可以对各种打开文件进行操作,不仅可以管理文件锁,还可以获取和设置文件相关标志位,以及复制文件描述符

#include <unistd.h>
 #include <fcntl.h>
 int fcntl(int fd, int cmd, ... /* arg */ );
    参数:    
        fd文件描述符
        cmd:    
                F_GETLK:检查文件锁状态
                F_SETLK:设置lock描述的文件锁
                F-SETLKW:对F_SETLK的阻塞版本(W表述wait())
        返回值:
            成功返回 0
            出错 -1
如果cmd和锁操作相关,则第三个参数类型为struct *flock其定义如下:

         struct flock {
               ...
               short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
               short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* Starting offset for lock */
               off_t l_len;     /* Number of bytes to lock */
               pid_t l_pid;     /* PID of process blocking our lock
                                   (set by F_GETLK and F_OFD_GETLK) */
               ...
           };

flock结构体成员含义:

        l_type:
               F_RDLCK    读取锁(共享锁)
               F_WRLCK    写入锁(排斥锁)
               F_UNLCK    解锁
 

        l_start    
            加锁区域在文件中相对位移量(字节),与lwhence值一起决定加锁区域的起始位置
         l_len
            加锁区域长度
          l_pid
             具有阻塞当前进程的锁,其持有的进城后的进程号存放在l_pid中,仅由F_GETLK返回。
  


3. Linux ではすべてがファイルです

1.1 ファイルの属性

相対関数を詳しく理解するには、man + 関数名を使用します。プログラミング中に関数を表示する必要がある場合は、関数名に直接カーソルを置き、次に ESC、次に Shift+K を押して、対応する関数ライブラリにジャンプします。q を 2 回押して vim に戻り、関数を書き込みます。

            #include <sys/types.h>
            #include <sys/stat.h>
            #include <unistd.h>

            int stat(const char *pathname, struct stat *statbuf);
            int fstat(int fd, struct stat *statbuf);
            int lstat(const char *pathname, struct stat *statbuf);
        
        三个函数的返回:若成功则为0,若出错则为-1,并且设置errno.
        给定一个pathname的情况下:
         stat函数返回一个与此命名文件有关的信息结构
         fstat函数获得已在描述符filedes上打开的文件的有关信息
         lstat函数类似于stat,但是当命名的文件是一个符号连接时,
           lstat返回该符号连接的有关信息,而不是由该符号连接引用
        的文件的信息。
           struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* Inode number inode编号*/
               mode_t    st_mode;        /* File type and mode 文件类型以及权限*/
               nlink_t   st_nlink;       /* Number of hard links 硬链接数*/
               uid_t     st_uid;         /* User ID of owner 文件拥有者的用户ID*/
               gid_t     st_gid;         /* Group ID of owner 文件所属组的组ID*/
               dev_t     st_rdev;        /* Device ID (if special file) */
               off_t     st_size;        /* Total size, in bytes 文件大小 */
               blksize_t st_blksize;     /* Block size for filesystem I/O */
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* Time of last access 最后一次访问时间*/
               struct timespec st_mtim;  /* Time of last modification 最后一次修改时间*/
               struct timespec st_ctim;  /* Time of last status change 最后一次状态改变时间 */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };
            //通过用户ID获得用户信息
             #include <sys/types.h>
             #include <pwd.h>
             struct passwd *getpwuid(uid_t uid);
               struct passwd {
                   char   *pw_name;       /* username */
                   char   *pw_passwd;     /* user password */
                   uid_t   pw_uid;        /* user ID */
                   gid_t   pw_gid;        /* group ID */
                   char   *pw_gecos;      /* user information */
                   char   *pw_dir;        /* home directory */
                   char   *pw_shell;      /* shell program */
               };
            //通过组ID获得组的信息
            #include <sys/types.h>
            #include <grp.h>
            struct group *getgrgid(gid_t gid);
                struct group {
                   char   *gr_name;        /* group name */
                   char   *gr_passwd;      /* group password */
                   gid_t   gr_gid;         /* group ID */
                   char  **gr_mem;         /* NULL-terminated array of pointers
                                              to names of group members */
               };

2. ディレクトリ操作

ディレクトリはファイルでもあります。ディレクトリは開いたり閉じたり、読み取りや書き込みを行うことができます。

    1. 打开目录
            #include <sys/types.h>
            #include <dirent.h>

            DIR *opendir(const char *name);
            参数:
                name:目录名字
            返回值:成功返回DIR类型的指针,失败返回NULL
    2. 读取目录
            #include <dirent.h>

            struct dirent *readdir(DIR *dirp);
            返回值:成功返回一个结构体指针,失败返回NULL
               struct dirent {
                   ino_t          d_ino;       /* Inode number 索引的节点号*/
                   off_t          d_off;       /* Not an offset; see below 在目录文件中的偏移*/
                   unsigned short d_reclen;    /* Length of this record 文件名的长度*/
                   unsigned char  d_type;      /* Type of file; not supported
                                                  by all filesystem types 文件类型*/
                   char           d_name[256]; /* Null-terminated filename 文件名*/
               };
    3. 关闭目录
            #include <sys/types.h>
            #include <dirent.h>
            int closedir(DIR *dirp);  
    4. 修改程序当前的工作目录
            #include <unistd.h>

            int chdir(const char *path);
            参数:
                path:程序运行更改后的目录
            返回值:成功返回0,失败-1 ,并且设置错误号

次の記事: 人生の多重過程、老病死、死体収集、死にたくない

Linux マルチタスクプログラミング (1)_Jixi Compiler のブログ - CSDN ブログ

おすすめ

転載: blog.csdn.net/apple_71040140/article/details/132403401