当前位置: 首页 > news >正文

linux系统编程(二)

1、fcntl

#include <unistd.h>
int fcntl(int fd, int cmd, ...)

fcntl用于控制文件描述符,该系统调用有很多功能,功能用cmd来控制,fcntl后面的参数根据cmd来填充。

我们常用的cmd有:

  • F_GETFL:获取文件状态标志,所谓文件状态标志指的是文件在打开后的各种行为和状态的,比如说O_APPEND表示写入操作会将数据追加到文件末尾,O_NONBLOCK表示文件操作(如读取或写入)处于非阻塞模式。O_CREAT是一个用于文件创建的一次性标志,一旦文件创建成功,它就不再是文件状态的一部分
  • F_SETFL:修改文件状态标志,要注意的是允许修改的标志有O_APPEND、O_NONBLOCK、O_NOATIME、O_ASYNC 和 O_DIRECT,不能用于改变文件的打开模式(从只读改为读写)!!!
  • F_DUPFD:复制文件描述符,第三个参数为指定拷贝的文件描述符

我们在判断F_GETFL,要用

if ((flags & O_RDONLY) == O_RDONLY)

如果只是获取读写权限可以用

int accessMode = flags & O_ACCMODE;
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char **argv) {int fd1 = -1;fd1 = open("fcntl.txt", O_RDWR);if(fd1 < 0) {printf("open file failed, error msg: %s\n", strerror(errno));exit(1);}int flags = 0;flags = fcntl(fd1, F_GETFL);if(flags == -1) {printf("fcntl F_GETFD fail, error msg: %s\n", strerror(errno));exit(1);}printf("flags: %x\n", flags);int accessMode = flags & O_ACCMODE;switch (accessMode) {case 0:printf("access mode is O_RDONLY\n");break;case 1:printf("access mode is O_WRONLY\n");break;case 2:printf("access mode is O_RDWR\n");break;default:printf("access mode is %x\n", accessMode);break;}if ((flags & O_RDONLY) == O_RDONLY) {printf("flag has O_RDONLY\n");	}if ((flags & O_RDWR) == O_RDWR) {printf("flag has O_RDWR\n");	}if ((flags & O_APPEND) ==  O_APPEND) {printf("flag has O_APPEND\n");	}/*if (flags & O_CREAT) {printf("flag has O_CREAT\n");	}*/const char *s = "today is sunday, happy :>\n";ssize_t written = write(fd1, s, strlen(s));if(written < 0) {printf("write fail, written: %ld, error msg: %s\n", written, strerror(errno));} else {printf("written: %ld\n", written);}mode_t mode = O_APPEND;flags = fcntl(fd1, F_SETFL, mode);if(flags == -1) {printf("fcntl F_SETFL fail\n");}written = write(fd1, s, strlen(s));if(written < 0) {printf("write fail, written: %ld, error msg: %s\n", written, strerror(errno));} else {printf("written: %ld\n", written);}int fd2 = fcntl(fd1, F_DUPFD);printf("fd1:%d, fd2:%d\n", fd1, fd2);const char *s2 = "hello, i am fd2\n";written = write(fd2, s2, strlen(s2));if(written < 0) {printf("write fail, written: %ld, error msg: %s\n", written, strerror(errno));} else {printf("written: %ld\n", written);}off_t off = lseek(fd2, 0, SEEK_SET);off = lseek(fd1, 0, SEEK_CUR);printf("fd1 file pointer is %ld\n", off);return 0;
}

执行得到的结果是:

flags: 8002
access mode is O_RDWR
flag has O_RDONLY
flag has O_RDWR
written: 26
written: 26
fd1:3, fd2:4
written: 16
fd1 file pointer is 0

2、dup

#include <unistd.h>
int dup(int oldfd);

dup用于复制一个打开的文件描述符,它会返回最小的未使用的文件描述符。

默认情况下STDIN_FILENO(0)、STDOUT_FILENO(1)、STDERR_FILENO(2),这三个文件描述符是始终打开的。

#include <unistd.h>
#include <stdio.h>#define MAX_SIZE 1024int main(int argc, char **argv) {int fd = -1;fd = dup(STDIN_FILENO);printf("fd: %d\n", fd);char buf[MAX_SIZE];ssize_t size = 0;while((size = read(fd, buf, MAX_SIZE-1)) > 0) {buf[size] = '\0';printf("read size:%ld -> %s", size, buf);}close(fd);return 0;
}

dup总是返回最小的未使用的文件描述符,如果我们想要返回指定的文件描述符,可以用dup2。dup2可以看作是close和dup的合成。

