IO进线程

1.七大文件类型

普通文件 - (XXX.c XXX.h  XXX.cpp ....)
目录文件 d
链接文件 l (软链接 相当于windows的快捷方式)
管道文件 p (进程间通信)
字符设备文件 c (/dev/video0 /dev/USB0)
块设备文件 b (磁盘)
套接字文件 s (用于网络间通信)

2.系统调用和库函数

1)系统调用 
    //文件IO open 打开文件 read() /write() close() ... 
    //通过文件描述符来操作文件..
用户空间进程访问内核的接口
把用户从底层的硬件编程中解放出来
极大的提高了系统的安全性
使用户程序具有可移植性
是操作系统的一部分
 系统调用:	
	上层应用向操作系统发出请求,操作系统响应并处理相应的硬件,并把处理结果返回给上层应用;

2)库函数 
   //标准IO fopen() fread() fwrite() fclose() ...
   //文件流指针来操作文件
库函数为了实现某个功能而封装起来的API集合
提供统一的编程接口,更加便于应用程序的移植
是语言或者应用程序的一部分

3.标准IO

3.1 流 FILE

标准IO用FILE 结构体来保存打开的文件信息;
文本流 和 二进制流(linux)
typedef struct _IO_FILE FILE;
struct _IO_FILE
{
  int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */

  /* The following pointers correspond to the C++ streambuf protocol. */
  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;

