【Linux系统编程学习】信号、信号集以其相关函数

此为牛客Linux C++和黑马Linux系统编程课程笔记。

文章目录

  • 0. 信号的概念
  • 1. Linux信号一览表
  • 2. 信号相关函数
  • 3. kill函数
  • 4. raise函数
  • 5. abort函数
  • 6. alarm函数
  • 7. setitimer函数
  • 8. signal函数
  • 9. 信号集
  • 10. 自定义信号集相关函数
  • 11. sigprocmask函数
  • 12. sigpending函数
  • 13. sigaction函数
  • 14. 内核实现信号捕捉过程

0. 信号的概念

在这里插入图片描述
A给B发送信号,B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。

信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。

每个进程收到的所有信号,都是由内核负责发送的,内核处理。

在这里插入图片描述

1. Linux信号一览表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
红色为重点掌握的信号

2. 信号相关函数

在这里插入图片描述
在这里插入图片描述

3. kill函数

#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);

功能:给任何的进程或者进程组pid, 发送任何的信号 sig

参数:

  • pid :

< 0 : 将信号发送给指定的进程
= 0 : 将信号发送给当前的进程组
= -1 : 将信号发送给每一个有权限接收这个信号的进程
< -1 : 这个pid=某个进程组的ID取反 (-12345)

  • sig : 需要发送的信号的编号或者是宏值,0表示不发送任何信号

kill(getppid(), 9);能够杀死父进程;kill(getpid(), 9);能够杀死当前进程。

4. raise函数

#include <sys/types.h>
#include <signal.h>int raise(int sig);

功能:给当前进程发送信号;

参数:sig : 要发送的信号;

返回值:成功 0, 失败 非0。

相当于kill(getpid(), sig);

5. abort函数

#include <sys/types.h>
#include <signal.h>void abort(void);

功能: 发送SIGABRT(编号为6)信号给当前的进程,杀死当前进程;

相当于kill(getpid(), SIGABRT);raise(SIGBRT);

6. alarm函数

设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止。

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

功能:设置定时器(闹钟)。函数调用,开始倒计时,当倒计时为0的时候,函数会给当前的进程发送一个信号:SIGALARM。

参数:seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。

返回值:

  • 之前没有定时器,返回0
  • 之前有定时器,返回之前的定时器剩余的时间

常用:使用alarm(0)取消定时器,返回旧闹钟余下秒数。

每个进程都有且只有唯一个定时器。 比如:进程先执行了alarm(10),2秒后又执行了一个alarm(5),alarm(5)的返回值是8,因为之前有定时器,返回的是之前定时器的剩余时间。然后从现在起该进程还是只有一个定时器,定时5秒,因为后来的定时器会刷新之前的定时器。