#include <unistd.h>
int dup2(int oldfd, int newfd);

dup2会先关闭newfd,然后执行复制。这是一个危险行为,安全的做法是,调用dup2前,如果newfd已经打开,显式调用close将其关闭。

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>#define MAX_SIZE 1024int main(int argc, char **argv) {int fd = -1;mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; fd = open("dup2.txt", O_RDWR | O_CREAT | O_APPEND, mode);printf("fd: %d\n", fd);if(dup2(fd, STDOUT_FILENO) < 0) {printf("dup2 fail! error msg: %s\n", strerror(errno));exit(1);}printf("today is sunday, happy!\n");close(fd);return 0;
}

dup2在这个例子中作重定向的作用,虽然标准输出被关闭了,但是printf还是尝试向标准输出写数据,数据就会被写到文件中了。

当然我们也可以重定向标准输入,重定向标准输入意思就是从输入读数据时,实际上是从一个文件读的数据。

3、pread、pwrite

之前使用的系统调用read、write,用它们读写完数据之后,文件偏移量都会发生变动。有的时候我们只想临时向某个位置插入,这时候有两种选择:

  • 先执行lseek,将偏移量移动到指定位置,读写数据,再执行lseek恢复文件偏移量
  • pread、pwrite,它会帮我们完成两次lseek的动作。
#include <unistd.h>ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, void *buf, size_t count, off_t offset);

以下是代码示例:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>#define MAX_SIZE 512int main(int argc, char **argv) {int fd = -1;mode_t mode = S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IROTH;fd = open("pread.txt", O_RDWR | O_CREAT, 0664 /*mode*/);if(fd < 0) {printf("open / create file fail, error msg: %s\n", strerror(errno));exit(1);} else {printf("open / create file success!\n");}const char *s =  "today is Monday, sunny, cold! be happy, work hard, enjoy work and easy work!\n";ssize_t written = 0;written = write(fd, s, strlen(s));if(written < strlen(s)) {printf("write fail, written: %ld\n", written);exit(1);}off_t offset = lseek(fd, 0, SEEK_CUR);printf("current offset: %ld\n", offset);char buf[MAX_SIZE];ssize_t readlen = 0;int len = 6;readlen = pread(fd, buf, len, 9);if(readlen < len) {printf("pread error, readlen:%ld\n", readlen);exit(1);} else {printf("pread OK!\n");buf[len] = '\0';offset = lseek(fd, 0, SEEK_CUR);printf("current offset:%ld, read word: %s\n", offset,  buf);}const char *s1 = "Android is hard! ";written = pwrite(fd, s1, strlen(s1), 16);if(written < strlen(s1)) {printf("pwrite fail, written: %ld", written);exit(1);} else {printf("pwrite OK!\n");offset = lseek(fd, 0, SEEK_CUR);printf("current offset:%ld, written: %ld\n", offset, written);}lseek(fd, 0, SEEK_SET);while((readlen = read(fd, buf, MAX_SIZE - 1)) > 0) {buf[readlen] = '\0';printf("readlen:%ld -> %s\n", readlen, buf);}close(fd);return 0;
}

输出结果如下:

open / create file success!
current offset: 77
pread OK!
current offset:77, read word: Monday
pwrite OK!
current offset:77, written: 17
readlen:77 -> today is Monday,Android is hard! happy, work hard, enjoy work and easy work!

4、readv、writev

之前了解的read、write、pread、pwrite都是一次只能写入一个缓冲区,或者一次读取一个缓冲区。linux还提供了readv和writev,可以一次将数据读取到多个缓冲区,一次将多个缓冲区写到文件中。

#include <sys/uio.h>ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);struct iovec {void *iov_base;size_t iov_len;
}

iovcnt表示iov数组中有几个元素,每个iovec都会有一个地址,然后有对应的数据长度。

writev用于将多个非连续缓冲区中的数据原子性地写入文件描述符,它的使用场景是把固定格式的数据组合到一起发送出去,例如有固定格式的网络包(数据头+数据)、写日志文件等(时间+数据)。

