【HIT-OSLAB-实验中的碎碎念】

news/2024/4/26 20:47:44/文章来源:https://blog.csdn.net/SDDHDY/article/details/128101819

文章目录

  • 应该养成的好习惯
  • 删除 替换 修改 内容时 记得留备份
  • 遇到问题要通过文字 图片 等多种途径去记录
  • 不同的项目应该在不同的文件夹进行处理
  • 代码文档 记得添加一些注释用于说明功能
  • 多输出有区别度的提示信息
  • s找bug 先定位错误 再改
  • 当一份代码有不同版本的时候 记得说明每份代码的版本 可能存在的bug 现在具备的功能
  • 写代码 先写思路 待思路明确后再进行修改
  • 多用man
  • lab 0熟悉实验环境
  • lab 1输出硬件参数
    • 前言
    • 实验环境
    • 实验内容
    • 说明:
    • 需要知道的基础知识
    • 修改/boot/bootsect.s 使其能在屏幕上输出提示信息(xxx is booting)
    • 修改/boot/setup.s 使其能输出(now we are in SETUP)
    • 将硬件参数(内存大小、硬盘参数)输出到屏幕上
  • lab2-system call
    • 实验目的
    • 实验内容
      • 指导书给的内容
        • iam()
        • whoami()
      • 简单点
    • 实验中的记录
      • 第一次报错信息
      • 怎么传值
      • 第二次错误 segment fault
      • 第三个错误
      • 第四个问题
    • 实验原理
    • 附记
      • 将测试脚本 挂载到linux0.11中(在files文件夹中)
      • 测试程序(防止没有files)
      • kernel中的makefile代码
  • lab 3进程运行轨迹的跟踪和统计
    • 实验目的
    • 实验内容
    • 几个问题
    • sched.c & sleep_on()
    • process.c
    • 监控命令
    • process.c v2
    • W阻塞态 E退出 J就绪态 R运行态 N新建
    • 去除掉window下的^M
    • ubuntu可过编译 linux0.11挂了
    • 出现了连续两行一样的内容
    • 某个进程前后两个状态是一致的
    • 修改完的代码如下
    • process.c v3
    • process.sh
    • sleep on
    • 思考问题
      • 结合自己的体会 从程序设计者的角度看 单进程编程和多进程编程最大的区别是什么
      • 你是如何修改时间片的? 仅仅针对样本程序建立的进程,在修改时间片前后 log文件的统计结果都是怎么样的,结合你的修改分析一下为什么会这样,或者为什么没变化?
  • lab 7 Proc 文件系统的实现
    • 实验内容
    • procfs简介
    • 实现思路
    • ll /proc
    • cat
    • cat /proc/psinfo
    • cat /proc/meminfo
    • cat /proc/hdinfo
    • include/sys/stat.h
    • fs/namei.c
    • init/main.c
    • fs/read_write.c
    • fs/proc_dev.c
    • 死循环了
    • mycat.c
    • proc.sh
    • fs/final proc_dev.c
    • read_write.c
    • fs/Makefile
    • include/linux/mm.h
    • 思考问题
    • 遇到的几个问题BUG
  • lab 6 字符显示
  • lab4 实现信号量
    • int sys_lseek(unsigned int fd, off_t offset, int origin) 文件句柄 新的文件读写偏移 偏移起始位置 SEEK_SET (0) SEEK_CUR(1)SEEK_END
    • gcc -o test test.c -lpthread
    • 很有意思一错误 if(!fork)
    • ubuntu下信号量使用不对
    • bug1,if(!fork);
    • bug2,fopen,open失效;
    • bug3,创建了进程和信号量,但是消费者无限等待,
    • bug4,在0.11上一直出现string or char...编译错误,
    • pc.c
    • pc_hdc.c > pc_hdc.log
    • pv.c
    • sem.h
    • sem.c
    • test.c
    • testsem.c v2
    • sem.c v2
    • sem.c v3
    • sem.c v3.5
    • sem v4
  • lab5 address
    • shmid_ds
    • int shmget(key_t key, size_t size, int shmflg)
    • int shmat(int shmid, const void *shmaddr, int shmflg)
    • int shmdt(const void *shmaddr)
    • shm_com.h
    • shmread.c
    • shmwrite.c
    • mmpc.c v1 -pthread
    • include/unistd.h
    • kernel/system_call.s
    • include/linux/shm.h
    • mmpc_hdc.c
    • kernel/shm.c
    • linux shmid_ds linux2.6
    • unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags)

应该养成的好习惯

删除 替换 修改 内容时 记得留备份

遇到问题要通过文字 图片 等多种途径去记录

不同的项目应该在不同的文件夹进行处理

代码文档 记得添加一些注释用于说明功能

多输出有区别度的提示信息

s找bug 先定位错误 再改

当一份代码有不同版本的时候 记得说明每份代码的版本 可能存在的bug 现在具备的功能

写代码 先写思路 待思路明确后再进行修改

多用man

lab 0熟悉实验环境

IA-32(X86)结构的PC机

主要的软件环境是Bochs + gcc + 编辑器 + OS + Linux0.11源代码

根据要求编写程序,修改Linux0.11的源代码

用gcc编译后,

在bochs中的虚拟环境中运行,调试目标代码(bochs是一个免费开源的IA-32架构的PC机模拟器,在它模拟出的环境中可以运行多种操作系统。)

在Ubuntu上制作了hit-oslab集成环境。解压就可以使用。

cd /home/shiyanlou/oslab/tar -zxvf hit-oslab-linux-20110823.tar.gzls -al

oslab采用bochs 模拟器加载这个image文件,模拟执行Linux0.11。

bochs目录

与bochs相关的执行文件、数据文件和配置文件

run脚本

run是运行bochs的脚本命令

  • run 脚本

run 是运行 bochs 的脚本命令。

运行后 bochs 会自动在它的虚拟软驱 A 和虚拟硬盘上各挂载一个镜像文件,软驱上挂载是 linux-0.11/Image,硬盘上挂载的是 hdc-0.11.img。

因为 bochs 配置文件中的设置是从软驱 A 启动,所以 Linux 0.11 会被自动加载。

而 Linux 0.11 会驱动硬盘,并 mount 硬盘上的文件系统,也就是将 hdc-0.11.img 内镜像的文件系统挂载到 0.11 系统内的根目录 —— /。在 0.11 下访问文件系统,访问的就是 hdc-0.11.img 文件内虚拟的文件系统。

  • hdc-0.11.img 文件

hdc-0.11.img 文件的格式是 Minix 文件系统的镜像。

Linux 所有版本都支持这种格式的文件系统,所以可以直接在宿主 Linux 上通过 mount 命令访问此文件内的文件,达到宿主系统和 bochs 内运行的 Linux 0.11 之间交换文件的效果。

Windows 下目前没有(或者是还没发现)直接访问 Minix 文件系统的办法,所以要借助于 fdb.img,这是一个 1.44M 软盘的镜像文件,内部是 FAT12 文件系统。将它挂载到 bochs 的软驱 B,就可以在 0.11 中访问它。而通过 filedisk 或者 WinImage,可以在 Windows 下访问它内部的文件。

hdc-0.11.img 内包含有:

  • Bash shell;
  • 一些基本的 Linux 命令、工具,比如 cp、rm、mv、tar;
  • vi 编辑器;
  • gcc 1.4 编译器,可用来编译标准 C 程序;
  • as86 和 ld86;
  • Linux 0.11 的源代码,可在 0.11 下编译,然后覆盖现有的二进制内核。

其他文件在后面用到的时候会进行单独讲解。

pwdcd ./linux-0.11/make clean && make all
# linux-0.11/imagecd ~/oslab/./run# asm level
cd ~/oslab/./dbg-asmhelp
# reg = watch reg
# # c level
cd ~/oslab
./dbg-c# open new terminator
cd ~/oslab
./rungdb
#次序不能变 否则无法连接gdb
gcc test.cgcc test.c -o testgcc -E test.c -o test.igcc -S test.igcc -c test.sgcc test.o -o testgcc -o1 test.c -o test
apt install gcc make dkms
apt install build-essential linux-headers-`uname -r`
./share.sh # 执行文件
ls -l share.sh # 查看文件权限
chmod u+x share.sh  # 修改用户模式
./share.shls

文章目录

  • 应该养成的好习惯
  • 删除 替换 修改 内容时 记得留备份
  • 遇到问题要通过文字 图片 等多种途径去记录
  • 不同的项目应该在不同的文件夹进行处理
  • 代码文档 记得添加一些注释用于说明功能
  • 多输出有区别度的提示信息
  • s找bug 先定位错误 再改
  • 当一份代码有不同版本的时候 记得说明每份代码的版本 可能存在的bug 现在具备的功能
  • 写代码 先写思路 待思路明确后再进行修改
  • 多用man
  • lab 0熟悉实验环境
  • lab 1输出硬件参数
    • 前言
    • 实验环境
    • 实验内容
    • 说明:
    • 需要知道的基础知识
    • 修改/boot/bootsect.s 使其能在屏幕上输出提示信息(xxx is booting)
    • 修改/boot/setup.s 使其能输出(now we are in SETUP)
    • 将硬件参数(内存大小、硬盘参数)输出到屏幕上
  • lab2-system call
    • 实验目的
    • 实验内容
      • 指导书给的内容
        • iam()
        • whoami()
      • 简单点
    • 实验中的记录
      • 第一次报错信息
      • 怎么传值
      • 第二次错误 segment fault
      • 第三个错误
      • 第四个问题
    • 实验原理
    • 附记
      • 将测试脚本 挂载到linux0.11中(在files文件夹中)
      • 测试程序(防止没有files)
      • kernel中的makefile代码
  • lab 3进程运行轨迹的跟踪和统计
    • 实验目的
    • 实验内容
    • 几个问题
    • sched.c & sleep_on()
    • process.c
    • 监控命令
    • process.c v2
    • W阻塞态 E退出 J就绪态 R运行态 N新建
    • 去除掉window下的^M
    • ubuntu可过编译 linux0.11挂了
    • 出现了连续两行一样的内容
    • 某个进程前后两个状态是一致的
    • 修改完的代码如下
    • process.c v3
    • process.sh
    • sleep on
    • 思考问题
      • 结合自己的体会 从程序设计者的角度看 单进程编程和多进程编程最大的区别是什么
      • 你是如何修改时间片的? 仅仅针对样本程序建立的进程,在修改时间片前后 log文件的统计结果都是怎么样的,结合你的修改分析一下为什么会这样,或者为什么没变化?
  • lab 7 Proc 文件系统的实现
    • 实验内容
    • procfs简介
    • 实现思路
    • ll /proc
    • cat
    • cat /proc/psinfo
    • cat /proc/meminfo
    • cat /proc/hdinfo
    • include/sys/stat.h
    • fs/namei.c
    • init/main.c
    • fs/read_write.c
    • fs/proc_dev.c
    • 死循环了
    • mycat.c
    • proc.sh
    • fs/final proc_dev.c
    • read_write.c
    • fs/Makefile
    • include/linux/mm.h
    • 思考问题
    • 遇到的几个问题BUG
  • lab 6 字符显示
  • lab4 实现信号量
    • int sys_lseek(unsigned int fd, off_t offset, int origin) 文件句柄 新的文件读写偏移 偏移起始位置 SEEK_SET (0) SEEK_CUR(1)SEEK_END
    • gcc -o test test.c -lpthread
    • 很有意思一错误 if(!fork)
    • ubuntu下信号量使用不对
    • bug1,if(!fork);
    • bug2,fopen,open失效;
    • bug3,创建了进程和信号量,但是消费者无限等待,
    • bug4,在0.11上一直出现string or char...编译错误,
    • pc.c
    • pc_hdc.c > pc_hdc.log
    • pv.c
    • sem.h
    • sem.c
    • test.c
    • testsem.c v2
    • sem.c v2
    • sem.c v3
    • sem.c v3.5
    • sem v4
  • lab5 address
    • shmid_ds
    • int shmget(key_t key, size_t size, int shmflg)
    • int shmat(int shmid, const void *shmaddr, int shmflg)
    • int shmdt(const void *shmaddr)
    • shm_com.h
    • shmread.c
    • shmwrite.c
    • mmpc.c v1 -pthread
    • include/unistd.h
    • kernel/system_call.s
    • include/linux/shm.h
    • mmpc_hdc.c
    • kernel/shm.c
    • linux shmid_ds linux2.6
    • unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags)

lab 1输出硬件参数

前言

此文为哈工大李治军老师所教授操作系统课程的配套实验中的实验一 —— 操作系统的引导 的记录。

在学完李志军老师所教授课程后,决定通过实验加深对OS的进一步理解。

所需要说明的是,linux-0.11是linux最早的版本。当前的linux和其相比,早已天差地别。

同时,硬件也发生了较大的变化。(0.11还考虑软盘 floppy disk)

因此,在现在的环境下学习linux0.11,其意义更多的在于扫除操作系统的神秘感。

那为什么不选择更现代的操作系统来进行实验呢?

因为操作系统是真正的复杂系统。太过于庞大,太过于复杂,容易在细节中迷失对操作系统整体的把握。
(太难了, 代码看不懂)

linux0.11相较于其他OS,已算小型,但代码量也不小。学起来也不算容易。

同时,其较好地实现了操作系统的几个视图(进程,内存,文件),可供初学者仔细研究。
(多看看进程调度函数)

实验环境

该实验可在蓝桥云课对应实验课中进行实验。

但是很麻烦,不保存之前结果,还吃网络,操作的很烦。

于是,我就在我的虚拟机上配置了相关环境。

环境配置可参考该博文。
(记得关注自己的实验文件夹,我曾以为自己没出最后的oslab,还自己一个一个解压,配置环境变量,最后发现别人的脚本早做好了)

另外,建议做一个共享文件夹,用git去管理源码。能省不少麻烦。

软件环境:Bochs + Ubuntu14.04 + gcc3.4 + vim

模拟硬件环境:IA-32 X86架构

汇编语言:as86语法

实验内容

说明:

原实验内容如下:

此次实验的基本内容是:

阅读《Linux内核完全注释》的第6章,对计算机和Linux 0.11的引导过程进行初步的了解;
按照下面的要求改写0.11的引导程序bootsect.s
有兴趣同学可以做做进入保护模式前的设置程序setup.s。
改写bootsect.s主要完成如下功能:

bootsect.s能在屏幕上打印一段提示信息“XXX is booting…”,其中XXX是你给自己的操作系统起的名字,例如LZJos、Sunix等(可以上论坛上秀秀谁的OS名字最帅,也可以显示一个特色logo,以表示自己操作系统的与众不同。)
改写setup.s主要完成如下功能:

bootsect.s能完成setup.s的载入,并跳转到setup.s开始地址执行。而setup.s向屏幕输出一行"Now we are in SETUP"。
setup.s能获取至少一个基本的硬件参数(如内存参数、显卡参数、硬盘参数等),将其存放在内存的特定地址,并输出到屏幕上。
setup.s不再加载Linux内核,保持上述信息显示在屏幕上即可。

如果直接按照它的来,则每次不再编译全部的OS,只是编译一小部分。
这样的话,就不能直接用它给的命令快速完成,而且无法看到这么修改是否会对OS整体产生影响。(输出能产生什么影响呢)
又因为我懒(不想写硬件读写,太折磨了),所以我直接在linux-0.11源代码的基础上做出了要求实现的功能。

需要知道的基础知识

由于当前仍然处于操作系统的引导时期,因此并不存在操作系统所提供给你的系统调用。

可以用的,只有BIOS的中断。

在实验中会用到的是int 0x10

具体的解释可见该博文

另外就是基础的汇编知识
在此仅仅对几个在接下来的实验中容易出错的进行说明。
bp默认寻址es,bx默认寻址ds。
si默认寻址ds,di默认寻址es
sp寻址ss 压栈 sp = sp - 2 出栈sp = sp + 2

call 会将当前的cs,ip进行压栈
ret 会从栈中弹出ip,cs。

rep movsb 串传送指令(按照字节传输cx次 依据df决定传输方向,cld指令将df置为0 , 正向传输 传输完一个后 si,di自增, std将DF置为1,逆向传输,传输完一字节后,si,di自减)。
movsw 按照字(此时是2byte)其余类似

lds reg, mem 将内存处的字 赋值给 对应寄存器
lds si, [40x41] 即将 40x41 = 0x104地址处的 字 赋值给 si

以及
盘片(platter)
磁头(head)
磁道(track)
扇区(sector)
柱面(cylinder)

修改/boot/bootsect.s 使其能在屏幕上输出提示信息(xxx is booting)

很简单,直接修改源码中的输出字符串(msg1) 和 对应的长度(cx)即可。

! Print some inane message
! read the cursor positionmov	ah,#0x03		! read cursor posxor	bh,bhint	0x10mov	cx,#46 ! length of string for output # mov	bx,#0x0007		! page 0, attribute 7 (normal)mov	bp,#msg1mov	ax,#0x1301		! write string, move cursorint	0x10msg1:.byte 13,10.ascii "Lucifer system Created by rebelOverWaist".byte 13,10,13,10

之后用提供的make clean && make all 命令进行编译
再用./run 脚本运行

修改/boot/setup.s 使其能输出(now we are in SETUP)

这个也没什么难的。

只不过是自己写了bootsect中的字符串输出代码罢了。

(注意,此时需要将es置为9200 因为要寻址字符串 否则结果不对)

	 mov ax, #SETUPSEGmov es, axmov ah, #0x03xor bh, bhint 0x10mov cx, #12mov bx, #0x0007mov bp, #msgmov ax, #0x1301int 0x10msg:.byte 13, 10.ascii "setup...".byte 13, 10, 13, 10

然后再编译运行即可。
就可看到字符显示了。

将硬件参数(内存大小、硬盘参数)输出到屏幕上

分为两步,第一步读取硬件参数,第二步,拿到硬件参数,通过一定格式显示。
第一步我没写,直接用linux源码去解决了。
第二步需要将数据按16进制进行输出,所以需要写以下两个函数,将一个16位数按16进制输出,输出回车换行。实验指导给了如下实例代码。
(也不一定非要用给的代码 实现这两个函数的功能的方法有很多)
(在此提供一下思路 对于16进制的输出,可以先打表 "0123…f"然后利用si这种去偏移寻址 就不用去看是数字还是字符了 至于回车换行,要是懒的话 可以直接在字符串后面加一个CRLF(13,10))
如果按照实验指导给的代码直接写:

    !以16进制方式打印栈顶的16位数print_hex:mov    cx,#4         ! 4个十六进制数字mov    dx,(bp)     ! 将(bp)所指的值放入dx中,如果bp是指向栈顶的话print_digit:rol    dx,#4        ! 循环以使低4比特用上 !! 取dx的高4比特移到低4比特处。mov    ax,#0xe0f     ! ah = 请求的功能值,al = 半字节(4个比特)掩码。and    al,dl         ! 取dl的低4比特值。add    al,#0x30     ! 给al数字加上十六进制0x30cmp    al,#0x3ajl    outp          !是一个不大于十的数字add    al,#0x07      !是a~f,要多加7outp: int    0x10loop    print_digitret
    !打印回车换行print_nl:mov    ax,#0xe0d     ! CRint    0x10mov    al,#0xa     ! LFint    0x10ret

会被栈搞得头晕。
因为call 和 ret 存在入栈和出栈的存在, 会对SS和SP进行修改。
如果按照默认的,可能会不小心修改了什么代码,什么数据。
(这个时候还在实地址模式,只有BIOS这种才是只读的)
这种时候会出现各类奇奇怪怪的BUG,只有调试了。

设置好ss和sp 😃
还有es 😃
注意0x10 和 10 别掉0x了 😃
记得跳过两个函数:-)

