前言

我希望找到这里的学弟学妹能基于我的内容完成的更好,这里的代码和思路应该是你们的下限!!

我分享这些笔记的初衷是因为我觉得csdn上与之相关的不少博客都收费,但是我当时做的时候,我觉得就那么一点东西有什么可收费的,咱们当程序员的最重要的是什么?是开源精神!所以我把自己的笔记都公开分享了,我觉得都是软院一家人,有些不必要的坑我可以帮你们踩,你们可以把时间花在加深对操作系统的理解过程上,而不是像我当时一样整天周转于各种博客看各种配置bug,一天下来看似解决了很多问题,实际这个实验真正需要我理解的地方我实际理解根本不够。这是我踩的最大的坑!所以我把自己很菜的笔记分享在这里,如果能对你们有启迪或者帮到你们我都会很开心!这是我的初衷!

如果你们有什么看不懂的问题,经过自己思考仍然无法解决我非常欢迎你们给我私信,但是我不希望现在打开私信几乎都是上来问候也没有就直接管我要代码要报告的!

如果这些笔记让你们觉得可以直接私聊我当伸手党我会很难受!并且我希望这种人别来找我,因为我觉得所有该写的都写了,我没有任何义务要帮那些想要敷衍老师的人完成作业!


吉林大学操作系统上机(实验二:处理机调度——实时调度算法EDF和RMS)
菜鸡博客,只是作为学习记录。。。操作系统要好好学!

1. 上机准备

我们老师建议体会在linux下的gcc编程,所以如果不用学校提供的上机电脑,在自己电脑上体会的话我们要搞一个linux环境。
这里的话有很多途径(比如下VMware虚拟机安装对应的linux版本iso文件,装虚拟机有硬件隔离,做一些安全性测试比较实用),我使用的是WSL(微软拿出的一款让鱼和熊掌兼得的方案 :Windows Subsystem for Linux,也就是 Windows 系统中自带 Linux 子系统)
对应的配置环境可以参考博客:
Windows 10开启Linux子系统

tips:

  • windows10系统中,WSL文件位置在哪儿?
    资源管理器中输入:\\wsl$
    对应的路径是:
    C:\Users\用户名\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs
    (根据自己的用户名和WSL系统,替换路径中高亮的部分)
    这样的话就可以利用windows的图形交互界面直接修改对应文件数据了。

  • 装好的linux初始状态没有gcc编译器,需要自己下。下的时候因为ubuntu的下载源地址在欧洲,最好换为国内的镜像源,网上教程很多,可以了解一下跟着做。

2. linux 下的一些重要操作

  • 显示当前目录中所有的文件:ls
  • 进入子目录:cd 目录名
  • 退到上级目录:cd ..
  • 建立新目录:mkdir 目录名
  • 如何挂载优盘: mount –t vfat /dev/sdb /mnt/usb (优盘就挂载在/mnt/usb下.)
  • 如何进入编辑界面: gedit 文件名(可以在这个程序中编写C的原代码)
  • 切换根用户的权限:su
  • 切换回某用户:su 用户名
  • 查看进程状态:ps aux
表格名称 各自含义
USER 用户
PID 进程号
%CPU 进程占用的CPU百分比
%MEM 占用内存的百分比
STAT 进程状态 例如S 处于休眠状态;R 正在运行;Z 僵尸进程不存在但暂时无法消除
COMMAND 命令

3. GCC编程

gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。

而我们知道,一个C/C++程序从开始编码到生成二进制可执行文件至少要经过4个步骤:

  • 预处理:对源文件的宏进行展开
  • 编译:将源程序编译成汇编文件
  • 汇编:将汇编文件编译成机器码
  • 链接:将目标文件和外部符号进行链接,生成可执行文件

