防御式 bash 编程
防御式 bash 编程译文
资料来源:
https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming/
更新
1
2021.08.26 初始
防御式 bash 编程译文
资料来源:
https://kfirlavi.herokuapp.com/blog/2012/11/14/defensive-bash-programming/
更新
1 | 2021.08.26 初始 |
Debian 10 -> 11 ; kernel 4.19 -> 5.19
资料来源:
https://zhuanlan.zhihu.com/p/400092868
https://blog.im.ci/study-notes/linux-notes/684/
https://blog.im.ci/study-notes/linux-notes/684/
更新
1 | 2023.01.06 初始 |
这篇草稿有了 4 年了吧,已经不是拖延症的问题了…
资料来源:
更新
1 | 2022.03.27 初始 |
linux 文件权限详解,uid gid 等
资料来源:
linux 命令行与 shell 脚本大全
更新
1 | 20.11.07 初始化 |
顾名思义,共享内存就是说两个不同的进程 A、B 可以共同享有一块内存区域
整个处理流程是
shmget 函数
shmget 被用来开辟/初始化一段共享内存.其他进程使用相同的 key 通过 shgat 获取同一段共享内存.,只有 shmget 函数才直接使用信号量键,所有其他的信号量函数使用由 semget 函数返回的信号量标识符.
函数原型
1 | int shmget(key_t key, size_t size, int shmflg); |
key : 与信号量的 semget 函数一样,使用共享内存 key(非 0 整数)shmget 函数成功时返回一个与 key 相关的共享内存标识符 (非负整数),用于多进程的共享.调用失败返回 -1.
size : 开辟的共享内存大小 (字节).
shmflg : 权限控制,与 IPC_CREAT 做或操作,控制其他进程对共享内存权限. 0644 即代表其他进程只有读的权限.
shmat 函数
用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间
函数原型
1 | void *shmat(int shm_id, const void *shm_addr, int shmflg); |
shm_id: 由 shmget 函数返回的共享内存标识.
shm_addr: 指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址.
shm_flg: 标志位,通常为 0.
shmdt 函数
将共享内存从当前进程中分离,使该共享内存对当前进程不再可用.
函数原型:
1 | int shmdt(const void *shmaddr); |
shmaddr: shmat 函数返回的地址指针,调用成功时返回 0,失败时返回 -1.
shmctl 函数
控制共享内存.
函数原型
1 | int shmctl(int shmid, int cmd, struct shmid_ds *buf) |
shm_id: shmget 函数返回的共享内存标识符
command: 要采取的操作,以下三个值 :
buf: 结构指针,指向共享内存模式和访问权限的结构.
1 | shmid_ds结构至少包括以下成员: |
linux 默认限制共享内存总大小由 SHMMAX 值确定.默认值未 32MB
读取
1 | cat /proc/sys/kernel/shmmax |
当超过系统限制时 提示 unable to attach to shared memory
.
规避
直接修改/proc.无需重启
1 | echo "2147483648" > /proc/sys/kernel/shmmax |
可以将命令写入启动脚本 /etc/rc.local 中.保证重启生效
使用 sysctl 命令修改
1 | sysctl -w kernel.shmmax=2147483648 |
可以将此参数插入到 /etc/sysctl.conf 启动文件中
1 | echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf |
永久生效.
与大小类似的,共享内存创建的总数量由 SHMMNI 参数确定.
读取
1 | cat /proc/sys/kernel/shmmni |
默认情况下是 4096.
一般不需要修改.
shmat 即挂载共享内存到进程的进程空间.
当同一进程多次调用 shmat 挂载同一共享内存时,shamat 每次返回的地址都不同,相当于在进程的线性空间中存在多个实际指向同一块共享内存.直到最后进程线性空间消耗殆尽.
解决:
需要在挂载共享内存前,判断申请的共享内存指针是否为空,为 NULL ,则第一次加载此共享内存.否则不再重复加载.
1 | void* p = NULL; |
共享内存创建有大小之分.key 相同情况下,容量小的共享内存会获得之前创建的大的共享内存的内容.有可能导致之前创建共享内存的进程崩溃.
解决
1 | Shmid = Shmget(key, size,IPC_CREATE|IPC_EXCL); |
*p->next
访问.rtc 子系统的源码在 /drivers/rtc
删减了很多 rtc-xxx.c 的驱动,只留下了 ds1307 作为示例,这里看到实际上代码并不多。
具体文件分析
RTC 子系统具体可分为 3 层:
初看 linux 系统的人来说,这个图够头晕的了,但是呢,实际上没那么麻烦。由浅入深,一点一点来分析。
rtc 子系统基于字符设备,字符设备对应的肯定是 rtc-dev.c 了,我们的分析由 rtc-dev 起步。
rtc-dev.c
典型的字符设备,模块的初始化/卸载自然是 rtc_dev_init(void) 和 rtc_dev_exit(void)。
设备接入,添加/删除设备 rtc_dev_add_device(struct rtc_device _rtc) 和 e) void rtc_dev_del_device(struct rtc_device_rtc)
还有 ioctl 和 open 等函数,熟悉字符设备驱动的不用多说。
追踪一下这两组函数在哪里调用的。
rtc_dev_init(void) 对应实在系统初始化时使用,对应 rtc_init(void) 也是在系统初始化之后调用。
rtc_dev_add_device() 是在驱动匹配后调用,rtc_device_register() 也是在驱动匹配后调用。打开 rtc-ds1307.c>-prob 函数,能找到 rtc_device_register() 也就证实了这个思路。
接下来转入 class.c
linux 驱动模型中 class.c 对应类的意思,是 rtc 类。
每个 class 对应都有自己核心数据结果,对应 rtc 类就是 rtc-device
rtc_device
rtc_device 代表 RTC 设备基础的数据结构
数据结构
1 | struct rtc_device { |
很长?很😵对不对?只要关注一点就行 _
1 |
|
上文书说到,驱动程序的 prob 函数里面调用了 rtc_device_register() 这货的类型就是 rtc_device。参加驱动程序怎样调用 rtc_device_register(),与其他核心的基本结构不同的是,驱动程序以不是以 rtc-device 为参数注册设备到子系统,而是注册函数会返回一个 rtc_deivce 的结构给驱动。
rtc_class_ops
这个是 rtc_device 的一部分。_
数据结构
1 | struct rtc_class_ops { |
该结构体中函数大多数都是和 rtc 芯片的操作有关,需要驱动程序实现。
所有 RTC 驱动都必须实现 read_time、set_time 函数,其他函数可选。
参考其他的资料,class 的分析如下
static int __init rtc_init(void)
static void _exit rtc_exit(void)
struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)_
void rtc_device_unregister(struct rtc_device *rtc)
static void rtc_device_release(struct device dev)
上图
使用 rtc 子系统首先需要在内核编译选项中启用 RTC 子系统支持。
_ 系统启动后,如配置启用 rtc 子系统,则会首先执行 rtc_init_ 函数,创建 rtc 类、初始化相关成员、分配设备号等
创建 rtc 类后,之后调用 rtc_dev_init() 动态分配 rtc 字符设备的设备号。之后调用 rtc_sysfs_init() 初始化/sys/class/rtc 目录中的属性文件
Rtc 设备本质上属于字符设备,依附于系统内总线。一般来说 cpu 内部 rtc 依附于 platform 总线,外置 rtc 芯片则依附于通信方式对应总线。其过程与通用字符设备相似,rtc 子系统在设备注册过程中附加了 prob 和 sysfs 相关的注册和初始化操作。
上图
Rtc 设备挂载后,相应总线会搜索匹配的驱动程序,驱动程序成功 match 后,进入驱动实现的 probe 函数,执行设备注册等操作。
完成总线设备注册后,probe 会跳转到 rtc_device_register() 函数,将设备注册到 rtc 子系统。
Rtc 设备本质属于字符设备,会调用 rtc_dev_prepare() 函数,初始化字符设备,设置 rtc 相关的 file operation 函数集合。
之后依次调用 rtc_dev_add_device(rtc)、rtc_sysfs_add_device(rtc)、rtc_proc_add_device(rtc) ,进行注册字符设备、在/sys/rtc/目录下创建一个闹钟属性文件、创建/proc/driver/rtc 目录等操作。
rtc_device_register() 会将驱动实现的 rtc_class_ops 结构体与具体设备联系起来。
在 rtc-proc.c、rtc_sysfs 和 ioctl 命令中,所有的操作调用的都是 interface.c 提供的接口,这里以 ioctl 的一个例子说明整个调用的过程
上图
以 icotl 命令 RTC_RD_TIME 为例,说明命令调用的流程。
RTC_RD_TIME 对应的是/dev 下 ioctl 命令,调用被转发至/rtc-dev.c
rtc-dev.c->rtc_dev_ioctl(struct file *file,unsigned int cmd, unsigned long arg) 函数中。RTC_RD_TIME 对应的代码为 err = rtc_read_time(rtc, &tm); rtc_read_time 是 interface.c 文件提供的接口之一。
interface.c->rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) 函数中对应 rtc_class_ops 转发代码为 err = rtc->ops->read_time(rtc->dev.parent, tm); 将操作转发至匹配的 rtc 设备。
设备驱动这里以 rtc-ds1307 为例,但设备注册时通过 mcp794xx_rtc_ops 结构体将 rtc_class_ops 对应函数与驱动程序实现的函数绑定
1 | static const struct rtc_class_ops mcp794xx_rtc_ops = { |
最终执行转入 ds1307.c-> ds1307_get_time 函数,执行与硬件相关的操作。
由前半部分可知,/sys/class/rtc/是在 rtc-init 调用 rtc_sysfs_init 后生成。
1 |
|
这里的 rtc_groups 是 rtc-sysfs.c 中定义了这样一个 attribute 函数指针数组:
1 |
|
_ 在 rtc_sysfs_init 函数调用后绑定了 sysfs 节点操作函数的集合,使得设备匹配驱动程序后而生成对应的 rtcn 文件夹。
dev_attr_name和dev_attr_data
由宏 DEVICE_ATTR_RO
和 DEVICE_ATTR_RW 生成,他们分别定义了只读的和可读可写的 attribute 节点。每个属性函数下都有 DEVICE_ATTR_XX() 宏声明,绑定到对应 attribute 节点。
proc 文件系统是软件创建的文件系统,内核通过他向外界导出信息,下面的每一个文件都绑定一个函数,当用户读取这个文件的时候,这个函数会向文件写入信息。
rtc-proc.c 中初始化了 file_operations 结构体:
1 | static const struct file_operations rtc_proc_fops = { |
_RTC 驱动在向 RTC 核心注册自己的时候,由注册函数 rtc_device_resgister 调用 rtc_proc_add_device 来实现 proc 接口的初始化,这个函数如下定义:
1 |
|
主要是完成创建文件节点,并将文件的操作函数与节点联系起来。调用这个函数后,在/proc/driver 目录下就会有一个文件 rtc
在/dev 下用户可以通过两种方式访问 rtc 设备,第一个是通过字符设备定义的 open、read 等函数(需要驱动程序实现)、另一个是通过定义的 ioctl 命令。第一种方式是直接打开 rtc-dev.c 定义的 open 等函数,在 open 等中直接调用驱动程序实现的函数。通过 ioctl 命令访问则是将操作转发到了 interface.c 定义的接口,间接调用驱动程序实现的函数。
ioctl() 函数访问/dev 下的设备。以下是典型函数:_
ioctl(fd,RTC_ALM_SET, &rtc_tm);
设置 alarm 中断的触发时刻,不超过 24 小时。第三个参数为 structrtc_time 结构体,读取时会忽略年月日信息。alarm 中断与 wakeupalarm 中断只能同时使用 1 个,以最后一次设定为准。
ioctl(fd,RTC_ALM_READ, &rtc_tm)
读取 alarm 中断的触发时刻。
ioctl(fd,RTC_WKALM_SET, &alarm);
设置 wakeupalarm 中断的触发时刻, wakeupalarm 中断的触发时刻可以在未来的任意时刻。alarm 中断与 wakeupalarm 中断只能同时使用 1 个,以最后一次设定为准。
ioctl(fd,RTC_WKALM_RD, &alarm);
读取 wakeupalarm 中断的触发时刻。
ioctl(fd,RTC_IRQP_SET, tmp);
设置周期中断的频率,tmp 的值必须是 2 的幂,非 Root 用户无法使用 64HZ 以上的周期中断。
ioctl(fd,RTC_IRQP_READ, &tmp);
读取周期中断的频率。
ioctl(fd,RTC_SET_TIME, &rtc_tm)
更新 RTC 芯片的当前时间。
ioctl(fd,RTC_RD_TIME, &rtc_tm);
读取 RTC 硬件中的当前时间。
以 open 操作为例,在用户层对/dev 下设备执行 open 会被转发至 rtc_dev_open(struct inode *inode, struct file *file)
函数,通过 err= ops->open ? ops->open(rtc->dev.parent) : 0;
判断驱动程序是否通过连接的 rtc_class_ops 结构体实现了 open 函数,驱动程序实现了 open 函数,则将 open 操作转发至驱动程序。
Hwclock 命令或使用测试文件。
http://blog.csdn.net/u011118014/article/details/43232693
调用 chdir 即可
1 | chdir("/info"); |
源码,打开文件夹返回对应文件夹的 DIR 结构体
1 | DIR *dir; |
DIR 结构体
1 | struct __dirstream |
保存文件夹相关内容,无需深究
源码,遍历文件
1 | struct direct *ent; |
dirent 结构体
1 | struct dirent |
dirent 指向目录和目录中某个具体文件,但还是桥梁作用,访问文件具体内容还需要通过 d_name 找到 stat 结构体支援.
源码,获取 stat 结构体
1 | stat f_stat; |
stat 结构体是指向文件的结构体
1 | struct stat { |
之后愉快访问吧
资料来源:
<>
更新
1 | 2023.11.28 初始 |
资料来源:
<>
更新
1 | 2023.11.28 初始 |