由于我没做第一步,因此花了一点时间阅读了一下给的源代码。
源代码实现的功能是把硬件各个参数读到的9000:0 - 9000:200处。
最开始的0是光标
2是内存大小
4是display page
6 的低字节是video mode, 高字节为 window width
8
10
12放的是EGA/VGA(不懂)
0080-008f存放的是硬盘0的相关参数
0090-009f存放的是硬件1的相关参数(如果有的话)
格式为:

位移大小说明
0x00word柱面数
0x02byte磁头数
0x0Ebyte每磁道扇区数
0x0Fbyte保留

这个位移更精确的叫法应该是偏移,是距离0080,0090的偏移

注意了上述细节,明确了内存图像。
就可以很轻松地写出下列代码了

! start print hardwaremov ax, #INITSEG! in case ss:sp will alter codemov ss, axmov sp, #0xff00!ss:sp =  9ff00 ! es:bp mov ax, #SETUPSEG mov es, axPrint_Cursor:mov ah, #0x03xor bh, bhint 0x10
! additionmov cx, #11mov bx, #0x0007mov bp, #Cursormov ax, #0x1301int 0x10
! set bp = 0x0000 mov ax, #0mov bp, axmov dx, (bp)call print_hexcall print_ln
! end print Cursor
Print_Memory:mov ah, #0x03xor bh, bhint 0x10mov cx, #12mov bx, #0x0007mov bp, #Memorymov ax, #0x1301int 0x10mov ax, #2mov bp, axmov dx, (bp)call print_hex!show KBmov ah, #0x03xor bh, bhint 0x10mov cx, #2mov bx, #0x0007mov bp, #KBmov ax, #0x1301int 0x10call print_lnPrint_Cyl_hd0:mov ah, #0x03xor bh, bhint 0x10mov cx, #9mov bx, #0x0007mov bp, #Cyl_hd0mov ax, #0x1301int 0x10mov ax, #0x0080mov bp, axmov dx, (bp)call print_hexcall print_lnPrint_Head_hd0:mov ah, #0x03xor bh, bhint 0x10mov cx, #9mov bx, #0x0007mov bp, #Head_hd0mov ax, #0x1301int 0x10!show Head_hd0mov ax, #0x0082mov bp, axmov dx, (bp)and dx, #0x00ffcall print_hexcall print_ln
Print_Sector_hd0:mov ah, #0x03xor bh, bhint 0x10mov cx, #11mov bx, #0x0007mov bp, #Sector_hd0mov ax, #0x1301int 0x10mov ax, #0x008emov bp, axmov dx, (bp)and dx, #0x00ffcall print_hexcall print_lnjmp end_print
! function 
print_hex:mov cx, #4print_digital:rol dx, #4mov ax, #0x0e0fand al, dladd al, #0x30cmp al, #0x3ajl out_padd al, #0x07 out_p:int 0x10loop print_digitalret print_ln:mov ax, #0x0e0dint 0x10mov al, #0xAint 0x10 ret end_print:
! end print hardware data
Cursor:.ascii "Cursor Pos:"Memory:.ascii "Memory Size:"Cyl_hd0:.ascii "Cyls_hd0:"
Head_hd0:.ascii "Head_hd0:"
Sector_hd0:.ascii "Sector_hd0:"
KB:.ascii "KB"

整体很简单,找光标,输出提示字符串,输出对应参数,输出换行
再按这种方式输出所有要的进行了。

最后显示到的结果如下:
在这里插入图片描述
最后的最后,需要将这些参数和/bochs/bochsrc.bxrc中的

……
megs: 16
……
ata0-master: type=disk, mode=flat, cylinders=410, heads=16, spt=38
……

相关参数进行对比,都吻合了,就成功了。(这不是我的参数)

lab2-system call

实验目的

  • 建立对系统调用接口的深入认识
  • 掌握系统调用的基本过程
  • 能完成系统调用的全面控制
  • 为后续实验做准备

实验内容

指导书给的内容

iam()

第一个系统调用是iam(),其原型为:

int iam(const char * name);

完成的功能是将字符串参数name的内容拷贝到内核中保存下来。要求name的长度不能超过23个字符。返回值是拷贝的字符数。如果name的字符个数超过了23,则返回“-1”,并置errno为EINVAL。

在kernal/who.c中实现此系统调用。

whoami()

第二个系统调用是whoami(),其原型为:

int whoami(char* name, unsigned int size);

它将内核中由iam()保存的名字拷贝到name指向的用户地址空间中,同时确保不会对name越界访存(name的大小由size说明)。返回值是拷贝的字符数。如果size小于需要的空间,则返回“-1”,并置errno为EINVAL。

也是在kernal/who.c中实现。

简单点

两个函数

在kernel/who.c中一块实现。

iam存储一个字符串

whoami将这个字符串复制到给定的字符指针所指向的区域。

如果长度不够,就返回-1,置errno为EINVAL。

实验中的记录

/lib/_who.c
#define __LIBRARY__
#include <unistd.h>
_syscall1(int, iam, const char*, name);
_syscall2(int, whoami, char*, name, unsigned int, size);
/include/unistd.h#define __NR_whoami 72
#define __NR_iam 73
/include/linux/sys.h
extern int sys_whoami();
extern int sys_iam();fn_ptr sys_call_table [] = {...sys_whoami, sys_iam
};
/kernel/system_call.s
nr_system_calls = 74
/kernel/who.c#include <linux/kernel.h>
int sys_iam(const char* name) {return -1;
}int sys_whoami(char *name, unsigned int size) {return -1;
}
/kernel/Makefile
OBJS = sched.o system_call.o traps.o asm.o fork.o \panic.o printk.o vsprintf.o sys.o exit.o \signal.o mktime.o who.o### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h \../include/errno.h
gcc -o iam iam.c -Wall
./iam
sync //写入磁盘

第一次报错信息

iam.c#include <stdio.h>
#define __LIBRARY__
#include <unistd.h>_syscall1(int, iam, const char*, name);
_syscall2(int, whoami, char*, name, unsigned int, size);int main () {printf("%d\n", iam("rebel over waist"));printf("%d\n", whoami("who are you", 0));return 0;
}
__NR_whoami undeclared
__NR_iam undeclared在unistd.h中存在函数调用
in function iam:
__NR_iam undeclaredin function whoami:
__NR_whoami undeclared原因
在写好的库中嗯
也就是在编译完运行之后的系统库中
确实不存在这两个号
我记得之前提到过在0.11环境下编译C程序,包含的头文件都在/usr/include目录下。该目录下的unistd.h是标准头文件(它和0.11源码树中的unistd.h并不是同一个文件,虽然内容可能相同),没有__NR_whoami和__NR_iam两个宏,需要手工加上它们,也可以直接从修改过的0.11源码树中拷贝新的unistd.h过来。

怎么传值

又遇到了一个问题
就是参数并没有传进去
刚刚查看了汇编的源代码
确实是传了的
但是不知道为什么是那样

我觉得是因为指针传递的问题

也就是内核的数据和用户的数据其实并不是共享的

你所传递的字符串是用户区域的字符串的首地址

.globl _iam
_iam:pushl %ebpmovl %esp, %ebp ; esp -> ebpsubl $4, %esp ;alloc 4 bytespushl %ebx ; push ebx movl $73, %eaxmovl 8(%ebp), %ebx; get address of stringint 0x80movl %eax, -4(%ebp)cmpl $0, -4(%ebp)jl L2movl -4(%ebp), %eaxjmp L1.align 2
L2:movl -4(%ebp), %eaxnegl %eaxmovl %eax, _errnomovl $-1, %eaxjmp L1.align 2
L1:leal -8(%ebp), %esppopl %ebxleaveret.align 2
system_call:cmpl $nr_system_calls-1,%eax    # 调用号如果超出范围的话就在eax中置-1并退出ja bad_sys_callpush %ds                        # 保存原段寄存器值push %espush %fs
# 一个系统调用最多可带有3个参数,也可以不带参数。下面入栈的ebx、ecx和edx中放着系统
# 调用相应C语言函数的调用函数。这几个寄存器入栈的顺序是由GNU GCC规定的,
# ebx 中可存放第1个参数,ecx中存放第2个参数,edx中存放第3个参数。
# 系统调用语句可参见头文件include/unistd.h中的系统调用宏。pushl %edxpushl %ecx		# push %ebx,%ecx,%edx as parameterspushl %ebx		# to the system callmovl $0x10,%edx		# set up ds,es to kernel spacemov %dx,%dsmov %dx,%es
# fs指向局部数据段(局部描述符表中数据段描述符),即指向执行本次系统调用的用户程序的数据段。
# 注意,在Linux 0.11 中内核给任务分配的代码和数据内存段是重叠的,他们的段基址和段限长相同。movl $0x17,%edx		# fs points to local data spacemov %dx,%fs
# 下面这句操作数的含义是:调用地址=[_sys_call_table + %eax * 4]
# sys_call_table[]是一个指针数组,定义在include/linux/sys.h中,该指针数组中设置了所有72
# 个系统调用C处理函数地址。call sys_call_table(,%eax,4)        # 间接调用指定功能C函数

所以fs是关键

include/asm/segment.h
static inline unsigned char get_fs_byte(const char * addr)
{unsigned register char _v;__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));return _v;
}static inline void put_fs_byte(char val,char *addr)
{
__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr));
}

第二次错误 segment fault

length: 16
general protection: 0000
fs: 0010
base: 1000 0000
limit: 0400 0000
Segmentation fault

之前的代码使用的是指针版本

现在的代码使用的是数组版本

可能是因为char * p = const char* s

不被允许,p被修改了0?

第三个错误

代码自己测完全没问题

但是给测试程序,怎么也跑不对。

而且很奇怪,内核函数的错误输出在前面,但是测试样例在后面。

我还以为是我看错了,又特地加上了字符串长度,结果还是这样的。

但是我看了看程序的源码,都是正常的啊。

很正常的顺序执行,不就是调用了一下函数,和应该有的结果做了一下比较。

而且这个报错还真的就是返回值为-1的时候才有的报错。

晕了。

到网上找到了一份说是全通过的代码,但是看了一眼它的具体实现,我的天,sys_iam, sys_whoami连errno都不带,返回值不是-1,返回的是-EINVAL, 但是测试程序和我的一样,这是咋全对的?

又看了他的iam.c 和whoami.c,额,直接根据返回值给errno赋值。啧啧啧。

不想管了。

第四个问题

我在linux0.11是root用户

但是怎么也修改不了iam.c这种文件的删除,重命名权限。

总是说不是文件所有者

但是我用ls -al看了一下,所有者都是root啊

搞不懂

实验原理

系统调用的实现首先得理解下面这些步骤

应用程序调用库函数(API);
API将系统调用号存入EAX,然后通过中断调用使系统进入内核态;
内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
系统调用完成相应功能,将返回值存入EAX,返回到中断处理函数;
中断处理函数返回到API中;
API将EAX返回给应用程序。

调用API

为了方便去做系统调用和接口,linux采取了统一的系统调用接口,这些调用区分是参数个数和调用号。

参数最多3个。放到ebx ecx edx中。

调用号放到eax中。

调用号在unistd.h中。(注意,这里(include/unistd.h)的unistd.h和你跑的linux0.11中的unistd.h不是一个,也就是说你就算在include/unistd.h中修改了,在linux0.11中的那个也还是原来的样子。而你的应用程序,实际上include的是系统中的。所以你得找到这个库文件,然后给它加上几个系统调用号。这样才能在代码中调用成功。

系统调用总数在system_call.s中,由于你要新加系统调用,就得改一下总数。

system_call.s中的代码其实不需要修改,这是一个统一的接口,用来进行进程切换等等操作。

最后会根据include/linux/sys.h中的函数表找到对应功能的函数。

所以你这个时候得在这个表sys_call_table的指定位置,也就是你的系统调用号对应的位置,填上你要执行的函数。

这个函数并不在这头文件中实现。

所以你得在前面说明这是一个外部函数,然后找到去进行具体的实现。

实现就蛮简单了。完成指定的功能即可。

注意,包括一些用到的头文件。

比如printk,errno,EINVAL,get_fs_byte.

附记

如果你在实验过程中感到很苦恼,不知道什么该写什么,那就去看看linux中哪些系统调用是如何实现的?

在此举出几个有帮助的。

open, 和文件相关,涉及到了get_fs_byte

close 简单 明确 知道怎么写入口

syst.c 头文件该包含哪些

以及一些起到辅助作用的代码,用了老久时间琢磨,找了一堆方法,最后搞定的。

(整体项目扔github上了,但是老是登不上,麻烦)

将测试脚本 挂载到linux0.11中(在files文件夹中)

这个说实话,查了老久的bochs教程,怎么做软盘啊,怎么输入到软盘啊,最后看了看别人的实现,发现直接找到之前挂载的硬盘,然后复制进去就行了。嗯,很好。

#########################################################################
# File Name:    syscall.sh
# Author:       rebelOverWaist
#########################################################################
#!/bin/bash
make clean && make all
cd ..
sudo ./mount-hdc
cd linux-0.11
cp iam.c whoami.c ../hdc/usr/root
cp testlab2.c testlab2.sh ../hdc/usr/root
cd ..
sudo umount hdc
./run

测试程序(防止没有files)

/** Compile: "gcc testlab2.c"* Run: "./a.out"*/#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#define __LIBRARY__
#include <unistd.h>_syscall2(int, whoami,char*,name,unsigned int,size);
_syscall1(int, iam, const char*, name);#define MAX_NAME_LEN        23
#define NAMEBUF_SIZE        (MAX_NAME_LEN + 1)
/* truncate a long name to SHORT_NAME_LEN for display */
#define SHORT_NAME_LEN      (MAX_NAME_LEN + 2)/*           name               score */
#define TEST_CASE { \{"x",                           10,  1, NAMEBUF_SIZE,  1},\{"sunner",                      10,  6, NAMEBUF_SIZE,  6},\{"Twenty-three characters",      5, 23, NAMEBUF_SIZE, 23},\{"123456789009876543211234",     5, -1, 0,            -1},\{"abcdefghijklmnopqrstuvwxyz",   5, -1, 0,            -1},\{"Linus Torvalds",               5, 14, NAMEBUF_SIZE, 14},\{"",                             5,  0, NAMEBUF_SIZE,  0},\{"whoami(0xbalabala, 10)",       5, 22,           10, -1},\{NULL, 0, 0, 0, 0}  /* End of cases */ \
}
/*改动一:增加size,和rval2*/int test(const char* name, int max_score, int expected_rval1, int size, int expected_rval2);
void print_message(const char* msgfmt, const char* name);struct test_case 
{char *name;int score;int rval1;  /* return value of iam() *//*改动2:增加size,和rval2定义*/int size;   /*Patch for whoami,2009.11.2*/int rval2;  /* return value of whoami() */
};int main(void)
{struct test_case cases[] = TEST_CASE;int total_score=0, i=0;while (cases[i].score != 0){int score;printf("Test case %d:", i+1);/*改动3:增加size,和rval2的参数阿*/score = test( cases[i].name, cases[i].score, cases[i].rval1,cases[i].size,cases[i].rval2 );total_score += score;i++;}printf("Final result: %d%%\n", total_score);return 0;}/*改动4:增加size,和rval2的声明*/
int test(const char* name, int max_score, int expected_rval1, int size, int expected_rval2)
{int rval;int len;char * gotname;int score=-1;assert(name != NULL);print_message("name = \"%s\", length = %d...", name);/*Test iam()*/len = strlen(name);rval = iam(name);/* printf("Return value = %d\n", rval);*//*改动5:增加的expected_rval1*/if (rval == expected_rval1){if (rval == -1 && errno == EINVAL) /*The system call can detect bad name*/{/* print_message("Long name, %s(%d), detected.\n", name);*/printf("PASS\n");score = max_score;}else if (rval == -1 && errno != EINVAL){printf("\nERROR iam(): Bad errno %d. It should be %d(EINVAL).\n", errno, EINVAL);score = 0;}/* iam() is good. Test whoami() next. */}else{printf("\nERROR iam(): Return value is %d. It should be %d.\n", rval, expected_rval1);score = 0;}if (score != -1) return score;/*Test whoami()*/gotname = (char*)malloc(len+1);if (gotname == NULL)exit(-1);memset(gotname, 0, len+1);/* printf("Get: buffer length = %d.\n", len+1); */rval = whoami(gotname, size);/* printf("Return value = %d\n", rval); *//*改动6:增加的expected_rval2*/
/*改动++:比较多 ,但还是顺序的改改*/if(rval == expected_rval2){   if(rval == -1){printf("PASS\n");score = max_score;}       else {if (strcmp(gotname, name) == 0){/* print_message("Great! We got %s(%d) finally!\n", gotname); */printf("PASS\n");score = max_score;}else{print_message("\nERROR whoami(): we got %s(%d). ", gotname);print_message("It should be %s(%d).\n", name);score = 0;}}}else if (rval == -1){printf("\nERROR whoami(): Return value is -1 and errno is %d. Why?\n", errno);score = 0;}else {printf("\nERROR whoami(): Return value should be %d, not %d.\n", expected_rval2, rval);score = 0;}free(gotname);assert(score != -1);return score;
}void print_message(const char* msgfmt, const char* name)
{char short_name[SHORT_NAME_LEN + 4] = {0};int len;len = strlen(name);if (len == 0){strcpy(short_name, "NULL");}else if (len <= SHORT_NAME_LEN){strcpy(short_name, name);}else{memset(short_name, '.', SHORT_NAME_LEN+3);memcpy(short_name, name, SHORT_NAME_LEN);}printf(msgfmt, short_name, len);
}
#/bin/shstring1="Sunner"
string2="Richard Stallman"
string3="This is a very very long string!"score1=10
score2=10
score3=10expected1="Sunner"
expected2="Richard Stallman"
expected3="Richard Stallman"echo Testing string:$string1
./iam "$string1"
result=`./whoami`
if [ "$result" = "$expected1" ]; thenecho PASS.
elsescore1=0echo FAILED.
fi
score=$score1echo Testing string:$string2
./iam "$string2"
result=`./whoami`
if [ "$result" = "$expected2" ]; thenecho PASS.
elsescore2=0echo FAILED.
fi
score=$score+$score2echo Testing string:$string3
./iam "$string3"
result=`./whoami`
echo "$result"
if [ "$result" = "$expected3" ]; thenecho PASS.
elsescore3=0echo FAILED.
fi
score=$score+$score3let "totalscore=$score"
echo Score: $score = $totalscore%
/* ************************************************************************
> File Name:     whoami.c
> Author:        rebelOverWaist
> Created Time:  2022年11月05日 星期六 18时30分35秒
> Description:  ************************************************************************/
#define __LIBRARY__
#include <stdio.h>
#include <unistd.h>
#include <errno.h>_syscall2(int, whoami, char*, name, unsigned int, size)
#define SIZE 23
int main (void) {char name[SIZE + 1];int res;res = whoami(name, SIZE+1);if(res == -1) {errno = EINVAL;} else {printf("%s\n", name);}return res;
}
/* ************************************************************************
> File Name:     iam.c
> Author:        rebelOverWaist
> Description:   ************************************************************************/
#define __LIBRARY__
#include <unistd.h>
#include <errno.h>_syscall1(int, iam, const char*, name)#define NAMELEN 100char name[NAMELEN];int main (int argc, char *argv[]) {int res;int namelen = 0;if(2 <= argc) {while((name[namelen] = argv[1][namelen]) != '\0')namelen++;printf("iam.c: %s, %d\n", name, namelen);res = iam(name);if(res == -1) errno = EINVAL;return res;}return 0;
}

kernel中的makefile代码

