视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
实验二进程同步实验报告
2025-09-26 21:59:01 责编:小OO
文档
实验二 进程同步

一、实验目的:

掌握基本的同步算法,理解经典进程同步问题的本质;学习使用Linux的进程同步机制,掌握相关API的使用方法;能利用信号量机制,采用多种同步算法实现不会发生死锁的哲学家进餐程序。

二、实验平台:

虚拟机:VMWare12

操作系统:kali linux

编辑器:Gedit 

编译器:Gcc

三、实验内容:

(1)以哲学家进餐模型为依据,在Linux控制台环境下创建5个进程,用semget函数创建一个信号量集(5个信号量,初值为1),模拟哲学家的思考和进餐行为:每一位哲学家饥饿时,先拿起左手筷子,再拿起右手筷子;筷子是临界资源,为每一支筷子定义1个互斥信号量;想拿到筷子需要先对信号量做P操作,使用完释放筷子对信号量做V操作。

伪代码描述:

semaphore chopstick[5]={1,1,1,1,1};

•第i位哲学家的活动可描述为:     

  printf("%d is thinking\\ni);

   printf("%d is hungry\\ni);

 [i])拿左筷子 

  [(i+1) % 5])拿右筷子 

 printf("%d is eating\\ni);          

  [i])放左筷子 

  [(i+1) % 5])放右筷子 

  …

      }while[true]; 

运行该组进程,观察进程是否能一直运行下去,若停滞则发生了什么现象?并分析原因。

(2)解决哲学家进餐问题可采用如下方法:a.仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐;b.至多只允许有4位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐;c.规定奇数号哲学家先拿起他左手的筷子,然后再拿起他右手的筷子,而偶数号哲学家则先拿起他右手的筷子,然后再拿起他左手的筷子。方法a在示例程序中给出,请用方法b和c写出不会发生死锁的哲学家进餐程序。

  (3)设计程序,实现生产者/消费者进程(线程)的同步与互斥。在该程序中创建4个进程(或线程)模拟生产者和消费者,实现进程(线程)的同步与互斥。

四、实验步骤:

一、Linux的信号量机制

为了简化对多个信号量的操作,Linux系统中提出了信号量集的概念。一个信号量集对象中可以容纳多个信号量,System V 信号量的分配和操作是以信号量集为单位的。

1.  进程利用信号量获得共享资源的步骤:

a.测试控制该资源的信号量;

b.若信号量为正,则进程可以使用该资源;

使用资源时,进程将该信号量减1(P操作);

不再使用资源时,进程将该信号量值加1(V操作);

c.若信号量为0,则进程进入睡眠状态

P操作和V操作都原子操作。

2.  创建或打开信号量集

semget()系统调用创建一个新信号量集或获取一个既有信号量集的表示符。

函数原型:

#include

int semget(key_t key, int nsems, int flag);

返回值:成功返回信号量集ID,出错返回-1。

nsems信号量集中信号量个数;flags:IPC_CREAT,IPC_EXCL,权限组合;

第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。

第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

注意:建立信号量集时每个信号量的初始值不确定。

3.  信号量控制操作

semctl()系统调用在一个信号量集或集合中的单个信号量上执行各种控制操作。

函数原型:

#include

int semctl (int semid, int semnum, int cmd,  …/*union semun arg*/)

semnum:信号量编号或0,表示对指定信号量做控制操作;

cmd: 操作命令,实施的控制操作;

cmd参数:

IPC_STAT     获取信号量集的属性

IPC_SET      设置信号量集的属性

IPC_RMID    删除信号量集

GETVAL      返回semnum信号量的值

SETVAL       设置semnum信号量的值

GETALL      获取所有信号量的值

SETALL       设置所有信号量的初始值

第四个参数是union semun

union semun{

    int  val;

    struct semid_ds  *buf;

    unsigned short  *array;

}

例:定义一个信号量集,含3个信号量,初始值分别为(2,5,1)。

semid = semget(IPC_PRIVATE,3,IPC_CREAT|IPC_EXCL|0777);