以下是writev的一个简单示例:

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
#include <sys/uio.h>#define MAX_SIZE 512int main(int argc, char **argv) {int fd = -1;fd = open("writev.txt" , O_RDWR | O_CREAT, 0644);if(fd < 0) {printf("open file failed, error msg: %s\n", strerror(errno));exit(1);}struct iovec iov[2];time_t t;const char *s = "hello writev!\n";ssize_t written = 0;char buf[MAX_SIZE];for(int i = 0; i < 7; i++) {t = time(NULL);char *tmp = ctime(&t);memcpy(buf, tmp, strlen(tmp));buf[strlen(tmp) - 1] = ' ';     // remove '\n'iov[0].iov_base = buf;iov[0].iov_len = strlen(tmp);iov[1].iov_base = s;iov[1].iov_len = strlen(s);written = writev(fd, iov, 2);if(written !=  (iov[0].iov_len + iov[1].iov_len)) {printf("writev fail, written: %ld, error msg: %s\n", written, strerror(errno));exit(1);} else {printf("writev success! written: %ld\n", written);}sleep(1);}off_t offset = lseek(fd, 0, SEEK_CUR);printf("current offset: %ld\n", offset);ssize_t readlen = 0;lseek(fd, 0, SEEK_SET);while((readlen = read(fd, buf, MAX_SIZE-1)) >  0) {buf[readlen] = '\0';printf("len: %ld -> %s\n", readlen, buf);}close(fd);return 0;
}

readv用于从文件描述符中读取数据到多个非连续的缓冲区中,它的使用场景主要是把固定格式的数据读到不同的缓冲区中,比如说网络包,固定格式的文件。

以下是一个readv示例:

#include <unistd.h>
#include <stdio.h>
#include <sys/uio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>#define TIME_LEN 25
#define STR_LEN  14
#define MAX_LEN  50int main(int argc, char **argv) {int fd = -1;fd = open("writev.txt", O_RDONLY);if(fd < 0) {printf("open file failed, error msg: %s\n", strerror(errno));exit(1);}// "Mon Dec  9 13:29:17 2024 hello writev!\n"char bufTime[MAX_LEN];char bufStr[MAX_LEN];struct iovec iov[2];iov[0].iov_base = bufTime;iov[0].iov_len = TIME_LEN;iov[1].iov_base = bufStr;iov[1].iov_len = STR_LEN;ssize_t rd = 0;while((rd = readv(fd, iov, 2)) > 0) {bufTime[TIME_LEN] = '\0';bufStr[STR_LEN] = '\0';printf("readv OK, rd len: %ld\n""-> time: %s\n""-> str: %s\n",rd, bufTime, bufStr);}close(fd);return 0;
}

输出结果

readv OK, rd len: 39
-> time: Mon Dec  9 13:29:16 2024
-> str: hello writev!readv OK, rd len: 39
-> time: Mon Dec  9 13:29:17 2024
-> str: hello writev!readv OK, rd len: 39
-> time: Mon Dec  9 13:29:18 2024
-> str: hello writev!readv OK, rd len: 39
-> time: Mon Dec  9 13:29:19 2024
-> str: hello writev!readv OK, rd len: 39
-> time: Mon Dec  9 13:29:20 2024
-> str: hello writev!readv OK, rd len: 39
-> time: Mon Dec  9 13:29:21 2024
-> str: hello writev!readv OK, rd len: 39
-> time: Mon Dec  9 13:29:22 2024
-> str: hello writev!

5、truncate

有的时候我们需要清空文件,这时候可以使用truncate方法

#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length)

以下是使用示例:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>#define MAX_SIZE 1024int main(int argc, char **argv) {int ret = truncate("truncate.txt", 0);if(ret < 0) {printf("truncate fail! error msg: %s\n", strerror(errno));exit(1);} else {printf("truncate success!\n");}int fd1 = open("writev.txt", O_RDONLY);int fd2 = open("truncate.txt", O_RDWR);off_t offset = lseek(fd2, 0, SEEK_CUR);printf("truncate.txt offset: %ld\n", offset);char buf[MAX_SIZE];ssize_t rd = 0;int len = 0;while((rd = read(fd1, buf, MAX_SIZE)) > 0) {write(fd2, buf, rd);len += rd;}offset = lseek(fd2, 0, SEEK_CUR);printf("current truncate.txt offset: %ld\n", offset);offset = 0;while((rd = pread(fd2, buf, MAX_SIZE-1, offset)) > 0) {buf[rd] = '\0';printf("off:%ld -> %s\n", offset, buf);offset += rd;}ret = ftruncate(fd2, 0);if(ret < 0) {printf("ftruncate fail! error msg: %s\n", strerror(errno));exit(1);} else {printf("ftruncate success!\n");}offset = lseek(fd2, 0, SEEK_CUR);printf("after ftruncate, current truncate.txt offset: %ld\n", offset);close(fd1);close(fd2);return 0;
}