注意,alarm定时是与与进程状态无关(自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵尸…无论进程处于何种状态,alarm都计时。

看以下示例程序:

#include <unistd.h>
#include <stdio.h>
int main()
{int i;alarm(1);for(i = 0; ; i++) {printf("%d\n", i);}return 0;
}

用定时器让程序执行1s后停止。
我们用time ./alarm来查看该程序的运行时间:
在这里插入图片描述
可以看到实际运行时间几乎是1秒,但是发现用户时间和系统时间加起来与总的运行时间不同,这是为什么呢。

实际执行时间 = 系统时间 + 用户时间 + 等待时间。程序的很多时间浪费在printf上了。

7. setitimer函数

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

功能:设置定时器(闹钟)。可以替代alarm函数。精度微妙us,可以实现周期性定时。

参数:

  • which : 定时器以什么时间计时,有以下三种参数,一般用第一种自然定时。
    ITIMER_REAL: 真实时间(自然定时),时间到达发送 SIGALRM 常用
    ITIMER_VIRTUAL: 用户时间,时间到达发送 SIGVTALRM
    ITIMER_PROF: 以该进程在用户态和内核态下所消耗的时间来计算,时间到达发送 SIGPROF
  • new_value: 设置定时器的属性
struct itimerval {      // 定时器的结构体struct timeval it_interval;  // 每个阶段的时间,间隔时间struct timeval it_value;     // 延迟多长时间执行定时器
};struct timeval {        // 时间的结构体time_t      tv_sec;     //  秒数     suseconds_t tv_usec;    //  微秒    
};
  • old_value :记录上一次的定时的时间参数,一般不使用,指定NULL

如以下示例程序能够实现延迟3秒,每2秒发送一次信号。

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>// 过3秒以后,每隔2秒钟定时一次
int main() {struct itimerval new_value;// 设置间隔的时间new_value.it_interval.tv_sec = 2;new_value.it_interval.tv_usec = 0;// 设置延迟的时间,3秒之后开始第一次定时new_value.it_value.tv_sec = 3;new_value.it_value.tv_usec = 0;int ret = setitimer(ITIMER_REAL, &new_value, NULL); // 非阻塞的printf("定时器开始了...\n");if(ret == -1) {perror("setitimer");exit(0);}getchar();return 0;
}

由于还没有介绍signal信号捕捉函数,setitimer发出的信号让程序终止,所以无法演示其周期性发送信号的功能,接下来介绍signal函数。

8. signal函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能:设置某个信号的捕捉行为

注意:并不是该函数来捕捉信号,该函数只是向内核注册对某个信号的捕捉行为。

参数:

  • signum: 要捕捉的信号
  • handler: 捕捉到信号要如何处理,可以有以下三种参数:
    - SIG_IGN : 忽略信号
    - SIG_DFL : 使用信号默认的行为
    - 回调函数 : 这个函数是内核调用,程序员只负责写,捕捉到信号后如何去处理信号。

回调函数:
- 需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义
- 不是程序员调用,而是当信号产生,由内核调用
- 函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以了。

返回值:

  • 成功,返回上一次注册的信号处理函数的地址。第一次调用返回NULL
  • 失败,返回SIG_ERR,设置错误号

注意:SIGKILL 和 SIGSTOP不能被捕捉,不能被忽略。

在setitimer的示例代码中加入signal后,示例代码如下:

void myfunc(int num) {printf("捕捉到了信号的编号是:%d\n", num);printf("xxxxxxx\n");
}// 过3秒以后,每隔2秒钟定时一次
int main() {// 注册信号捕捉// signal(SIGALRM, SIG_IGN);// signal(SIGALRM, SIG_DFL);// void (*sighandler_t)(int); 函数指针,int类型的参数表示捕捉到的信号的值。signal(SIGALRM, myfunc);struct itimerval new_value;// 设置间隔的时间new_value.it_interval.tv_sec = 2;new_value.it_interval.tv_usec = 0;// 设置延迟的时间,3秒之后开始第一次定时new_value.it_value.tv_sec = 3;new_value.it_value.tv_usec = 0;int ret = setitimer(ITIMER_REAL, &new_value, NULL); // 非阻塞的printf("定时器开始了...\n");if(ret == -1) {perror("setitimer");exit(0);}getchar();return 0;
}

signal的第二个参数传入函数地址,当当前进程捕捉到SIGALRM信号时,将执行程序员自定义的myfunc函数,myfun函数的int类型参数是捕捉到的信号的值(编号)。
程序运行结果如下:
在这里插入图片描述
程序运行3秒后第一次发出信号,程序输出一次,然后每隔2秒发出一次信号。

9. 信号集

一个进程的PCB中除了包含进程id,状态,工作目录,用户id,组id,文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集。

阻塞信号集(信号屏蔽字): 将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)

未决信号集:

  • 信号产生,未决信号集中描述该信号的位立刻翻转为1,表信号处于未决状态。当信号被处理对应位翻转回为0。这一时刻往往非常短暂。
  • 信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。
    在这里插入图片描述
    信号集本质上是一个64位的二进制数,其每一位的0或1代表着该位序号对应的信号的状态。
    在这里插入图片描述
    当信号产生时,PCB中未决信号集中的该位立即置为1,然后去阻塞信号集的同样位置查看是否为1,如果阻塞信号集的对应位置也为1,说明该信号要阻塞,未决信号集的该位置保持1不变;直到阻塞解除,这个信号就被处理。