unsigned short vals[3] = {2,5,1};

union semnu se;

se.array = vals;

semclt(semid,0,SETALL,se);

4.  信号量集操作

semop()系统调用在semid标识的信号量集中的信号量上执行一个或多个up或down操作,可用于进程间的同步和互斥。

函数原型:

#include

int semop(int semid, struct sembuf *semop, size_t nops);

返回:成功返回0,出错返回-1。

Struct sembuf{

     sem_op; /*operation(negative, 0, positive*/

 short   sem_flg;      /*IPC_NOWAIT,SEM_UNDO*/

}

例:对上文定义的信号量集semid中的3个信号量,分别执行如下操作:1: p(1); 2: p(3); 3: v(3);

其semop的操作语句为:

struct sembuf ops[3] = {{0, -1, SEM_UNDO}, {1,-3, SEM_UNDO},{2,3,  SEM_UNDO}};

semop(semid, ops ,3);

5.组合权限说明

0666  文件或目录的所有者、所有者所在组、其他用户对该对象均可读、可写rw-rw-rw-

0777  文件或目录的所有者、所有者所在组、其他用户对该对象均可读、可写、可执行

rwxrwxrwx

附:示例程序(左、右两只筷子均可用时才允许拿起筷子)

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#ifdef    _SEM_SEMUN_UNDEFINED

union semun

{

    int val;

    struct semid_ds *buf;

    unsigned short *array;

    struct seminfo *__buf;

};

#endif

#define    ERR_EXIT(m) \

    do { \

        perror(m); \

        exit(EXIT_FAILURE); \

    } while(0)

int wait_1chopstick(int no,int semid)

{

    struct sembuf sb = {no,-1,0};

    int ret;

    ret = semop(semid,&sb,1);

if(ret < 0) {

        ERR_EXIT("semop");

    }

    return ret;

}

int free_1chopstick(int no,int semid)

{

    struct sembuf sb = {no,1,0};

    int ret;

    ret = semop(semid,&sb,1);

if(ret < 0) {

        ERR_EXIT("semop");

    }

    return ret;

}

#define    DELAY (rand() % 5 + 1)

void wait_for_2chopstick(int no,int semid)

{

    int left = no;

    int right = (no + 1) % 5;

    struct sembuf buf[2] = {

        {left,-1,0},

        {right,-1,0}

    };

    semop(semid,buf,2);

}

void free_2chopstick(int no,int semid)

{

    int left = no;

    int right = (no + 1) % 5;

    struct sembuf buf[2] = {

        {left,1,0},

        {right,1,0}

    };

    semop(semid,buf,2);

}

void philosophere(int no,int semid)

{

    srand(getpid());

    for(;;) {

       #if 0

        printf("%d is thinking\\n",no);

        sleep(DELAY);

        printf("%d is hungry\\n",no);

        wait_for_2chopstick(no,semid);

        printf("%d is eating\\n",no);

        sleep(DELAY);

        free_2chopstick(no,semid);

    #else

        int left = no;

        int right = (no + 1) % 5;

        printf("%d is thinking\\n",no);

        sleep(DELAY);

        printf("%d is hungry\\n",no);

        wait_1chopstick(left,semid);

        sleep(DELAY);

        wait_1chopstick(right,semid);

        printf("%d is eating\\n",no);

        sleep(DELAY);

        free_1chopstick(left,semid);

        free_1chopstick(right,semid);

    #endif

    }

}

int main(int argc,char *argv[])

{

    int semid;

    semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666);

if(semid < 0) {

        ERR_EXIT("semid");

    }

    union semun su;

    su.val = 1;

    int i;

for(i = 0;i < 5;++i) {

        semctl(semid,i,SETVAL,su);

    }

    int num = 0;

    pid_t pid;

for(i = 1;i < 5;++i) {

        pid = fork();

        if(pid < 0) {

            ERR_EXIT("fork");

        }

        if(0 == pid) {

            num = i;

            break;

        }

    }

    philosophere(num,semid);

    return 0;

}

五、实验结果

下载本文

显示全文
专题