我们要注意的是,ftruncate执行完成之后,文件描述符的文件偏移量没有被修改!所以如果要重新写的话,得执行lseek。

6、mkstemp

有的时候我们想用临时文件,C库提供了一个mkstemp和 tmpfile给我们使用

#include <stdlib.h>
int mkstemp(char *template);

template是文件名称,返回的是一个fd,但是删除的时候需要自己执行unlink方法。

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>int main(int argc, char **argv) {char tmp[] = "./tmp.XXXXXX";int fd = mkstemp(tmp);if(fd < 0) {printf("can not create tmp file, err msg: %s\n", strerror(errno));} else {printf("create tmp file OK. fd: %d, filename: %s\n", fd, tmp);}unlink(tmp);return 0;
}

要注意的是,填入的文件名称最后6位必须是XXXXXX,否则将无法创建文件,会有Invalid argument。文件创建成功,tmp会自动变成新的文件名,所以不要用const!

can not create tmp file, err msg: Invalid argument

如果我们自己不执行unlink,那么文件会一直存在

-rw------- 1 xxx xxx     0 129 14:52 tmp.gOq8Xb

tmpfile是stdio提供的一个方法,

#include <stdio.h>
FILE *tmpfile();

它会创建一个文件流,程序结束时会自动删除该文件,不需要我们再手动删除。

相关文章:

linux系统编程(二)

1、fcntl #include <unistd.h> int fcntl(int fd, int cmd, ...)fcntl用于控制文件描述符&#xff0c;该系统调用有很多功能&#xff0c;功能用cmd来控制&#xff0c;fcntl后面的参数根据cmd来填充。 我们常用的cmd有&#xff1a; F_GETFL&#xff1a;获取文件状态标志…...

uni-app登录界面样式

非常简洁的登录、注册界面模板&#xff0c;使用uni-app编写&#xff0c;直接复制粘贴即可&#xff0c;无任何引用&#xff0c;全部公开。 废话不多说&#xff0c;代码如下&#xff1a; login.vue文件 <template><view class"screen"><view class"…...

windows C#-定义抽象属性

以下示例演示如何定义抽象属性。 抽象属性声明不提供属性访问器的实现&#xff0c;它声明该类支持属性&#xff0c;而将访问器实现留给派生类。 以下示例演示如何实现从基类继承抽象属性。 此示例由三个文件组成&#xff0c;其中每个文件都单独编译&#xff0c;产生的程序集由…...

ERROR: KeeperErrorCode = NoNode for /hbase/master

原因分析 通过上面的情景模拟&#xff0c;我们可以看到报错的原因在于zookeeper中出现问题&#xff0c;可能是zookeeper中的/hbase/master被删除&#xff0c;或者是在hbase集群启动之后重新安装了zookeeper&#xff0c;导致zookeeper中的/hbase/master节点数据异常。 1. 停止…...

Deepin 23 踩坑记

&#xff08;首发地址&#xff1a;学习日记 https://www.learndiary.com/2024/12/deepin23-questions/&#xff09; Deepin 23 是由统信软件技术有限公司牵头开发一款开源 Linux 桌面操作系统&#xff08;参考链接1&#xff09;&#xff0c;从2022年发布预览版&#xff08;参考…...

mysql笔记——索引

索引 InnoDB采用了B树索引结构。 相比于二叉树&#xff0c;层级更少&#xff0c;搜索效率高。 B树中叶子节点和非叶节点都会存储数据&#xff0c;导致段页式存储中一页存储的键值减少&#xff0c;指针也会减少&#xff0c;要同样保存大量数据&#xff0c;只能增加树的高度&a…...

考研数据结构——简答题总结

数据结构的4种基本结构及特点&#xff1a; 数组&#xff08;Array&#xff09;&#xff1a; 特点&#xff1a;数组是一种线性数据结构&#xff0c;使用连续的内存空间存储元素&#xff0c;可以通过索引直接访问任意位置的元素。优点&#xff1a;访问速度快&#xff0c;因为元…...

Qt Creator 里面设置MSVC 为 utf-8