10. 自定义信号集相关函数

以下信号集相关的函数都是对自定义的信号集进行操作。

int sigemptyset(sigset_t *set);
  • 功能:清空信号集中的数据,将信号集中的所有的标志位置为0
  • 参数:set,传出参数,需要操作的信号集
  • 返回值:成功返回0, 失败返回-1
int sigfillset(sigset_t *set);
  • 功能:将信号集中的所有的标志位置为1
  • 参数:set:传出参数,需要操作的信号集
  • 返回值:成功返回0, 失败返回-1
int sigaddset(sigset_t *set, int signum);
  • 功能:设置信号集中的某一个信号对应的标志位为1,表示阻塞这个信号
  • 参数:
    - set:传出参数,需要操作的信号集
    - signum:需要设置阻塞的那个信号
  • 返回值:成功返回0, 失败返回-1
int sigdelset(sigset_t *set, int signum);
  • 功能:设置信号集中的某一个信号对应的标志位为0,表示不阻塞这个信号
  • 参数:
    - set:传出参数,需要操作的信号集
    - signum:需要设置不阻塞的那个信号
  • 返回值:成功返回0, 失败返回-1
int sigismember(const sigset_t *set, int signum);
  • 功能:判断某个信号是否阻塞
  • 参数:
    - set:需要操作的信号集
    - signum:需要判断的那个信号
  • 返回值:
    1 : signum被阻塞
    0 : signum不阻塞
    -1 : 失败

一个用到以上函数的示例程序如下:

#include <signal.h>
#include <stdio.h>int main() {// 创建一个信号集sigset_t set;// 清空信号集的内容sigemptyset(&set);// 判断 SIGINT 是否在信号集 set 里int ret = sigismember(&set, SIGINT);if(ret == 0) {printf("SIGINT 不阻塞\n");} else if(ret == 1) {printf("SIGINT 阻塞\n");}// 添加几个信号到信号集中sigaddset(&set, SIGINT);sigaddset(&set, SIGQUIT);// 判断SIGINT是否在信号集中ret = sigismember(&set, SIGINT);if(ret == 0) {printf("SIGINT 不阻塞\n");} else if(ret == 1) {printf("SIGINT 阻塞\n");}// 判断SIGQUIT是否在信号集中ret = sigismember(&set, SIGQUIT);if(ret == 0) {printf("SIGQUIT 不阻塞\n");} else if(ret == 1) {printf("SIGQUIT 阻塞\n");}// 从信号集中删除一个信号sigdelset(&set, SIGQUIT);// 判断SIGQUIT是否在信号集中ret = sigismember(&set, SIGQUIT);if(ret == 0) {printf("SIGQUIT 不阻塞\n");} else if(ret == 1) {printf("SIGQUIT 阻塞\n");}return 0;
}

11. sigprocmask函数

之前的信号集函数均是对自定义的信号集进行操作,那如何修改内核中的阻塞信号集呢?可以使用sigprocmask函数,用自定义的信号集设置内核阻塞信号集。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

功能:将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)。

参数:

  • how : 如何对内核阻塞信号集进行处理,有以下可选参数:

SIG_BLOCK: 将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变(假设内核中默认的阻塞信号集是mask, mask | set)。

SIG_UNBLOCK: 根据用户设置的数据,对内核中的数据进行解除阻塞(相当于 mask = mask & ~set)。

SIG_SETMASK: 用set覆盖内核中原来的值。

  • set :已经初始化好的用户自定义的信号集
  • oldset : 保存设置之前的内核中的阻塞信号集的状态,可以是 NULL。

返回值: 成功:0 ;失败:-1,并设置错误号。

12. sigpending函数

#include <signal.h>
int sigpending(sigset_t *set);

功能:获取内核中的未决信号集。

参数:set,传出参数,保存的是内核中的未决信号集中的信息。

13. sigaction函数

sigaction函数通常用于替代signal函数,用来捕捉信号,同时自定义信号的处理动作。

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

功能:检查或者改变信号的处理。信号捕捉。