#
# Makefile for the FREAX-kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#AR	=ar
AS	=as --32
LD	=ld
LDFLAGS	=-m elf_i386 -x
CC	=gcc-3.4 -march=i386
CFLAGS	=-m32 -g -Wall -O -fstrength-reduce -fomit-frame-pointer \-finline-functions -nostdinc -I../include
CPP	=gcc-3.4 -E -nostdinc -I../include.c.s:$(CC) $(CFLAGS) \-S -o $*.s $<
.s.o:$(AS) -o $*.o $<
.c.o:$(CC) $(CFLAGS) \-c -o $*.o $<OBJS  = sched.o system_call.o traps.o asm.o fork.o \panic.o printk.o vsprintf.o sys.o exit.o \signal.o mktime.o who.okernel.o: $(OBJS)$(LD) -m elf_i386 -r -o kernel.o $(OBJS)syncclean:rm -f core *.o *.a tmp_make keyboard.sfor i in *.c;do rm -f `basename $$i .c`.s;done(cd chr_drv; make clean)(cd blk_drv; make clean)(cd math; make clean)dep:sed '/\#\#\# Dependencies/q' < Makefile > tmp_make(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \$(CPP) -M $$i;done) >> tmp_makecp tmp_make Makefile(cd chr_drv; make dep)(cd blk_drv; make dep)### Dependencies:
who.s who.o: who.c ../include/linux/kernel.h ../include/unistd.h \../include/errno.h
exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \../include/asm/segment.h
fork.s fork.o: fork.c ../include/errno.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \../include/asm/segment.h ../include/asm/system.h
mktime.s mktime.o: mktime.c ../include/time.h
panic.s panic.o: panic.c ../include/linux/kernel.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h
printk.s printk.o: printk.c ../include/stdarg.h ../include/stddef.h \../include/linux/kernel.h
sched.s sched.o: sched.c ../include/linux/sched.h ../include/linux/head.h \../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \../include/asm/segment.h
signal.s signal.o: signal.c ../include/linux/sched.h ../include/linux/head.h \../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h
sys.s sys.o: sys.c ../include/errno.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \../include/sys/times.h ../include/sys/utsname.h
traps.s traps.o: traps.c ../include/string.h ../include/linux/head.h \../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h
vsprintf.s vsprintf.o: vsprintf.c ../include/stdarg.h ../include/string.h

之前遇到的错误可能是输出缓冲区的问题。

lab 3进程运行轨迹的跟踪和统计

实验目的

  • 掌握Linux下的多进程编程技术;
  • 通过对进程运行轨迹的跟踪来形象化进程的概念;
  • 在进程运行轨迹跟踪的基础上进行相应的数据统计,从而能对进程调度算法进行实际的量化评价,更进一步加深对调度和调度算法的理解,获得能在实际操作系统上对调度算法进行实验数据对比的直接经验。

实验内容

进程从创建(Linux下调用fork())到结束的整个过程就是进程的生命期,进程在其生命期中的运行轨迹实际上就表现为进程状态的多次切换,如进程创建以后会成为就绪态;当该进程被调度以后会切换到运行态;在运行的过程中如果启动了一个文件读写操作,操作系统会将该进程切换到阻塞态(等待态)从而让出CPU;当文件读写完毕以后,操作系统会在将其切换成就绪态,等待进程调度算法来调度该进程执行……

(原理解释,也就是进程的几个状态之间的切换如何做到)

1.基于模板process.c编写多进程的样本程序,实现如下功能:

1-1.所有子进程都并行运行,每个子进程的实际运行时间一般不超过30s。

1-2.父进程向标准输出打印所有子进程的ID,并在所有子进程都退出才退出。

2.在Linux0.11上实现进程运行轨迹的跟踪。基本任务是在内核中维护一个日志文件/var/process.log,把从操作系统启动到系统关机中所有进程的运行轨迹都记录在这一log文件中

3.在修改过的0.11上运行样本程序,通过分析log文件,统计该程序建立的所有进程的等待时间、完成时间(周转时间)和运行时间,然后计算平均等待时间,平均完成时间和吞吐量。可以自己编写统计程序,也可以使用python脚本程序stat_log.py进行统计。

4.修改0.11进程调度的时间片,然后再运行相同的样本程序,统计同样的时间数据,和原有的情况对比,体会不同时间片带来的差异

/var/process.log的文件格式必须为pid X time

pid 是进程的ID,

X为操作类型

N 新建

J 进入就绪态

R 进入运行态

W 进入阻塞态

E 退出

time是系统的滴答时间tick

用制表符分割 pid\tX\t time\n

读完实验指导,感觉最难的在于process.c的编写

也就是样本程序怎么编的问题

几个问题

  1. 结合自己的体会,谈谈从程序设计者的角度看,单进程编程和多进程编程最大的区别是什么?
  2. 你是如何修改时间片的?仅针对样本程序建立的进程,在修改时间片前后,log文件的统计结果(不包括Graphic)都是什么样?结合你的修改分析一下为什么会这样变化,或者为什么没变化?

sched.c & sleep_on()

// 把当前任务置为不可中断的等待状态,并让睡眠队列指针指向当前任务。
// 只有明确的唤醒时才会返回。该函数提供了进程与中断处理程序之间的同步机制。函数参数P是等待
// 任务队列头指针。指针是含有一个变量地址的变量。这里参数p使用了指针的指针形式'**p',这是因为
// C函数参数只能传值,没有直接的方式让被调用函数改变调用该函数程序中变量的值。但是指针'*p'
// 指向的目标(这里是任务结构)会改变,因此为了能修改调用该函数程序中原来就是指针的变量的值,
// 就需要传递指针'*p'的指针,即'**p'.
void sleep_on(struct task_struct **p)
{struct task_struct *tmp;// 若指针无效,则退出。(指针所指向的对象可以是NULL,但指针本身不应该为0).另外,如果// 当前任务是任务0,则死机。因为任务0的运行不依赖自己的状态,所以内核代码把任务0置为// 睡眠状态毫无意义。if (!p)return;if (current == &(init_task.task))panic("task[0] trying to sleep");// 让tmp指向已经在等待队列上的任务(如果有的话),例如inode->i_wait.并且将睡眠队列头的// 等等指针指向当前任务。这样就把当前任务插入到了*p的等待队列中。然后将当前任务置为// 不可中断的等待状态,并执行重新调度。tmp = *p;*p = current;current->state = TASK_UNINTERRUPTIBLE;schedule();// 只有当这个等待任务被唤醒时,调度程序才又返回到这里,表示本进程已被明确的唤醒(就// 续态)。既然大家都在等待同样的资源,那么在资源可用时,就有必要唤醒所有等待该该资源// 的进程。该函数嵌套调用,也会嵌套唤醒所有等待该资源的进程。这里嵌套调用是指一个// 进程调用了sleep_on()后就会在该函数中被切换掉,控制权呗转移到其他进程中。此时若有// 进程也需要使用同一资源,那么也会使用同一个等待队列头指针作为参数调用sleep_on()函数,// 并且也会陷入该函数而不会返回。只有当内核某处代码以队列头指针作为参数wake_up了队列,// 那么当系统切换去执行头指针所指的进程A时,该进程才会继续执行下面的代码,把队列后一个// 进程B置位就绪状态(唤醒)。而当轮到B进程执行时,它也才可能继续执行下面的代码。若它// 后面还有等待的进程C,那它也会把C唤醒等。在这前面还应该添加一行:*p = tmp.//这里很有意思//因为在sched函数中其实是可以对进程的状态进行修改的 但是可以存在多个sleep函数//但是实际上运行的进程只有一个//所以又重新调度后 当前进程就会被挂起//但是又会有多个进程被挂起//因此这里利用函数的堆栈,tmp形成了一个隐式的堆栈//注意在sched中会进行进程的唤醒//当存在中断信号 还存在其他信号的时候 就可以去唤醒了 说明要的都解决了//wake up 函数是将真正if (tmp)                    // 若在其前还有存在的等待的任务,则也将其置为就绪状态(唤醒).tmp->state=0;
}

process.c

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>#define HZ 100void cpuio_bound(int last, int cpu_time, int io_time);
/*子进程并行运行 
* 每个子进程的实际运行时间不超过30s
* 父进程向标准输出打印所有子进程ID
* 在所有子进程都退出后才退出
*/
#define N 8
static int index = 0;
static unsigned long stat;
static int sid[N];
typedef void (*fn_ptr)();
fn_ptr subprocess_table[] = {subprocess_printf,subprocess_leftShift,subprocess_rightShift,subprocess_factor,subprocess_string,subprocess_matrix,subprocess_sort,subprocess_hash
};
void subprocess_printf() {exit(0);
}void subprocess_leftShift() {exit(0);
}void subprocess_rightShift() {exit(0);
}void subprocess_factor() {exit(0);
}void subprocess_string() {exit(0);  
}void subprocess_matrix() {exit(0);
}void subprocess_sort() {exit(0);
}void subprocess_hash() {exit(0);
}
int main (int argc, char* argv[]) {for(; index < N; ++index) {if(!(pid= fork()) {sid[index] = pid;}}wait(&stat);return 0;
}/** 此函数按照参数占用CPU和I/O时间* last: 函数实际占用CPU和I/O的总时间,不含在就绪队列中的时间,>=0是必须的* cpu_time: 一次连续占用CPU的时间,>=0是必须的* io_time: 一次I/O消耗的时间,>=0是必须的* 如果last > cpu_time + io_time,则往复多次占用CPU和I/O* 所有时间的单位为秒*/
void cpuio_bound(int last, int cpu_time, int io_time) {struct tms start_time, current_time;//start time; current_time clock_t utime, stime;//usr time; system timeint sleep_time;//sleep timewhile(last > 0) {/*CPU Burst*/times(&start_time);/* 其实只有t.tms_utime才是真正的CPU时间。但我们是在模拟一个* 只在用户状态运行的CPU大户,就像“for(;;);”。所以把t.tms_stime* 加上很合理。*/do {times(&current_time);utime = current_times.tms_utime - start_time.tms_utime;stime = current_time.tms_stime - start_time.tms_stime;}while((utime + stime)/HZ) < cpu_time);last -= cpu_time;if(last <= 0)break;/*IO Burst*//*sleep(1)模拟1秒钟的I/O操作*/sleep_time = 0;while(sleep_time < io_time){sleep(1);sleep_time++;}last -= sleep_time;}
}
 系统调用waipid().挂起当前进程,直到pid指定的子进程退出(终止)或收到要求终止该进程的信号,
// 或者是需要调用一个信号句柄(信号处理程序)。如果pid所指向的子进程早已退出(已成所谓的僵死进程),
// 则本调用将立刻返回。子进程使用的所有资源将释放。
// 如果pid > 0,表示等待进程号等于pid的子进程。
// 如果pid = 0, 表示等待进程组号等于当前进程组号的任何子进程。
// 如果pid < -1,表示等待进程组号等于pid绝对值的任何子进程。
// 如果pid = -1,表示等待任何子进程。
// 如 options = WUNTRACED,表示如果子进程是停止的,也马上返回(无须跟踪)
// 若 options = WNOHANG, 表示如果没有子进程退出或终止就马上返回。
// 如果返回状态指针 stat_addr不为空,则就将状态信息保存到那里。
// 参数pid是进程号,*stat_addr是保存状态信息位置的指针,options是waitpid选项。
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{int flag, code;             // flag标志用于后面表示所选出的子进程处于就绪或睡眠态。struct task_struct ** p;verify_area(stat_addr,4);
repeat:flag=0;// 从任务数组末端开始扫描所有任务,跳过空项、本进程项以及非当前进程的子进程项。for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {if (!*p || *p == current)continue;if ((*p)->father != current->pid)continue;// 此时扫描选择到的进程p肯定是当前进程的子进程。// 如果等待的子进程号pid>0,但与被扫描子进程p的pid不相等,说明它是当前进程另外的// 子进程,于是跳过该进程,接着扫描下一个进程。if (pid>0) {if ((*p)->pid != pid)continue;// 否则,如果指定等待进程的pid=0,表示正在等待进程组号等于当前进程组号的任何子进程。// 如果此时被扫描进程p的进程组号与当前进程的组号不等,则跳过。} else if (!pid) {if ((*p)->pgrp != current->pgrp)continue;// 否则,如果指定的pid < -1,表示正在等待进程组号等于pid绝对值的任何子进程。如果此时// 被扫描进程p的组号与pid的绝对值不等,则跳过。} else if (pid != -1) {if ((*p)->pgrp != -pid)continue;}// 如果前3个对pid的判断都不符合,则表示当前进程正在等待其任何子进程,也即pid=-1的情况,// 此时所选择到的进程p或者是其进程号等于指定pid,或者是当前进程组中的任何子进程,或者// 是进程号等于指定pid绝对值的子进程,或者是任何子进程(此时指定的pid等于-1).接下来根据// 这个子进程p所处的状态来处理。switch ((*p)->state) {// 子进程p处于停止状态时,如果此时WUNTRACED标志没有置位,表示程序无须立刻返回,于是// 继续扫描处理其他进程。如果WUNTRACED置位,则把状态信息0x7f放入*stat_addr,并立刻// 返回子进程号pid.这里0x7f表示的返回状态是wifstopped()宏为真。case TASK_STOPPED:if (!(options & WUNTRACED))continue;put_fs_long(0x7f,stat_addr);return (*p)->pid;// 如果子进程p处于僵死状态,则首先把它在用户态和内核态运行的时间分别累计到当前进程// (父进程)中,然后取出子进程的pid和退出码,并释放该子进程。最后返回子进程的退出码和pid.case TASK_ZOMBIE:current->cutime += (*p)->utime;current->cstime += (*p)->stime;flag = (*p)->pid;                   // 临时保存子进程pidcode = (*p)->exit_code;             // 取子进程的退出码release(*p);                        // 释放该子进程put_fs_long(code,stat_addr);        // 置状态信息为退出码值return flag;                        // 返回子进程的pid// 如果这个子进程p的状态既不是停止也不是僵死,那么就置flag=1,表示找到过一个符合// 要求的子进程,但是它处于运行态或睡眠态。default:flag=1;continue;}}// 在上面对任务数组扫描结束后,如果flag被置位,说明有符合等待要求的子进程并没有处于退出或// 僵死状态。如果此时已设置WNOHANG选项(表示若没有子进程处于退出或终止态就立刻返回),就// 立刻返回0,退出。否则把当前进程置为可中断等待状态并重新执行调度。当又开始执行本进程时,// 如果本进程没有收到除SIGCHLD以外的信号,则还是重复处理。否则,返回出错码‘中断系统调用’// 并退出。针对这个出错号用户程序应该再继续调用本函数等待子进程。if (flag) {if (options & WNOHANG)                  // options = WNOHANG,则立刻返回。return 0;current->state=TASK_INTERRUPTIBLE;      // 置当前进程为可中断等待态schedule();                             // 重新调度。if (!(current->signal &= ~(1<<(SIGCHLD-1))))goto repeat;elsereturn -EINTR;                      // 返回出错码(中断的系统调用)}// 若没有找到符合要求的子进程,则返回出错码(子进程不存在)。return -ECHILD;
}

监控命令

top

查看所有的进程

u 用于筛查指定用户进程

h获得帮助

Ubuntu下PS可以现实当时各个进程的状态

ps aux 显示所有进程的

ps aux | grep process 只显示名为process的进程

man可查看更多的信息(手册)

0.11 F1可以查看当前进程的状态。

process.c v2

此份代码仅仅能够记录N 和E 缺少了阻塞等情况。

/** utf-8* rebelOverWaist* 2022/11/13* **/#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>#define HZ 100void cpuio_bound(int last, int cpu_time, int io_time);
/*子进程并行运行 
* 每个子进程的实际运行时间不超过30s
* 父进程向标准输出打印所有子进程ID
* 在所有子进程都退出后才退出
*/
#define N 8
typedef int (*fn_ptr)();extern int subprocess_printf();
extern int subprocess_leftShift();
extern int subprocess_rightShift();
extern int subprocess_factor();
extern int subprocess_string();
extern int subprocess_matrix();
extern int subprocess_sort();
extern int subprocess_hash();static int index = 0;
static unsigned long stat;
static int sid[N];fn_ptr subprocess_table[] = {subprocess_printf,subprocess_leftShift,subprocess_rightShift,subprocess_factor,subprocess_string,subprocess_matrix,subprocess_sort,subprocess_hash
};int subprocess_printf() {printf("printf begin\n");cpuio_bound(5, 1, 0);printf("printf end\n");return 0;
}int subprocess_leftShift() {printf("leftShift begin\n");cpuio_bound(10, 1, 1);printf("leftShift end\n");return 0;
}int subprocess_rightShift() {printf("rightShift begin\n");cpuio_bound(20, 2, 3);printf("rightShift end\n");return (0);
}int subprocess_factor(){printf("factor begin\n");cpuio_bound(30, 4, 1);printf("factor end\n");return (0);
}int subprocess_string() {printf("string begin\n");cpuio_bound(12, 2, 1);printf("string end\n");return (0);  
}int subprocess_matrix() {printf("matrix begin\n");cpuio_bound(15, 2, 3);printf("matrix end\n");return (0);
}int subprocess_sort() {printf("sort begin\n");cpuio_bound(24, 4, 4);printf("sort end\n");return (0);
}int subprocess_hash() {printf("hash begin\n");cpuio_bound(18, 4, 5);printf("hash end\n");return (0);
}
int main (int argc, char* argv[]) {int pid;FILE* log = fopen("./process.log", "w");struct tms start, end;for(; index < N; ++index) {if(!fork()) {pid = getpid();sid[index] = pid;printf("times -> %ld clock -> %ld\n",times(&start), clock());fprintf(log, "\t%d\tN\t%ld\n", pid, start.tms_utime + start.tms_stime);subprocess_table[index]();printf("times -> %ld clock -> %ld\n",times(&end), clock());fprintf(log, "\t%d\tE\t%ld\n", pid, end.tms_utime+end.tms_stime -start.tms_utime-start.tms_stime);exit(0);}}wait(&stat);fclose(log);return 0;
}/** 此函数按照参数占用CPU和I/O时间* last: 函数实际占用CPU和I/O的总时间,不含在就绪队列中的时间,>=0是必须的* cpu_time: 一次连续占用CPU的时间,>=0是必须的* io_time: 一次I/O消耗的时间,>=0是必须的* 如果last > cpu_time + io_time,则往复多次占用CPU和I/O* 所有时间的单位为秒*/
void cpuio_bound(int last, int cpu_time, int io_time) {struct tms start_time, current_time;//start time; current_time clock_t utime, stime;//usr time; system timeint sleep_time;//sleep timewhile(last > 0) {/*CPU Burst*/times(&start_time);/* 其实只有t.tms_utime才是真正的CPU时间。但我们是在模拟一个* 只在用户状态运行的CPU大户,就像“for(;;);”。所以把t.tms_stime* 加上很合理。*/do {times(&current_time);utime = current_time.tms_utime - start_time.tms_utime;stime = current_time.tms_stime - start_time.tms_stime;}while(((utime + stime)/HZ) < cpu_time);last -= cpu_time;if(last <= 0)break;/*IO Burst*//*sleep(1)模拟1秒钟的I/O操作*/sleep_time = 0;while(sleep_time < io_time){sleep(1);sleep_time++;}last -= sleep_time;}
}

W阻塞态 E退出 J就绪态 R运行态 N新建

void sleep_on(struct task_struct **p)
{struct task_struct *tmp;……tmp = *p;*p = current;  //仔细阅读,实际上是将current插入“等待队列”头部,tmp是原来的头部current->state = TASK_UNINTERRUPTIBLE; //切换到睡眠态schedule();  //让出CPUif (tmp)tmp->state=0;    //唤醒队列中的上一个(tmp)睡眠进程。0换作TASK_RUNNING更好//在记录进程被唤醒时一定要考虑到这种情况,实验者一定要注意!!!
}
/* TASK_UNINTERRUPTIBLE和TASK_INTERRUPTIBLE的区别在于不可中断的睡眠* 只能由wake_up()显式唤醒,再由上面的 schedule()语句后的**   if (tmp) tmp->state=0;** 依次唤醒,所以不可中断的睡眠进程一定是按严格从“队列”(一个依靠* 放在进程内核栈中的指针变量tmp维护的队列)的首部进行唤醒。而对于可* 中断的进程,除了用wake_up唤醒以外,也可以用信号(给进程发送一个信* 号,实际上就是将进程PCB中维护的一个向量的某一位置位,进程需要在合* 适的时候处理这一位。感兴趣的实验者可以阅读有关代码)来唤醒,如在* schedule()中:**  for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)*      if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&*         (*p)->state==TASK_INTERRUPTIBLE)*         (*p)->state=TASK_RUNNING;//唤醒** 就是当进程是可中断睡眠时,如果遇到一些信号就将其唤醒。这样的唤醒会* 出现一个问题,那就是可能会唤醒等待队列中间的某个进程,此时这个链就* 需要进行适当调整。interruptible_sleep_on和sleep_on函数的主要区别就* 在这里。*/
void interruptible_sleep_on(struct task_struct **p)
{struct task_struct *tmp;…tmp=*p;*p=current;
repeat:    current->state = TASK_INTERRUPTIBLE;schedule();if (*p && *p != current) { //如果队列头进程和刚唤醒的进程current不是一个,说明从队列中间唤醒了一个进程,需要处理(**p).state=0;   //将队列头唤醒,并通过goto repeat让自己再去睡眠goto repeat;}*p=NULL;if (tmp)tmp->state=0;  //作用和sleep_on函数中的一样
}

去除掉window下的^M

第一种方法:cat -A filename 就可以看到windows下的断元字符 ^M要去除他,最简单用下面的命令:dos2unix filename第二种方法:sed -i ‘s/^M//g' filename#注意:^M的输入方式是 Ctrl + v ,然后Ctrl + M第三种方法:#vi filename:1,$ s/^M//g^M 输入方法: ctrl+V ,ctrl+M第四种方法:#cat filename |tr -d ‘/r' > newfile#^M 可用 /r 代替补充:Linux基本命令①ls 意为list 列出当前文件夹中的文件-l 显示文件的属性 可用ll来表示②alias 别名 看看是否有别名的文件③cd dir 跳跃目录 -P选项 将路径中的链接文件替换成链接指向的文件路径④pwd 查看当前工作的文件夹名 使用-P的选项,会直接进入到其中,相当于cd相关阅读:Linuxshell脚本不执行问题实例分析shell脚本不执行问题:某天研发某同事找我说帮他看看他写的shell脚本,死活不执行,报错。我看了下,脚本很简单,也没有常规性的错误,报“:badinterpreter:Nosuchfileordirectory”错。看这错,我就问他是不是在windows下编写的脚本,然后在上传到linux服务器的……果然。原因:在DOS/windows里,文本文件的换行符为rn,而在*nix系统里则为n,所以DOS/Windows里编辑过的文本文件到了*nix里,每一行都多了个^M。解决:1)重新在linux下编写脚本;2)vi:%s/r//g:%s/^M//g(^M输入用Ctrl+v,Ctrl+m)附:sh-x脚本文件名,可以单步执行并回显结果,有助于排查复杂脚本问题。

ubuntu可过编译 linux0.11挂了

我写的代码ubuntu 没有问题

但是就是跑不过linux0.11

换成别人的又对了

skip

skip

出现了连续两行一样的内容

阻塞的输出在调度函数输出了一次

在阻塞它的时候又输出了一次

某个进程前后两个状态是一致的

原因还是在于调度算法那里,如果只是单纯的需要去切换,比如要换3了,这个时候前面又不是因为2要换的。

那么这里就不太对了。

所以需要去判断一下

如果一样的话,需要加上一句就绪,不然不对

修改完的代码如下

void schedule(void)
{int i,next,c;struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p) {if ((*p)->alarm && (*p)->alarm < jiffies) {(*p)->signal |= (1<<(SIGALRM-1));(*p)->alarm = 0;}if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&(*p)->state==TASK_INTERRUPTIBLE) {/*wake up interruptible process*/fprintk(3, "%d\tJ\t%ld\n", (*p)->pid, jiffies);/*end wake up*/(*p)->state=TASK_RUNNING;}}/* this is the scheduler proper: */while (1) {c = -1;next = 0;i = NR_TASKS;p = &task[NR_TASKS];while (--i) {if (!*--p)continue;if ((*p)->state == TASK_RUNNING && (*p)->counter > c)c = (*p)->counter, next = i;}if (c) break;for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p)(*p)->counter = ((*p)->counter >> 1) +(*p)->priority;}if(task[next]->pid != current->pid) {/*current process will sleep*///fprintk(3, "%d\tW\t%ld\n", pid, sleep_jiffies);/*end sleep*//*Running *///if reschedule 's reason is time over //then be ready first and running if(current->state == TASK_RUNNING) {fprintk(3, "%d\tJ\t%ld\n", current->pid, jiffies);}fprintk(3, "%d\tR\t%ld\n",task[next], jiffies);/*end*/}switch_to(next);
}
int sys_pause(void)
{current->state = TASK_INTERRUPTIBLE;/*interruptible*/if(current->pid != 0)fprintk(3, "%d\tW\t%ld\n", current->pid, jiffies);/*end interruptible*/schedule();return 0;
}