所以gcc中的-c会生成对应目标文件,-o命令生成二进制可执行文件。基本流程就是:

  • vi [目标文件].c
  • gcc [目标文件].c -o [可执行文件]`
  • ./[可执行文件]

gcc最基本的用法是∶

  • gcc [options] [filenames]
    (1)其中options是编译器所需要的参数;
    (2)filenames给出相关的文件名称。
  • -o output_filename
    确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
  • -lname
    在连接时,装载名字为“libname.a”的函数库,该函数库位于系统预设的目录或者由-L选项确定的目录下。例如,-lm表示连接名为“libm.a”的数学函数库。
  • -Ldir
    制定编译的时候,搜索库的路径 ,这个选项告诉连接程序,首先到-L指定的目录中去寻找,然后到系统预设路径中寻找.

举个例子: gcc –lm -L/home/mylib ts.c –o ts.out
这时,预编译、编译连接ts.c,生成一个指定名称为ts.out的可执行文件,在编译过程中需要引入m库.对于一些标准库来说,我们没有必要指出路径.只要它们在起缺省库的路径下就可以了 , 系统的缺省库的路径/usr/lib /usr/local/lib /lib在这三个路径下面的库,我们可以不指定路径 。

练习一下:万物起点——打印helloworld

①创建一个.c文件vi hello.c

②按英文输入法下的i进入写状态,开始编写代码内容,下方会变为”INSERT“

③输完后按Esc退出写状态,再输入:wq保存退出

④指令:gcc hello.c -o helloworld
gcc编译程序,编译完成后,在你的文件中出现一个helloworld运行文件。(输入ls可以查看)
3.指令:./helloworld

可以看到成功输出。前期工作结束,开始我们的操作系统实验课练习。

4.实验一 进程与线程——Linux进程与线程通讯

实验目的

深刻理解线程与进程的概念,掌握线程与进程在组成成分上的差别,以及与其相适应的通讯方式和应用目标。

实验内容

  • 以Linux系统进程和线程机制为背景,掌握fork()和clone()系统调用的形式和功能,以及与其相适应的高级通讯方式,由fork派生的子进程之间通过pipe通讯,由clone创建的线程之间通过共享内存通讯,后者需要考虑互斥问题。
  • 以生产者/消费者问题为例,通过实验理解fork()和clone()两个系统调用的区别。程序要求能够创建4个进程或线程,生产者和消费者之间能够传递数据。

实验准备

fork系统调用

pid=fork()

创建一个子进程,子进程是父进程的完整复制,fork()创建的新进程是当前进程(返回值除外)的副本。子进程的所有资源都继承父进程,得到父进程资源的副本,既然为副本,也就是说,二者并不共享地址空间(采用写时复制技术)。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID(pid);
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;

clone系统调用

参考来源

#define _GNU_SOURCE
#include <sched.h>
int clone(int (*func)(void*),void *child_stack,int flags,void *func_arg,..../*pid_t *ptid,struct user_desc *tls,pid_t *ctid*/);Return process ID of child on success,or -1 on error
  • 如同fork(),由clone()创建的新进程几近于父进程的翻版。但是与fork()不同的是,克隆生成的子进程继续运行时不以调用处为起点,转而去调用以参数func所指定的函数,func又称为子函数。调用子函数时的参数由func_arg指定。经过转换,子函数可对改参数的含义自由解读,例如可以作为整型值(int),也可以视为指向结构的指针。

  • 当函数func返回或者是调用exit()(或者_exit())之后,克隆产生的子进程就会终止。照例,父进程可以通过wait()一类函数来等待克隆子进程。

  • 因为克隆产生的子进程可能共享父进程内存,所以它不能使用父进程的栈。调用者必须分配一块大小适中的内存空间供子进程的栈使用,同时将这块内存的指针置于参数child_stack中。

  • 参数flags服务于双重目的。首先,其低字节中存放着子进程的终止信号,子进程退出时其父进程将收到这一信号。(如果克隆产生的子进程因信号而终止,父进程依然会收到SIGCHLD信号)该字节也可能为0,这时将不会产生任何信号。

  • clone()函数中的flags参数是各位掩码的组合:

掩码 意义
共享文件描述符:CLONE_FILES 如果指定了该标志,父子进程会共享同一个打开文件描述符表。也就是说,无论哪个进程对文件描述符的分配与释放都会影响宁一个进程。
共享与文件系统相关的信息:CLONE_FS 如果指定了该标志,那么父子进程将共享与文件系统相关的信息:权限掩码、根目录以及当前工作目录。也就是说无论在哪个进程中调用umask()、chdir()或者chroot(),都将影响到另一个进程。
共享对信号的处置设置:CLONE_SIGHAND 如果设置了该标志,那么父子进程将共享同一信号处置表。无论在哪个进程中调用sigaction()或者signal()来改变对信号处置的设置,都会影响其他进程对信号的处置。
共享父进程的虚拟内存:CLONE_VM 如果设置了该标志,父子进程将会共享同一份虚拟内存页。无论哪一个进程更新了内存,或是调用了mmap()、munmap(),另一进程同样会观察到变化。
线程组:CLONE_THREAD 若设置了该标志,则会将子进程置于父进程的线程组中。如果未设置该标志,那么会将子进程置于新的线程组中。
线程库支持:CLONE_PARENT_SETTID、CLONE_CHILD_SETTID和CLONE_CHILD_CLEARTID 为实现POSIX线程,Linux2.6提供了对CLONE_PARENT_SETTID、CLONE_CHILD_SETTID和CLONE_CHILD_CLEARTID的支持。这些标志将会影响clone()对参数ptid、ctid的处理。如果设置了CLONE_PARENT_SETTID,内核会将子进程的线程ID写入ptid所指向的位置。如果设置了CLONE_CHILD_SETTID,那么clone()会将子线程的线程ID写入指针ctid所指向的位置。如果设置了CLONE_CHILD_CLEARTID,则会在子进程终止时将ctid所指向的内存清零。
线程本地存储:CLONE_SETTLS 如果设置了该标志,那么参数tls所指向的user_desc结构会对线程所使用的线程本地存储缓冲区加以描述。
共享systemV信号量的撤销值:CLONE_SYSVSEM 如果设置了该标志,父子进程会将共享同一个SystemV信号量撤销值列表。
每进程挂载命名空间:CLONE_NEWNS 将子进程的父进程置为调用者的父进程:CLONE_PARENT
进程跟踪:CLONE_PTRACE和CLONE_UNTRACED 如果设置了CLONE_PTRACE且正在跟踪子进程,那么也会对子进程进行跟踪。从Linux2.6起,即可设置CLONE_UNTRACED标志,这也意味着跟踪进程不能强制其子进程设置为CLONE_PTRACE
挂起父进程直至子进程退出或者调用exec():CLONE_VFORK 如果设置了该标识,父进程将一直挂起,直至子进程调用exec()或者_exit()来释放虚拟内存资源为止。

pipe系统调用

参考来源

// fd[0]是管道读取端的fd(文件描述符)
// fd[1]是管道写入端的fd
// 成功时返回:0
// 错误时为-1
int pipe(int fds[2]);

从概念上讲,管道(pipe)是两个进程之间的连接,它使得一个进程的标准输出成为另一个进程的标准输入(通信)。

管道仅是单向通信,一个进程向管道写入,另一个进程则从管道中读取。这是一个被视为“虚拟文件”的主内存区域。创建的进程及其所有子进程都可以使用管道进行读写。一个进程可以写入这个管道,而另一个相关的进程可以从中读取。如果管道为空,而且管道的输入口至少有一个进程打开着,那么一个进程试图读取管道的时将会被挂起。

wait()

wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束:

  • 如果在调用 wait()时子进程已经结束,则 wait()会立即返回子进程结束状态值。而子进程的结束状态值会由参数 status 返回,子进程的进程识别码也会一起返回。
  • 如果不在意结束状态值,则参数 status 可以设成 NULL。

如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回返回值-1:

pid_t  pid;
int status=0;
i=wait(&status);

i返回的是子进程的识别码PID;status中存的是子进程的结束状态;可用宏WIFEXITED(status)得到子进程的exit(3)的状态也是3;

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main(int argc,char *agrv[]){pid_t pid;//pid表示fork函数返回地值pid=fork();//创建一个子进程if(pid<0)//异常perror("error in fork!\n");if(pid==0){//父进程识别到这是子进程在运行int i=0;for(i=0;i<4;i++){printf("this is son process,the process's pid is %d.\n",getpid());sleep(2);//让子进程睡眠2秒,看看父进程的行为}exit(1);}else{//子进程结束,父进程运行int status=0;wait(&status);if(WIFEXITED(status)!=0){printf("son process  is over and return %d \n",WIFEXITED(status));}printf("this is father process\n");}return 0;
}

输出结果:

sem_post(&status)和sem_wait(&status)

sem_post函数

int sem_post(sem_t *sem);

作用是给信号量的值加上一个“1”。 当有线程阻塞在这个信号量上时,调用这个函数会使其中一个线程不再阻塞,选择机制是有线程的调度策略决定的。

sem_wait函数

int sem_wait(sem_t * sem);

它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。

pthread_mutex_lock(&mutex)和pthread_mutex_unlock(&mutex)

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
①一个线程等待"条件变量的条件成立"而挂起;
②另一个线程使"条件成立"(给出条件成立信号)。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

pthread_mutex_init(&mutex,NULL);//初始化
pthread_mutex_lock(&mutex); //互斥锁上锁
pthread_mutex_unlock(&mutex);//互斥锁解锁

注意:1)函数都是原子操作,可以为理解为一条指令,不会被其他程序打断
2)pthread_cond_wait,先会解除当前线程的互斥锁,然后挂线线程,等待条件变量满足条件。一旦条件变量满足条件,则会给线程上锁,继续执行pthread_cond_wait。

编译:gcc thread_test.c -o thread_test -lpthread

实验设计

  1. 用pipe()创建一个管道文件,然后用fork()创建两个生产进程和两个消费进程,它们之间通过pipe()传递信息。
  2. 用clone()创建四个轻进程(线程),用参数指明共享内存等资源,通过共享内存模拟生产消费问题,利用pthread_mutex_lock(), pthread_mutex_unlock()等函数实现对共享存储区访问的互斥。

fork系统调用实验代码

#include "sys/types.h"
#include "sys/file.h"
#include "unistd.h"
char r_buf[4];  //读缓冲
char w_buf[4];  //写缓冲
int pipe_fd[2];
pid_t pid1, pid2, pid3, pid4;
int producer(int id);
int consumer(int id);
int main(int argc,char **argv){  if(pipe(pipe_fd)<0){//传输出错printf("pipe create error \n");exit(-1);}else{printf("pipe is created successfully!\n");//谁占用管道谁进行数据传输if((pid1=fork())==0)producer(1);if((pid2=fork())==0)producer(2);if((pid3=fork())==0)consumer(1);if((pid4=fork())==0)consumer(2);}close(pipe_fd[0]);  //需要加上这两句close(pipe_fd[1]);  //否者会有读者或者写者永远等待int i,pid,status;for(i=0;i<4;i++)pid=wait(&status);  //返回子进程状态exit(0);
}
//生产者
int producer(int id){printf("producer %d is running!\n",id);close(pipe_fd[0]);int i=0;for(i=1;i<10;i++){sleep(3);if(id==1) //生产者1strcpy(w_buf,"aaa\0");else  //生产者2strcpy(w_buf,"bbb\0");if(write(pipe_fd[1],w_buf,4)==-1)printf("write to pipe error\n");    }close(pipe_fd[1]);printf("producer %d is over!\n",id);exit(id);
}
//消费者
int consumer(int id){close(pipe_fd[1]); printf("      producer %d is running!\n",id);if (id==1)  //消费者1strcpy(w_buf,"ccc\0");else  //消费者2strcpy(w_buf,"ddd\0");while(1){sleep(1);strcpy(r_buf,"eee\0");if(read(pipe_fd[0],r_buf,4)==0)break;     printf("consumer %d get %s, while the w_buf is %s\n",id,r_buf,w_buf);}close(pipe_fd[0]);printf("consumer %d is over!\n", id);exit(id);
}

输出结果:

分析fork()实验结果:

由程序(1)结果可见,当一个进程改变其空间数据时,其他进程空间对应数据内容并未改变,说明在使用fork()语句创建的子进程与其父进程具有相对独立的地址空间,在解决生产消费的问题时,可以采用pipe()进行通信。因为子进程复制了父进程的打开文件表,所以pipe()所建立的通信管道可被子进程继承,生产和消费进程可以通过对同一管道文件的读写进行通讯。
程序(1)中,消费者从管道中接收生产者发送的数据,并且和自己存储区中的数据进行比较,两者的数据是不同的,说明两个进程拥有不同的存储空间。

其中我觉得最难理解的就是父子进程具有相对独立的地址空间这句话,那到底指向的地址一模一样呢?还是说子进程开辟了一个新的?而且我们知道了子进程几乎是父进程的完全复制(副本),子进程独有的仅仅是自己的进程号资源和计时器等,当我们改变子进程内容又不希望影响到父进程同时一定程度节约系统开销,我们实际上做的事情是:

  • 调用fork(),子进程拥有一份和父进程相同的页表
  • 页表中指向RAM代码段的部分是不会改变的(因为前提是父子进程的代码段肯定相同,那么代码段就没必要复制,故内核将代码段标记为只读,这样父子进程就可以安全的共享此代码段了)。
  • 而指向数据段,堆段,栈段的内容我们既要保证父子进程相互独立,又要保证内核的利用率 (肯定不能在内存直接开个新空间拷贝个一摸一样的,太浪费了!) ,所以我们就让父子先指向同一块物理内存页,然后内存捕获请求,处理请求时将对应请求中要修改的页面创建拷贝到新页面 (写时复制技术)这样就智能多啦! ,这样就达到了父子存储空间相对独立的效果。

    参考博客

clone系统调用实验代码

#define _GNU_SOURCE
#include "sched.h"
#include<sys/types.h>
#include<sys/syscall.h>
#include<unistd.h>
#include <pthread.h>
#include "stdio.h"
#include "stdlib.h"
#include "semaphore.h"
#include "sys/wait.h"
#include "string.h"int producer(void * args);
int consumer(void * args);
pthread_mutex_t mutex;
sem_t product;
sem_t warehouse;char buffer[8][4];
int bp=0;int main(int argc,char** argv){pthread_mutex_init(&mutex,NULL);//初始化sem_init(&product,0,0);sem_init(&warehouse,0,8);int clone_flag,arg,retval;char *stack;//clone_flag=CLONE_SIGHAND|CLONE_VFORK
//clone_flag=CLONE_VM|CLONE_FILES|CLONE_FS|CLONE_SIGHAND;clone_flag=CLONE_VM|CLONE_SIGHAND|CLONE_FS|    CLONE_FILES;//printf("clone_flag=%d\n",clone_flag);int i;for(i=0;i<2;i++){  //创建四个线程arg = i;//printf("arg=%d\n",*(arg));stack =(char*)malloc(4096); retval=clone(producer,&(stack[4095]),clone_flag,(void*)&arg);//printf("retval=%d\n",retval);stack=(char*)malloc(4096); retval=clone(consumer,&(stack[4095]),clone_flag,(void*)&arg);//printf("retval=%d\n\n",retval);
usleep(1);}exit(1);
}int producer(void *args){int id = *((int*)args);int i;for(i=0;i<10;i++){sleep(i+1);  //表现线程速度差别sem_wait(&warehouse);pthread_mutex_lock(&mutex);if(id==0)strcpy(buffer[bp],"aaa/0");elsestrcpy(buffer[bp],"bbb/0");bp++;printf("producer %d produce %s in %d\n",id,buffer[bp-1],bp-1);pthread_mutex_unlock(&mutex);sem_post(&product);}printf("producer %d is over!\n",id);exit(id);
}int consumer(void *args){int id = *((int*)args);int i;for(i=0;i<10;i++){sleep(10-i);  //表现线程速度差别sem_wait(&product);pthread_mutex_lock(&mutex);bp--;printf("consumer %d get %s in %d\n",id,buffer[bp],bp+1);strcpy(buffer[bp],"zzz\0");pthread_mutex_unlock(&mutex);sem_post(&warehouse);}printf("consumer %d is over!\n",id);exit(id);
}

编译:gcc -pthread clone.c -o clone.out
运行:./clone.out
输出结果(输出片段节选):


producer0 produce aaa in 0
producer1 produce bbb in 1
producer0 produce aaa in 2
producer1 produce bbb in 3
producer0 produce aaa in 4
producer1 produce bbb in 5
consumer0 get bbb in 5
consumer1 get aaa in 4
producer0 produce aaa in 4
producer1 produce bbb in 5
producer0 produce aaa in 6
producer1 produce bbb in 7
consumer0 get bbb in 7
consumer1 get aaa in 6
producer0 produce aaa in 6
producer1 produce bbb in 7
consumer0 get bbb in 7
consumer1 get aaa in 6
producer0 produce aaa in 6
producer1 produce bbb in 7
consumer0 get bbb in 7
consumer1 get aaa in 6
producer0 produce aaa in 6
producer1 produce bbb in 7
consumer0 get bbb in 7
consumer1 get aaa in 6
consumer0 get bbb in 5
consumer1 get aaa in 4
producer0 produce aaa in 4
producer1 produce bbb in 5
consumer0 get bbb in 5
consumer1 get aaa in 4
consumer0 get bbb in 3
consumer1 get aaa in 2
consumer0 get bbb in 1
consumer1 get aaa in 0
producer0 produce aaa in 0
producer0 is over!
producer1 produce bbb in 1
producer1 is over!
consumer0 get bbb in 1
consumer0 is over!
consumer1 get aaa in 0
consumer1 is over!

分析clone()实验结果:

而由程序(2)结果可见,clone()语句在创建进程时,可通过参数设定子进程与父进程是否共享存储空间,从而可以创建真正意义上的线程。生产者和消费者进程共享内存,从而可以通过共享内存直接交换数据。但是多个进程共享内存需要互斥机制,程序中定义了临界区变量mutex和两个信号量product,warehouse,临界区变量用于共享内存操作的互斥,信号量分别实现了生产者和消费者的等待。
程序(2)中,消费者输出存储区中的数据,并且存储区中的数据随着生产者存入的数据而发生变化,说明clone()语句通过clone_flag的参数设定实现了共享内存。若在实验中除去CLONE_VM选项,将出现非预期的结果。


这里留个坑,上述clone系统的输出是理想化输出,我自己并没有跑出来,我得到的是没有输出的文件:
老师给我的解释是:因为clone线程这个实验比较老,linux更新很多代了,就可能存在linux对旧版本不兼容,更新后的头文件无法识别其中的一些参数,所以编译能通过但是参数识别出现问题对应功能就无法实现,这段代码问题出在clone_flag中的CLONE_SIGHAND,经过检验CLONE_SIGHAND加上之后clone()函数调用始终返回-1,也就是新建不成功,然后我试过把CLONE_SIGHAND去掉换成CLONE_VFORK,就能够clone()成功并返回一个进程号,并且输出能看到一些如下图所示(但是这样并不是我们追求的线程通信,因为这段代码只涉及线程,但是下面代码为什么在进程producer中明明应该有10个循环但显示只有8个然后一直卡着了,我还没想通,或者换句话说CLONE_VFORK是如何影响线程的呢?我在这里留个坑,我只是举个例子说明是这个传参的问题。解决办法可以试试用学校机房的老版本虚拟机或者你自己下一个老版本的linux):

我自己的ubuntu和gcc版本为:

我觉得配置很麻烦,博主还是一只备战期末的大二小菜狗,暑假有时间配置一个老版本的试试,hu~

思考题

• 在Linux环境中,除pipe和clone公共内存通讯外,还可以采用shm和msg通讯方式,试查阅相关资料了解这些通讯方式,并使用上述任一种方式模拟实现“生产者/消费者”问题。
• 比较Linux系统中pipe、clone、shm和msg四种高级通讯方法的优缺点以及各自适应的环境。

参考博客:shm共享内存
参考博客:msg消息队列

代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>#define SIZE 1024int main()
{int shmid;char *shmaddr;struct shmid_ds buf;int flag = 0;int pid;//创建共享内存shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600);if ( shmid < 0 ){perror("get shm ipc_id error");return -1;}//创建子进程 调用fork会返回两次 父进程返回创建的进程,子进程返回0pid = fork();if( pid == 0 ){//子进程//将共享内存映射到本地进程shmaddr = (char *)shmat(shmid, NULL, 0);if( (int)shmaddr == -1 ){perror("shmat addr error");return -1;}strcpy( shmaddr, "Hi,I am child process!\n");//断开映射shmdt( shmaddr);return 0;}else if( pid > 0 ){//父进程//可能会出现父进程比子进程提前结束执行,所以,延迟等待子进程先执行完sleep(3);//得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中flag = shmctl ( shmid, IPC_STAT, &buf);if( flag == -1 ){perror("shmctl shm error");return -1;}printf("shm_segsz=%d bytes\n", buf.shm_segsz);printf("parent pid=%d,shm_cpid=%d\n",getpid(),buf.shm_cpid);printf("chlid pid=%d,shm_lpid=%d\n",pid,buf.shm_lpid);//把共享内存映射到本地shmaddr = (char *)shmat(shmid, NULL, 0);if( (int)shmaddr == -1 ){perror("shmat addr error");return -1;}printf("%s",shmaddr);//断开共享内存连接shmdt(shmaddr);//删除共享内存shmctl(shmid,IPC_RMID,NULL);}else{perror("fork error");shmctl(shmid,IPC_RMID,NULL);}return 0;
}

吉林大学操作系统上机(实验一:Linux进程与线程通讯)相关推荐

  1. 操作系统实验报告linux进程管理,计算机操作系统实验报告三Linux进程基本管理.doc...

    GDOU-B-11-112广东海洋大学学生实验报告书(学生用表) GDOU-B-11-112 实验名称 Linux进程基本管理 课程名称 计算机操作系统 课程号 学院(系) 专业 统 班级 学生姓名 ...

  2. 操作系统课设之Linux 进程管理

    前言 课程设计开始了,实验很有意思,写博客总结学到的知识 白嫖容易,创作不易,学到东西才是真 本文原创,创作不易,转载请注明!!! 本文链接 个人博客:https://ronglin.fun/arch ...

  3. 大学操作系统上机实验

      上机实验指导书 课程名称 操作系统 主撰人  刘天波 审核人  栾方军 沈阳建筑大学信息学院 2014 年3月 课程名称:操作系统 上机学时:16 适用专业:计算机科学与技术 先修课程:C语言 一 ...

  4. linux系统进程控制实验报告,Linux进程控制实验报告.doc

    里奴性进程控制实验报告 实验名称: Linux进程控制 实验要求:一.编写一个Linux系统C程序,由父亲创建2个子进程,再由子进程各自从控制台接收一串字符串,保存在各自的全局字符串变量中,然后正常结 ...

  5. (大集合)Linux进程和线程的基本编程、通讯和例程【杂记】

    Linux 进程 和 线程 的基本 编程.通讯 和 例程 注:[杂记]系列为日常网搜资料的堆砌而积累成之.如有错误恭谢指出.标识为"原创"其实不完全是,只是多引用再整理和加上自己的 ...

  6. Linux进程与线程的区别 详细总结(面试经验总结)

    首先,简要了解一下进程和线程.对于操作系统而言,进程是核心之核心,整个现代操作系统的根本,就是以进程为单位在执行任务.系统的管理架构也是基于进程层面的.在按下电源键之后,计算机就开始了复杂的启动过程, ...

  7. Linux进程与线程的区别

    2019独角兽企业重金招聘Python工程师标准>>> Linux进程与线程的区别 cnyinlinux 本文较长,耐心阅读,必有收获! 进程与线程的区别,早已经成为了经典问题.自线 ...

  8. 一文搞定Linux进程和线程(详细图解)

    Linux 进程和线程 本篇文章我们就深入理解一下 Linux 内核来理解 Linux 的基本概念之进程和线程.系统调用是操作系统本身的接口,它对于创建进程和线程,内存分配,共享文件和 I/O 来说都 ...

  9. 【Linux进程、线程、任务调度】二 fork/vfork与写时拷贝 线程的本质 托孤 进程睡眠和等待队列

    学习交流加(可免费帮忙下载CSDN资源): 个人微信: liu1126137994 学习交流资源分享qq群1(已满): 962535112 学习交流资源分享qq群2(已满): 780902027 学习 ...

最新文章

  1. SpringBoot优雅编码之:Lombok加持
  2. Qt学习(三):事件、定时器、事件过滤器
  3. protocol buffer介绍(protobuf)
  4. 5怎么选国外节点_外卖包装怎么选?这5个技巧要掌握
  5. Visual C++ 中的ODBC编程
  6. 【数据结构笔记45】通过中间步骤区分简单插入排序和归并排序
  7. PE框架学习之道:PE框架——style的配置
  8. 软件测试技术进阶篇——花椒测试平台 - 接口篇
  9. C根据输入的城市坐标求各城市间的距离
  10. vim全文替换字符串
  11. 配置maven使用阿里云仓库
  12. linux socket 阻塞服务端 非阻塞客户端,Linux socket非阻塞connect方法
  13. 计161--Problem H: 求三角形面积-gyy
  14. 常见windows进程说明
  15. Python批量处理表格有用吗_python批量读入图片、处理并批量输出(可用于深度学习训练集的制作)...
  16. [渝粤教育] 中国地质大学 大学物理(上) 复习题 (2)
  17. Windows应用程序C#创建MDI父窗体和子窗体
  18. 由Sensor光电转换效率和光谱功率分布曲线联想到的白平衡增益计算
  19. 广东高中生多少人_广东省高中生100米短跑记录是多少?谢谢
  20. Jepsen:分布式系统最早的混沌框架

热门文章

  1. Bitlocker使用及原理
  2. NC200211 装备合成
  3. android改变整个app字体大小,Android系统字体大小如何影响app的字体大小?
  4. 继北极星项目后,又一款低成本AR眼镜开源方案:OpenAR
  5. Nlog 日志框架使用介绍
  6. Arduino VS 树莓派:哪个才是你的菜?
  7. js延迟执行与循环执行
  8. 动动“意念”,音乐直流大脑,马斯克脑机接口真能实现?
  9. 华为鸿蒙nova,华为Nova5怎么升级鸿蒙系统 Nova5升级鸿蒙系统教程
  10. powerpc linux 交叉编译环境配置