参数:

  • signum : 需要捕捉的信号的编号或者宏值(信号的名称)
  • act :捕捉到信号之后的处理动作
  • oldact : 上一次对信号捕捉相关的设置,一般不使用,传NULL即可

返回值: 成功 0 失败 -1

其中参数act的类型sigaction结构体定义如下:

struct sigaction {// 函数指针,指向的函数就是信号捕捉到之后的处理函数void     (*sa_handler)(int);// 不常用void     (*sa_sigaction)(int, siginfo_t *, void *);// 临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号。sigset_t   sa_mask;// 使用哪一个信号处理对捕捉到的信号进行处理// 这个值可以是0,表示使用sa_handler,也可以是SA_SIGINFO表示使用sa_sigactionint        sa_flags;// 被废弃掉了void     (*sa_restorer)(void);};

其中sa_sigaction和sa_restorer我们基本用不到,所以掌握以下三个即可:
① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 或 SIG_DFL表执行默认动作。
sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
③ sa_flags:通常设置为0,表使用默认属性。

该函数与signal函数最大的区别就在于sa_mask上,sa_mask是程序员自定义的一个信号集,该信号集充当调用信号处理函数时的一个临时的阻塞信号集,也就是说:

进程正常运行时,默认PCB中有一个信号屏蔽字(阻塞信号集),假定为☆,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆来指定。而是用sa_mask来指定。调用完信号处理函数,再恢复为☆。

示例程序如下:

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>void catchFunc(int signo) {printf("捕捉到了信号:%d \n", signo);
}int main() {struct sigaction act;act.sa_handler = catchFunc;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, SIGQUIT); // 想要在捕捉函数中屏蔽SIGQUIT信号int res = sigaction(SIGINT, &act, NULL);if(res == -1) {perror("sigaction error");exit(1);}while(1);return 0;
}

执行结果如下:
每次在终端输入ctrl+c(产生SIGINT信号)时,输出:捕捉到了信号2。
在这里插入图片描述
当在键盘中输入ctrl+\(产生SIGQUIT)时, 程序退出。那么有个问题,程序中不是已经设置了sigaddset(&act.sa_mask, SIGQUIT);来屏蔽信号了吗?为什么输入ctrl+\时, 程序依然会退出?是因为sigaction函数设置的sa_mask只在信号处理函数执行中生效,输出语句后信号处理函数以及执行完毕。

再看下面示例程序:该程序让信号处理函数睡眠10秒。

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>void catchFunc(int signo) {printf("捕捉到了信号:%d \n", signo);sleep(10);printf("-----finish-----");
}int main() {struct sigaction act;act.sa_handler = catchFunc;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, SIGQUIT); // 想要在捕捉函数中屏蔽SIGQUIT信号int res = sigaction(SIGINT, &act, NULL);if(res == -1) {perror("sigaction error");exit(1);}return 0;
}

运行程序后,输入ctrl+c,终端输出如下:
在这里插入图片描述
此时10秒以内,依然在执行信号捕捉函数catchFunc,也就是说当前sa_mask是生效的。此时我们输入crtl+\:
在这里插入图片描述
程序并没有退出,因为此时sa_mask中屏蔽了SIGQUIT信号。等待10秒过后:
在这里插入图片描述
发现程序自动退出,这是因为10秒过后信号捕捉函数catchFunc执行完毕,临时的阻塞信号集(sa_mask)失效,此时生效的是原PCB中的阻塞信号集,未决信号集(SIGQUIT处的值为1)查询到后阻塞信号集中SIGQUIT处的值是0后,SIGQUIT信号递达,程序退出。

这里还有一个值得注意的细节:
在这里插入图片描述
当信号捕捉函数catchFunc执行时,我输入了多个ctrl+c后,信号捕捉函数执行完毕后,只输出了一个“捕捉到了信号:2”,这是因为我们无论向当前进程发出多少个相同信号,未决信号集的对应位都是1,无法记录相同信号的数量,所以当临时阻塞信号集被取消后,只输出了一个“捕捉到了信号:2”。有以下结论:

阻塞的常规信号不支持排队,产生多次只记录一次。(后32个实时信号支持排队)。

14. 内核实现信号捕捉过程

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384561.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统编程学习】父进程捕获SIGCHLD信号以处理僵尸进程

配合之前说过的sigaction函数和waitpid函数&#xff0c;我们可以解决子进程变成僵尸进程的问题。 先看如下示例程序&#xff1a; #include <sys/time.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> …

【Linux系统编程学习】Linux线程控制原语

此为牛客Linux C课程笔记。 0. 关于线程 注意&#xff1a;LWP号和线程id不同&#xff0c; LWP号是CPU分配时间片的依据&#xff0c;线程id是用于在进程内部区分线程的。 1. 线程与进程的区别 对于进程来说&#xff0c;相同的地址(同一个虚拟地址)在不同的进程中&#xff0c;反…

【Linux网络编程学习】预备知识(网络字节序、IP地址转换函数、sockaddr数据结构)

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 网络字节序 我们已经知道&#xff0c;内存中的多字节数据相对于内存地址有大端和小端之分。 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分&#xff0c;那么如何定义网络数…

【Linux网络编程学习】socket API(socket、bind、listen、accept、connect)及简单应用

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 什么是socket 所谓 socket&#xff08;套接字&#xff09;&#xff0c;就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。 一个套接字就是网络上进程通信的一端&#xff0c;提供了应用层进程利用网络协议交换…

【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 多进程版 1.1 思路 大体思路与上一篇的单进程版服务器–客户端类似&#xff0c;都是遵循下图&#xff1a; 多进程版本有以下几点需要注意&#xff1a; 由于TCP是点对点连接&#xff0c;服务器主进程连接了一个客户端以后…

【Linux网络编程学习】I/O多路复用——select和poll

此为牛客Linux C课程和黑马Linux系统编程笔记。 0. I/O多路复用 所谓I/O就是对socket提供的内存缓冲区的写入和读出。 多路复用就是指程序能同时监听多个文件描述符。 之前的学习中写了多进程和多线程版的简单服务器模型&#xff0c;但是有个问题&#xff1a;每次新来一个客…

【Linux网络编程学习】阻塞、非阻塞、同步、异步以及五种I/O模型

文章目录1. 基本概念1.1 阻塞与非阻塞1.2 同步与异步1.3 为什么没有“异步阻塞”2. 五种IO模型2.1 阻塞 blocking2.2 非阻塞 non-blocking2.3. IO复用&#xff08;IO multiplexing&#xff09;2.4 信号驱动&#xff08;signal-driven&#xff09;2.5 异步&#xff08;asynchron…

STM32时钟树解析

本人之前其实也用STM32做过一些小东西&#xff0c;但因为时钟的初始化一般是直接在SystemInit时钟系统初始化函数里直接配置为72MHz&#xff0c;所以对于STM32的时钟框图并没有怎么理会&#xff0c;今天刚好有空就重新看了一下并写一篇博客记录一下吧&#xff0c;以免以后又忘了…

S3C2440时钟体系

S3C2440在默认情况下&#xff0c;整个系统全靠一个12MHz的外部晶振提供频率来工作运行的&#xff0c;也就是说CPU、内存、UART、ADC等所有需要用到时钟频率的硬件都工作在12MHz下&#xff0c;但是通过查阅芯片手册我们知道CPU时钟最高可为400MHZ&#xff0c;那么怎么设置时钟让…

关于MCU、CPU扩展SDRAM的一个小知识

像上图这种硬件电路图上的16个数据位和我们在初始化SDRAM的时候设置的16位数据位宽是指我们读写SDRAM的时候可以同时读写16个数据位&#xff0c;数据线越多肯定越快&#xff0c;但是数据线也不可能无限增加&#xff0c;我们在程序里是可以读写8位&#xff0c;16位&#xff0c;3…

S3C2440扩展SDRAM