  int _fileno;
  int _flags2;
  __off_t _old_offset; /* This used to be _offset but it's too small.  */

  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

**** 标准IO减少系统调用的次数;
缓冲区就是一个应用层的内存块;
缓冲区作用于输入输出设备和cpu之间,为了使得低速的输入输出设备和高速的cpu工作相协调,避免输入输出设备占用cpu,使其高效率工作;
***当缓冲区满的时候进行写操作,缓冲区空的时候读操作;
缓冲区类型: //linux 只考虑输出缓冲区
	全缓冲: 默认的缓冲类型 (默认缓冲区满的时候刷新)
	行缓冲: 和终端相关,遇'\n'刷新,缓冲区满刷新
	无缓冲: 标准错误流输出错误信息,直接输出到文件;
系统默认定义好的三个流:
	标准输入 stdin   0
    标准输出 stdout  1
    标准错误 stderr  2
缓冲区的刷新机制:(只考虑输出缓冲区)
	1.缓冲区满的时候刷新
    2.针对遇行缓冲,遇到'\n'3.当流被正常关闭的时候(程序正常结束),也会刷新
    4.当遇到输入函数的时候,也会刷新;
	5.强制刷新 fflush();
		#include <stdio.h>
		int fflush(FILE *stream);
		{
            功能: 强制刷新缓冲区;
            参数: 文件流指针
            返回值: 成功返回0,失败返回-1并设置错误号;
        }

3.2 打开流(打开文件) fopen

#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
{
    功能: 打开一个文件
    参数: 
    	pathname:
    		文件名(可以包含路径)
        mode: 打开方式
            r: 只读 文件必须存在
            r+: 读写 文件必须存在
            w: 只写 文件存在则清空文件内容 不存在则创建
            w+: 读写 其他同w
            a: 追加写,文件不存在则创建
            a+: 读写 从头读从末尾写,文件不存在创建; 
    返回值:成功返回一个文件流指针,失败返回NULL,并设置错误号;
    fopen 默认创建的权限为0666,
        	最终权限: 0666 & ~umask(权限掩码)
            umask 命令 可以查看系统的权限掩码; //0002
    0666 & ~0002 -->
     110 110 110   ~ 000 000 010
      &
     111 111 101
     
     110 110 100 ---> 664
}

错误号:
 	#include <errno.h>
	extern int errno;
	根据错误号可以打印错误信息;
	perror(){
         #include <stdio.h>
		 void perror(const char *s);{
             功能:根据错误号打印错误信息,(先打印s再打印错误信息)
             s: 一般表示函数名;
         }
    strerror{
         #include <string.h>

       	char *strerror(int errnum);
        {
            参数: 错误号
            返回值:返回错误信息;
        }

    }
	int fprintf(FILE *stream, const char *format, ...);
    }

3.3 流的关闭

linux 下 流的打开是有限制的,最多只能打开1024(包含系统默认打开3)int fclose(FILE *stream);
{
    功能:关闭文件流指针;
   	参数: 
    	stream: 文件流指针
    返回值:成功返回0, 失败返回-1(EOF)并设置错误号;
    不能重复关闭同一个流指针;
}

3.4 文件的读和写

1.按字符读写
    fgetc() / fputc()
    getchar() / putchar()
2.按行读写
    fgets() / fputs()
    gets() / puts();
3.按对象读写
    fread() / fwrite();

3.5 按字符读写

读取(输入):
#include <stdio.h>
 int fgetc(FILE *stream); //fgetc(stdin) 等价于 getchar()
{
    参数: stream:输入流
    返回值:成功返回读取到的字符,失败或者读到文件的末尾返回EOF;
}
写入(输出):
#include <stdio.h>

int fputc(int c, FILE *stream);
{
    参数:
    	c: 输出的内容
        stream:输出流
    返回值:返回输出的字符,失败返回EOF;
}

3.6 按行读写

读取:
	fgets()
	gets(); //fgets(buf, sizeof(buf), stdin);
 #include <stdio.h>
	char *fgets(char *s, int size, FILE *stream);
{
 	功能:向一个打开的文件流当中读取内容;
    参数:
    	s: 指向用来保存读取到的数据的空间首地址
        size:最多只能读取size-1个字节,最后一个位置总会自动加上一个'\0'
        stream:输入流;
    返回值:成功返回s,读到文件末尾或者失败返回NULL;
    
    注意: 遇到'\n'结束, 读取到size-1个字符时也结束;
    当输入的字符个数大于等于size-1,不会输入'\n', 最后一个位置永远是'\0'; //多余的字符会保存在输入缓冲区中 
    当输入的字符个数小于size-1时,会输入'\n';
    buf:
    	hello\n\0 --> hello\0\0
    	aaa\n\0 --> aaa\0\0
        buf[strlen(buf)-1] = '\0';
    
}

写入:
    fputs(){
        int fputs(const char *s, FILE *stream);
        {
            功能:向打开的文件输出字符串;
            参数:
            	s:用来保存输出的内容;
            	stream:输出流;
            返回值: 成功返回一个非负数,失败返回EOF;
            //不带换行
        }
        
    }
    puts() //自带换行
作业:
	题目要求:编程读写一个文件test.txt,每隔1秒向文件中写入一行数据,类似这样: 
1,  2007-7-30 15:16:42  
2,  2007-7-30 15:16:43
该程序应该无限循环,直到按Ctrl-C中断程序。
再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序号,比如: 
1,  2007-7-30 15:16:42
2,  2007-7-30 15:16:43
3,  2007-7-30 15:19:02
4,  2007-7-30 15:19:03
5,  2007-7-30 15:19:04
    //time();
	//localtime(); //注意全缓冲
	
	

1.按对象读写

/输入:
fread:
    #include <stdio.h>
	size_t fread(void *ptr, size_t size, size_t nmemb, 
                 FILE *stream);
{
    功能:向一个打开的文件()按照对象读取内容;
    参数:
    	ptr:指向用来保存读取到的内容空间首地址;
    	size:要读取的单个对象(数据项)的大小
        nmemb:要读取的对象(数据项)个数;
    	stream:输入流 
    返回值:成功返回实际读到的对象个数,读到文件的末尾或者失败返回0;
}/输出
fwrite:
    size_t fwrite(const void *ptr, size_t size, size_t nmemb,
                   FILE *stream);
{
    功能:向一个打开的文件()写入内容;
    参数:
    	ptr:指向待写入的内容;
    	size:要写入的单个对象(数据项)的大小
        nmemb: 要写入的对象(数据项)的个数
        stream:输出流
    返回值:成功返回实际写入的对象个数;
}

2.判断文件的末尾/流出错

int feof(FILE *stream);
{
    读到文件末尾返回1,否则返回0}
int ferror(FILE *stream)
{
    流出错返回1,否则返回0;
}
//练习: 用fread 和 fwrite 实现文件的拷贝;11:30 - 11:40

3. 文件的定位