void sleep_on(struct task_struct **p)
{struct task_struct *tmp;if (!p)return;if (current == &(init_task.task))panic("task[0] trying to sleep");tmp = *p;//carefully read insert current into the wait queue's head; tmp is the last head*p = current;//switch to sleepcurrent->state = TASK_UNINTERRUPTIBLE;/*process sleep*/fprintk(3, "%d\tW\t%ld\n", tmp->pid, jiffies);/*end print sleep*/schedule();//give CPU to othersif (tmp) {/*wake up*/fprintk(3, "%d\tJ\t%ld\n", tmp->pid, jiffies);/*end print*/tmp->state=TASK_RUNNING;//wake up queue's nearly process }//WARNNING be careful with this situiation
}
void interruptible_sleep_on(struct task_struct **p)
{struct task_struct *tmp;if (!p)return;if (current == &(init_task.task))panic("task[0] trying to sleep");tmp=*p;*p=current;
repeat:	current->state = TASK_INTERRUPTIBLE;/*process sleep*/fprintk(3, "%d\tW\t%ld\n", tmp->pid, jiffies);/*end print sleep*/schedule();if (*p && *p != current) {//the head of queue isn't the current process//queue exist this case: pop from the the middle of queue//wake up the head of queue //sleep current process/*wake up */fprintk(3, "%d\tJ\t%ld\n", (**p).pid, jiffies);/*end print*/(**p).state=TASK_RUNNING;goto repeat;}*p=NULL;if (tmp) {/*wake up*/fprintk(3, "%d\tJ\t%ld\n", tmp->pid, jiffies);/*end print*/tmp->state=TASK_RUNNING;}
}
void wake_up(struct task_struct **p)
{if (p && *p) {/*wake up*/fprintk(3, "%d\tJ\t%ld\n", (**p).pid, jiffies);/*end print*/(**p).state=TASK_RUNNING;*p=NULL;}
}
/**  Ok, this is the main fork-routine. It copies the system process* information (task[nr]) and sets up the necessary registers. It* also copies the data segment in it's entirety.*/
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,long ebx,long ecx,long edx,long fs,long es,long ds,long eip,long cs,long eflags,long esp,long ss)
{struct task_struct *p;int i;struct file *f;p = (struct task_struct *) get_free_page();if (!p)return -EAGAIN;task[nr] = p;/*process created*/fprintk(3, "%d\tN\t%ld\n", last_pid, jiffies);/*print end*/*p = *current;	/* NOTE! this doesn't copy the supervisor stack */p->state = TASK_UNINTERRUPTIBLE;p->pid = last_pid;p->father = current->pid;p->counter = p->priority;p->signal = 0;p->alarm = 0;p->leader = 0;		/* process leadership doesn't inherit */p->utime = p->stime = 0;p->cutime = p->cstime = 0;p->start_time = jiffies;p->tss.back_link = 0;p->tss.esp0 = PAGE_SIZE + (long) p;p->tss.ss0 = 0x10;p->tss.eip = eip;p->tss.eflags = eflags;p->tss.eax = 0;p->tss.ecx = ecx;p->tss.edx = edx;p->tss.ebx = ebx;p->tss.esp = esp;p->tss.ebp = ebp;p->tss.esi = esi;p->tss.edi = edi;p->tss.es = es & 0xffff;p->tss.cs = cs & 0xffff;p->tss.ss = ss & 0xffff;p->tss.ds = ds & 0xffff;p->tss.fs = fs & 0xffff;p->tss.gs = gs & 0xffff;p->tss.ldt = _LDT(nr);p->tss.trace_bitmap = 0x80000000;if (last_task_used_math == current)__asm__("clts ; fnsave %0"::"m" (p->tss.i387));if (copy_mem(nr,p)) {task[nr] = NULL;free_page((long) p);return -EAGAIN;}for (i=0; i<NR_OPEN;i++)if ((f=p->filp[i]))f->f_count++;if (current->pwd)current->pwd->i_count++;if (current->root)current->root->i_count++;if (current->executable)current->executable->i_count++;set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));p->state = TASK_RUNNING;	/* do this last, just in case *//*ready to run*/fprintk(3, "%d\tJ\t%ld\n", last_pid, jiffies);/*write end*/return last_pid;
}
int do_exit(long code)
{int i;free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));free_page_tables(get_base(current->ldt[2]),get_limit(0x17));for (i=0 ; i<NR_TASKS ; i++)if (task[i] && task[i]->father == current->pid) {task[i]->father = 1;if (task[i]->state == TASK_ZOMBIE)/* assumption task[1] is always init */(void) send_sig(SIGCHLD, task[1], 1);}for (i=0 ; i<NR_OPEN ; i++)if (current->filp[i])sys_close(i);iput(current->pwd);current->pwd=NULL;iput(current->root);current->root=NULL;iput(current->executable);current->executable=NULL;if (current->leader && current->tty >= 0)tty_table[current->tty].pgrp = 0;if (last_task_used_math == current)last_task_used_math = NULL;if (current->leader)kill_session();current->state = TASK_ZOMBIE;current->exit_code = code;/*process exit*/fprintk(3, "%d\tE\t%ld\n", current->pid, jiffies);/*end print*/tell_father(current->father);schedule();return (-1);	/* just to suppress warnings */
}

process.c v3

其实就是把注释给去掉了

但是去掉就对了

感觉是因为编码格式的锅

/** utf-8* rebelOverWaist* 2022/11/13*/#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>#define HZ 10 
#define N 8extern void cpuio_bound(int last, int cpu_time, int io_time);
typedef int (*fn_ptr)();extern int subprocess_printf();
extern int subprocess_leftShift();
extern int subprocess_rightShift();
extern int subprocess_factor();
extern int subprocess_string();
extern int subprocess_matrix();
extern int subprocess_sort();
extern int subprocess_hash();static int index = 0;
static int sid[N];fn_ptr subprocess_table[] = {subprocess_printf,subprocess_leftShift,subprocess_rightShift,subprocess_factor,subprocess_string,subprocess_matrix,subprocess_sort,subprocess_hash
};int subprocess_printf() {printf("printf begin\n");cpuio_bound(10, 1, 0);printf("printf end\n");return 0;
}int subprocess_leftShift() {printf("leftShift begin\n");cpuio_bound(10, 0, 1);printf("leftShift end\n");return 0;
}int subprocess_rightShift() {printf("rightShift begin\n");cpuio_bound(10, 1, 1);printf("rightShift end\n");return (0);
}int subprocess_factor(){printf("factor begin\n");cpuio_bound(10, 1, 9);printf("factor end\n");return (0);
}int subprocess_string() {printf("string begin\n");cpuio_bound(10, 9, 1);printf("string end\n");return (0);  
}int subprocess_matrix() {printf("matrix begin\n");cpuio_bound(15, 2, 3);printf("matrix end\n");return (0);
}int subprocess_sort() {printf("sort begin\n");cpuio_bound(8, 4, 4);printf("sort end\n");return (0);
}int subprocess_hash() {printf("hash begin\n");cpuio_bound(9, 4, 5);printf("hash end\n");return (0);
}
int main (int argc, char* argv[]) {int pid;FILE* file = fopen("./process.txt", "w");for(; index < N; ++index) {if(!fork()) {pid = getpid();fprintf(file,"%4d", pid);sid[index] = pid;subprocess_table[index]();return 0;}}wait(&index);fclose(file);return 0;
}void cpuio_bound(int last, int cpu_time, int io_time) {struct tms start_time, current_time;clock_t utime, stime;int sleep_time;while(last > 0) {times(&start_time);do {times(&current_time);utime = current_time.tms_utime - start_time.tms_utime;stime = current_time.tms_stime - start_time.tms_stime;}while(((utime + stime)/HZ) < cpu_time);last -= cpu_time;if(last <= 0)break;sleep_time = 0;while(sleep_time < io_time){sleep(1);sleep_time++;}last -= sleep_time;}
}

要修改时间片的轮转时间,直接修改进程0的初始化的参数的优先级即可。

这样的话,可以把后面的都给了改了。

在INIT_TASK中定义。

修改INIT_TASK即可

这个其实是进程0的相关参数

不过所有的进程都是0的孩子

所以都继承自它。

(当然有系统调用可以改优先级)

process.sh

cd ..
sudo ./mount-hdc
cd linux-0.11sudo cp ../hdc/var/process.log ../process.log
sudo ../files/stat_log.py ../process.log
sudo ../files/stat_log.py ../process.log 0 1 2 3 4sudo umount ../hdc

sleep on

函数的输入是

指向 指向等待队列的指针 的指针 **p

也就是说*p 就是指向等待队列的指针

在这份代码中使用的结构是tmp = *p

*p =current

然后将当前的current所指向的任务给休眠掉 ->state = INTERRUPT ible

之后再度重新休眠

注意,当前进程接下来需要跑的代码就是这个函数之后的代码

于是如果当前的进程不再被选择的话,这个进程就会停止在这里。

具体可参见这个实验的代码

思考问题

结合自己的体会 从程序设计者的角度看 单进程编程和多进程编程最大的区别是什么

考虑问题不再是竖着线性的了。

同时需要去保证各类资源的访问的先后顺序。

单进程可以一路到底,代码的书写顺序就是执行顺序,但是多进程中,可能会存在一些其他的进程,修改了当前进程的资源,导致结果和预期的顺序结构不再一样;

你是如何修改时间片的? 仅仅针对样本程序建立的进程,在修改时间片前后 log文件的统计结果都是怎么样的,结合你的修改分析一下为什么会这样,或者为什么没变化?

修改进程0的初始值即可

当时间片,(权值增加后)

单个任务运行时间增加,但是相应的,等待时间会增加。

如果时间片变短,内核时间会变少,因为cpu的主要时间花在调度进程上了。

所以这是一个权衡的系统。

没有最好的方法。

lab 7 Proc 文件系统的实现

实验内容

在Linux 0.11上实现procfs(proc文件系统)内的psinfo结点。当读取此结点的内容时,可得到系统当前所有进程的状态信息。例如,用cat命令显示/proc/psinfo的内容,可得到:

# cat /proc/psinfo
pid    state    father    counter    start_time
0    1    -1    0    0
1    1    0    28    1
4    1    1    1    73
3    1    1    27    63
6    0    4    12    817
# cat /proc/hdinfo
total_blocks:62000; 
free_blocks:39037; 
used_blocks:22963; 
total_inodes:20666; 
... 

procfs及其结点要在内核启动时自动创建。相关功能实现在fs/proc.c文件内。

procfs简介

正式的Linux内核实现了procfs,它是一个虚拟文件系统,通常被mount到/proc目录上,通过虚拟文件和虚拟目录的方式提供访问系统参数的机会,所以有人称它为“了解系统信息的一个窗口”。这些虚拟的文件和目录并没有真实地存在在磁盘上,而是内核中各种数据的一种直观表示。虽然是虚拟的,但它们都可以通过标准的系统调用(open()、read()等)访问。

例如,/proc/meminfo中包含内存使用的信息,可以用cat命令显示其内容:

$ cat /proc/meminfo 
MemTotal:       384780 kB
MemFree:         13636 kB
Buffers:         13928 kB
Cached:         101680 kB
SwapCached:        132 kB
Active:         207764 kB
Inactive:        45720 kB
SwapTotal:      329324 kB
SwapFree:       329192 kB
Dirty:               0 kB
Writeback:           0 kB
……

其实,Linux的很多系统命令就是通过读取/proc实现的。例如uname -a 的部分信息就来自/proc/version,而uptime的部分信息来自/proc/uptime和/proc/loadavg。

关于procfs更多的信息请访问:http://en.wikipedia.org/wiki/Procfs

实现思路

这个实验主要就是让自己做了一个虚拟文件

/proc/psinfo, /proc/meminfo, /proc/hdinfo

当使用cat命令,也就是read它们的时候,显示信息。就好像这些东西就是一个真实存在的文件一样。但是我们知道,实际上这只是一个显示。

当使用read去读取这些文件的时候

会发现它是PROC类型。

因此转向对应的处理函数,也就是我在proc_dev.c中所写的那些。

读取文件,也就是把文件的数据写到用户给定的缓冲区中。

用户所给定的缓冲区毫无疑问,肯定在用户态。

但是我们当前在内核态。

所以这里很自然的就需要使用put_fs_系列函数。

接下来要做的事情就很简单了,在内核态做一个字符串,这个字符串放的要给用户的信息,通过put_fs函数给用户。

还剩下的问题就是如何填充这个字符串。

很自然的使用sprintf,但是Linux0.11没有,那就自己造一个喽。

最好放在prink.c那里,以后其他地方要调用,更方便。记得在头文件中声明一下,不然用不了。

再接下来就是怎么找到那些数据了。

查一下就知道哪些地方包含了哪些信息了。

至于该怎么具体使用这些信息,可以看看linux-0.11是怎么做的。

毕竟人不会保存用不到的信息。

把这些方法都抄来了,(需要小改)

再结合一下前文的代码,就可以实现proc了。

ll /proc

显示出文件的信息正确(我总共建了3个)

出现3个就对了

cat

#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{char buf[513] = {'\0'};int nread;
//因此其会使用read函数//而read函数我们又新加了一条分支到我们新创建的文件类型int fd = open(argv[1], O_RDONLY, 0);while(nread = read(fd, buf, 512)){buf[nread] = '\0';puts(buf);}return 0;
}

cat /proc/psinfo

cat /proc/meminfo

for(i =0; i < PAGING_PASS; ++i)if(!mem_map[i])free++;
printk("%d pages free")memtotal
memfree
memavailable
buffers
cached

cat /proc/hdinfo

include/sys/stat.h

#define S_IFPROC 0030000#define S_ISPROC(m) (((m) & S_IFMT) == S_IFPROC)

fs/namei.c

if(S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode)) inode->i_zone[0] = dev;

init/main.c

static inline _syscall2(int, mkdir, const char*, name, mode_t, mode)
static inline _syscall3(int, mknod, const char*, filename, mode_t, mode, dev_t, dev)mkdir("/proc", 0755);
mknod("/proc/psinfo", S_IFPROC | 0444, 0);
mknod("/proc/hdinfo", S_IFPROC | 0444, 1);
//
//mknod("/proc/inodeinfo", S_IFPROC | 0444, 2);
//MemTotal MemFree MemAvailable
mknod("/proc/meminfo", S_IFPROC | 0444, 2);

fs/read_write.c

extern int read_proc(int dev, char* buf, int count, off_t* pos);read()
...
if(S_ISPROC(inode->i_mode)) {return read_proc(inode->i_zone[0], buf, count, &file->f_pos);
}
...

fs/proc_dev.c

#include <errno.h>#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/system.h>int read_proc(int dev, char* buf, int count, off_t* pos) {return 0;
}

遇到一个很奇怪的问题

传递的pos指向的是145212

这样直接就超了

?为什么会这样

