精华内容
下载资源
问答
  • 实验一、进程控制 实验二、线程和管道通信实验 实验三、Shell实验(MSH) 实验四、进程同步实验 实验五、进程互斥实验 实验实验报告
  • 山东大学操作系统实验代码,其中包含五个实验分别放置了文件夹
  • 山东大学操作系统实验课程实验6,时间2012年11月
  • 山东大学操作系统实验课实验7&实验8,时间2012年12月
  • C语言实现调度算法实验-山东大学操作系统实验三。C语言实现调度算法实验-山东大学操作系统实验三。C语言实现调度算法实验-山东大学操作系统实验三。
  • 山东大学 操作系统 实验四 进程同步实验
  • 山东大学操作系统实验八 磁盘移臂调度算法实验
  • 山东大学大二下操作系统的课程实验,相比大三上的课程设计简单很多。
  • 计算机软件专业的同学看过来,最新山东大学操作系统实验一到实验六的源代码!
  • 山东大学 操作系统实验6 死锁问题实验的程序
  • 山东大学操作系统实验5,进程互斥实验的源代码
  • 山东大学软件学院2019年操作系统实验,涵盖实验指导书上实验一到实验七的独立实验的源码
  • 山东大学操作系统实验第六个,进程同步的源代码
  • 实验题目:内存页面置换算法问题 假请在以上示例实验程序中补充“增强二次机会”等置换算法的模拟程序。输入不同的内存页面引用串和实存帧数,观察并分析其页面置换效果和性能,并将其与 LRU 和FIFO 算法进行比较。...
  • 本文记录完成2020年上班期操作系统实验过程中的学习和参考资料欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你...

    基础

    pv基础

    • 信号量S:
      若大于等于0,则表示可供并发进程使用的资源的实体数;
      若小于0,则表示正在等待使用临界区的进程的个数
      所以初值应该设为大于0
    • 原语:不可中断的程序段
    • P:S-1
      若结果仍然大于等于0,则进程继续执行;
      若结果小于0,则进程阻塞,进入与该信号相对应的队列当中,然后转进程调度。
      V:S+1
      若结果仍然大于0,则进程继续执行;
      若结果小于等于0(注意0的情况在第二种),则从该信号的等待队列中唤醒一个等待进程,然后再返回原来的进程继续执行或转进程调度。
    • 临界资源:每次仅允许一个进程访问的资源,例如打印机,消息缓冲队列,变量,缓冲区等等。
    • 大多数情况下同步已经实现了互斥,同步是在互斥的基础上通过其他机制实现访问者对资源的有序访问(互斥是无序的)。

    Makefile

    跟我一起写Makefile(一)
    跟我一起写Makefile(二)
    (系列文章都在评论中有指路)
    初次尝试离开IDE编程,对工程的编译有了更深的认识。

    这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
    make会在当前目录下找名字叫“Makefile”或“makefile”的文件。如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
    ————————————————
    版权声明:本文为CSDN博主「haoel」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/haoel/article/details/2886

    每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。
    更为稳健的做法是:
    .PHONY : clean
    clean :
    -rm edit *.o

    前面说过,.PHONY意思表示clean是一个“伪目标”,。而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。
    ———————————————— 版权声明:本文为CSDN博主「haoel」的原创文章,遵循 CC 4.0 BY-SA
    版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/haoel/article/details/2887

    fork()

    fork()父子进程共享
    fork()父子进程运行优先顺序
    在实验中遇到了一点问题,如下代码:

    #include <unistd.h> 
    #include <sys/types.h> 
    
    main () 
    { 
            pid_t pid; 
            pid=fork(); 
            if (pid < 0) 
                printf("error in fork!"); 
            else if (pid == 0) 
                printf("i am the child process, my process id is %d\n",getpid()); 
            else 
                printf("i am the parent process, my process id is %d\n",getpid()); 
    }
    

    在很多文章中,都先输出child process,再输出parent process,而我运行之后结果相反。查找资料fork()父子进程运行先后顺序了解到可能与linux系统版本有关,也有可能原因可能是:

    ·在多处理系统中,它们可能会同时访问一个cpu;
    ·在单处理系统中,fork()之后很可能总是先调度父进程(优化性能),但如果恰好父进程的cpu时间片到期了,则执行子进程。

    (还发现先输出子进程的资料时间都是比较老的了,估计与版本有关系)

    函数

    read()
    用于文件描述符对应的文件中读取数据,原型:
    ssize_t read(int fd,void*buf,size_t count)
    #参数说明:
    fd: 是文件描述符, 从command line获取数据时,为0
    buf: 为读出数据的缓冲区;
    count: 为每次读取的字节数(是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移)
    #返回值:
    成功:返回读出的字节数
    失败:返回-1,并设置errno,如果在调用read之前到达文件末尾,则这次read返回0

    write()
    用于将数据写入到文件描述符对应的文件,原型:
    ssize_t write(int fd,const void*buf,size_t count);
    #参数说明:
    fd:是文件描述符(输出到command line,就是1)
    buf:通常是一个字符串,需要写入的字符串 count:是每次写入的字节数
    #返回值:
    成功:返回写入的字节数
    失败:返回-1并设置errno

    read()/write()函数的第二个参数是保存在缓冲区的数据/需要写入的数据,在“进程的通信”实验中,用到管道,那么buf参数所代表的就是保存在管道中的数据/要写入管道的数据。

    实验四:进程同步实验

    基础

    • 在Linux的proc文件系统中有3个虚拟文件动态记录了ipcs命令(包括ipcs -m共享内存情况,ipcs -s信号灯数组,ipcs -q消息队列)显示的当前IPC对象的信息:
      /proc/sysvipc/shm 共享内存 (sharing memory)
      /proc/sysvipc/sem 信号量 (semaphore)
      /proc/sysvipc/msg 消息队列 (message)
      可以利用它们在程序执行时获取有关IPC对象的当前信息。

    • BUFSIZ的值,BUFSIZ为系统默认的缓冲区大小。也可以自己定义#define BUFSIZ 256*。

    • key_t的本质,System V IPC使用key_t值作为它们的名字,在Redhat linux(后续验证默认都在该平台下)下key_t被定义为int类型。

    • IPC结构的键(key)与标识符(ID)

    示例

    变量

    生产者消费者共享缓冲区的变量:buff_key, buff_num, buff_ptr(ptr是共享指针啦…)
    生产者放产品位置的共享指针:pput_key, pput_num, pput_ptr
    消费者取产品位置的共享指针:cget_key, cget_num, cget_ptr
    生产者有关的信号量:cons_key, cmtx_key, cons_sem, cmtx_sem, sem_val, sem_flg, shm_flg

    函数
    1. int shmget(key_t key,int size,int flags)
      参数:
      shm_key 共享内存的键值,可以为IPC_PRIVATE(linux进程通信IPC之IPC_PRIVATE与ftok比较),也可以指定一个整数值。
      shm_size 共享内存字节长度
      shm_flags 共享内存权限位
      返回值:调用成功后,若key是用新整数指定并且flags中设置了IPC_CREATE,系统将此值与其他共享内存区的 key 进行比较,如果存在相同的 key ,说明共享内存区已存在,此时返回该共享内存区的标识符,否则新建一个共享内存区并返回其标识符。不成功则返回-1。
      semget,msgget返回值相似,参数略有不同。
    2. int get_ipc_id(char proc_file,key_t key)*
      从/proc/sysvipc/文件系统中获取IPC的id号
      参数中proc_file对应/proc/sysvipc/目录中的IPC文件;key对应要获取的IPC的id号的键值
    3. int set_sem(key_t sem_key,int sem_val,int sem_flg)
      建立一个具有n个信号灯的信号量,返回一个信号灯数组的标识符sem_id
      参数:信号灯数组的键值,信号灯的个数,数组的存取权限
    4. char * set_shm(key_t shm_key,int shm_num,int shm_flg)
      建立一个具有n个字节的共享内存区,返回指向该内存区的首地址的指针shm_buf
    5. int set_msq(key_t msq_key,int msq_flg)
      返回一个消息队列的标识符msq_id
    6. down(int sem_id)
      当sem_id的值是0的时候,不能再减了,阻塞;如果是1如果更高的话,则可以继续。

    吸烟者问题(Smoker Problem)

    桌子上有容量为1的缓冲区,只能有一种组合在里面。相当于一共有4种同步关系:

    1. 桌子上有组合1,则smoker1取走材料,开始吸烟
    2. 桌子上有组合2,则smoker2取走材料,开始吸烟
    3. 桌子上有组合3,则smoker3取走材料,开始吸烟
    4. 桌子上没有组合,那么吸烟完成之后,供应者将下一个组合放到桌子上
      并且三个吸烟者的吸烟行为互斥,即只能有一个吸烟者吸烟。

    思路:将缓冲区的大小设置为1,all_sem(表示缓冲区中的状况),prod_sem(表示生产者同步信号),ab_sem,bc_sem,ac_sem分别表示各个吸烟者的信号。生产者的操作:down(all_sem) down(prod_sem),表示缓冲区被占用了,然后将材料组合放入缓冲区中,再up(producer),并提醒对应的吸烟者。而吸烟者的操作:down(ab_sem),从缓冲区中读取到材料后,down(all_sem),则又回到初始的状态,并且已经变成下一个组合。

    实验五:进程互斥实验

    IPC之消息队列(struct msqid_ds)

    理发店问题思路:理发室有3个理发椅子和3个理发师,有一个容纳4位顾客的沙发,和一间可容纳13位顾客的等候室。情况分为:

    1. 当沙发和等候室都没有人的时候,理发师正在睡觉或者顾客小于3人,正在被理发;
    2. 当沙发上的有人且人数小于4人时,代表等候室中没有人,且有3人正在理发,沙发上的顾客都在等候;
    3. 当沙发上的人数为4人,等候室的人数等于0时,同情况3;
    4. 当等候室的人数大于0时,表示有3位顾客正在理发,沙发上坐着4位顾客,以及等候室中还有顾客在等待;
    5. 当等候室的人数等于13时,表示理发店中的顾客已经满员,不允许有其他顾客进入了。

    再补充…*

    展开全文
  • 实验题目:死锁问题 在两个城市南北方向之间存在一条铁路,多列火车可以分别从两个城市的车站排队等待进入车道向对方城市行驶,该铁路在同一时间,只能允许在同一方向上行车,如果同时有相向的火车行驶将会撞车。请模拟...
  • 实验题目:理发店问题 理发店问题:假设理发店的理发室中有3个理发椅子和3个理发师,有一个可容纳4个顾客坐等理发的沙发。此外还有一间等候室,可容纳13位顾客等候进入理发室。顾客如果发现理发店中顾客已满(超过...
  • 理发店问题:假设理发店的理发室中有 3 个理发椅子和 3 个理发师,有一个可容 纳 4 个顾客坐等理发的沙发。此外还有一间等候室,可容纳 13 位顾客等候进入理发 室。顾客如果发现理发店中顾客已满(超过 20 人),就不进入...
  • 在现代计算机系统中,操作系统已经成为整个计算机系统的核心。所有计算机 硬件和应用软件都必须依赖操作系统的支撑,接受操作系统的管理。一个性能优秀 的操作系统会使整个计算机系统的资源发挥到极致;同时也为用户...
  • 实验四、进程同步实验 独立实验——抽烟者问题 实验题目: 抽烟者问题。假设一个系统中有三个抽烟者进程,每个抽烟者不断地卷烟并抽烟。抽烟者卷起并抽掉一颗烟需要有三种材料:烟草、纸和胶水。一个抽烟者有烟草,...

    实验四、进程同步实验 独立实验——抽烟者问题

    实验题目:

    抽烟者问题。假设一个系统中有三个抽烟者进程,每个抽烟者不断地卷烟并抽烟。抽烟者卷起并抽掉一颗烟需要有三种材料:烟草、纸和胶水。一个抽烟者有烟草,一个有纸,另一个有胶水。系统中还有两个供应者进程,它们无限地供应所有三种材料,但每次仅轮流提供三种材料中的两种。得到缺失的两种材料的抽烟者在卷起并抽掉一颗烟后会发信号通知供应者,让它继续提供另外的两种材料。这一过程重复进行。 请用以上介绍的 IPC 同步机制编程,实现该问题要求的功能。

    题目分析:

    • 题中有五个角色:三个抽烟者,两个生产者
    • 生产者循环供应a b c中的两种:ab、bc、ac、ab……
    • 某个消费者无限拥有a b c中的一个,而一直在等另外两个产品
    1. 因此生产者之间必要一个信号量用来同步生产者的生产(即不允许同时访问缓存区)
    2. 缓存区需要控制满和空,因此需要一个信号量同步缓存区,防止满了依然生产或者空了消费者依然获取。为了简单起见,缓存区的大小设置为1个即可,由于循环访问缓存区,缓存区的大小对实验的正确性没有影响。
    3. 三个消费者之间需要的东西不同,所以各自独立编写文件,并各自拥有一个信号量
    • 总之,本题需要五个信号量,命名和作用如下:
      all:缓存区的空或满。
      producer:生产者生产同步信号量。
      ab、ac、bc:分别是三个消费者的独立信号量。

    生产者和消费者的伪代码

    生产者:
    while(1){
    	P(all);
    	P(producer);
    	生产产品
    	V(producer);
    	if(生产的产品)
    		V(对应的消费者);
    }
    
    消费者:
    while(1){
    	P(自己的信号量);
    	抽烟
    	V(all);
    }
    

    实验完整代码

    头文件ipc.h:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/sem.h>
    #include <sys/msg.h>
    #define BUFSZ 256
    //建立或获取 ipc 的一组函数的原型说明
    int get_ipc_id(char *proc_file,key_t key);
    char *set_shm(key_t shm_key,int shm_num,int shm_flag);
    int set_msq(key_t msq_key,int msq_flag);
    int set_sem(key_t sem_key,int sem_val,int sem_flag);
    int down(int sem_id);
    int up(int sem_id);
    /*信号灯控制用的共同体*/
    typedef union semuns
    {
        int val;
    } Sem_uns;
    /* 消息结构体*/
    typedef struct msgbuf
    {
        long mtype;
        char mtext[1];
    } Msg_buf;
    //生产消费者共享缓冲区即其有关的变量
    key_t buff_key;
    int buff_num;
    char *buff_ptr;
    //生产者放产品位置的共享指针
    key_t pput_key;
    int pput_num;
    int *pput_ptr;
    //消费者取产品位置的共享指针
    key_t cget_key;
    int cget_num;
    int *cget_ptr;
    //生产者和消费者有关的信号量
    key_t ab_key;
    key_t ac_key;
    key_t bc_key;
    key_t all_key;
    key_t produce_key;
    int ab_int;
    int ac_int;
    int bc_int;
    int all_int;
    int produce_int;
    
    int sem_val;
    int sem_flg;
    int shm_flg;
    
    各种轮子ipc.c:
    #include "ipc.h"
    /*
     * get_ipc_id() 从/proc/sysvipc/文件系统中获取 IPC 的 id 号
     * pfile: 对应/proc/sysvipc/目录中的 IPC 文件分别为
     * msg-消息队列,sem-信号量,shm-共享内存
     * key: 对应要获取的 IPC 的 id 号的键值
    */
    int get_ipc_id(char *proc_file,key_t key)
    {
        FILE *pf;
        int i,j;
        char line[BUFSZ],colum[BUFSZ];
        if((pf = fopen(proc_file,"r")) == NULL)
        {
            perror("Proc file not open");
            exit(EXIT_FAILURE);
        }
        fgets(line, BUFSZ,pf);
        while(!feof(pf))
        {
            i = j = 0;
            fgets(line, BUFSZ,pf);
            while(line[i] == ' ')
                i++;
            while(line[i] !=' ')
                colum[j++] = line[i++];
            colum[j] = '\0';
            if(atoi(colum) != key)
                continue;
            j=0;
            while(line[i] == ' ')
                i++;
            while(line[i] !=' ')
                colum[j++] = line[i++];
            colum[j] = '\0';
            i = atoi(colum);
            fclose(pf);
            return i;
        }
        fclose(pf);
        return -1;
    }
    /*
    * 信号灯上的 down/up 操作
    * semid:信号灯数组标识符
    * semnum:信号灯数组下标
    * buf:操作信号灯的结构
    */
    int down(int sem_id)
    {
        struct sembuf buf;
        buf.sem_op = -1;
        buf.sem_num = 0;
        buf.sem_flg = SEM_UNDO;
        if((semop(sem_id,&buf,1)) <0)
        {
            perror("down error ");
            exit(EXIT_FAILURE);
        }
        return EXIT_SUCCESS;
    }
    int up(int sem_id)
    {
        struct sembuf buf;
        buf.sem_op = 1;
        buf.sem_num = 0;
        buf.sem_flg = SEM_UNDO;
        if((semop(sem_id,&buf,1)) <0)
        {
            perror("up error ");
            exit(EXIT_FAILURE);
        }
        return EXIT_SUCCESS;
    }
    /*
    * set_sem 函数建立一个具有 n 个信号灯的信号量
    * 如果建立成功,返回 一个信号灯数组的标识符 sem_id
    * 输入参数:
    * sem_key 信号灯数组的键值
    * sem_val 信号灯数组中信号灯的个数
    * sem_flag 信号等数组的存取权限
    */
    int set_sem(key_t sem_key,int sem_val,int sem_flg)
    {
        int sem_id;
        Sem_uns sem_arg;
    //测试由 sem_key 标识的信号灯数组是否已经建立
        if((sem_id = get_ipc_id("/proc/sysvipc/sem",sem_key)) < 0 )
        {
    //semget 新建一个信号灯,其标号返回到 sem_id
            if((sem_id = semget(sem_key,1,sem_flg)) < 0)
            {
                perror("semaphore create error");
                exit(EXIT_FAILURE);
            }
    //设置信号灯的初值
            sem_arg.val = sem_val;
            if(semctl(sem_id,0,SETVAL,sem_arg) <0)
            {
                perror("semaphore set error");
                exit(EXIT_FAILURE);
            }
        }
        return sem_id;
    }
    /*
    * set_shm 函数建立一个具有 n 个字节 的共享内存区
    * 如果建立成功,返回 一个指向该内存区首地址的指针 shm_buf
    * 输入参数:
    * shm_key 共享内存的键值
    * shm_val 共享内存字节的长度
    * shm_flag 共享内存的存取权限
    */
    char* set_shm(key_t shm_key,int shm_num,int shm_flg)
    {
        int i,shm_id;
        char* shm_buf;
    //测试由 shm_key 标识的共享内存区是否已经建立
        if((shm_id = get_ipc_id("/proc/sysvipc/shm",shm_key)) < 0 )
        {
    //shmget 新建 一个长度为 shm_num 字节的共享内存,其标号返回到 shm_id
            if((shm_id = shmget(shm_key,shm_num,shm_flg)) <0)
            {
                perror("shareMemory set error");
                exit(EXIT_FAILURE);
            }
    //shmat 将由 shm_id 标识的共享内存附加给指针 shm_buf
            if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
            {
                perror("get shareMemory error");
                exit(EXIT_FAILURE);
            }
            for(i=0; i<shm_num; i++)
                shm_buf[i] = 0; //初始为 0
        }
    //shm_key 标识的共享内存区已经建立,将由 shm_id 标识的共享内存附加给指针 shm_buf
        if((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0)
        {
            perror("get shareMemory error");
            exit(EXIT_FAILURE);
        }
        return shm_buf;
    }
    /*
    * set_msq 函数建立一个消息队列
    * 如果建立成功,返回 一个消息队列的标识符 msq_id
    * 输入参数:
    * msq_key 消息队列的键值
    * msq_flag 消息队列的存取权限
    */
    int set_msq(key_t msq_key,int msq_flg)
    {
        int msq_id;
    //测试由 msq_key 标识的消息队列是否已经建立
        if((msq_id = get_ipc_id("/proc/sysvipc/msg",msq_key)) < 0 )
        {
    //msgget 新建一个消息队列,其标号返回到 msq_id
            if((msq_id = msgget(msq_key,msq_flg)) < 0)
            {
                perror("messageQueue set error");
                exit(EXIT_FAILURE);
            }
        }
        return msq_id;
    }
    
    生产者 producer.c:
    #include "ipc.h"
    int main(int argc,char *argv[])
    {
        int rate;
    //可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
        if(argv[1] != NULL)
            rate = atoi(argv[1]);
        else
            rate = 1; //不指定为 1 秒
    //共享内存使用的变量
        buff_key = 101;//缓冲区任给的键值
        buff_num = 1;//缓冲区任给的长度
        pput_key = 102;//生产者放产品指针的键值
        pput_num = 1; //指针数
        shm_flg = IPC_CREAT | 0644;//共享内存读写权限
    
    //获取缓冲区使用的共享内存,buff_ptr 指向缓冲区首地址
        buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);
    //获取生产者放产品位置指针 pput_ptr
        pput_ptr = (int *)set_shm(pput_key,pput_num,shm_flg);
    //信号量使用的变量
    
        ab_key = 201;//有C的消费者控制键值
        ac_key = 202;//有B的消费者控制键值
        bc_key = 203;//有A的消费者控制键值
        all_key = 204;//对一个缓冲区的控制键值
        produce_key = 205;//对两个生产者的同步键值
        sem_flg = IPC_CREAT | 0644;
    //生产者同步信号灯初值设为缓冲区最大可用量
        sem_val = buff_num;
    //获取生产者同步信号灯,引用标识存 all_int
        all_int = set_sem(all_key,sem_val,sem_flg);
    //消费者初始无产品可取,同步信号灯初值设为 0
        sem_val = 0;
        ab_int = set_sem(ab_key,sem_val,sem_flg);
        ac_int = set_sem(ac_key,sem_val,sem_flg);
        bc_int = set_sem(bc_key,sem_val,sem_flg);
    //生产者互斥信号灯初值为 1
        sem_val = 1;
    //获取生产者互斥信号灯,引用标识存 produce_int
        produce_int = set_sem(produce_key,sem_val,sem_flg);
        int i = 0;
        
        while(1){
            int d = (i++)%3;
            down(all_int);
            down(produce_int);
            buff_ptr[*pput_ptr] = 'A'+ d;
            sleep(rate);
    	if(d==0)
                printf("%d把烟草和纸放入[%d]缓存区\n",getpid(),*pput_ptr);
            else if(d==1)
                printf("%d把胶水和纸放入[%d]缓存区\n",getpid(),*pput_ptr);
            else
                printf("%d把烟草和胶水放入[%d]缓存区\n",getpid(),*pput_ptr);
    	*pput_ptr = (*pput_ptr+1) % buff_num;
            up(produce_int);
            if(d==0)
                up(bc_int);
            else if(d==1)
                up(ac_int);
            else
                up(ab_int);
        }
        return EXIT_SUCCESS;
    }
    
    
    抽烟者A haveA.c:
    #include "ipc.h"
    int main(int argc,char *argv[])
    {
        int rate;
    //可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
        if(argv[1] != NULL)
            rate = atoi(argv[1]);
        else
            rate = 1; //不指定为 1 秒
    //共享内存 使用的变量
        buff_key = 101; //缓冲区任给的键值
        buff_num = 1; //缓冲区任给的长度
        cget_key = 103; //消费者取产品指针的键值
        cget_num = 1; //指针数
        shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    //获取缓冲区使用的共享内存,buff_ptr 指向缓冲区首地址
        buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);
    //获取消费者取产品指针,cget_ptr 指向索引地址
        cget_ptr = (int *)set_shm(cget_key,cget_num,shm_flg);
    //信号量使用的变量
        ab_key = 201;//有C的消费者控制键值
        ac_key = 202;//有B的消费者控制键值
        bc_key = 203;//有A的消费者控制键值
        all_key = 204;//对一个缓冲区的控制键值
        produce_key = 205;//对两个生产者的同步键值
        sem_flg = IPC_CREAT | 0644;
    //生产者同步信号灯初值设为缓冲区最大可用量
        sem_val = buff_num;
    //获取生产者同步信号灯,引用标识存 all_int
        all_int = set_sem(all_key,sem_val,sem_flg);
    //消费者初始无产品可取,同步信号灯初值设为 0
        sem_val = 0;
    //    ab_int = set_sem(ab_key,sem_val,sem_flg);
    //    ac_int = set_sem(ac_key,sem_val,sem_flg);
        bc_int = set_sem(bc_key,sem_val,sem_flg);
    生产者互斥信号灯初值为 1
    //    sem_val = 1;
    获取生产者互斥信号灯,引用标识存 produce_int
    //    produce_int = set_sem(produce_key,sem_val,sem_flg);
        while(1)
        {
            down(bc_int);
            sleep(rate);
            printf("%d  1号吸烟者得到了:烟草和纸[%d]%c\n",getpid(),*cget_ptr ,buff_ptr[*cget_ptr]);
            *cget_ptr = (*cget_ptr+1) % buff_num;
            up(all_int);
        }
        return EXIT_SUCCESS;
    }
    
    
    抽烟者B haveB.c:
    #include "ipc.h"
    int main(int argc,char *argv[])
    {
        int rate;
    //可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
        if(argv[1] != NULL)
            rate = atoi(argv[1]);
        else
            rate = 1; //不指定为 1 秒
    //共享内存 使用的变量
        buff_key = 101; //缓冲区任给的键值
        buff_num = 1; //缓冲区任给的长度
        cget_key = 103; //消费者取产品指针的键值
        cget_num = 1; //指针数
        shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    //获取缓冲区使用的共享内存,buff_ptr 指向缓冲区首地址
        buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);
    //获取消费者取产品指针,cget_ptr 指向索引地址
        cget_ptr = (int *)set_shm(cget_key,cget_num,shm_flg);
    //信号量使用的变量
        ab_key = 201;//有C的消费者控制键值
        ac_key = 202;//有B的消费者控制键值
        bc_key = 203;//有A的消费者控制键值
        all_key = 204;//对一个缓冲区的控制键值
        produce_key = 205;//对两个生产者的同步键值
        sem_flg = IPC_CREAT | 0644;
    //生产者同步信号灯初值设为缓冲区最大可用量
        sem_val = buff_num;
    //获取生产者同步信号灯,引用标识存 all_int
        all_int = set_sem(all_key,sem_val,sem_flg);
    //消费者初始无产品可取,同步信号灯初值设为 0
        sem_val = 0;
    //    ab_int = set_sem(ab_key,sem_val,sem_flg);
        ac_int = set_sem(ac_key,sem_val,sem_flg);
    //    bc_int = set_sem(ab_key,sem_val,sem_flg);
    生产者互斥信号灯初值为 1
    //    sem_val = 1;
    获取生产者互斥信号灯,引用标识存 produce_int
    //    produce_int = set_sem(produce_key,sem_val,sem_flg);
        while(1)
        {
            down(ac_int);
            sleep(rate);
            printf("%d  2号吸烟者得到了:胶水和纸 [%d]%c\n",getpid(),*cget_ptr ,buff_ptr[*cget_ptr]);
            *cget_ptr = (*cget_ptr+1) % buff_num;
            up(all_int);
        }
        return EXIT_SUCCESS;
    }
    
    
    抽烟者C haveC.c:
    #include "ipc.h"
    int main(int argc,char *argv[])
    {
        int rate;
    //可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度
        if(argv[1] != NULL)
            rate = atoi(argv[1]);
        else
            rate = 1; //不指定为 1 秒
    //共享内存 使用的变量
        buff_key = 101; //缓冲区任给的键值
        buff_num = 1; //缓冲区任给的长度
        cget_key = 103; //消费者取产品指针的键值
        cget_num = 1; //指针数
        shm_flg = IPC_CREAT | 0644; //共享内存读写权限
    //获取缓冲区使用的共享内存,buff_ptr 指向缓冲区首地址
        buff_ptr = (char *)set_shm(buff_key,buff_num,shm_flg);
    //获取消费者取产品指针,cget_ptr 指向索引地址
        cget_ptr = (int *)set_shm(cget_key,cget_num,shm_flg);
    //信号量使用的变量
        ab_key = 201;//有C的消费者控制键值
        ac_key = 202;//有B的消费者控制键值
        bc_key = 203;//有A的消费者控制键值
        all_key = 204;//对一个缓冲区的控制键值
        produce_key = 205;//对两个生产者的同步键值
        sem_flg = IPC_CREAT | 0644;
    //生产者同步信号灯初值设为缓冲区最大可用量
        sem_val = buff_num;
    //获取生产者同步信号灯,引用标识存 all_int
        all_int = set_sem(all_key,sem_val,sem_flg);
    //消费者初始无产品可取,同步信号灯初值设为 0
        sem_val = 0;
        ab_int = set_sem(ab_key,sem_val,sem_flg);
    //    ac_int = set_sem(ac_key,sem_val,sem_flg);
    //    bc_int = set_sem(ab_key,sem_val,sem_flg);
    //生产者互斥信号灯初值为 1
    //    sem_val = 1;
    //获取生产者互斥信号灯,引用标识存 produce_int
    //    produce_int = set_sem(produce_key,sem_val,sem_flg);
        while(1)
        {
            down(ab_int);
            sleep(rate);
            printf("%d  3号吸烟者得到了:胶水和烟草 [%d]%c\n",getpid(),*cget_ptr ,buff_ptr[*cget_ptr]);
            *cget_ptr = (*cget_ptr+1) % buff_num;
            up(all_int);
        }
        return EXIT_SUCCESS;
    }
    
    
    makefile:
    hdrs = ipc.h
    opts = -g -c
    p_src = producer.c ipc.c
    p_obj = producer.o ipc.o
    a_src = haveA.c ipc.c
    a_obj = haveA.o ipc.o
    b_src = haveB.c ipc.c
    b_obj = haveB.o ipc.o
    c_src = haveC.c ipc.c
    c_obj = haveC.c ipc.o
    all:	haveA	haveB	haveC producer
    haveA:		$(a_obj)
    	gcc $(a_obj) -o haveA
    haveA.o: 	$(a_src) $(hdrs)
    	gcc $(opts) $(a_src) -lrt
    haveB:		$(b_obj)
    	gcc $(b_obj) -o haveB
    haveB.o: 	$(b_src) $(hdrs)
    	gcc $(opts) $(b_src) -lrt
    haveC:		$(c_obj)
    	gcc $(c_obj) -o haveC
    haveC.o: 	$(c_src) $(hdrs)
    	gcc $(opts) $(c_src) -lrt
    producer: 	$(p_obj)
    	gcc $(p_obj) -o producer
    producer.o: 	$(p_src) $(hdrs)
    	gcc $(opts) $(p_src) -lrt
    clean:
    	rm haveA haveB haveC producer *.o 
    

    程序运行效果:

    在这里插入图片描述

    注意!

    信号量的key似乎是运行一次结束后并不会删除,导致成功运行一次之后关闭terminal,再次开启时就没有正确的阻塞出现了(生产者无法被阻塞而疯狂生产)。
    目前还不太知道 ipc.c 这个文件的具体实现原理,所以现在只能被迫通过重启虚拟机来重现实验的成功情景。

    凡是出现问题,重启一下说不定就好了呢?

    之后是更难的实验五和实验六,心态出现了一丝变化。

    展开全文
  • 3.4 独立实验 设有两个并发执行的父子进程,不断循环输出各自号、优先数和调度策略。 设有两个并发执行的父子进程,不断循环输出各自号、优先数和调度策略。 设有两个并发执行的父子进程,不断循环输出各自号、优先...
  • 山东大学的看过来啦,实验1-7,第8个还没做呢,做完之后再传,直接加上姓名,学号,班级就可以教了。
  • 实验题目 编写一个父子协作进程,父进程创建一个子进程并控制它每隔 3 秒显示一次当前目录中的文件名列表。 实验代码 pctl.h #include <sys/types.h> #include <wait.h> #include <unistd.h> #...

    实验题目

    编写一个父子协作进程,父进程创建一个子进程并控制它每隔 3 秒显示一次当前目录中的文件名列表。

    实验代码

    pctl.h

    #include <sys/types.h> 
    #include <wait.h> 
    #include <unistd.h> 
    #include <signal.h> 
    #include <stdio.h> 
    #include <stdlib.h> //进程自定义的键盘中断信号处理函数
     typedef void (*sighandler_t) (int); 
     void sigcat()
     { 
         printf("%d Process continue\n",getpid());
         }
    

    pctl.c

    #include "pctl.h"
    int main( int argc,char* argv[])
    {
        int t;
        int pid;
        int status;
        int childpid;
        char *args[]={"/bin/ls","-a",NULL};
        signal(SIGUSR1,(sighandler_t)sigcat);
        pid=fork();
        if(pid<0)
        {
            printf("Create Process fail!\n");
        }
        if(pid==0)
        {
            while ((1))
            {
                pause();
                childpid=fork();
                if(childpid<0)
                    exit(EXIT_FAILURE);
                if(childpid==0)
                   { //printf("I am child process %d\n",getpid());
                    printf("child process will Running:\n");
                    status=execve(args[0],args,NULL);}
               
               
                }
    
    
            }
        else{
                    printf("I am parent process %d\n",getpid());
                    while (1)
                    {
                        /* code */
                        sleep(3);
                        kill(pid,SIGUSR1);
                        printf("%d prarent sent siganl:\n",getpid());
                        
                    }
        }
        return EXIT_SUCCESS;
    }
    

    编译
    gcc -c pctl.c -o pctl.o
    gcc pctl.o -o pctl

    运行结果
    在这里插入图片描述

    展开全文
  • 实验一:参考以上示例程序中建立并发进程的方法,编写一个多进程并发执行程序。父进程首先创建一个执行ls命令的子进程然后再创建一个执行ps命令的子进程,并控制ps命令总在ls命令之前执行。
  • 示例实验 下面的实验想要完成的效果是,在执行这个程序的时候,通过命令行传参,执行shell命令。 // // Created by 吉松阳 on 2021/4/8. // #include "pctrl.h" int main(int argc, char *argv[]) { printf("参数...

    示例实验

    下面的实验想要完成的效果是,在执行这个程序的时候,通过命令行传参,执行shell命令。

    //
    // Created by 吉松阳 on 2021/4/8.
    //
    #include "pctrl.h"
    
    int main(int argc, char *argv[]) {
        printf("参数有%d个\n", argc);
        int pid; //存放子进程号
        int status; 存放子进程返回状态
        char *args[] = {"/usr/bin/ls", "-al", NULL};
        signal(SIGINT, handler);
        pid = fork();
        if (pid < 0) {
            printf("创建进程失败!\n");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            printf("我是子进程 %d , 我的父进程是 %d\n", getpid(), getppid());
            // 暂停, 知道受到信号,(受到信号后可以默认处理,也可以自定义处理函数)
            printf("子进程pause了\n");
            pause();
            sleep();
            printf("%d 子进程要继续执行: ", getpid());
            printf("我是子进程 %d , 现在我的父进程是 %d\n", getpid(), getppid());
            // 执行命令行的命令
            if (argv[1] != NULL) {
                for (int i = 1; argv[i] != NULL; i++) {
                    printf("%s ", argv[i]);
                }
                printf("\n");
                status = execve(argv[1], &argv[1], NULL);
            } else {
                for (int i = 0; args[i] != NULL; i++) {
                    printf("%s ", args[i]);
                }
                printf("\n");
                status = execve(args[0], args, NULL);
            }
            // 下面这行代码不会执行到,因为地址空间已经被替换了
            printf("子进程退出\n");
        } else {
            sleep(2);
            printf("父进程睡眠2秒结束\n");
            printf("我是父进程 %d\n", getpid());
            //如果在命令行上输入了子进程要执行的命令, 则父进程等待子进程执行结束
            if (argv[1] != NULL) {
                printf("等待子进程结束\n");
                waitpid(pid, &status, 0);
                printf("我的子进程的退出状态是 %d\n", status);
            } else {
                //如果在命令行上没输入子进程要执行的命令,唤醒子进程,与子进程并发执行不等待子进程执行结束
                if (kill(pid, SIGINT) >= 0) {
                    printf("%d 父进程唤醒了 %d 子进程\n", getpid(), pid);
                }
                printf("%d 进程没有等待子进程运行结束\n", getpid());
            }
        }
        printf("父进程退出\n");
        return EXIT_SUCCESS;
    }
    
    //
    // Created by 吉松阳 on 2021/4/8.
    //
    
    #ifndef OS_PROCESS_PCTRL_H
    #define OS_PROCESS_PCTRL_H
    
    #include <sys/types.h>
    #include <wait.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    //进程自定义的键盘中断信号处理函数
    void handler(int sig){
        printf("收到信号 %d\n",sig);
        printf("%d 进程继续\n",getpid());
    }
    
    #endif //OS_PROCESS_PCTRL_H
    

    注意如果为了让子进程先pause, 后父进程给它发中断信号(如果命令行没有传参的话),所以我选择让父进程睡眠2秒。

    演示结果。(Ctrl+C给进程发出中断信号)

    songyangji@SongyangJi-Ubuntu-DeskStop:~/OS实验1$ ./pctrl /usr/bin/ls -l
    参数有3个
    我是子进程 6573 , 我的父进程是 6572
    子进程pause了
    父进程睡眠2秒结束
    我是父进程 6572
    等待子进程结束
    ^C收到信号 2
    收到信号 2
    6572 进程继续
    6573 进程继续
    6573 子进程要继续执行: 我是子进程 6573 , 现在我的父进程是 6572
    /usr/bin/ls -l 
    total 64
    -rwxrwxr-x 1 songyangji songyangji 13912 Apr  9 08:41 list_every_3seconds
    -rw-rw-r-- 1 songyangji songyangji  1067 Apr  9 08:41 list_every_3seconds.c
    -rwxrwxr-x 1 songyangji songyangji 14032 Apr  9 00:33 pctrl
    -rw-r--r-- 1 songyangji songyangji  2284 Apr  9 23:20 pctrl.c
    -rw-r--r-- 1 songyangji songyangji   404 Apr  9 00:02 pctrl.h
    -rwxrwxr-x 1 songyangji songyangji 13984 Apr  9 00:54 pctrl1
    -rw-r--r-- 1 songyangji songyangji  2243 Apr  9 00:53 pctrl1.c
    我的子进程的退出状态是 0
    父进程退出
    

    如果是命令行没有传参的话:

    songyangji@SongyangJi-Ubuntu-DeskStop:~/OS实验1$ ./pctrl
    参数有1个
    我是子进程 7630 , 我的父进程是 7629
    子进程pause了
    父进程睡眠2秒结束
    我是父进程 7629
    7629 父进程唤醒了 7630 子进程
    7629 进程没有等待子进程运行结束
    父进程退出
    收到信号 2
    7630 进程继续
    songyangji@SongyangJi-Ubuntu-DeskStop:~/OS实验1$ 7630 子进程要继续执行: 我是子进程 7630 , 现在我的父进程是 1341
    /usr/bin/ls -al 
    total 72
    drwxrwxr-x  2 songyangji songyangji  4096 Apr  9 23:26 .
    drwxr-xr-x 26 songyangji songyangji  4096 Apr  9 16:09 ..
    -rwxrwxr-x  1 songyangji songyangji 13912 Apr  9 08:41 list_every_3seconds
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 08:41 list_every_3seconds.c
    -rwxrwxr-x  1 songyangji songyangji 14032 Apr  9 23:26 pctrl
    -rw-r--r--  1 songyangji songyangji  2302 Apr  9 23:26 pctrl.c
    -rw-r--r--  1 songyangji songyangji   404 Apr  9 00:02 pctrl.h
    -rwxrwxr-x  1 songyangji songyangji 13984 Apr  9 00:54 pctrl1
    -rw-r--r--  1 songyangji songyangji  2243 Apr  9 00:53 pctrl1.c
    
    

    独立实验

    父进程和子进程协作,做到每三秒列出当前目录的信息。

    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <wait.h>
    //进程自定义的键盘中断信号处理函数
    
    static volatile int running = 1;
    
    void handler(int sig) {
        running = 0;
        printf("%d 收到信号 %d\n", getpid(), sig);
    }
    
    int main() {
    
        signal(SIGINT, handler);
        char *args[] = {"/usr/bin/ls", "-la", NULL};
        int pid;
        int status;
    
        while (running) {
            pid = fork();
            if (pid < 0) {
                printf("创建子进程失败");
            } else if (pid == 0) {
                printf("我是子进程 %d , 我的父进程是 %d \n", getpid(), getppid());
                printf("即将执行 ls 命令 ............\n");
                execve(args[0], args, NULL);
            } else {
                printf("我是父进程%d\n", getpid());
                // 等待子进程完成
                waitpid(pid, &status, 0);
                printf("%d 子进程退出, 退出状态码为 %d\n", pid, status);
                sleep(3);
            }
        }
        printf("%d 进程退出\n",getpid());
        return EXIT_SUCCESS;
    }
    

    演示效果(中断的话,键盘ctrl+c发信号)

    songyangji@SongyangJi-Ubuntu-DeskStop:~/OS实验1$ ./list_every_3seconds 
    我是父进程8604
    我是子进程 8605 , 我的父进程是 8604 
    即将执行 ls 命令 ............
    total 76
    drwxrwxr-x  2 songyangji songyangji  4096 Apr  9 23:31 .
    drwxr-xr-x 26 songyangji songyangji  4096 Apr  9 23:31 ..
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 23:31 list3s.c
    -rwxrwxr-x  1 songyangji songyangji 13912 Apr  9 08:41 list_every_3seconds
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 08:41 list_every_3seconds.c
    -rwxrwxr-x  1 songyangji songyangji 14032 Apr  9 23:26 pctrl
    -rw-r--r--  1 songyangji songyangji  2302 Apr  9 23:26 pctrl.c
    -rw-r--r--  1 songyangji songyangji   404 Apr  9 00:02 pctrl.h
    -rwxrwxr-x  1 songyangji songyangji 13984 Apr  9 00:54 pctrl1
    -rw-r--r--  1 songyangji songyangji  2243 Apr  9 00:53 pctrl1.c
    8605 子进程退出, 退出状态码为 0
    我是父进程8604
    我是子进程 8615 , 我的父进程是 8604 
    即将执行 ls 命令 ............
    total 76
    drwxrwxr-x  2 songyangji songyangji  4096 Apr  9 23:31 .
    drwxr-xr-x 26 songyangji songyangji  4096 Apr  9 23:31 ..
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 23:31 list3s.c
    -rwxrwxr-x  1 songyangji songyangji 13912 Apr  9 08:41 list_every_3seconds
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 08:41 list_every_3seconds.c
    -rwxrwxr-x  1 songyangji songyangji 14032 Apr  9 23:26 pctrl
    -rw-r--r--  1 songyangji songyangji  2302 Apr  9 23:26 pctrl.c
    -rw-r--r--  1 songyangji songyangji   404 Apr  9 00:02 pctrl.h
    -rwxrwxr-x  1 songyangji songyangji 13984 Apr  9 00:54 pctrl1
    -rw-r--r--  1 songyangji songyangji  2243 Apr  9 00:53 pctrl1.c
    8615 子进程退出, 退出状态码为 0
    我是父进程8604
    我是子进程 8625 , 我的父进程是 8604 
    即将执行 ls 命令 ............
    total 76
    drwxrwxr-x  2 songyangji songyangji  4096 Apr  9 23:31 .
    drwxr-xr-x 26 songyangji songyangji  4096 Apr  9 23:31 ..
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 23:31 list3s.c
    -rwxrwxr-x  1 songyangji songyangji 13912 Apr  9 08:41 list_every_3seconds
    -rw-rw-r--  1 songyangji songyangji  1067 Apr  9 08:41 list_every_3seconds.c
    -rwxrwxr-x  1 songyangji songyangji 14032 Apr  9 23:26 pctrl
    -rw-r--r--  1 songyangji songyangji  2302 Apr  9 23:26 pctrl.c
    -rw-r--r--  1 songyangji songyangji   404 Apr  9 00:02 pctrl.h
    -rwxrwxr-x  1 songyangji songyangji 13984 Apr  9 00:54 pctrl1
    -rw-r--r--  1 songyangji songyangji  2243 Apr  9 00:53 pctrl1.c
    8625 子进程退出, 退出状态码为 0
    ^C8604 收到信号 2
    8604 进程退出
    
    

    无中文注释版

    #include <unistd.h>
    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <wait.h>
    //进程自定义的键盘中断信号处理函数
    
    static volatile int running = 1;
    
    void handler(int sig) {
        running = 0;
        //printf("%d 收到信号 %d\n", getpid(), sig);
    }
    
    int main() {
    
        signal(SIGINT, handler);
        char *args[] = {"/usr/bin/ls", "-la", NULL};
        int pid;
        int status;
    
        while (running) {
            pid = fork();
            if (pid < 0) {
                printf("创建子进程失败");
            } else if (pid == 0) {
                //printf("我是子进程 %d , 我的父进程是 %d \n", getpid(), getppid());
                //printf("即将执行 ls 命令 ............\n");
                execve(args[0], args, NULL);
            } else {
                //printf("我是父进程%d\n", getpid());
                // 等待子进程完成
                waitpid(pid, &status, 0);
                //printf("%d 子进程退出, 退出状态码为 %d\n", pid, status);
                sleep(3);
            }
        }
        //printf("%d 进程退出\n",getpid());
        return EXIT_SUCCESS;
    }
    
    展开全文

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 242
精华内容 96
关键字:

山东大学操作系统实验