1.filp->private_data = &gpioled; /* 设置私有数据 */
static int led_open(struct inode *inode, struct file *filp)
{
/* 通过判断原子变量的值来检查LED有没有被别的应用使用 */
if (!atomic_dec_and_test(&gpioled.lock)) {
atomic_inc(&gpioled.lock); /* 小于0的话就加1,使其原子变量等于0 */
return -EBUSY; /* LED被使用,返回忙 */
}
filp->private_data = &gpioled; /* 设置私有数据 */
return 0;
}
将 private_data
设置为指向 gpioled
结构体的指针可以允许文件操作函数访问或修改与该LED设备相关的数据
2.查找文件
查找pinctrl-stm32.c这个某个目录及其子目录下是否有这个文件
sudo find ./ -type f -name "pinctrl-stm32.c"
3.使用 ls 命令查看到的大小是文件的逻辑大小,自然是包括了空洞部分大小和真实数据部分大小。
4.du 命令查看到的大小是文件实际占用存储块的大小。
5.标志文件IO
6.printf()、 fprintf()、 dprintf()、 sprintf()、 snprintf()区别
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *buf, const char *format, ...);
int snprintf(char *buf, size_t size, const char *format, ...);
printf()函数用于将格式化数据写入到标准输出;
dprintf()和 fprintf()函数用于将格式化数据写入到指定的文件中,两者不同之处在于, fprintf()使用 FILE 指针指定对应的文件、而 dprintf()则使用文件描述符 fd 指定对应的文件;
sprintf()、 snprintf()函数可将格式化的数据存储在用户指定的缓冲区 buf 中。
7.printf 输出格式
8.scanf()、 fscanf()、 sscanf()区别
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
scanf()函数可将用户输入(标准输入)的数据进行格式化转换;
fscanf()函数从 FILE 指针指定文件中读取数据,并将数据进行格式化转换;
sscanf()函数从参数 str 所指向的字符串中读取数据,并将数据进行格式化转换;
9.scanf输入格式
scanf("%hd", var); //匹配 short int 类型数据
scanf("%hhd", var); //匹配 signed char 类型数据
scanf("%ld", var); //匹配 long int 类型数据
scanf("%f", var); //匹配 float 类型数据
scanf("%lf", var); //匹配 double 类型数据
scanf("%Lf", var); //匹配 long double 类型数据
10.文件 I/O 内核缓冲区和 stdio 缓冲区之间的联系与区别
11.Linux 系统中,可将硬件设备分为字符设备和块设备,所以就有了字符设备文件和块设备文件两种文件类型。虽然有设备文件,但是设备文件并不对应磁盘上的一个文件,也就是说设备文件并不存在于磁盘中,而是由文件系统虚拟出来的,一般是由内存来维护, 当系统关机时,设备文件都会消失; 字符设备文件一般存放在 Linux 系统/dev/目录下,所以/dev 也称为虚拟文件系统 devfs。
12.譬如调用 open 函数新建文件时, mode 参数指定为 0777, 假设 umask 为 0002,那么实际权限为:0777 & (~0002) = 0775
13.文件权限
14.程序终止
15.什么是进程
进程其实就是一个可执行程序的实例,可执行程序就是一个可执行文件,文件是一个
静态的概念,存放磁盘中,如果可执行文件没有被运行,那它将不会产生什么作用,当它被运行之后,它将会对系统环境产生一定的影响,所以可执行程序的实例就是可执行文件被运行
17.环境变量查看与添加
在 shell 终端下可以使用 env 命令查看到 shell 进程的所有环境变量
使用 export 命令还可以添加一个新的环境变量或删除一个环境变量:
export LINUX_APP=123456 # 添加 LINUX_APP 环境变量
使用"export -n LINUX_APP"命令则可以删除 LINUX_APP 环境变量。
export -n LINUX_APP # 删除 LINUX_APP 环境变量
在执行程序的时候,在其路径前面添加环境变量,以 name=value 的形式添加,如果是多个环境变量,则在./app 前面放置多对 name=value 即可,以空格分隔。
18.C 语言程序组成部分
19.Liunx虚拟地址
在 32 位系统中,每个进程的逻辑地址空间均为 4GB, 这 4GB 的内存空间按照 3:1 的比例
进行分配,其中用户进程享有 3G 的空间,而内核独自享有剩下的 1G 空间
虚拟地址会通过硬件 MMU(内存管理单元)映射到实际的物理地址空间中, 建立虚拟地址到物理地址的映射关系后,对虚拟地址的读写操作实际上就是对物理地址的读写操作, MMU 会将虚拟地址“翻译”为对应的物理地址
程序代码加载到内存的地址是由系统随机分配的, 是无法预知的, 所以程序的运行地址在编译程序时是无法确认的。
20.系统调度
对于单核 CPU 计算机来说,在某一个时间它只能运行某一个进程的代码指令,那其它进程怎么办呢(多核处理器也是如此,同一时间每个核它只能运行某一个进程的代码) ?这里就出现了调度的问题,系统是这样做的,每一个进程(或线程)执行一段固定的时间,时间到了之后切换执行下一个进程或线程,依次轮流执行,这就称为调度,由操作系统负责这件事情。
21.父子进程调用注意项
⚫ 对于行缓冲设备,可以加上对应换行符,譬如 printf 打印输出字符串时在字符串后面添加\n 换行符,对于 puts()函数来说,本身会自动添加换行符;
⚫ 在调用 fork()之前,使用函数 fflush()来刷新 stdio 缓冲区,当然,作为另一种选择,也可以使用setvbuf()和 setbuf()来关闭 stdio 流的缓冲功能,这些内容在 3.11 中已经给大家介绍过;
⚫ 子进程调用_exit()退出进程、而非使用 exit(),调用_exit()在退出时便不会刷新 stdio 缓冲区,这也解释前面为什么我们要在子进程中使用_exit()退出这样做的一个原因。 将示例代码 9.9.2 中子进程的退出操作 exit()替换成_exit()进行测试, 打印的结果便只会显示一次字符串。
22.回话与进程组、进程之间的关系
当用户在某个终端登录时,一个新的会话就开始了; 当我们在 Linux 系统下打开了多个终端窗口时,实际上就是创建了多个终端会话。
23.什么是线程
线程是参与系统调度的最小单位。 它被包含在进程之中, 是进程中的实际运行单位。一个线程指的是进程中一个单一顺序的控制流(或者说是执行路线、执行流), 一个进程中可以创建多个线程, 多个线程实现并发运行, 每个线程执行不同的任务。 譬如某应用程序设计了两个需要并发运行的任务 task1 和 task2,可将两个不同的任务分别放置在两个线程中。
多进程编程的劣势:
进程间切换开销大。多个进程同时运行(指宏观上同时运行,无特别说明,均指宏观上),微观上依然是轮流切换运行,进程间切换开销远大于同一进程的多个线程间切换的开销,通常对于一些中小型应用程序来说不划算。 |
进程间通信较为麻烦。 每个进程都在各自的地址空间中、相互独立、隔离,处在于不同的地址空间 |
多线程编程优势:
同一进程的多个线程间切换开销比较小。
同一进程的多个线程间通信容易。 它们共享了进程的地址空间,所以它们都是在同一个地址空间中,通信容易。
线程创建的速度远大于进程创建的速度。
多线程在多核处理器上更有优势!
24.并行 串行 并发区别
⚫ 串行:一件事、一件事接着做
⚫ 并发:交替做不同的事;
⚫ 并行:同时做不同的事。
多核处理器和单核处理器:对于单核处理器来说,只有一个执行单元,同时只能执行一条指令;而对于多核处理起来说,有多个执行单元,可以并行执行多条指令,譬如 8 核处理器,那么可以并行
执行 8 条不同的指令。
25.线程同步:互斥锁、条件变量、自旋锁、读写锁
1.互斥锁(mutex)又叫互斥量,从本质上说是一把锁,在访问共享资源之前对互斥锁进行上锁,在访问完成后释放互斥锁(解锁);对互斥锁进行上锁之后,任何其它试图再次对互斥锁进行加锁的线程都会被阻塞,直到当前线程释放互斥锁。如果释放互斥锁时有一个以上的线程阻塞,那么这些阻塞的线程会被唤醒,它们都会尝试对互斥锁进行加锁,当有一个线程成功对互斥锁上锁之后,其它线程就不能再次上锁了,只能再次陷入阻塞,等待下一次解锁。
2.死锁:程序中使用一个以上的互斥锁,如果允许一个线程一直占有第一个互斥锁,并且在试图锁住第二个互斥锁时处于阻塞状态,但是拥有第二个互斥锁的线程也在试图锁住第一个互斥锁。因为两个线程都在相互请求另一个线程拥有的资源,所以这两个线程都无法向前运行,会被一直阻塞,于是就产生了死锁
3.条件变量用于自动阻塞线程,知道某个特定事件发生或某个条
件满足为止,通常情况下,条件变量是和互斥锁一起搭配使用的。 使用条件变量主要包括两个动作:
⚫ 一个线程等待某个条件满足而被阻塞;
⚫ 另一个线程中,条件满足时发出“信号”。
为了说明这个问题,来看一个没有使用条件变量的例子, 生产者---消费者模式,生产者这边负责生产产品、而消费者负责消费产品,对于消费者来说,没有产品的时候只能等待产品出来,有产品就使用它。
4.自旋锁与互斥锁很相似, 从本质上说也是一把锁,在访问共享资源之前对自旋锁进行上锁,在访问完成后释放自旋锁(解锁);事实上,从实现方式上来说,互斥锁是基于自旋锁来实现的,所以自旋锁相较于互斥锁更加底层。
5.读写锁有3 种状态:读模式下的加锁状态(以下简称读加锁状态)、写模式下的加锁状态(以下简称写加锁状态)和不加锁状态(见) ,一次只有一个线程可以占有写模式的读写锁,但是可以有多个线程同时占有读模式的读写锁。因此可知,读写锁比互斥锁具有更高的并行性!
26.阻塞IO和非阻塞IO区别和优点:
阻塞其实就是进入了休眠状态,交出了 CPU 控制权,譬如 wait()、 pause()、 sleep()等函数都会进入阻塞。当对文件进行读操作时,如果数据未准备好、 文件当前无数据可读,那么读操作可能会使调用者阻塞,直到有数据可读时才会被唤醒, 这就是阻塞式 I/O 常见的一种表现;如果是非阻塞式 I/O,即使没有数据可读,也不会被阻塞、而是会立马返回错误!
阻塞IO优点:
所以阻塞式 I/O 的优点在于能够提升 CPU 的处理效率,当自身条件不满足时,进入阻塞状态,交出 CPU资源,将 CPU 资源让给别人使用;而非阻塞式则是抓紧利用 CPU 资源,譬如不断地去轮训, 这样就会导致该程序占用了非常高的 CPU 使用率!
27.普通IO和存储映射IO文件复制区别:
![](https://i-blog.csdnimg.cn/blog_migrate/43583fe5e6725dd06a9f8c27c4be6845.png)
使用存储映射 I/O 减少了数据的复制操作, 所以在效率上会比普通 I/O 要高,其次上面也讲了,普通 I/O 中间涉及到了很多的函数调用过程,这些都会导致普通 I/O 在效率上会比存储映射 I/O 要低。
应用层与内核层是不能直接进行交互的,必须要通过操作系统提供的系统调用或库函数来与内核进行数据交互,包括操作硬件。通过存储映射 I/O 将文件直接映射到应用程序地址空间中的一块内存区域中,也就是映射区;直接将磁盘文件直接与映射区关联起来,不用调用 read()、 write()系统调用,直接对映射区进行读写操作即可操作磁盘上的文件,而磁盘文件中的数据也可反应到映射区中,这就是一种共享,可以认为映射区就是应用层与内核层之间的共享内存。
28.文件锁 线程锁区别:
1.互斥锁、自旋锁以及读写锁,文件锁与这些锁一样,都是内核提供的锁机制, 锁机制实现用
于对共享资源的访问进行保护; 只不过互斥锁、自旋锁、 读写锁与文件锁的应用场景不一样, 互斥锁、自旋锁、读写锁主要用在多线程环境下,对共享资源的访问进行保护, 做到线程同步。
2.文件锁, 顾名思义是一种应用于文件的锁机制, 当多个进程同时操作同一文件时,我们怎么保证文件数据的正确性, linux 通常采用的方法是对文件上锁, 来避免多个进程同时操作同一文件时产生竞争状态。譬如进程对文件进行 I/O 操作时,首先对文件进行上锁,将其锁住,然后再进行读写操作;只要进程没有对文件进行解锁,那么其它的进程将无法对其进行操作;这样就可以保证,文件被锁住期间,只有它(该进程)可以对其进行读写操作。
29.sysfs文件系统
sysfs 文件系统的主要功能便是对系统设备进行管理
sysfs 文件系统中的目录,包括 block、 bus、 class、 dev、 devices、 firmware、 fs、 kernel、
module、 power 等,每个目录下又有许多文件或子目录
系统中所有的设备(对象)都会在/sys/devices 体现出来,是 sysfs 文件系统中最重要的目录结构;而/sys/bus、 /sys/class、 /sys/dev 分别将设备按照挂载的总线类型、功能分类以及设备号的形式将设备组织存放在这些目录中,这些目录下的文件都是链接到了/sys/devices 中
30.应用层想要对底层硬件进行操控方式
⚫ /dev/目录下的设备文件(设备节点) ;
⚫ /sys/目录下设备的属性文件。
1.具体使用哪种方式需要根据不同功能类型设备进行选择,有些设备只能通过设备节点进行操控,而有些设备只能通过 sysfs 方式进行操控;当然跟设备驱动具体的实现方式有关,通常情况下,一般简单地设备会使用 sysfs 方式操控,其设备驱动在实现时会将设备的一些属性导出到用户空间 sysfs 文件系统,以属性文件的形式为用户空间提供对这些数据、属性的访问支持,譬如 LED、 GPIO 等。
2.对于一些较复杂的设备通常会使用设备节点的方式, 譬如 LCD 等、触摸屏、摄像头等。