奇怪了我真是

file = current->filp[fd];

嗯,因为我输出的是地址

死循环了

for有啥问题

没啥问题,但是现在改了改又好了

返回值和文件指针的问题

第二个问题

显示的结果不对

p+i 和p[i]写混了

顺带一提 中间还遇到了malloc(sizeof(1024))和malloc(1024)这种问题

mycat.c

/* ************************************************************************
> File Name:     mycat.c
> Author:        rebelOverWaist
> Description:   ************************************************************************/
#include <stdio.h>
#include <unistd.h>int main (int argc, char* argv[]) {char buf[9] = {'\0'};int nread;int fd = open(argv[1], 0, 0);while(nread = read(fd, buf, 8)) {buf[nread] = '\0';puts(buf);}return 0;
}

proc.sh

#########################################################################
# File Name:    proc.sh
# Author:       rebelOverWaist
# mail:         FAIZ030612@163.com
# Created Time: 2022年11月15日 星期二 14时50分47秒
#########################################################################
#!/bin/bash
# copy of hdc/usr/root/proc.sh
echo "ls -l /proc/*info"
ls -l /proc/*infoecho "cat /proc/psinfo"
cat /proc/psinfoecho "cat /proc/hdinfo"
cat /proc/hdinfoecho "cat /proc/meminfo"
cat /proc/meminfo 

fs/final proc_dev.c

/* ************************************************************************
> File Name:     proc_dev.c
> Author:        rebelOverWaist
> Format:        utf-8
> Description:   lab-9 proc  kernel************************************************************************/
#include <errno.h>#include <linux/sched.h> // struct task_struct* task[NR_TASKS]
#include <linux/kernel.h> // include malloc() and free() size should less than 4KB 1 page
// now kernel's head include sprintf() 
// fprintk() 
#include <linux/mm.h> // get free page calc_mem 
#include <linux/head.h>//mm
#include <asm/segment.h>
typedef int (*pr_ptr)(char* buf, int count, off_t* pos);#define set_bit(bitnr,addr) ({\
register int __res; \__asm__("bt %2, %3;setb %%al":"=a"(__res):"a"(0),"r"(bitnr),"m"(*(addr))); \__res; })
//hdinfo -> super 
/** total_blocks: * free_blocks: * used_blocks: * total inodes: ** */
int read_hd(char* buf, int count, off_t* pos) {struct super_block* sb;int used = 0;int read = 0;int i, j;char* proc_buf;if(!(proc_buf = (char*)malloc(sizeof(1024)))) {printk("no memory to alloc!\n");return 0;}//dev 0x301 789sb = get_super(0x301);//blocksread += sprintf(proc_buf+read, "Total blocks:%d\n",sb->s_nzones);i = sb->s_nzones;while(--i >= 0) {if(set_bit(i & 8191, sb->s_zmap[i>>13]->b_data)) ++used;}read += sprintf(proc_buf+read, "Used blocks:%d\n", used);read += sprintf(proc_buf+read, "Free blocks:%d\n", sb->s_nzones-used);// inodesread += sprintf(proc_buf+read, "Total inodes:%d\n", sb->s_ninodes);used = 0;i = sb->s_ninodes+1;while(--i >= 0){if(set_bit(i&8191, sb->s_imap[i>>13]->b_data))++used;}read += sprintf(proc_buf+read, "Used inodes:%d\n", used);read += sprintf(proc_buf+read, "Free inodes:%d\n", sb->s_ninodes-used);//to buffer proc_buf[read] = '\0';for(i = *pos, j = 0; i < read && j <  count; ++i, ++j)put_fs_byte(proc_buf[i], buf+j);free(proc_buf);*pos += j;return j;
}// psinfo -> struct task_struct* task[NR_TASKS] 
int read_ps(char* buf, int count, off_t* pos) {int read = 0;int i = *pos, j = 0;struct task_struct ** p;char* proc_buf;if(!(proc_buf = (char*)malloc(1024))) {printk("no memory to alloc!");return 0;}read += sprintf(proc_buf+read,"pid\tstate\tfather\tcounter\tstart_time\n");for(p = &FIRST_TASK + 1; p <= &LAST_TASK; ++p) {if(*p) {read += sprintf(proc_buf+read,"%ld\t%ld\t%ld\t%ld\t%ld\n",(*p)->pid, (*p)->state, (*p)->father,(*p)->counter,(*p)->start_time);        }}proc_buf[read] = '\0';//printk("read->%d pos->%d count->%d j->%d\n", read, i, count, j);//if read 0 byteif(!read)return 0;for(; i < read && j < count; ++i, ++j) {if(!proc_buf[i])break;put_fs_byte(proc_buf[i], buf+j);}free(proc_buf);*pos += j;return j;
}// meminfo -> mm 
// calc_mem function
int read_mem(char* buf, int count, off_t* pos) {int i,j,k,free=0;long* pg_tbl;char* proc_buf;int read = 0;if(!(proc_buf = (char*)malloc(1024))) {printk("no memory to alloc!\n");return 0;}/*for(i = 0; i < PAGING_PAGES; ++i) {if(!mem_map[i])++free;}*/read += sprintf(proc_buf+read, "total page:%d\n", PAGING_PAGES);// read += sprintf(proc_buf+read, "free page: %d\n", free);// read += sprintf(proc_buf+read, "used page: %d\n", PAGING_PAGES-free);for(i = 2; i < 1024; ++i) {if(1 & pg_dir[i]) {pg_tbl = (long*)(0xfffff000 & pg_dir[i]);for(j=k=0; j < 1024; ++j) {if(pg_tbl[j] & 1)++k;}read += sprintf(proc_buf+read,"Pg-dir[%d] uses %d pages\n", i, k);}}for(i = *pos, j = 0; i < read && j < count; ++i, ++j)put_fs_byte(proc_buf[i], buf+j);*pos += j;return j;
}static pr_ptr pr_table[] = {read_ps, /* /proc/psinfo  */read_hd, /* /proc/hdinfo  */read_mem  /* /proc/mminfo  */
};#define NR_DEVS ((sizeof (pr_table)) / (sizeof (pr_ptr)))/** dev = no of dev* buf = the pointer of  user space uesd for receive datas* count = the size of buf* pos = the last pos of file pointer point to * */
int read_proc(int dev, char* buf, int count, off_t* pos) {int i = 0; pr_ptr call_addr;if(dev >= NR_DEVS) return -ENODEV;if(!(call_addr = pr_table[dev]))return -ENODEV;return call_addr(buf, count, pos);
}

read_write.c

/*oslab 9*/
extern int read_proc(int dev, char* buf, int count, off_t* pos);
/*oslab 9*/int sys_read(unsigned int fd, char* buf, int count) /*add new file type (inode)*///if is the proc file then read it if(S_ISPROC(inode->i_mode)) {/** inode->i_zone[0] = mknod -> dev* buf = point -> users space (need put_fs get_fs)* count = size of buf* &file->pos = the position pointed by the last file pointer* */// printk("inode->i_dev:%d\n", inode->i_dev);return read_proc(inode->i_zone[0], buf, count, &file->f_pos);}/*end add new file type (inode)*///this msg is the error msg //769 incase// printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);

fs/Makefile

AR	=ar
AS	=as
CC	=gcc-3.4 -march=i386
LD	=ld
CFLAGS	=-m32 -g -Wall -fstrength-reduce -fomit-frame-pointer \-nostdinc -I../include
CPP	=gcc-3.4 -E -nostdinc -I../include.c.s:$(CC) $(CFLAGS) \-S -o $*.s $<
.c.o:$(CC) $(CFLAGS) \-c -o $*.o $<
.s.o:$(AS) -o $*.o $<OBJS=	open.o read_write.o inode.o file_table.o buffer.o super.o \block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \bitmap.o fcntl.o ioctl.o truncate.o proc_dev.ofs.o: $(OBJS)$(LD) -m elf_i386 -r -o fs.o $(OBJS)clean:rm -f core *.o *.a tmp_makefor i in *.c;do rm -f `basename $$i .c`.s;donedep:sed '/\#\#\# Dependencies/q' < Makefile > tmp_make(for i in *.c;do $(CPP) -M $$i;done) >> tmp_makecp tmp_make Makefile### Dependencies:
proc_dev.o: proc_dev.c ../include/errno.h ../include/linux/sched.h \../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \../include/linux/head.h ../include/asm/segment.h 
bitmap.o: bitmap.c ../include/string.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h
block_dev.o: block_dev.c ../include/errno.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \../include/asm/segment.h ../include/asm/system.h
buffer.o: buffer.c ../include/stdarg.h ../include/linux/config.h \../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \../include/sys/types.h ../include/linux/mm.h ../include/signal.h \../include/linux/kernel.h ../include/asm/system.h ../include/asm/io.h
char_dev.o: char_dev.c ../include/errno.h ../include/sys/types.h \../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \../include/asm/segment.h ../include/asm/io.h
exec.o: exec.c ../include/errno.h ../include/string.h \../include/sys/stat.h ../include/sys/types.h ../include/a.out.h \../include/linux/fs.h ../include/linux/sched.h ../include/linux/head.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \../include/asm/segment.h
fcntl.o: fcntl.c ../include/string.h ../include/errno.h \../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \../include/sys/types.h ../include/linux/mm.h ../include/signal.h \../include/linux/kernel.h ../include/asm/segment.h ../include/fcntl.h \../include/sys/stat.h
file_dev.o: file_dev.c ../include/errno.h ../include/fcntl.h \../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \../include/linux/kernel.h ../include/asm/segment.h
file_table.o: file_table.c ../include/linux/fs.h ../include/sys/types.h
inode.o: inode.c ../include/string.h ../include/sys/stat.h \../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \../include/linux/kernel.h ../include/asm/system.h
ioctl.o: ioctl.c ../include/string.h ../include/errno.h \../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \../include/signal.h
namei.o: namei.c ../include/linux/sched.h ../include/linux/head.h \../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h \../include/string.h ../include/fcntl.h ../include/errno.h \../include/const.h ../include/sys/stat.h
open.o: open.c ../include/string.h ../include/errno.h ../include/fcntl.h \../include/sys/types.h ../include/utime.h ../include/sys/stat.h \../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h
pipe.o: pipe.c ../include/signal.h ../include/sys/types.h \../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \../include/linux/mm.h ../include/asm/segment.h
read_write.o: read_write.c ../include/sys/stat.h ../include/sys/types.h \../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \../include/signal.h ../include/asm/segment.h
stat.o: stat.c ../include/errno.h ../include/sys/stat.h \../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/mm.h ../include/signal.h \../include/linux/kernel.h ../include/asm/segment.h
super.o: super.c ../include/linux/config.h ../include/linux/sched.h \../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \../include/asm/system.h ../include/errno.h ../include/sys/stat.h
truncate.o: truncate.c ../include/linux/sched.h ../include/linux/head.h \../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \../include/signal.h ../include/sys/stat.h

include/linux/mm.h

#ifndef _MM_H
#define _MM_H#define PAGE_SIZE 4096
/*oslab 9*/
#define LOW_MEM 0x100000
#define PAGING_MEMORY (15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100
/*oslab 9*/extern unsigned long get_free_page(void);
extern unsigned long put_page(unsigned long page,unsigned long address);
extern void free_page(unsigned long addr);
/*oslab 9*/
extern void calc_mem(void);
/*oslab 9*/#endif

思考问题

  1. 如果要求你在psinfo之外再实现另一个结点,具体内容自选,那么你会实现一个给出什么信息的结点?为什么?

mem结点,因为简单。不是。

因为页,进程,磁盘合起来是一个操作系统整块。知道了这些信息能够帮助了解操作系统的整体

  1. 一次read()未必能读出所有的数据,需要继续read(),直到把数据读空为止。而数次read()之间,进程的状态可能会发生变化。你认为后几次read()传给用户的数据,应该是变化后的,还是变化前的?

我认为应该是变化后的。

  1. 如果是变化后的,那么用户得到的数据衔接部分是否会有混乱?如何防止混乱?

数据衔接部分可能会存在混乱,比如进程,之前有进程存在,有的又没了。

直接从当前文件位置开始读取,如果之前的数据不再读了,之后的数据如果存在,当前就可以读,不然就无法读。

  1. 如果是变化前的,那么该在什么样的情况下更新psinfo的内容?

  2. 删除文件以后,/proc/inodeinfo那个inode号的inode,你发现了什么,为什么会这样?

emm,没做inodeinfo,大概率是inode被干掉了。

遇到的几个问题BUG

记忆最深刻的怕是read了

首先描述一下事件发生的顺序。

首先是我认为写用户区的代码不应该放在接口函数那里,应该在每个设备的具体函数上实现。

毕竟每种需求是不一样的,从工程角度考虑应该尽可能的降低耦合度。

于是我拿回去了,

然后基本上原封不动的改了。

结果,死循环了。

当时百思不得其解,就开始注释代码,看看问题到底出在那里了。

结果,我发现,当我把我一段for注释掉,就正常了。

然后我把for变成了最简单的形式for(j = 0; j < 10; ++j) {}

结果还是死循环,我当时真的是郁闷极了。

结果去路上的时候突然想到了cat命令,然后看了看,cat的核心代码。

瞬间明白我错在哪里了。

while(read()){}

上述循环其实靠的是read的返回值,但是我的返回值一直是*pos.

而我把*pos在循环之后赋值为了j。

于是就导致了每次返回的都是*pos.

其实改成 += j

然后返回 j 就对了,但是当时脑袋已经晕了,结果连这个问题都搞错了。

然后我还是很疑惑为什么之前都能正常运行,结果发现我之前未实现时默认返回的是0

所以直接退了

所以我以为我对了。

居然连read都没写对,唉。

这次BUG的原因是我对*pos 和 读取的字符数搞晕头了。

导致后续都错了。

希望以后认真吧!

lab 6 字符显示

最后的结果

按下F12后,输出的都是不可见的字符

问题1

按下F12后

会调用func函数,它的功能是什么

进行转义