本文主要目的是记录一下S3C2440扩展SDRAM的一些知识&#xff0c;方便以后查阅。 通过查阅手册我们知道&#xff0c;2440有8个可以用来扩展内存的BANK&#xff0c;其中第6和第7还可用来扩展SDRAM 下面我们来看一下2440扩展SDRAM需要设置哪些寄存器。 一、BWSCON寄存器 该寄存器…

汇编语言的相对跳转和绝对跳转以及反汇编代码解析

上图第一行的b1 main为相对跳转&#xff0c;即跳转到pcoffset,其中pc为当前pc值&#xff0c;offset可以理解为偏移地址&#xff0c;也就是根据当前所在地址加上偏移地址实现跳转&#xff0c;为相对跳转。 我们来看看它的反汇编代码 上图清除完bss区后使用b1指令跳转到30000668…

韦东山嵌入式第一期14课第004节_und异常模示程序示例_P笔记

本节课的第一个程序韦老师是想让大家见识一下未定义异常&#xff0c;而第二个程序是对第一个程序进行改进&#xff0c;防止在某些条件下执行不了&#xff0c;下面就来讲一下第2个程序改进了哪些地方并且有什么用。 程序在此路径中&#xff1a;源码文档图片\源码\源码_20180321…

关于NOR FLASH地址左右移的问题

问题引入&#xff1a;不知道你会不会有这样的疑问&#xff1a;为什么在发送解锁命令时&#xff0c;我们不用右移一位&#xff0c;而发送扇区地址时却要右移一位&#xff08;nor_cmd函数内部已经左移一位&#xff09;&#xff0c;这里先补充说明一下说明是cpu角度和nor角度&…

在linux下利用ls命令进行模糊查找

如上图&#xff0c;我们当前路径下有三个文件&#xff0c;分别为helloworld.c以及helloworld和1.c&#xff0c;直接输入命令ls则显示所有文件&#xff0c;我们可以利用ls 加*的方向进行模糊查找。 输入ls 目录名 形式的命令行&#xff0c;则是对该目录名下的文件全部进行显示&a…

Linux下没有包含头文件(不知是哪个)导致编译无法通过的解决心得

最近写程序的时候编译出错了&#xff0c;提示信息为&#xff1a;invalid use of undefined type fb_var_screeninfo。显示根据英文知道是没有定义 fb_var_screeninfo这个类型&#xff0c;明显是缺少了某个头文件&#xff0c;但是缺少哪个头文件以及有什么又快又好的解决方法呢&…

Linux编译程序时加-I指定头文件位置

Linux下编译出现以下错误&#xff0c;错误的原因是在/usr/local/arm/arm-2009q3/bin/../arm-none-linux-gnueabi/libc/usr/include/freetype/config/下找不到ftheader.h&#xff0c;而我到该目录下看&#xff0c;发现路径是这样的rootubuntu:/usr/local/arm/arm-2009q3/arm-non…

关于源文件用不同的编码方式编写,会导致执行结果不一样的现象及解决方法

如果我们编写以下程序&#xff0c;并分别另存为ANSI和UTF-8两种不同的编码方式保存&#xff0c;放到Linux下编译并运行如下图&#xff0c;两端相同的程序以不同的编码方式保存编译后的运行结果不一样&#xff0c;./ansi采用ANSI编码方式&#xff0c;会自动采用GBK方式来保存中文…

arm-linux-gcc静态编译和动态编译的区别

很多教程会提到加上-static是静态编译&#xff0c;但对于新手来说没有用例子来说明可能不太好理解&#xff0c;今天我就介绍一下关于这方面知识的一个例子&#xff1a; 最近在做一个关于freetype字体的东西&#xff0c;需要依赖freetype官方提供的库&#xff0c;我已经把电脑这…

从0到1写RT-Thread内核——线程定义及切换的实现

从0写RT-Thread内核之线程定义及切换的实现具体可以分为以下六步来实现 一&#xff1a;分别定义线程栈、线程函数、线程控制块&#xff1b; ALIGN(RT_ALIGN_SIZE)//设置4字节对齐 /* 定义线程栈 */ rt_uint8_t rt_flag1_thread_stack[512]; rt_uint8_t rt_flag2_thread_stack…