本系列记录WHU《操作系统原理及安全》课程的一些资料,同时记录一些学习心得
这篇文章是第二章的作业,已经经过了GPT验证
(这章的作业写的有点赶……有些都是事后复现才弄好的……)
第一题
M个处理器,N个进程,处于运行态的进程个数可以是多少?处于就绪态和等待态的进程可以是多少?为什么?

第二题

① 编程补充完成课堂练习三个print问题
② 对于练习中的三行打印,如果把打印改成“%d,%d,%d\n”,getpid(),fork(),getpid()函数,结果会怎样?为什么?


第三题


这里目标是让execvp不能执行就行了。
具体原理见:https://www.bilibili.com/video/BV19C6CBVEAh
第四题


(这里最开始写错了,依然写清楚进程创建关系就行)
第五题


直接上代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n = atoi(argv[1]);
const char *name = "/shared_mem";
int fd = shm_open(name, O_CREAT | O_RDWR, 0666);
ftruncate(fd, 1024);
char *ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
pid_t pid = fork();
if (pid == 0)
{
int len = 0;
len += sprintf(ptr + len, "%d", n);
while (n != 1)
{
if (n % 2 == 0)
{
n = n / 2;
}
else
{
n = 3 * n + 1;
}
len += sprintf(ptr + len, ", %d", n);
}
return 0;
}
else
{
wait(NULL);
}
printf("Now data: %s\n", ptr);
munmap(ptr, 1024);
shm_unlink(name);
return 0;
}这块后面重新学习了一遍,这里详细记载一下:
创建与使用共享内存
引用自:https://blog.csdn.net/ababab12345/article/details/102931841
Linux中共享内存通常用这几个函数
int shm_open(const char *name, int oflag, mode_t mode);
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);
int shm_unlink(const char *name);
int ftruncate(int fd, off_t length);shm_open
首先使用shm_open函数创建一个共享内存文件(共享内存实际上就是这个文件在内存中的页缓存):
int fd = shm_open(name, O_CREAT | O_RDWR, 0666);参数作用如下:
const char *name :要打开或创建的共享内存文件名,创建的默认均在/dev/shm 内,不用携带完整路径。
int oflag :打开的文件操作属性:O_CREAT、O_RDWR、O_EXCL的按位或运算组合
具体含义与open函数相同,可供参考:
O_CREAT:在文件打开过程中创建新文件
O_RDONLY:以只读方式打开文件。
O_WRONLY:以只写方式打开文件。
O_RDWR:以读写方式打开文件。
O_APPEND:在文件末尾追加数据,而不是覆盖现有内容。
O_TRUNC:如果文件已经存在,将其截断 为空文件。
O_EXCL:与 O_CREAT 一起使用时,如果文件已经存在,则 open() 调用将失败。
O_SYNC:使文件写操作变为同步写入,即将数据立即写入磁盘。
O_NONBLOCK:以非阻塞方式打开文件,即使无法立即进行读写操作也不会被阻塞。ftruncate
重置文件大小。实际上,任何open打开的文件都可以用这个函数,不限于shm_open打开的文件。
mmap
然后使mmap 将打开的共享内存文件映射到内存:
char *ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);参数如下:
addr :要将文件映射到的内存地址,一般应该传递NULL来由Linux内核指定。
length :要映射的文件数据长度。
prot :映射的内存区域的操作权限(保护属性),包括PROT_READ(只读)、PROT_WRITE(只写)、PROT_READ|PROT_WRITE(读写)
flags :标志位参数,包括:MAP_SHARED、MAP_PRIVATE与MAP_ANONYMOUS。
MAP_SHARED: 建立共享,用于进程间通信,如果没有这个标志,则别的进程即使能打开文件,也看不到数据。
MAP_PRIVATE: 只有进程自己用的内存区域
MAP_ANONYMOUS: 匿名映射区(为附加属性,需要与或运算结合使用)
可另参考:https://chat.deepseek.com/share/39gv9rrfxjkslnagu6
此时创建的ptr就是共享内存。
munmap
解除内存映射,类似free()函数。
shm_unlink
删除共享内存映射文件。
总体而言,和传统C语言编程中的malloc和free的逻辑是十分类似的。
第六题

LINE C的输出是5。此时新的线程会对该进程中的value进行修改,同时因为执行了join所以也不用考虑条件竞争的问题。
LINE P的输出是0。因为这个进程没有创建新的进程,fork后栈空间也独立了,子进程不会影响父进程。
具体分析这里基本上又重新学习了一遍,这里是学习笔记:
参考:
Linux Manual:https://www.man7.org/linux/man-pages/man7/pthreads.7.html
Cppcheatsheet:https://cppcheatsheet.com/notes/os/os_thread.html
pthread_attr_init
这个函数用于初始化一个参数对象,可以用来规定线程分离状态、线程栈大小、调度策略和优先级等等,在示例里面这里面使用的就是默认设置。
参考:https://chat.deepseek.com/share/sw2ilbmrvikh4qopof
pthread_create
创建新线程的核心函数,函数原型如下:
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);其中thread用于接收新线程的tid,attr为参数,start_routine为执行的函数的函数指针,arg为传给函数的指针。
运行的函数只能有一个类型为void *参数,如果要传入多个参数需定义数组、结构体等。
pthread_join
等待创建线程完成的函数,类似进程里面的 wait,函数原型如下:
int pthread_join(pthread_t thread, void **retval);其中thread指定要等待哪个进程,retval用于接收返回值。
pthread_exit
同正常C语言编程中的 exit(0); 一样,相当于强制结束整个线程,比较显然,就不多做解释了。