将F12变成esc [[ l

然后在con_write中进行分析

并进行相关操作

在你的实现中,是否也实现了向文件的过滤

如果是,怎么实现只向终端输出,

如果不是,怎么把向文件输出的也一并进行过滤

当前代码很明显没有对文件进行过滤

要对文件进行过滤

其实很简单

只要处理的层级更高就行了

也就是在file_write这种函数中进行修改即可。

具体的应当查看sys_write

看看向文件写数据是哪个函数在管

然后把每次写的数据换成*就行了。

修改字符显示有多种方式

可以在按下F12后,调用功能函数的时候就换一个函数调用

而不是把它加入到func函数中

还可以检测之后产生的扫描码[[ A这种

看看程序是怎么处理的F1,仿造着来就行

总的思路就是按下键后,开启一把锁

所有的都是这把锁。

lab4 实现信号量

int sys_lseek(unsigned int fd, off_t offset, int origin) 文件句柄 新的文件读写偏移 偏移起始位置 SEEK_SET (0) SEEK_CUR(1)SEEK_END

 #include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value); //pshared:0表⽰线程间共享,非0表示进程间共享   //value是信号量初值,大于0表示才可以访问
int sem_destroy(sem_t *sem);
//摧毁 sem_t 信号量//P和V操作int sem_wait(sem_t *sem);//信号量等待,-1,P操作
int sem_post(sem_t *sem);//信号量发布,+1,V操作

gcc -o test test.c -lpthread

很有意思一错误 if(!fork)

pc.c

/* ************************************************************************
> File Name:     pc.c
> Author:        rebelOverWaist
> Description:   sem for producer, consumer question ************************************************************************/
#define __LIBRARY__
#include <unistd.h>// #include <linux/sem.h>#include <semaphore.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>#define BUFFER_SIZE 10/*
_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value);
_syscall1(int, sem_wait, sem_t*, sem);
_syscall1(int, sem_post, sem_t*, sem);
_syscall1(int, sem_unlink, const char*, name);
*/
char* fileName = "./pc.log";
sem_t full, empty;
sem_t access_mutex, print_mutex;
int kill = 0;//calc length of string 
off_t strlen(char *p) {off_t i = 0;while(*p++)++i;return i;
}//Produce item put them into buffer
void producer(int fd) {int M = 500, i;char* s = "000";printf("producer\n");for(i = 0; i < M; ++i) {// P(empty) sem_wait(&empty);// P(access_mutex)sem_wait(&access_mutex);// put item// if full then put from headif(!(i%BUFFER_SIZE)) {lseek(fd, 0, SEEK_SET);}sprintf(s, "%d\n", i);write(fd, s, strlen(s));// V(access_mutex)sem_post(&access_mutex);// V(full)sem_post(&full);}   
}//consume item from buffer
void consumer(int fd) {int pid = getpid();char* buf = "00000";printf("consumer pid->%d\n", pid);while(!kill) {// P(full)sem_wait(&full);// P(access_mutex)sem_wait(&access_mutex);read(fd, buf, 4);// V(access_mutex)sem_post(&access_mutex);// P(print_mutex)sem_wait(&print_mutex);// print pid: iprintf("%d: %s", pid, buf);// V(print_mutex)sem_post(&print_mutex);// V(empty)sem_post(&empty);}
}int main (int argc, char* argv[]) {int i;//create buffer int fd = open(fileName, O_RDWR);// create sem /*full = sem_open("full", 0);empty = sem_open("empty", BUFFER_SIZE);access_mutex = sem_open("access_mutex", 1);print_mutex = sem_open("print_mutex", 1);*/sem_init(&full, 1, 0);sem_init(&empty, 1, BUFFER_SIZE);sem_init(&access_mutex, 1, 1);sem_init(&print_mutex, 1, 1);printf("create producer process\n");//create producer processif(!fork()) {producer(fd);return 0;}printf("create consumer process\n");//create consumer processfor(i = 0; i < 10; ++i) {if(!fork) {printf("create really consumer\n");consumer(fd);return 0;}}printf("wait all subprocess exit\n");//wait all subprocess exitwait(&i);//relese all uesd resourecesclose(fd);/*sem_unlink("full");sem_unlink("empty");sem_unlink("access_mutex");sem_unlink("print_mutex");*/sem_destroy(&full);sem_destroy(&empty);sem_destroy(&access_mutex);sem_destroy(&print_mutex);return 0;
}
int main (int argc, char* argv[]) {int i;int fd = open(fileName, 02);
# 108 "pc.c"sem_init(&full, 1, 0);sem_init(&empty, 1, 10);sem_init(&access_mutex, 1, 1);sem_init(&print_mutex, 1, 1);printf("create producer process\n");if(!fork()) {producer(fd);return 0;}printf("create consumer process\n");for(i = 0; i < 10; ++i) {if(!fork) {printf("create really consumer\n");consumer(fd);return 0;}}printf("wait all subprocess exit\n");wait(&i);close(fd);sem_destroy(&full);sem_destroy(&empty);sem_destroy(&access_mutex);sem_destroy(&print_mutex);return 0;
}
	movl	$1, %edxmovl	$1, %esimovl	$print_mutex, %edicall	sem_initmovl	$.LC7, %edicall	putscall	forktestl	%eax, %eaxjne	.L13movl	-4(%rbp), %eaxmovl	%eax, %edicall	producermovl	$0, %eaxjmp	.L17
.L13:movl	$.LC8, %edicall	putsmovl	$0, -8(%rbp)jmp	.L15
.L16:movl	-8(%rbp), %eaxaddl	$1, %eaxmovl	%eax, -8(%rbp)
.L15:movl	-8(%rbp), %eaxcmpl	$9, %eaxjle	.L16movl	$.LC9, %edicall	putsleaq	-8(%rbp), %raxmovq	%rax, %rdimovl	$0, %eaxcall	wait

可以看到

pc.i文件中还有if(!fork)

但是pc.s文件中直接把它去掉了

那么这个循环变成了for(i = 0; i < 10; ++i){}

所以什么效果都没有

这是因为fork本身是一个地址,可以认为是一个指针(函数指针)

这个函数指针很明显不为空

那么!fork的结果必为假

所以那份代码根本不会执行

所以gcc就把它给去掉了

所以,函数没写()也能跑。

ubuntu下信号量使用不对

同样的代码,自己写的linux信号量可以

但是ubuntu用库函数就不行

除了改了一下函数

都没动

unterminated string or character constant

25

30 22

kill

too many arg sem_open

bug1,if(!fork);

修改办法,添加括号。原因,被误解为了函数指针

bug2,fopen,open失效;

修改办法,改参数,换函数。原因,权限和多个进程读写问题。

bug3,创建了进程和信号量,但是消费者无限等待,

首先确认了在Linux0.11上没什么问题。其次确定了别人在0.11上能跑的代码在Ubuntu上换了一个函数也不行。于是开始查手册man sem_init,发现自己的用法理解没什么偏差。于是怀疑是不是自己的代码缺少了点东西,于是到网上找了一份能正常运行的代码,在另外一个文件夹进行了,可以正常工作。于是就再将自己的代码嵌入到该代码的框架中,还是没有问题的。接下来,将我的代码的几个函数复制到了这份代码,还是没有问题。再将这份代码迁移到工作目录。编译运行,还是没问题。但是我的两份代码还是存在问题。看到我的子进程没有释放资源就换回了,怀疑是内核空间的信号量申请了未释放。重启电脑,再次运行,还是不行。于是自己根据刚刚的那份代码,修改了我的代码(几乎重写),编译,报错。再次运行,还是死循环。于是用别人代码中的输出信号量值的代码在我代码的不同区域进行了测试,报错,段错误。(其实这里我就大概明白了,权限问题)当时很高兴,因为段错误可比那些看不见摸不着的编译错误好改的多。但还是确认了一下,是哪些代码引起的段错误。(就是我刚加的啦)于是仔细对比了我的代码和他代码的区别,发现他的代码特地给sem指定了权限,0666(c语言8进制0开头)所以他的代码访问不会出现问题,但是我的没有,默认的权限不够,因此访问时就会出现段错误。把这里改完后,就改完了这份代码的问题。可以在Ubuntu上正常解决生产者消费者问题了。

bug4,在0.11上一直出现string or char…编译错误,

应该是哪个字符不对我用各种软件检查了老半天,都没发现问题,联系到之前的经验,于是尝试把所有的注释全给删了,再次编译,通过了。
至此,使用信号量实验成功做完了pc.c函数。

pc.c

/* ************************************************************************
> File Name:     pc.c
> Author:        rebelOverWaist
> Description:   sem for producer, consumer question ************************************************************************/
#define __LIBRARY__
#include <unistd.h>
#include <semaphore.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>/*
_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value);
_syscall1(int, sem_wait, sem_t*, sem);
_syscall1(int, sem_post, sem_t*, sem);
_syscall1(int, sem_unlink, const char*, name);
*/#define NR_TASKS 5 /*how many tasks created*/
#define NR_ITEMS 20 /*how many items produced*/
#define BUFFER_SIZE 5 /*size of file buffer*/
// semaphores'name 
#define FULL "full"
#define MUTEX "mutex"
#define EMPTY "empty"// this is the file's name used for buffer
const char* FILENAME = "pc.log";
// used for kill consumer process
int kill = 0;//Produce item put them into buffer
// file WRONLY 
void producer(int file, sem_t *full, sem_t *empty, sem_t *mutex) {unsigned int i;int pid = getpid();int pfull, pempty, pmutex;/*get this value*/sem_getvalue(full,&pfull);sem_getvalue(empty,&pempty);sem_getvalue(mutex,&pmutex);    printf("full \tvalue=%d\n",pfull);printf("empty \tvalue=%d\n",pempty);printf("mutex \tvalue=%d\n",pmutex);   for(i = 0; i < NR_ITEMS; ++i) {sem_wait(empty);sem_wait(mutex);// if full then put from headif(!(i%BUFFER_SIZE)) {lseek(file, 0, SEEK_SET);}write(file, (char*)&i, sizeof(i)); printf("pid->%d\tproduces item->%d\n", pid,i);fflush(stdout);sem_post(mutex);sem_post(full);}   // producer have produced all items then kill all consumerkill = 1;
}//consume item from buffer
//file RDONLY
void consumer(int file, sem_t *full, sem_t *empty, sem_t *mutex) {int pid = getpid();unsigned int i;printf("consumer%d have been created\n", pid);while(!kill) {sem_wait(full);sem_wait(mutex);if(!read(file, (char*)&i, sizeof(i))) {lseek(file, 0, SEEK_SET);read(file, (char*)&i, sizeof(i));}printf("consumer\t%d consume %d\n", pid, i);sem_post(mutex);sem_post(empty);}printf("pid %d now dead\n", pid);fflush(stdout);
}int main (int argc, char* argv[]) {int i;int fi, fo;int pfull, pempty, pmutex;char* filename;sem_t *full, *empty, *mutex;// if have cmd arg then use it, otherwise use default pathfilename = argc > 1 ? argv[1] : FILENAME;fi = open(FILENAME,O_WRONLY);fo = open(FILENAME,O_RDONLY);// create sem 
/*full = sem_open(FULL, 0);empty = sem_open(EMPTY, BUFFER_SIZE);mutex = sem_open(MUTEX, 1);*/full=sem_open(FULL,O_CREAT,0666,0);empty=sem_open(EMPTY,O_CREAT,0666,BUFFER_SIZE);mutex = sem_open(MUTEX, O_CREAT,0666,1);if(full==(void*)-1 || empty==(void*)-1|| mutex==(void*)-1){perror("sem_open failure");}// get this valuesem_getvalue(full,&pfull);sem_getvalue(empty,&pempty);sem_getvalue(mutex,&pmutex);    /* printf("full \tvalue=%d\n",pfull);printf("empty \tvalue=%d\n",pempty);printf("mutex \tvalue=%d\n",pmutex);  *///create producer processif(!fork()) {printf("now create producer process\n");fflush(stdout);producer(fi, full, empty, mutex);goto END;}//create consumer processfor(i = 0; i < NR_TASKS; ++i) {if(!fork()) {consumer(fo, full, empty, mutex);goto END;}}//wait all subprocess exitwait(&i);
END://release resourecesclose(fi);close(fo);// sem_close(full);sem_close(empty);sem_close(mutex);sem_unlink(FULL);sem_unlink(EMPTY);sem_unlink(MUTEX);//printf("now say goodbye\n");return 0;
}

pc_hdc.c > pc_hdc.log

/*
* pc producer consumer
* code in hdc
* just for linux-0.11
*/
#define __LIBRARY__
#include <unistd.h>
#include <linux/sem.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value)
_syscall1(int, sem_wait, sem_t*, sem)
_syscall1(int, sem_post, sem_t*, sem)
_syscall1(int, sem_unlink, const char*, name)#define NR_CONSUMERS 5
#define NR_ITEMS 20
#define BUFFER_SIZE 5 
const char* FILENAME = "buffer.log";
int ckill = 0;
void producer(int file, sem_t *full, sem_t *empty, sem_t *mutex) {unsigned int i;int pid = getpid();printf("producer%d have been created\n", pid);fflush(stdout);for(i = 0; i < NR_ITEMS; ++i) {sem_wait(empty);sem_wait(mutex);if(!(i%BUFFER_SIZE)) {lseek(file, 0, SEEK_SET);}write(file, (char*)&i, sizeof(i)); printf("pid->%d\tproduces item->%d\n", pid,i);fflush(stdout);sem_post(mutex);sem_post(full);}ckill = 1;
}void consumer(int file, sem_t *full, sem_t *empty, sem_t *mutex) {int pid = getpid();unsigned int i;printf("consumer%d have been created\n", pid);fflush(stdout);while(!ckill) {sem_wait(full);sem_wait(mutex);if(!read(file, (char*)&i, sizeof(i))) {lseek(file, 0, SEEK_SET);read(file, (char*)&i, sizeof(i));}printf("consumer\t%d consume %d\n", pid, i);fflush(stdout);sem_post(mutex);sem_post(empty);}printf("pid %d now dead\n", pid);fflush(stdout);
}int main (int argc, char* argv[]) {int i;int fi, fo;char* filename;sem_t *full, *empty, *mutex;filename = argc > 1 ? argv[1] : FILENAME;fi = open(FILENAME,O_WRONLY | O_CREAT | O_TRUNC, 0222);fo = open(FILENAME,O_RDONLY | O_TRUNC, 0444);full = sem_open("full", 0);empty = sem_open("empty", BUFFER_SIZE);mutex = sem_open("mutex", 1);for(i = 0; i < NR_CONSUMERS; ++i) {if(!fork()) {consumer(fo, full, empty, mutex);goto END;}}if(!fork()) {producer(fi, full, empty, mutex);goto END;}wait(&i);
END:close(fi);close(fo);sem_unlink("full");sem_unlink("empty");sem_unlink("mutex");return 0;
}

pv.c

#include <fcntl.h>           
#include <sys/stat.h>        
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>#define BUFFER_SIZE 5
#define FILENAME "something"
#define NR_TASKS 10
#define NR_ITEMS 20
#define FULL "full"
#define EMPTY "empty"
#define MUTEX "mutex"int kill = 0;
//Produce item put them into buffer
void producer(int file, sem_t* empty, sem_t* mutex, sem_t* full) {unsigned int i;int pid = getpid();int pmutex, pempty, pfull;for(i = 0; i < NR_ITEMS; ++i) {// P(empty) sem_wait(empty);// P(access_mutex)sem_wait(mutex);// put item// if full then put from headif(!(i%BUFFER_SIZE)) {lseek(file, 0, SEEK_SET);}write(file, (char*)&i, sizeof(i)); printf("pid->%d\tproduces item->%d\n", pid,i);fflush(stdout);// V(access_mutex)sem_post(mutex);// V(full)sem_post(full);/*get this value*/sem_getvalue(full,&pfull);sem_getvalue(empty,&pempty);sem_getvalue(mutex,&pmutex);    printf("full \tvalue=%d\n",pfull);printf("empty \tvalue=%d\n",pempty);printf("mutex \tvalue=%d\n",pmutex);
fflush(stdout);}   kill = 1;
}//consume item from buffer
void consumer(int file, sem_t* full, sem_t* empty, sem_t* mutex) {int pid = getpid();unsigned int i;printf("consumer%d have been created\n", pid);while(!kill) {printf("consumer%d now wait full\n", pid);fflush(stdout);// P(full)sem_wait(full);printf("consumer%d now wait access_mutex\n", pid);fflush(stdout);// P(access_mutex)sem_wait(mutex);//printf("consumer%d now read \n", pid);fflush(stdout);if(!read(file, (char*)&i, sizeof(i))) {lseek(file, 0, SEEK_SET);read(file, (char*)&i, sizeof(i));}// V(access_mutex)sem_post(mutex);printf("consumer%d now post access_mutex\n", pid);fflush(stdout);// V(empty)sem_post(empty);}printf("pid %d now dead\n", pid);fflush(stdout);
}
int main(int argc,char *argv[]){/* create the named pipe fifo*/int fi, fo;fi = open(FILENAME,O_WRONLY);fo = open(FILENAME,O_RDONLY);/*open the semaphore*/sem_t *full,*empty, *mutex;int pfull, pempty, pmutex;full=sem_open(FULL,O_CREAT,0666,0);empty=sem_open(EMPTY,O_CREAT,0666,BUFFER_SIZE);mutex = sem_open(MUTEX, O_CREAT,0666,1);if(full==(void*)-1 || empty==(void*)-1|| mutex==(void*)-1){perror("sem_open failure");}//print sem addressprintf("sem address\n");printf("full=%p\n",full);printf("empty=%p\n",empty);printf("mutex=%p\n",mutex);/*get this value*/sem_getvalue(full,&pfull);sem_getvalue(empty,&pempty);sem_getvalue(mutex,&pmutex);    printf("full \tvalue=%d\n",pfull);printf("empty \tvalue=%d\n",pempty);printf("mutex \tvalue=%d\n",pmutex);   if(!fork()) {producer(fi, empty, mutex, full);goto END;}int i;for(i = 0; i < NR_TASKS; ++i) {if(!fork()) {consumer(fo, full, empty, mutex);goto END;		}}wait(&i);
END:/*close the file*/close(fi);close(fo);sem_close(empty);sem_close(full);sem_close(mutex);/* release resource*/unlink(FILENAME);sem_unlink(EMPTY);sem_unlink(MUTEX);sem_unlink(FULL);return 0;}

sem.h

//head of semaphore
//include/linux/sem.h
#ifndefine __SEMAPHORE__
#define __SEMAPHORE__typedef struct SEMAPHORE{//name of semaphoreconst char* name;//wait task of current semphorestruct task_struct *wait_task;struct SEMAPHORE *next;//value of semaphoreunsigned int value;unsigned int lock;
}sem_t;
/*
//create or open a semaphore by name
sem_t *sem_open(const char *name, unsigned int value);
//P operation wait a semaphore
int sem_wait(sem_t *sem);
//V operation post a semaphore to wake up 
int sem_post(sem_t *sem);
//release
int sem_unlink(const char *name);
*/
#endif

sem.c

//implement of semaphore
//kernel/sem.h#include <linux/sem.h>#include <linux/kernel.h>
//for schedule sleep_on wake_up
#include <linux/sched.h>#include <asm/segment.h>#include <unistd.h>
/*
* a data structure for sem
* struct array
*/
sem_t *sem_head = NULL;
/*
sem_t sem_list[NR_SEM] = {{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},
};
unsigned int nr_sem = 0;
*/
int sem_strcmp(char *p, char *s) {for(;*p == *s; ++p, ++s) {if(*p == '\0')return 0;}return (*p - *s);
}sem_t* sys_sem_open(const char *name, unsigned int value) {//dummy headif(!sem_head) {sem_head = (sem_t*)malloc(sizeof(sem_t));sem_head->next = NULL;}unsigned int length = 0;char c;char *p = name, *pname, *first;sem_t *sem = sem_head;//calc the name's lengthwhile((c  = get_fs_byte(p)) != '\0') {++length;++p;}//reset pointerp = name;pname = (char*)malloc(length+1);first = pname;//get sem's namewhile((c = get_fs_byte(p)) != '\0') {*first = *p++;++first;}*first = '\0';//adjust if the semaphore existswhile(sem->next) {//if foundif(sem_strcmp(sem->next->name, pname) == 0) {return sem->next;}sem = sem->next;}//assign value to sem->nextsem->next = (sem_t*)malloc(sizeof(sem_t));sem = sem->next;sem->name = pname;sem->value = value;sem->wait_task = NULL;printk("%s semaphore had created!\n", sem->name);return sem;
}char* get_string_from_usr(char *usr) {unsigned int length = 1;char c;char *kernel, *p = usr, *first;while((c = get_fs_byte(p))) {++length;++p;}first = kernel = (char*)malloc(length);p = usr;while((c = get_fs_byte(p))) {*first = c;++p;++first;}*first = '\0';return kernel;
}int sys_sem_unlink(const char *name) {//if have no nodeif(!sem_head || !sem_head->next)return -1;sem_t *sem = sem_head, *tmp;char *pname = get_string_from_usr(name);while(sem->next) {if(sem_strcmp(sem->next->name, pname) == 0) {//found ittmp = sem->next;sem->next = tmp->next;//delete itfree(tmp->name);free(tmp);free(pname);return 0;} sem = sem->next;}free(pname);return -1;
}
//0 succuess -1 failed
int sys_sem_wait(sem_t *sem) {cli();printk("sem %s wait now\n", sem->name);--sem->value;if(sem->value < 0) {//sem->wait_task = current;sleep_on(&sem->wait_task);}sti();return 0;
}
//0 succuess -1 failed
int sys_sem_post(sem_t* sem) {cli();printk("sem %s post now\n", sem->name);++sem->value;if(sem->value > 0) {// is that should be changed ?// manual said this should used the while loop for the reason that the wake_up task can't runningwake_up(&sem->wait_task);}sti();return -1;
}

ae43c

e80b7

test.c

#define __LIBRARY__
#include <unistd.h>
#include <linux/semaphore.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value)
_syscall1(int, sem_wait, sem_t*, sem)
_syscall1(int, sem_post, sem_t*, sem)
_syscall1(int, sem_unlink, const char*, name)int main (int argc, char* argv[]) {sem_t *p = sem_open("mutex", 0);sem_t *s = sem_open("mutex", 0);return 0;
}

testsem.c v2

#define __LIBRARY__
#include <unistd.h>
#include <linux/semaphore.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value)
_syscall1(int, sem_wait, sem_t*, sem)
_syscall1(int, sem_post, sem_t*, sem)
_syscall1(int, sem_unlink, const char*, name)int main (int argc, char* argv[]) {sem_t *p = sem_open("mutex", 0);sem_t *s = sem_open("something", 0);sem_t *mutex = sem_open("mutex", 0);if(!fork()) {sem_wait(mutex);printf("print mutex\n");sem_post(s);return 0;}sem_post(mutex);sem_wait(s);printf("unlink p->%d\n", sem_unlink("mutex"));printf("unlink mutex->%d\n", sem_unlink("mutex"));printf("unlink s->%d\n", sem_unlink("something"));return 0;
}

sem.c v2

//implement of semaphore
//kernel/sem.h#include <linux/sem.h>#include <linux/kernel.h>
//for schedule sleep_on wake_up
#include <linux/sched.h>#include <asm/segment.h>#include <unistd.h>
/*
* a data structure for sem
* struct array
*/
sem_t *sem_head = NULL;
/*
sem_t sem_list[NR_SEM] = {{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},
};
unsigned int nr_sem = 0;
*/
int sem_strcmp(char *p, char *s) {for(;*p == *s; ++p, ++s) {if(*p == '\0')return 0;}return (*p - *s);
}sem_t* sys_sem_open(const char *name, unsigned int value) {//dummy headif(!sem_head) {sem_head = (sem_t*)malloc(sizeof(sem_t));sem_head->next = NULL;}unsigned int length = 0;char c;char *p = name, *pname, *first;sem_t *sem = sem_head;//calc the name's lengthwhile((c  = get_fs_byte(p)) != '\0') {++length;++p;}//reset pointerp = name;pname = (char*)malloc(length+1);first = pname;//get sem's namewhile((c = get_fs_byte(p)) != '\0') {*first = *p++;++first;}*first = '\0';//adjust if the semaphore existswhile(sem->next) {//if foundif(sem_strcmp(sem->next->name, pname) == 0) {return sem->next;}sem = sem->next;}//assign value to sem->nextsem->next = (sem_t*)malloc(sizeof(sem_t));sem = sem->next;sem->name = pname;sem->value = value;sem->wait_task = NULL;sem->lock = 1;printk("%s semaphore had created!\n", sem->name);return sem;
}char* get_string_from_usr(char *usr) {unsigned int length = 1;char c;char *kernel, *p = usr, *first;while((c = get_fs_byte(p))) {++length;++p;}first = kernel = (char*)malloc(length);p = usr;while((c = get_fs_byte(p))) {*first = c;++p;++first;}*first = '\0';return kernel;
}int sys_sem_unlink(const char *name) {//if have no nodeif(!sem_head || !sem_head->next)return -1;sem_t *sem = sem_head, *tmp;char *pname = get_string_from_usr(name);while(sem->next) {if(sem_strcmp(sem->next->name, pname) == 0) {//found ittmp = sem->next;sem->next = tmp->next;//delete itfree(tmp->name);free(tmp);free(pname);return 0;} sem = sem->next;}free(pname);return -1;
}
//这两个函数不对 得改
//0 succuess -1 failed
int sys_sem_wait(sem_t *sem) {cli();//printk("sem %s wait now\n", sem->name);--sem->value;if(sem->value < 0) {//sem->wait_task = current;while(sem->lock)sleep_on(&sem->wait_task);}sem->lock = 1;sti();return 0;
}
//0 succuess -1 failed
int sys_sem_post(sem_t* sem) {cli();//printk("sem %s post now\n", sem->name);++sem->value;if(sem->value > 0) {// is that should be changed ?// manual said this should used the while loop for the reason that the wake_up task can't runningsem->lock = 0;wake_up(&sem->wait_task);}sti();return 0;
}

sem.c v3

//implement of semaphore
//kernel/sem.h#include <linux/sem.h>#include <linux/kernel.h>
//for schedule sleep_on wake_up
#include <linux/sched.h>#include <asm/segment.h>#include <unistd.h>
/*
* a data structure for sem
* struct array
*/
sem_t *sem_head = NULL;
/*
sem_t sem_list[NR_SEM] = {{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},{NULL, 0, NULL},
};
unsigned int nr_sem = 0;
*/
int sem_strcmp(char *p, char *s) {for(;*p == *s; ++p, ++s) {if(*p == '\0')return 0;}return (*p - *s);
}sem_t* sys_sem_open(const char *name, unsigned int value) {//dummy headif(!sem_head) {sem_head = (sem_t*)malloc(sizeof(sem_t));sem_head->next = NULL;}unsigned int length = 0;char c;char *p = name, *pname, *first;sem_t *sem = sem_head;//calc the name's lengthwhile((c  = get_fs_byte(p)) != '\0') {++length;++p;}//reset pointerp = name;pname = (char*)malloc(length+1);first = pname;//get sem's namewhile((c = get_fs_byte(p)) != '\0') {*first = *p++;++first;}*first = '\0';//adjust if the semaphore existswhile(sem->next) {//if foundif(sem_strcmp(sem->next->name, pname) == 0) {return sem->next;}sem = sem->next;}//assign value to sem->nextsem->next = (sem_t*)malloc(sizeof(sem_t));sem = sem->next;sem->name = pname;sem->value = value;sem->wait_task = NULL;sem->lock = 1;printk("%s semaphore had created!\n", sem->name);return sem;
}char* get_string_from_usr(char *usr) {unsigned int length = 1;char c;char *kernel, *p = usr, *first;while((c = get_fs_byte(p))) {++length;++p;}first = kernel = (char*)malloc(length);p = usr;while((c = get_fs_byte(p))) {*first = c;++p;++first;}*first = '\0';return kernel;
}int sys_sem_unlink(const char *name) {//if have no nodeif(!sem_head || !sem_head->next)return -1;sem_t *sem = sem_head, *tmp;char *pname = get_string_from_usr(name);while(sem->next) {if(sem_strcmp(sem->next->name, pname) == 0) {//found ittmp = sem->next;sem->next = tmp->next;//delete itfree(tmp->name);free(tmp);free(pname);return 0;} sem = sem->next;}free(pname);return -1;
}//这两个函数不对 得改
//0 succuess -1 failed
int sys_sem_wait(sem_t *sem) {cli();--sem->value;if(sem->value < 0) {while(sem->lock)sleep_on(&sem->wait_task);}sem->lock = 1;sti();return 0;
}
//0 succuess -1 failed
int sys_sem_post(sem_t* sem) {cli();++sem->value;if(sem->value <= 0) {sem->lock = 0;wake_up(&sem->wait_task);}sti();return 0;
}

sem.c v3.5

/*为什么就对了啊 为什么按我的方式就不对 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊*/
int sys_sem_wait(sem_t *sem) {cli();while(sem->value <= 0) {sleep_on(&sem->wait_task);}sem->value--;sti();return 0;
}int sys_sem_post(sem_t *sem) {cli();sem->value++;if((sem->value) == 1) {wake_up(&sem->wait_task);}sti();return 0;
}

sem v4

//这个版本可以解决上述问题
//其实也可以看作是3.5版本的拓展版本
int sys_sem_wait(sem_t *sem) {cli();if(sem->value <= 0) {while(sem->lock)sleep_on(&sem->wait_task);}sem->lock = 1;--sem->value;sti();return 0;
}int sys_sem_post(sem_t *sem) {cli();++sem->value;if(sem->lock && sem->value > 0) {sem->lock = 0;wake_up(&sem->wait_task);}sti();return 0;
} 

还是回到这两个函数来

如果直接使用

--sem->value;
if(sem->value < 0) {sleep_on()
}++sem->value;
if(sem->value <= 0) {wake_up()
}

这样的代码其实是没有达到需求的。

因为这样代码能够起作用,默认的是sleep和wake只睡眠和唤醒一个进程。

但是对于linux来说,不是这样的。

他利用sleep_on和wake_up实现了一个链表,但是这个链表是隐式的,但并不妨碍它起作用。

当我们使用wake_up函数后,当前进程也就可以再被调度了。

但是在他继续执行后,其将会将这个链表的剩余结点的头结点唤醒。

最终导致整个链表唤醒。

这样就会导致,本来应该是一个进程通过的信号量,反而会出现多个信号量通过。

此时就不能再仅仅使用if去进行判断了。

这里需要使用while,不断的去让后面这些苏醒的进程再度睡眠(为什么linux不一开始就做单个进程的?)

一种思路就是直接利用value,在等待的时候,不再直接退出,而是看当时value的值,如果value的值大于0了。

也就是说有资源已经被放置,可以让一个进程去拿,那么这个进程去拿这个走,然后把标记这个资源已经被拿走了。

否则则让这些进程继续等待。

那么对于post,我们每次放完资源后,就可以进行查看,只要资源总数超过了0,就可以通知进程来取数据了。

因此,现在的代码变为

int sys_sem_wait(sem_t *sem)  {cli();while(sem->value <= 0) {sleep_on(&sem->wait_task);}sem->value--;sti();return 0;
}int sys_sem_post(sem_t *sem) {cli();++sem->value;if(sem->value >= 1) {wake_up(&sem->wait_task);}return 0;
}

现在的value表示的是有多少资源可以被使用。

与通常的代码相比,差距主要体现在了P操作的value–的地方和V操作的判断条件。

value–放在后面的原因正是因为有多个进程可能被唤醒,为了去阻塞后面的那么多进程,因此就使用了value去不断进行判断。

如果我们像通常的所用的那种方式,先减再循环判断,就会出现死循环的情况。

为什么呢?

因为我们的循环条件是 value <= 0

按照之前的方式,value将会被用于表示有多少个进程在等待着资源,但是假如生产者生产能力有限,每次只能生产1个,那么当等待的进程多余1个的时候,p语句将永远无法达到1个以上。也就不会再去唤醒任何进程。

因此上面的进程就无限休眠了。

当我们将value–移动到后面之后,value就不再表示有多少个在等待,其只会为0,1。

也就不会再出现上述问题。

把这个理解清楚了,这个问题就很好理解了。

至于之前的使用lock,但还是不对,关键在于P操作。

当P操作每次会修改lock的值,即便没有进程在等待。

这对于wakeup函数来说,无所谓。

因为,没有就返回嘛。但对于执行锁命令的lock则是灭顶之灾。

因为会莫名的解锁,不再承担功能。

所以还是会出现问题。

lab5 address

error while loading shared libraries: libstdc++.so.6:wrong ELF class:ELFCLASS64
32位系统想调入64位的库?
没错
就是这个意思
下载兼容库就没有问题了
又遇到了很奇怪的东西
就是当我使用某个版本的bxrc.src的时候
可以运行dgb-asm 但是无法运行./run当我使用另外一个版本的试试
可以正常run 但是不能运行dgb-asm
呃呃呃
man shmget
man 
man#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg)returns the identifier of the System V shared memory segment associated with the value of the argument key.shmflg specifies both IPC_CREAT and IPC_EXCL and a shared memory segment already exists for key.//key != IPC_PRIVATE
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);shmat attaches the System V shared memory segment identified by shmid to address space of the calling process.The attaching address is specified shmaddr with one of the following criteriaint shmdt(const void *shmaddr)detaches the shared memory segment located at the address specified by shmaddr from the address space of the calling process. The to-be-detached segment must be currently attached with shmaddr equal to the value returned by the attaching shmat() call.

shmid_ds

int shmget(key_t key, size_t size, int shmflg)

最后的这个shmflg是对创建的共享内存的要求

SHM_NORESERVE 不要为当前段使用swap space

也就是直接写穿 没有cache

when swap space is reserved, one has the guarantee that it is possible to modify the segment. 
when swap space is not reserved one might get SIGSEGV upon a write if no physical memory is available.

using IPC_NEW

key 可以是非负整数,或者是IPC_PRIVATE

如果是非负整数,shmflg需要IPC_CREAT。

如果是IPC_PRIVATE,shmflg不需要IPC_CREAT

size 是共享内存的大小(字节)

shmflg是权限标志。与文件的权限一样

函数执行成功 返回共享内存标识符

否则返回-1

int shmat(int shmid, const void *shmaddr, int shmflg)

shmid 上面哪个函数shmget返回的共享内存标识符。

shmaddr :通常为0

flag 通常为0

函数执行成功,返回映射到进程中的地址,否则返回-1.

if shmaddr is NULL, the system chooses a suitable(unused) address at which to attach the segment.
if shmaddr isn't NULL and SHM_RND is specified in shmflg, the attach occurs at the address equal to shmaddr rounded down to the nearest multiple of SHMLBA.Otherwise shmaddr must be a page-aligned address at which the attach occurs.NULL就让系统去选择放入的地址
不是NULL就自行控制选择的地址
加了SHM_RND的就对当前地址向下取整到SHMLBA最接近的整数倍
否则这个给定的地址必须是页的整数倍

int shmdt(const void *shmaddr)

一个进程不再需要共享内存时,需要把它从进程地址空间删除掉

shmaddr是从shmat中获得的

shm_com.h

#ifndef __SHM_COM__
#define __SHM_COM__#define TEXT_SZ 2048struct shared_use_st {int written_by_you;char some_text[TEXT_SZ];
};#endif

shmread.c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm_com.h"int main (void) {int running = 1;void *shared_memory = NULL;struct shared_use_st *shared_stuff;int shmid = shmget ((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);if (shmid == -1) {fprintf (stderr, "shmget failed\n");exit (EXIT_FAILURE);}shared_memory = shmat ( shmid, NULL, 0 ); /* 映射共享内存 */if ( shared_memory == ( void * ) -1 ) {fprintf ( stderr, "shmat failed\n" );exit ( EXIT_FAILURE );}printf ( "Memory attached at %p\n", shared_memory );/* 让结构体指针指向这块共享内存 */shared_stuff = ( struct shared_use_st * ) shared_memory;shared_stuff->written_by_you = 0; /* 控制读写顺序 */while ( running ) { /* 循环的从共享内存中读数据,直到读到“end” */if ( shared_stuff->written_by_you ) {printf ( "You wrote: %s", shared_stuff->some_text );sleep ( 1 ); /* 读进程睡一秒,同时会导致写进程睡一秒,这样做到读了之后再写 */shared_stuff->written_by_you = 0;if ( strncmp ( shared_stuff->some_text, "end", 3 ) == 0 ) {running = 0; /* 结束循环 */}}}if ( shmdt ( shared_memory ) == -1 ) { /* 删除共享内存 */fprintf ( stderr, "shmdt failed\n" );exit ( EXIT_FAILURE );}exit ( EXIT_SUCCESS );
}

shmwrite.c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm_com.h"int main ( void ) {int running = 1;void *shared_memory = NULL;struct shared_use_st *shared_stuff;char buffer[BUFSIZ];int shmid = shmget ( ( key_t ) 1234, sizeof ( struct shared_use_st ), 0666 | IPC_CREAT );if ( shmid == -1 ) {fprintf ( stderr, "shmget failed\n" );exit ( EXIT_FAILURE );}cif ( shared_memory == ( void * ) -1 ) {fprintf ( stderr, "shmat failed\n" );exit ( EXIT_FAILURE );}printf ( "Memory attached at %p\n", shared_memory );/* 让结构体指针指向这块共享内存 */shared_stuff = ( struct shared_use_st * ) shared_memory;while ( running ) { /* 循环地向共享内存中写数据 */while ( shared_stuff->written_by_you == 1 ) {sleep ( 1 ); /* 等到读进程读完之后再写 */printf ( "waiting for client...\n" );}printf ( "Enter some text: " );fgets ( buffer, BUFSIZ, stdin );strncpy ( shared_stuff->some_text, buffer, TEXT_SZ );shared_stuff->written_by_you = 1;if ( strncmp ( buffer, "end", 3 ) == 0 ) {running = 0; /* 结束循环 */}}if ( shmdt ( shared_memory ) == -1 ) { /* 删除共享内存 */fprintf ( stderr, "shmdt failed\n" );exit ( EXIT_FAILURE );}exit ( EXIT_SUCCESS );
}
	/*创建或使用一个共享内存*/int shmid = shmget ( ( key_t ) 1234, sizeof ( struct shared_use_st ), 0666 | IPC_CREAT );void *shared_memory = shmat ( shmid, NULL, 0 ); /* 映射共享内存到本进程的地址区域内 返回的是一个地址*/shared_stuff = ( struct shared_use_st * ) shared_memory; /* 使用共享内存 */if ( shmdt ( shared_memory ) == -1 ) { /* 从本进程的地址区域内删除共享内存 */fprintf ( stderr, "shmdt failed\n" );exit ( EXIT_FAILURE );}

mmpc.c v1 -pthread

/* ************************************************************************
> File Name:     mmpc.c
> Author:        rebelOverWaist
> Description:   sem for producer, consumer question using shared memory for buf************************************************************************/
#define __LIBRARY__
#include <unistd.h>
#include <semaphore.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>// for share memory
#include <sys/ipc.h>
#include <sys/shm.h>/*
_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value);
_syscall1(int, sem_wait, sem_t*, sem);
_syscall1(int, sem_post, sem_t*, sem);
_syscall1(int, sem_unlink, const char*, name);_syscall2(int, shmget, key_t, key, size_t, size);
_syscall2(int, shmat, int, shmid, const void*, shmaddr);
_syscall1(int, shmdt, const void*, shmaddr);*/#define NR_TASKS 5 /*how many tasks created*/
#define NR_ITEMS 20 /*how many items produced*/
#define BUFFER_SIZE 5 /*size of file buffer*/
// semaphores'name 
#define FULL "full"
#define MUTEX "mutex"
#define EMPTY "empty"// used for kill consumer process
int kill = 0;//Produce item put them into buffer 
// file WRONLY 
void producer(int shmid, sem_t *full, sem_t *empty, sem_t *mutex) {unsigned int i;int pid = getpid();int pfull, pempty, pmutex;//mapping shmid to current process and get the pointervoid *shared_memory = shmat(shmid, NULL, 0);unsigned int *shared = (unsigned int*)shared_memory;/*get this value*/sem_getvalue(full,&pfull);sem_getvalue(empty,&pempty);sem_getvalue(mutex,&pmutex);    printf("full \tvalue=%d\n",pfull);printf("empty \tvalue=%d\n",pempty);printf("mutex \tvalue=%d\n",pmutex);   fflush(stdout);for(i = 0; i < NR_ITEMS; ++i) {sem_wait(empty);sem_wait(mutex);// if full then put from headshared[i%BUFFER_SIZE] = i; printf("pid->%d\tproduces item->%d\n", pid,i);fflush(stdout);sem_post(mutex);sem_post(full);}   // producer have produced all items then kill all consumerkill = 1;if(shmdt(shared_memory) == -1) {printf("%d producer shmdt failed\n", pid);}
}//consume item from buffer
//file RDONLY
void consumer(int shmid, sem_t *full, sem_t *empty, sem_t *mutex) {int pid = getpid();unsigned int i;printf("consumer%d have been created\n", pid);//mapping shmid to current process void* shared_memory = shmat(shmid, NULL, 0);unsigned int *shared = (unsigned int*)shared_memory;while(!kill) {sem_wait(full);sem_wait(mutex);i = shared[shared[BUFFER_SIZE]]; shared[BUFFER_SIZE] = (shared[BUFFER_SIZE]+1)%BUFFER_SIZE;printf("consumer\t%d consume %d\n", pid, i);sem_post(mutex);sem_post(empty);}printf("pid %d now dead\n", pid);fflush(stdout);if(shmdt(shared_memory) == -1) {printf("%d consumer shmdt failed\n", pid);}
}int main (int argc, char* argv[]) {int i;sem_t *full, *empty, *mutex;int pfull, pempty, pmutex;//get a shmget from int shmid = shmget((key_t)1234,(BUFFER_SIZE+1) * sizeof(unsigned int), 0666 | IPC_CREAT);
/*full = sem_open(FULL, 0);empty = sem_open(EMPTY, BUFFER_SIZE);mutex = sem_open(MUTEX, 1);*/full=sem_open(FULL,O_CREAT,0666,0);empty=sem_open(EMPTY,O_CREAT,0666,BUFFER_SIZE);mutex = sem_open(MUTEX, O_CREAT,0666,1);if(full==(void*)-1 || empty==(void*)-1|| mutex==(void*)-1){perror("sem_open failure");}// get this valuesem_getvalue(full,&pfull);sem_getvalue(empty,&pempty);sem_getvalue(mutex,&pmutex);    /* printf("full \tvalue=%d\n",pfull);printf("empty \tvalue=%d\n",pempty);printf("mutex \tvalue=%d\n",pmutex);  *///create producer processif(!fork()) {producer(shmid, full, empty, mutex);goto END;}//create consumer processfor(i = 0; i < NR_TASKS; ++i) {if(!fork()) {consumer(shmid, full, empty, mutex);goto END;}}//wait all subprocess exitwait(&i);
END://release resourecessem_close(full);sem_close(empty);sem_close(mutex);sem_unlink(FULL);sem_unlink(EMPTY);sem_unlink(MUTEX);////printf("now say goodbye\n");return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uqIjc86i-1669713466972)(D:\Typora\TyporaProject\Resources\mmpc.png)]

include/unistd.h

#define __NR_shmget 78
#define __NR_shmat 79
#define __NR_shmdt 80

kernel/system_call.s

nr_system_calls = 81

include/linux/shm.h

int shmget(key_t key, size_t size)

int shmat(int shmid, const void *shmaddr)

int shmdt(const void *shmaddr)

#ifndef __SHM__
#define __SHM__typedef unsigned int key_t;
//typedef unsigned int size_t;#define SHM_SIZE 1024typedef struct shm{//key to shmkey_t key;//size of shared memorysize_t size;//address of physical memoryunsigned long page;//address of current process void *addr;
}shm;int sys_shmget(key_t key, size_t size);
void *sys_shmat(int shmid, void *shmaddr);
int sys_shmdt(void *shmaddr);#endif

mmpc_hdc.c

#define __LIBRARY__
#include <unistd.h>
#include <linux/sem.h>
#include <stdio.h>#include <linux/sched.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>#include <sys/shm.h>_syscall2(sem_t*, sem_open, const char*, name, unsigned int, value);
_syscall1(int, sem_wait, sem_t*, sem);
_syscall1(int, sem_post, sem_t*, sem);
_syscall1(int, sem_unlink, const char*, name);_syscall2(int, shmget, key_t, key, size_t, size);
_syscall2(int, shmat, int, shmid, void*, shmaddr);
_syscall1(int, shmdt, void*, shmaddr);#define NR_TASKS 5
#define NR_ITEMS 20
#define BUFFER_SIZE 5#define FULL "full"
#define MUTEX "mutex"
#define EMPTY "empty"int kill = 0;void producer(int shmid, sem_t *full, sem_t *empty, sem_t *mutex) {unsigned int i;int pid = getpid();void *shared_memory = shmat(shmid, NULL);unsigned int *shared = (unsigned int*)shared_memory;for(i = 0; i < NR_ITEMS; ++i) {sem_wait(empty);sem_wait(mutex);shared[i%BUFFER_SIZE] = i; printf("pid->%d\tproduces item->%d\n", pid,i);fflush(stdout);sem_post(mutex);sem_post(full);}   kill = 1;if(shmdt(shared_memory) == -1) {printf("%d producer shmdt failed\n", pid);}
}void consumer(int shmid, sem_t *full, sem_t *empty, sem_t *mutex) {int pid = getpid();unsigned int i;printf("consumer%d have been created\n", pid);void* shared_memory = shmat(shmid, NULL);unsigned int *shared = (unsigned int*)shared_memory;while(!kill) {sem_wait(full);sem_wait(mutex);i = shared[shared[BUFFER_SIZE]]; shared[BUFFER_SIZE] = (shared[BUFFER_SIZE]+1)%BUFFER_SIZE;printf("consumer\t%d consume %d\n", pid, i);fflush(stdout);sem_post(mutex);sem_post(empty);}printf("pid %d now dead\n", pid);fflush(stdout);if(shmdt(shared_memory) == -1) {printf("%d consumer shmdt failed\n", pid);fflush(stdout);}
}int main (int argc, char* argv[]) {int i;sem_t *full, *empty, *mutex;int shmid = shmget((key_t)1234,(BUFFER_SIZE+1) * sizeof(unsigned int));full = sem_open(FULL, 0);empty = sem_open(EMPTY, BUFFER_SIZE);mutex = sem_open(MUTEX, 1);if(!fork()) {producer(shmid, full, empty, mutex);goto END;}for(i = 0; i < NR_TASKS; ++i) {if(!fork()) {consumer(shmid, full, empty, mutex);goto END;}}wait(&i);
END:sem_unlink(FULL);sem_unlink(EMPTY);sem_unlink(MUTEX);return 0;
}

kernel/shm.c

#include <linux/kernel.h>
#include <linux/sched.h>
#include <sys/shm.h>
#include <linux/mm.h>
#include <errno.h>//给定key 和size 获得一个物理page 返回这个页面的标识符号
int sys_shmget(key_t key, size_t size) {int i;unsigned long page;}
//给定一个页面的标识符号 如果shmaddr为NULL 就在当前进程选择合适的位置 插入这个页面 否则就将这个页面放到shmaddr指定的位置上
void *sys_shmat(int shmid, void *shmaddr) {}//给定当前进程指定的页面 把它给删掉
int sys_shmdt(void *shmaddr) {}
#define __LIBRARY__
#include <unistd.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <errno.h>static shm_ds shm_list[SHM_SIZE] = {{0, 0, 0, 0}};int sys_shmget(unsigned int key, size_t size)
{int i;unsigned long page;/* If the size exceeds the size of one page of memory */if (size > PAGE_SIZE){printk("shmget: size %u cannot be greater than the page size %ud. \n", size, PAGE_SIZE);return -ENOMEM;}if (key == 0){printk("shmget: key cannot be 0.\n");return -EINVAL;}for (i = 0; i < SHM_SIZE; i++){if (shm_list[i].key == key){add_mapping(page);return i;}}page = get_free_page();if (!page){return -ENOMEM;}printk("shmget get memory's address is 0x%08x\n", page);for (i = 0; i < SHM_SIZE; i++){if (shm_list[i].key == 0){shm_list[i].key = key;shm_list[i].size = size;shm_list[i].page = page;return i;}}return -1;
}void *sys_shmat(int shmid)
{unsigned long data_base, brk;if (shmid < 0 || SHM_SIZE <= shmid || shm_list[shmid].page == 0 || shm_list[shmid].key <= 0){return (void *)-EINVAL;}data_base = get_base(current->ldt[2]);//拿到dsprintk("current's data_base = 0x%08x,new page = 0x%08x\n", data_base, shm_list[shmid].page);brk = current->brk + data_base;//计算动态分配的地方current->brk += PAGE_SIZE;//直接把这一页分配给他if (put_page(shm_list[shmid].page, brk) == 0)//修改表{return (void *)-ENOMEM;}return (void *)(current->brk - PAGE_SIZE);//返回首地址
}

当前的程序会发生释放空页的异常

这是因为我们并没有递增当前页的使用情况。

导致最后出现了释放空页的情况。

linux shmid_ds linux2.6

struct shmid_ds {struct ipc_perm  shm_perm; /* operation perms */int   shm_segsz; /* size of segment (bytes) */__kernel_time_t  shm_atime; /* last attach time */__kernel_time_t  shm_dtime; /* last detach time */__kernel_time_t  shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short  shm_nattch; /* no. of current attaches */unsigned short   shm_unused; /* compatibility */void    *shm_unused2; /* ditto - used by DIPC */void   *shm_unused3; /* unused */
};struct shmid_kernel
{ struct shmid_ds  u;/* the following are private */unsigned long  shm_npages; /* size of segment (pages) */pte_t   *shm_pages; /* array of ptrs to frames -> SHMMAX */ struct vm_area_struct *attaches; /* descriptors for attaches */
};
/*shm_npages 字段表示共享内存使用了多少个内存页。*/
/*shm_pages 字段指向了共享内存映射的虚拟内存页表项数组等。*/
/*另外 struct shmid_ds 结构体用于管理共享内存的信息,而 shm_segs数组 用于管理系统中所有的共享内存。*/
static struct shmid_kernel *shm_segs[SHMMNI]; // SHMMNI等于128
asmlinkage long sys_shmget (key_t key, int size, int shmflg)
{struct shmid_kernel *shp;int err, id = 0;down(&current->mm->mmap_sem);spin_lock(&shm_lock);if (size < 0 || size > shmmax) {//size的判断err = -EINVAL;} else if (key == IPC_PRIVATE) {//IPC_PRIVATE的优先级最高 直接新建一个err = newseg(key, shmflg, size);} else if ((id = findkey (key)) == -1) {//如果没找到 if (!(shmflg & IPC_CREAT))//如果不让新建 那么就没法创建err = -ENOENT;else//否则创建一个err = newseg(key, shmflg, size);} else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {//如果又要创建 又要exlc 直接返回不存在err = -EEXIST;} else {//找打了已有的页shp = shm_segs[id];//让shp指向这个页if (shp->u.shm_perm.mode & SHM_DEST)err = -EIDRM;else if (size > shp->u.shm_segsz)err = -EINVAL;else if (ipcperms (&shp->u.shm_perm, shmflg))err = -EACCES;elseerr = (int) shp->u.shm_perm.seq * SHMMNI + id;}spin_unlock(&shm_lock);up(&current->mm->mmap_sem);return err;
}
asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
{struct shmid_kernel *shp;struct vm_area_struct *shmd;int err = -EINVAL;unsigned int id;unsigned long addr;unsigned long len;down(&current->mm->mmap_sem);spin_lock(&shm_lock);if (shmid < 0)//边界处理goto out;shp = shm_segs[id = (unsigned int) shmid % SHMMNI];//找到管理当前shmid的shmid_kernelif (shp == IPC_UNUSED || shp == IPC_NOID)goto out;
if (!(addr = (ulong) shmaddr)) {//如果是NULL则自动选择一个页面if (shmflg & SHM_REMAP)goto out;err = -ENOMEM;addr = 0;again:if (!(addr = get_unmapped_area(addr, shp->u.shm_segsz))) // 获取一个空闲的虚拟内存空间 如果获取不到退出goto out;if(addr & (SHMLBA - 1)) {//不太懂 如果不能分配吗?addr = (addr + (SHMLBA - 1)) & ~(SHMLBA - 1);goto again;}} else if (addr & (SHMLBA-1)) {//用户已制定if (shmflg & SHM_RND)addr &= ~(SHMLBA-1);       /* round down */elsegoto out;}
spin_unlock(&shm_lock);
err = -ENOMEM;
shmd = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);//不知道这个函数是什么
spin_lock(&shm_lock);
if (!shmd)goto out;
if ((shp != shm_segs[id]) || (shp->u.shm_perm.seq != (unsigned int) shmid / SHMMNI)) {kmem_cache_free(vm_area_cachep, shmd);err = -EIDRM;goto out;}
 shmd->vm_private_data = shm_segs + id;shmd->vm_start = addr;shmd->vm_end = addr + shp->shm_npages * PAGE_SIZE;shmd->vm_mm = current->mm;shmd->vm_page_prot = (shmflg & SHM_RDONLY) ? PAGE_READONLY : PAGE_SHARED;shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED| VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC| ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);shmd->vm_file = NULL;shmd->vm_offset = 0;shmd->vm_ops = &shm_vm_ops;shp->u.shm_nattch++;     /* prevent destruction */spin_unlock(&shm_lock);err = shm_map(shmd);spin_lock(&shm_lock);if (err)goto failed_shm_map;//插入新的进程insert_attach(shp,shmd);  /* insert shmd into shp->attaches */shp->u.shm_lpid = current->pid;shp->u.shm_atime = CURRENT_TIME;*raddr = addr;err = 0;
out:spin_unlock(&shm_lock);up(&current->mm->mmap_sem);return err;...
}//这个函数其实只完成寻找地址的过程 没有完成最后到物理页面的映射过程
static struct vm_operations_struct shm_vm_ops = {shm_open,  /* open - callback for a new vm-area open */shm_close,  /* close - callback for when the vm-area is released */NULL,   /* no need to sync pages at unmap */NULL,   /* protect */NULL,   /* sync */NULL,   /* advise */shm_nopage,  /* nopage */NULL,   /* wppage */shm_swapout  /* swapout */
};
//这个函数才是真正用来建立虚拟页和实际的物理页联系的函数 啧啧啧
static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share)
{pte_t pte;struct shmid_kernel *shp;unsigned int idx;struct page * page;shp = *(struct shmid_kernel **) shmd->vm_private_data;idx = (address - shmd->vm_start + shmd->vm_offset) >> PAGE_SHIFT;spin_lock(&shm_lock);
again:pte = shp->shm_pages[idx]; // 共享内存的页表项if (!pte_present(pte)) {   // 如果内存页不存在if (pte_none(pte)) {spin_unlock(&shm_lock);page = get_free_highpage(GFP_HIGHUSER); // 申请一个新的物理内存页if (!page)//申请不到了goto oom;clear_highpage(page);//初始化页spin_lock(&shm_lock);//继续锁if (pte_val(pte) != pte_val(shp->shm_pages[idx]))goto changed;} else {...}shm_rss++;pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));   // 创建页表项shp->shm_pages[idx] = pte;                      // 保存共享内存的页表项} else--current->maj_flt;  /* was incremented in do_no_page */done:get_page(pte_page(pte));spin_unlock(&shm_lock);//解锁current->min_flt++;return pte_page(pte);...
}

unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags)

unsigned long
get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags)
在当前进程的用户空间中获得一个未映射的起始地址.
其在do_mmap中有使用这个函数。
其源码分析如下:
unsigned long
get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags)
{unsigned long (*get_area)(struct file *, unsigned long,unsigned long, unsigned long, unsigned long);#针对特定平台的检查,目前arm64中arch_mmap_check 是一个空函数unsigned long error = arch_mmap_check(addr, len, flags);if (error)return error;#申请虚拟空间的地址不能超过最大值。这里可以知道虚拟空间size 的最大值就是TASK_SIZE/* Careful about overflows.. */if (len > TASK_SIZE)return -ENOMEM;#指向当前进程的unmap 空间的分配函数get_area = current->mm->get_unmapped_area;//?#file 不为空的话,则unmap 空间的分配函数执行file中指定的函数if (file) {if (file->f_op->get_unmapped_area)get_area = file->f_op->get_unmapped_area;} else if (flags & MAP_SHARED) {#如果file为空,说明可能申请的是匿名空间,这里检查如果是共享内存的话,则分配函数执行共享内存的分配函数/** mmap_region() will call shmem_zero_setup() to create a file,* so use shmem's get_unmapped_area in case it can be huge.* do_mmap_pgoff() will clear pgoff, so match alignment.*/pgoff = 0;get_area = shmem_get_unmapped_area;}#使用前面已经指定的分配函数来在未映射的虚拟空间中映射的空间中申请.addr = get_area(file, addr, len, pgoff, flags);if (IS_ERR_VALUE(addr))return addr;#addr +len 不能大于TASK_SIZEif (addr > TASK_SIZE - len)return -ENOMEM;#检查分配到的地址是否已经被映射,如果已经被映射则返回error,毕竟我们这里要分配的是进程未映射的空间。if (offset_in_page(addr))return -EINVAL;#secure检查error = security_mmap_addr(addr);return error ? error : addr;
}

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

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