 long ftell(FILE *stream);
{
    功能: 查看文件内部指针的位置;
    返回值:成功返回 文件内部指针距离开头的偏移量;
}

void rewind(FILE *stream);
{
    功能: 将文件内部指针置为开头;
}
int fseek(FILE *stream, long offset, int whence);
{
    功能: 对文件定位;
    参数:
    	stream: 文件流指针;
    	offset: 偏移量,可正可负;
            负数: 向前偏移;
    		正数:向后偏移
        whence: 
    		SEEK_SET: 文件的开头
            SEEK_CUR: 文件内部指针的当前位置
            SEEK_END: 文件的末尾
     返回值:成功返回0,识别返回-1并设置错误号;
}
实现对图片加密;
//将图片的前十个字节读取出来,逆序 再写回到原位置 --> 加密

4.格式化输出

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
{
    功能:向str指向的空间格式化输出;
}
按照格式输入;
  int sscanf(const char *str, const char *format, ...)
{
    将str里的数据按照格式输入到地址表当中;
}

5.文件IO

标准IO:
	ANSIC 标准(美国国家学会) 
    文件流指针操作文件
    有缓冲区
    高级IO
    适用于普通文件
    标准IO减少系统调用的次数(先缓冲区---系统调用)
    标准IO是在文件IO的基础上封装了缓冲机制;
    c库
  fopen() fread() fwrite() fclose() fseek()      
文件IO:
	posix 标准
    文件描述符
    无缓冲
    低级IO
    适用于特殊文件
    每次调用文件IO必然发生系统调用
    系统调用的API;
	open() read() write() close() lseek();

5.1 打开文件 open

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

 int open(const char *pathname, int flags);
{
    功能:打开一个已经存在的文件;
    参数:
    	pathname: 文件名
        flags:打开方式
        	O_RDONLY 只读 
       	 	O_WRONLY 只写
        	O_RDWR 读写
             
            O_TRUNC 清空
            O_APPEND 追加
            
    返回值: 成功返回一个文件描述符,失败返回-1并设置错误号;
    文件描述符 是一个非负整数,顺序分配,用来标识文件;
     系统默认打开三个文件描述符:
        0  标准输入 stdin
        1  标准输出 stdout
        2  标准错误 stderr
        
}
 int open(const char *pathname, int flags, mode_t mode);
{
    功能:文件存在则打开不存在则创建;注意:创建的时候必须和O_CREAT 联用以及需要加上第三个参数
    参数:
    	pathname: 文件名
        flags:打开方式
        	O_RDONLY 只读 
       	 	O_WRONLY 只写
        	O_RDWR 读写
            O_CREAT 创建
            O_TRUNC 清空
            O_APPEND 追加
       mode:存取权限
           用八进制表示,一个文件的最高权限0777
           最终权限: mode & ~umask
       返回值: 成功返回一个文件描述符,失败返回-1并设置错误号;         
}

5.2 文件的关闭 close

#include <unistd.h>

 int close(int fd);
{
    功能: 关闭打开的文件描述符;
    linux 下打开的文件描述符是有限制的,最多有1024;
    使用完的文件描述符需要关闭;
    返回值:成功返回0,失败返回-1并设置错误号;
}

5.3 文件的读取 read

 #include <unistd.h>

 ssize_t read(int fd, void *buf, size_t count);
{
    功能:向一个打开的文件读取内容;
    参数:
    	fd: 文件描述符
        buf:指向用来保存读取到内容的空间首地址;
    	count: 预计要读取的字节数;
    返回值:成功返回实际读到的字节数, 读到文件的末尾返回0,失败返回-1,并设置错误号;
}
eg:读取文件的前十个字节并输出到终端;

5.4 文件的写入 write

 #include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count)
{
    功能:向一个打开的文件写入内容;
    参数:
    	fd:文件描述符
        buf:保存要写入的数据的空间首地址;
    	count:预计要写入的字节数;
    返回值:成功返回实际写入的字节数,失败返回-1并设置错误号;
}

5.5 文件的定位 lseek

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

 off_t lseek(int fd, off_t offset, int whence);
{
	功能:实现对文件定位;
    参数:
    	fd: 文件描述符;
    	offset: 偏移量,可正可负;
        whence:
    		SEEK_SET 文件的开头
            SEEK_CUR 文件内部指针的当前位置
            SEEK_END 文件的末尾
    返回值:成功返回文件内部指针距离文件开头的长度,失败返回-1并设置错误号;
}

6. 目录

6.1 目录的打开

 #include <sys/types.h>
       #include <dirent.h>