在使用 Qt Creator 和 MSVC(Microsoft Visual C++)编译器进行开发时,我们可能会遇到中文乱码的问题。这通常是由于编码设置不正确导致的。 在 Qt Creator 中,你可以通过以下步骤设置默认编码为 UTF-8: 打开 Qt Creator,选择菜单栏中的“工具”(Tools) > “选项”(Opti…...

Java阶段三06

第3章-第6节 一、知识点 理解MVC三层模型、理解什么是SpringMVC、理解SpringMVC的工作流程、了解springMVC和Struts2的区别、学会使用SpringMVC封装不同请求、接收参数 二、目标 理解MVC三层模型 理解什么是SpringMVC 理解SpringMVC的工作流程 学会使用SpringMVC封装请求…...

Helm安装Mysql8主从复制集群

目录 一、Helm安装 二、安装mysql 1、拉取镜像 2、修改配置文件 3、创建mysql-secret 4、安装 一、Helm安装 这里不再赘叙&#xff0c;具体安装请参考官网 Helm | 快速入门指南 二、安装mysql 1、拉取镜像 #添加仓库 helm repo add bitnami https://charts.bitnami.c…...

嵌入式基础:Linux C语言:Day7

重点&#xff1a; strlen()函数\strcpy()函数\strcat实现\strcmp()实现 数组的清空&#xff1a;bzero函数、memset函数 一、字符数组 <1> 概念 字符数组本质上就是一个数组&#xff0c;保存一个个字符&#xff0c;也一般用来保存字符串 字符串由多个字符组成的一个字符…...

Tablesaw封装Plot.ly实现数据可视化

上文介绍tablesaw的数据处理功能&#xff0c;本文向你展示其数据可视化功能&#xff0c;并通过几个常用图表示例进行说明。 Plot.ly包装 可视化是数据分析的重要组成部分&#xff0c;无论你只是“查看”新数据集还是验证机器学习算法的结果。Tablesaw是一个开源、高性能的Java…...

RAG与Embedding:现代NLP的核心技术

本篇文章简单梳理我在了解RAG以及Embedding技术时的想法&#xff0c;仅供参考 文章目录 1. 引言背景为什么要关注RAG与Embedding技术 2. 基础概念与原理2.1 什么是RAG (Retrieval-Augmented Generation)定义工作流程优点与适用场景 2.2 什么是Embedding定义作用 3. Embedding在…...

力扣每日一题 - 1812. 判断国际象棋棋盘中一个格子的颜色

题目 还需要你前往力扣官网查看详细的题目要求 地址 1.给你一个坐标 coordinates &#xff0c;它是一个字符串&#xff0c;表示国际象棋棋盘中一个格子的坐标。下图是国际象棋棋盘示意图。2.如果所给格子的颜色是白色&#xff0c;请你返回 true&#xff0c;如果是黑色&#xff…...

Map 那些事儿

1. map 的基本结构 Go 的 map 是一种哈希表&#xff0c;其核心思想是通过哈希函数将键映射到某个位置&#xff08;桶&#xff09;以存储对应的值。它主要包含以下关键部分&#xff1a; •桶&#xff08;bucket&#xff09;&#xff1a;存储键值对的容器&#xff0c;map 中的元…...

GCP Case:MountKirk Games

游戏后端 根据游戏活动动态放大或缩小。 连接到托管的nos0l数据库服务。 运行定制的linux发行版。 游戏分析平台 根据游戏活动来扩大或缩小规模直接处理来自游戏服务器的传入数据。 处理由于移动网络缓慢而迟到的数据。 通过sql查询来访问至少10tb的历史数据 处理由用户…...

[创业之路-187]:《华为战略管理法-DSTE实战体系》-1-从UTStarcom的发展历程,如何辩证的看企业初期发展太顺利中的危机

目录 一、UTStarcom&#xff08;UT斯达康&#xff09;的发展历程 1、创立与初期发展 2、快速成长与上市 3、技术创新与业务拓展 4、战略调整与持续发展 二、从UTStarcom的发展历程&#xff0c;如何辩证的看企业初期发展太顺利中的危机 1、企业初期发展的顺利表现 2、顺…...

高级数据结构-树状数组

介绍 树状数组的推导 两个基础操作 模板-acwing795. 前缀和 #include<bits/stdc.h> using namespace std;const int N 1e610; int c[N]; int lowbit(int x){return x & -x; }int query(int x){int ans 0;for(; x; x - lowbit(x)) ans c[x];return ans; }void add…...

LeetCode279. 完全平方数(2024冬季每日一题 27)

给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#xff0c;其值等于一个整数自乘的积。例如&#xff0c;1、4、9 和 16 都是完全平方数&#xff0c;而 3 和 11 不是。 …...

Scala 隐式转换

object test {//复习隐式转换&#xff1a;//隐式转换&#xff1a;编译器 偷偷地&#xff0c;自动地帮我们把一种数据转换为另一种类型//例如&#xff1a;int --> double//它有失败的时候&#xff08;double --> int&#xff09;&#xff0c;有成功的时候//当它转换失败的…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...