相关文章

黎曼的几何基础,维度

黎曼的几何基础&#xff0c;让数学领先物理100年&#xff0c;维度是人类最大的障碍 - 知乎 高斯很早就有了“高维几何”的想法&#xff0c;他曾经向他的同事们说起假想完全生活在二维表面上的“书虫”&#xff0c;并想要把这推广到高维空间的几何学中去。然而&#xff0c;由于害…

进程与信号(三)

目录 一、前言 二、Signals 1、Signal Handling&#xff08;ctrlc.c&#xff09; 2、Sending Signals &#xff08;1&#xff09;alarm.c &#xff08;2&#xff09;A Robust Signals Interface &#xff08;3&#xff09;ctrlc2.c 3、Signal Sets &#xff08;1&…

ConstraintLayout布局扩展

相信大家对ConstraintLayout&#xff08;约束布局&#xff09;不陌生,这是google推出的一个强大控件&#xff0c;之所以强大其实主要归纳有两点&#xff1a;减少布局层次&#xff0c;能够轻松实现复杂布局。当然在我们实际使用过程中&#xff0c;是否真的减少了布局层次&#x…

FPGA实现图像对比度自动调整,提供2套工程源码和技术支持

目录1.算法原理介绍2.HLS算法实现3.工程1介绍&#xff1a;zynq7100实现4.工程2介绍&#xff1a;Kintex7实现5.上板调试验证6.福利&#xff1a;工程源码获取1.算法原理介绍 使用直方图均衡技术实现 使用直方图均衡技术将原始图像的灰度直方图从比较集中的某个灰度区间变成在全部…

ASCHIP_ISP Tool 工具 使用与更新

系列文章目录 ASCHIP-ISP Tool 版本1 2022 11 26ASCHIP-ISP Tool 版本1 使用说明 2022 11 26 软件介绍 对窗体进行初步配置&#xff0c;配置其大小与显示 一&#xff1a;软件介绍 ASCHIP_ISP Tool 工具是用于具有 ISP 功能型号类型的单片机进行 ISP 更新程序开发实验的配套上位…

[附源码]Python计算机毕业设计SSM隆庆祥企业服装销售管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【网络编程】第二章 网络套接字(socket+UDP协议程序)

&#x1f3c6;个人主页&#xff1a;企鹅不叫的博客 ​ &#x1f308;专栏 C语言初阶和进阶C项目Leetcode刷题初阶数据结构与算法C初阶和进阶《深入理解计算机操作系统》《高质量C/C编程》Linux ⭐️ 博主码云gitee链接&#xff1a;代码仓库地址 ⚡若有帮助可以【关注点赞收藏】…

Java-泛型实验

1.定义一个学生类Student&#xff0c;具有年龄age和姓名name两个属性&#xff0c;并通过实现Comparable接口提供比较规则&#xff08;返回两个学生的年龄差&#xff09;&#xff0c; 定义测试类Test&#xff0c;在测试类中定义测试方法Comparable getMax(Comparable c1, Compar…

Docker-JenKins安装及配置!

Jenkins官网&#xff1a;Jenkins 安装主机配置&#xff08;官方&#xff09;&#xff1a; 最低&#xff1a; 256 MB 内存 1 GB 的驱动器空间&#xff08;尽管如果将 Jenkins 作为 Docker 容器运行&#xff0c;则建议至少 10 GB&#xff09; 小团队推荐&#xff1a; 4 GB …

英文ppt怎么翻译成中文?教你几种ppt翻译方法

ppt文件受到很多人的喜欢是因为它展示起来直观清晰&#xff0c;无论是老师在课堂上使用ppt课件来教学&#xff0c;还是在工作汇报中用ppt文件展示设计的方案或取得的成果。但当我们需要把ppt文档里的文本内容里的外语翻译成中文的时候&#xff0c;我们应该怎么做呢&#xff1f;…

003. 电话号码的字母组合——回溯算法

1.题目链接&#xff1a; 17. 电话号码的字母组合 2.解题思路&#xff1a; 2.1.题目要求&#xff1a; 给定一个仅包含数字 2-9 的字符串 digits &#xff0c;返回所有它能表示的字母组合。 数字和字母的关系&#xff1a; 例子&#xff1a; 输入&#xff1a;"23" …

[Spring]第二篇:IOC控制反转

简单的说就是,创建对象的权利,或者是控制的位置,由JAVA代码转移到spring容器,由spring的容器控制对象的创建,就是控制反转. spring创建对象时,会读取配置文件,配置文件中主要配置接口和实现类的关系,每个接口对相应一个实现类,使用<bean>标签配置,<bean中的id可以随便…

学生选课系统

项目描述 通过项目背景的分析以及了解到现在学校面临的问题&#xff0c;特别需要一个选课管理系统保证学生信息以及各种课程成绩的准确性和实效性&#xff0c;通过利用计算机的高速计算和快速的统计分析&#xff0c;保证学生信息的最新记录。从教职工的角度老考虑&#xff0c;…

用VS软件开发“中国象棋“游戏<笔记摘录>

整体架构如上 1.很直观地去看这个中国象棋的界面,数一下它有多少行和多少列. 10行,9列:要注意这里数的是安放象棋的位置,有10行9列 这里我们首先想到的必然是二维数组,每一个行列交叉的点都设置成二维数组a[i][j]这样的格式,以此来确定棋盘上面每一个棋子的位置和走向. 我们…

详解 Spring Boot 项目中的配置文件

目录 1. Spring Boot 项目中配日文件的作用是什么 2. Spring Boot 配置文件的两种格式 3. properties 配置文件 3.1 properties 配置文件的基本语法 3.2 properties 配置文件的分类 3.3 如何读取配置文件 3.4 properties 配置文件的优缺点分析 4. yml 配置文件 4.1 yml …

BP神经网络PID从Simulink仿真到PLC控制实现(含博途PLC完整SCL源代码)

单神经元自适应PID控制博途PLC完整源代码,请参看下面的文章链接: 博途PLC单神经元自适应PID控制_RXXW_Dor的博客-CSDN博客_单神经元pid控制1、单神经元作为构成神经网络的基本单位,具有自学习和自适应能力,且结构简单易于计算,传统的PID具有结构简单、调整方便和参数整定…

【软件测试】8年资深测试说出来我们的心声......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 执着于手动的功能测…

SSM毕设项目 - 基于SSM的毕业设计管理系统(含源码+论文)

文章目录1 项目简介2 实现效果2.1 界面展示3 设计方案3.1 概述3.2 系统流程3.2.1 系统开发流程3.3.2 教师登录流程3.3.3 系统操作流程3.3 系统结构设计4 项目获取1 项目简介 Hi&#xff0c;各位同学好呀&#xff0c;这里是M学姐&#xff01; 今天向大家分享一个今年(2022)最新…

【Android App】实战项目之仿抖音的短视频分享App(附源码和演示视频 超详细必看)

需要全部代码请点赞关注收藏后评论区留言私信~~~ 与传统的影视行业相比&#xff0c;诞生于移动互联网时代的短视频是个全新行业&#xff0c;它制作方便又容易传播&#xff0c;一出现就成为大街小巷的时髦潮流。 各行各业的人们均可通过短视频展示自己&#xff0c;短小精悍的视频…

社区系统项目复盘-6

文章目录什么是Elasticsearch&#xff1f;Spring是怎么整合Elasticsearch的&#xff1f;开发社区搜索功能Elasticsearch实现全文搜索功能什么是Elasticsearch&#xff1f; Elasticsearch简介 一个分布式的、Restful风格的搜索引擎支持对各种类型的数据的检索搜索速度快&#xf…