   DIR *opendir(const char *name);
{
    功能: 打开目录
    参数:
    	name: 目录路径
    返回值:成功返回目录流指针,失败返回NULL并设置错误号;
}

6.2 目录的关闭

	  #include <sys/types.h>

       #include <dirent.h>

       int closedir(DIR *dirp);
{
    功能:关闭目录流指针;
    参数: 
    	dirp: 目录流指针
    返回值:成功返回0失败返回-1并设置错误号;
}

6.3 获取目录里的文件

#include <dirent.h>

  struct dirent *readdir(DIR *dirp);
{
    功能: 读取目录里的内容
    参数:
    	dirp: 目录流指针
     返回值:成功返回一个指针,指向保存目录信息的结构体,失败或者读到目录的末尾返回NULL并设置错误号;
}

//1.read / write 实现文件的拷贝,加上命令行参数
//2.实现文件的加密/解密(文件IO)
方法: 将文件里的所有字符加一实现加密,减一实现解密;

1. 修改文件权限

       int chmod(const char *pathname, mode_t mode);
       int fchmod(int fd, mode_t mode);
	返回值: 成功返回0失败返回-1并设置错误号;

2. 文件的属性

#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并设置错误号;

struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* Inode number */
               mode_t    st_mode;        /* File type and mode */
               nlink_t   st_nlink;       /* Number of hard links */
               uid_t     st_uid;         /* User ID of owner */
               gid_t     st_gid;         /* Group ID of owner */
               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
           };

根据系统提供的宏来获取文件类型:
			S_IFMT     0170000  (类型掩码) 
             st_mode & S_IFMT

           S_IFSOCK   0140000   socket
           S_IFLNK    0120000   symbolic link
           S_IFREG    0100000   regular file
           S_IFBLK    0060000   block device
           S_IFDIR    0040000   directory
           S_IFCHR    0020000   character device
           S_IFIFO    0010000   FIFO
通过宏函数确定文件类型
           S_ISREG(m)  is it a regular file?

           S_ISDIR(m)  directory?

           S_ISCHR(m)  character device?

           S_ISBLK(m)  block device?

           S_ISFIFO(m) FIFO (named pipe)?

           S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)

           S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)

判断文件的权限
        S_IRUSR     00400   owner has read permission
        S_IWUSR     00200   owner has write permission
        S_IXUSR     00100   owner has execute permission
       
        S_IRGRP     00040   group has read permission
        S_IWGRP     00020   group has write permission
        S_IXGRP     00010   group has execute permission

        S_IROTH     00004   others have read permission
        S_IWOTH     00002   others have write permission
        S_IXOTH     00001   others have execute permission
 获取用户名:
	struct passwd *getpwuid(uid_t uid);
 获取组名:
	struct group *getgrgid(gid_t gid);
 时间:
    st_mtim 最后修改的时间---> localtime 转成本地时间
 链接文件:
        获取目标文件:
		#include <unistd.h>

    ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

自己实现ls -l 

3.动态库和静态库

linux下的库有两种:静态库和共享库(动态库)。二者的不同点在于代码被载入的时刻不同。 
静态库:
    在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
    
	优点:程序在运行时与函数库再无瓜葛,移植方便

	缺点:浪费空间和资源,因为所有相关的对象文件(object file)与牵涉到的函数库(library)被链接合成一个可执行文件(executable file)。
	静态库: 命名 前缀 lib + 静态库名 + 后缀(扩展名) .a
        eg:
			libhello.a --> 库文件名
     静态库的制作:
		gcc -c demo.c -o demo.o
        ar crs libmylib.a demo.o
     静态库的链接:
		gcc main.c -L ./ -lmylib -static
          -L 指定库的路径
          -l 跟库名(-l库名)
        或者:
			gcc main.c libmylib.a 

动态库:
	在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。 
    共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
        动态库:命名 前缀 lib + 动态库名 + 后缀(扩展名).so
        eg:
			libhello.so --> 库文件名
        动态库的制作:
			gcc -fPIC -c demo.c -o demo.o
            gcc -shared -o libAAA.so demo.o
        动态库的使用:
		gcc main.c -L ./ -lAAA
        ./a.out:
	报错:
/a.out: error while loading shared libraries: libAAA.so: cannot open shared object file: No such file or directory
    系统会默认去/lib 下去找该库,找不到所以报错
    加载动态库:
	1.将库拷贝到/lib库下;
		eg:
			sudo cp libAAA.so /lib
	2.导入临时环境变量
        将库的路径加在LD_LIBRARY_PATH后
      eg:
        export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:home/jiangcx/class/240201/day3/lib/so_lib
    注意: 在其他终端下依旧找不到库;
  3.新建 sudo vi /etc/ld.so.conf.d/test.conf文件,在里面添加库的路径;
eg:/home/jiangcx/class/240201/day3/lib/so_lib
   保存并退出,刷新: sudo ldconfig即可永久生效

4.进程

程序:
	静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念
进程: 执行程序分配资源的总称;(空间、cpu、时间片)
     进程是一个程序的一次执行的过程
    一个动态的概念,它是程序执行的过程,包括创建、调度和消亡
    进程是程序执行和资源管理的最小单位  
    每一个进程都有独立的虚拟4G内存(虚拟内存:由系统和cpu决定,和物理内存无关)

4.1 进程的内容

进程包括:
	①程序:
    	正文段
    	用户数据段: 全局变量、静态变量
	②系统数据段
         程序控制块 PCB(task_struct)、CPU寄存器、pc(程序计数器)、堆栈
    程序控制块:
		进程id(进程的唯一标识 pid) //父进程 ppid
        进程用户
        进程的状态、优先级
        文件描述符表
    pc:程序计数器
       保存进程下一条指令的地址;
	进程的调度:
		linux 是单核的分时操作系统,CPU一次只能执行一个进程,当有多任务的时候,cpu会给进程分配时间片,在微观上各个进程顺序切换时间片执行,切换的时间片大概在几十毫秒;
		并发:
			多个任务切换时间片执行;
		并行:
			真正意义上的同时执行;

4.2 进程的类型

交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。
    jobs: 查看后台任务
    fg 任务编号 将后台任务切到前台运行;
	bg 将后台停止(ctrl + Z )的任务放到后台运行
    kill 可以向进程发送信号
    kill -l 查看信号的种类
    kill 信号编号 pid(进程号) //向进程发送信号
        eg: 
			kill -2 pid //ctrl + c
            kill -19 pid //ctrl + z

批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。
          (系统管理员)

守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。(后台服务器)

4.3 进程的运行状态

R 运行态:此时进程或者正在运行,或者准备运行。

等待态:此时进程在等待一个事件的发生或某种系统资源。
  可中断 S:需要某种信号来唤醒 (getchar())
  不可中断 D:和硬件交互的时候;(发送信号也不能打断)

停止态 T:此时进程被中止。

死亡态(僵尸态 Z):这是一个已终止的进程,但还在进程向量数组中占有一个task_struct结构。

4.4 进程状态码

R 运行态
D 不可中断等待
S 可中断等待
T 停止态
Z 僵尸态
+ 前台运行
> 高优先级

4.5 进程相关命令

ps -ef 查看进程的相关信息
pstree 查看进程树 
ps -aux 查看状态码
top 动态查询进程运行情况

4.6 进程优先级

-20 --- 19 值越低优先级越高
 nice: 按指定优先级运行进行
    nice -n 5 ./a.out
 renice: 修改正在运行的进程的优先级
     renice -n 2 pid(进程号) //普通用户只能把nice值增大;

4.7 创建子进程

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

 pid_t fork(void);
{
    功能:创建进程(创建一个子进程)
    返回值: 父进程返回子进程的进程号,子进程返回0,失败返回-1并设置错误号;
	//fork 函数一般不会失败,除非系统资源不足;
}
父子进程:
	1.父子进程没有固定的先后顺序(基本加了sleep延时也不行),谁有时间片谁执行;
	2.子进程会完全拷贝父进程的资源(虚拟4G内存);
		子进程拷贝完父进程资源之后,父子进程各自有独立的地址空间,互不影响;(每个进程都有一片独立的虚拟4G内存空间)
getpid() //获取当前进程的进程号
getppid() //获取父进程的进程号

   3.僵尸进程
           子进程的资源需要父进程回收;
           当子进程先结束,父进程没有回收子进程的资源,那么子进程就变成僵尸进程(不允许这种进程存在);
	4.孤儿进程
        父进程结束,子进程还在存活,此时子进程就变成了孤儿进程;
		子进程变成孤儿进程以后,由init进程(systemed进程)收养,由它回收资源 ;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式石油工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值