精华内容
下载资源
问答
  • linux进程间通信实验
    2021-05-12 14:22:02

    《Linux进程通信实验报告》由会员分享,可在线阅读,更多相关《Linux进程通信实验报告(5页珍藏版)》请在人人文库网上搜索。

    1、Linux进程通信实验报告一、 实验目的和要求1. 进一步了解对进程控制的系统调用方法。2. 通过进程通信设计达到了解UNIX或Linux系统中进程通信的基本原理。二、 实验内容和原理1. 实验编程,编写程序实现进程的管道通信(设定程序名为pipe.c)。使用系统调用pipe()建立一条管道线。而父进程从则从管道中读出来自于两个子进程的信息,显示在屏幕上。要求父进程先接受子进程P1发来的消息,然后再接受子进程P2发来的消息。2. 可选实验,编制一段程序,使其实现进程的软中断通信(设定程序名为softint.c)。使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键。

    2、盘上来的中断信号(即按Del键),当父进程接受这两个软中断的其中一个后,父进程用系统调用kill()向两个子进程分别发送整数值为16和17的软中断信号,子进程获得对应软中断信号后分别输出相应信息后终止。三、 实验环境一台安装了Red Hat Linux 9操作系统的计算机。四、 实验操作方法和步骤进入Linux操作系统,利用vi编辑器将程序源代码输入并保存好,然后打开终端对程序进行编译运行。五、 实验中遇到的问题及解决六、 实验结果及分析基本实验可选实验七、 源代码Pipe.c#includestdio.h#includeunistd.hmain()int i,j,fd2;char S100;。

    3、pipe(fd);if(i=fork=0)sprintf(S,child process 1 is sending a message n);write(fd1,S,50);sleep(3);return;if(j=fork()=0)sprintf(S,child process 2 is sending a message n);write(fd1,S,50);sleep(3);return;elsewait(0);read(fd0,S,50);printf(%s,S);read(fd0,S,50);printf(%s,S);return;Softint.c#includestdio.h#i。

    4、ncludeunsitd.hmain()int i,j,fd2;char S100;pipe(fd);if(i=fork=0)sprintf(S,child process 1 is sending a message n);write(fd1,S,50);sleep(3);return;if(j=fork()=0)sprintf(S,child process 2 is sending a message n);write(fd1,S,50);sleep(3);return;elsewait(0);read(fd0,S,50);printf(%s,S);read(fd0,S,50);printf(%s,S);return。

    更多相关内容
  • 实验Linux 进程间通信2 4 课时 实验目的 理解进程通信原理掌握进程中信号量共享内存消息队列相关的函数的使用 实验原理 Linux 下进程通信相关函数除上次实验所用的几个还有 信号量 信号量又称为信号灯它是用来协调...
  • 实验Linux进程间通信24课时 实验目的 理解进程通信原理掌握进程中信号量共享内存消息队列相关的函数的使用 实验原理 Linux下进程通信相关函数除上次实验所用的几个还有 信号量 信号量又称为信号灯它是用来协调不同...
  • UNIX Linux实验教程 4实验Linux进程间通信.doc
  • 操作系统课设之Linux 进程间通信

    千次阅读 2021-06-28 17:29:30
    操作系统课程设计 Linux进程通信 Linux消息通信机制

    前言

    课程设计开始了,实验很有意思,写博客总结学到的知识
    白嫖容易,创作不易,学到东西才是真
    本文原创,创作不易,转载请注明!!!
    本文链接
    个人博客:https://ronglin.fun/archives/175
    PDF链接:见博客网站
    CSDN: https://blog.csdn.net/RongLin02/article/details/118308512

    为了美观,实验源代码在结尾处,整合版见下
    链接:https://pan.baidu.com/s/1rXj1QJGuw-BVc5sQWret9w
    提取码:Lin2
    操作系统课程设计源代码
    本次操作系统课程设计合集
    操作系统课设之Windows 进程管理
    操作系统课设之Linux 进程管理
    操作系统课设之Linux 进程间通信
    操作系统课设之Windows 的互斥与同步
    操作系统课设之内存管理
    操作系统课设之虚拟内存页面置换算法的模拟与实现
    操作系统课设之基于信号量机制的并发程序设计
    操作系统课设之简单 shell 命令行解释器的设计与实现
    仅用于学习,如有侵权,请联系我删除

    实验题目

    Linux 进程间通信

    实验目的

    Linux 系统的进程通信机构(IPC)允许在任意进程间大批量地交换数据,通过本实验,理解熟悉 Linux 支持的消息通信机制。

    实验内容

    实验原理:主要用到4个函数

    int msgget(key_t key, int msgflag);
    

    key:某个消息队列的名字,用ftok()产生 (函数ftok的返回值)或IPC_PRIVATE,获取消息队列的标识符,创建和访问一个消息队列。

    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    

    msqid:消息队列标识符,由msgget获得;
    cmd:控制标识符,IPC_SET 命令:设置属主的用户标识符和组标识符,IPC_STAT 和 IPC_INFO 命令:获得资源状态信息,IPC_RMID 命令:释放这个资源;
    buf:消息队列管理结构体,请参见消息队列内核结构说明部分。

    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    

    msqid:消息队列标识符;
    msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下:

        struct s_msg{ /*msgp定义的参照格式*/
         long type; /* 必须大于0,消息类型 */
               char mtext[256]; /*消息正文,可以是其他任何类型*/
        } msgp;
    

    msgsz:要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度;
    msgflg:控制位,规定当核心用尽内部缓冲空间时应执行的动作

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    

    msqid:消息队列标识符;
    msgp:存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同;
    msgsz:要接收消息的大小,不含消息类型占用的4个字节;
    msgtyp:0:接收第一个消息;>0:接收类型等于msgtyp的第一个消息;<0:接收类型等于或者小于msgtyp绝对值的第一个消息;
    msgflg:规定倘若该队列无消息,核心应当做什么事,如果此时设置了 IPC_NOWAIT 标志,则
    立即返回,若在 flag 中设置了 MSG_NOERROR,且所接收的消息大小大于 size ,核心截断所接收的消息。
    msgrcv()解除阻塞的条件有以下三个:
    1.消息队列中有了满足条件的消息。
    2.msqid代表的消息队列被删除。
    3.调用msgrcv()的进程被信号中断。
    用以上4个函数实现Linux系统下的通信。

    实验步骤:
    在Linux虚拟机下,用codeblocks新建一个project,将指导书的代码copy到main.cpp文件下,多次运行查看结果。
    关键代码如下:

    #define MSGKEY 75
    struct msgform
    {
        long mtype;
        char mtext[1030];
    } msg;
    int msgqid,i;
    void CLIENT()
    {
        int i;
        msgqid=msgget(MSGKEY,0777);
        for (i=10; i>=1; i--)
        {
            msg.mtype=i;
            printf("(client) sent \n");
            msgsnd(msgqid,&msg,1024,0);
        }
        exit(0);
    }
    void SERVER()
    {
        msgqid=msgget(MSGKEY,0777|IPC_CREAT);
        do
        {
            msgrcv(msgqid,&msg,1030,0,0);
            printf("(Server) recieved\n");
        }
        while(msg.mtype!=1);
        msgctl(msgqid,IPC_RMID,0);
        exit(0);
    }
    int main()
    {
        while((i=fork())==-1);
        if(!i) SERVER();
        while((i=fork())==-1);
        if(!i) CLIENT();
        wait(0);wait(0);
        return 0;
    }
    

    四、实验结果与分析
    运行结果如下:
    在这里插入图片描述
    多次结果运行结果均为上,只有极少数运行结果是client发送一条server接受一条。
    对于以上结果,查阅资料可知:
    message的传送和控制并不保证完全同步,当一个程序不再激活状态的时候,它完全可能继续睡眠,造成上面现象,在多次send message后才receive message.这一点有助于理解消息转送的实现机理。
    消息通信的特点:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息的传递,自身就带有同步的控制.当等到消息的时候,进程进入睡眠状态,不再消耗CPU资源。

    小结与心得体会

    由于main函数两次fork(),最开始我认为server也会fork()出client子进程,也就是,我认为作为子进程的server还会生成”孙”进程,但是多次运行调试后,结果并不是我想的那样,单步调试加测试输出之后,我发现了 exit(0),它在子函数里边,也就是说在执行完server()/client()子函数之后,子进程直接结束,不再返回main()函数。
    对于Linux进程通信,之前了解的比较少,只是听说过管道的概念。这次验证实验了解了Linux进程间通信的基本方法,获益匪浅。
    =w=

    源代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    #include <unistd.h>
    #include<sys/wait.h>
    #define MSGKEY 75
    struct msgform
    {
        long mtype;
        char mtext[1030];
    } msg;
    int msgqid,i;
    void CLIENT()
    {
        int i;
        //flag 本身由操作允许权和控制命令值相“或”得到。
        msgqid=msgget(MSGKEY,0777);
        for (i=10; i>=1; i--)
        {
            msg.mtype=i;
            printf("(client) sent \n");
            //flag 规定当核心用尽内部缓冲空间时应执行的动作
            msgsnd(msgqid,&msg,1024,0);
        }
        exit(0);
    }
    void SERVER()
    {
        //IPC_CREAT | 0400 是否该队列应被创建;
        msgqid=msgget(MSGKEY,0777|IPC_CREAT);
        do
        {
            //type 是用户要读的消息类型:
            msgrcv(msgqid,&msg,1030,0,0);
            printf("(Server) recieved\n");
        }
        while(msg.mtype!=1);
        msgctl(msgqid,IPC_RMID,0);
        exit(0);
    }
    int main()
    {
        while((i=fork())==-1);
        if(!i)
            SERVER();
        while((i=fork())==-1);
        if(!i)
            CLIENT();
        wait(0);
        wait(0);
        return 0;
    }
    
    
    展开全文
  • 实验六:进程间通信实验目的:学会进程间通信方式:无名管道,有名管道,信号,消息队列,实验要求:在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道在进程中为 SIGBUS注册处理函数,并向该...

    实验六:进程间通信

    实验目的:

    学会进程间通信方式:无名管道,有名管道,信号,消息队列,

    实验要求:

    在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道

    在进程中为 SIGBUS注册处理函数,并向该进程发送SIGBUS信号

    创建一消息队列,实现向队列中存放数据和读取数据

    实验器材:

    软件:安装了 Linux的vmware虚拟机

    硬件:PC机一台

    实验步骤:

    (一)无名管道的使用

    1编写实验代码 pipe rw.c

    #in elude

    #in elude

    #in elude

    #i nclude

    #in elude

    #i nclude

    int mai n()

    {

    int pipe_fd[2];〃管道返回读写文件描述符

    pid_t pid;

    char buf_r[100];

    char* p_wbuf;

    int r_num;

    memset(buf_r,0,sizeof(buf_r));〃 将 buf_r 初始化

    char str1[]= "parent writel holle ” ”

    char str2[]= "parent write2 pipe”n”

    r_num=30;

    /*创建管道*/

    if(pipe(pipe_fd)<0)

    {

    prin tf("pipe create error'n ”);

    return -1;

    }

    /*创建子进程*/

    if((pid=fork())==0)〃子进程执行代码

    {

    〃1、子进程先关闭了管道的写端

    close(pipe_fd[1]);

    〃2、让父进程先运行,这样父进程先写子进程才有内容读

    sleep(2);

    〃3、读取管道的读端,并输出数据

    if(read(pipe_fd[O],buf_r, r_num)<0)

    {

    printf( read error!");

    exit(-1);

    }

    printf( %s\n",buf_r);

    〃4、关闭管道的读端,并退出

    close(pipe_fd[1]);

    }

    else if(pid>0) //父进程执行代码

    {

    〃1、父进程先关闭了管道的读端

    close(pipe_fd[0]);

    〃2、向管道写入字符串数据

    p_wbuf= &str1;

    write(pipe_fd[1],p_wbuf,sizof(p_wbuf));

    p_wbuf= &str2; write(pipe_fd[1],p_wbuf,sizof(p_wbuf));

    〃3、关闭写端,并等待子进程结束后退出

    close(pipe_fd[1]);

    }

    return 0;

    }

    /***********************

    #in clude

    #in clude

    #in clude

    #i nclude

    #in clude

    #i nclude

    int mai n()

    {

    int pipe_fd[2];//管道返回读写文件描述符

    pid_t pid;

    char buf_r[100];

    char* p_wbuf;

    int r num;

    memset(buf_r,0,sizeof(buf_r));〃 将 buf_r 初始化

    char str1[]="holle";

    char str2[]="pipe";

    r_num=10;

    /*创建管道*/

    if(pipe(pipe_fd)<0)

    {

    prin tf("pipe create error'n ”); return -1;

    }

    /*创建子进程*/

    if((pid=fork())==0)〃子进程执行代码

    {

    close(pipe_fd[1]);//1、子进程先关闭了管道的写端

    〃2、让父进程先运行,这样父进程先写子进程才有内容读

    〃3、读取管道的读端,并输出数据

    if(read(pipe_fd[0],buf_r, r_n um)<0)

    {

    prin tf("read1 error!");

    exit(-1);

    }

    prin tf("\npare nt write1 %s!",buf_r);

    sleep(1);

    if(read(pipe_fd[0],buf_r, r_n um)<0)

    {

    prin tf("read2 error!");

    exit(-1);

    }

    prin tf("\npare nt write2 %s!",buf_r);

    close(pipe_fd[1]);//4、关闭管道的读端,并退出

    exit(1);

    //prin tf("child error!");

    展开全文
  • 通过对进程间通信同步/互斥问题的编程实现,加深理解信号量和 P、V 操作的原理; 对 Windows 或 Linux 涉及的几种互斥、同步机制有更进一步的了解;熟悉 Windows 或 Linux 中定义的与互斥、同步有关的函数。
  • Linux第二次试验:Linux下的进程通信实验...理解Linux进程间通信机制。 掌握和使用消息队列实现进程间通信。 掌握和使用共享主存实现进程间通信。 掌握何使用信号量实现进程同步。 二、实验工具与设备 装有Linux系统

    前言

    为了帮助同学们完成痛苦的实验课程设计,本作者将其作出的实验结果及代码贴至CSDN中,供同学们学习参考。如有不足或描述不完善之处,敬请各位指出,欢迎各位的斧正!

    一、实验目的

    1. 理解Linux的进程间通信机制。
    2. 掌握和使用消息队列实现进程间通信。
    3. 掌握和使用共享主存实现进程间通信。
    4. 掌握何使用信号量实现进程同步。

    二、实验工具与设备

    装有Linux系统的计算机。

    三、实验预备知识

    Linux继承了Unix v的进程间通信机制,即消息队列、信号量和共享主存。通常把消息、信号量和共享主存统称为system v ipc对象或ipc资源。系统在进程请求ipc对象时在内核动态创建一个ipc数据结构并初始化,该结构定义了对该ipc对象访问的许可权, 每个对象都具有同样类型的接口-函数,这组函数为应用程序提供3种服务:
    通过信号量实现与其他进程的同步。
    通过消息队列以异步方式为通信频繁但数据量少的进程间通信提供服务。
    通过共享主存,为大数据量的进程间通信提供服务。

    1. 共性描述
      1.1. 公有的数据结构
      每一类ipc资源都有一个ipc_ids结构的全局变量用来描述同一类资源的共用数据,三种ipc对象对应的三个全局变量分别是semid_ds,msgid_ds和shmid_ds。
      ipc_ids结构定义如下:
    struct ipc_ids{
    		int size;   /*entries数组的大小*/
    		int in_use;	/*entries数组已使用的元素个数*/
    		int max_id;
    		unsigned short  seq;
    		unsigned short seq_max;
    		struct semaphore sem; /*控制对ipc_ids结构的访问*/
    		spinlock_t ary; 		/*自旋锁控制对数组entries的访问*/
    		struct ipc_id* entries;
    	};
    
    	struct ipc_id{ struct ipc_perm *p;};
    

    每个ipc对象都有一个ipc_perm数据结构,用于描述其属性。

    struct ipc_perm
    {
        __key_t __key;	/* ipc键*/
        __uid_t uid;                /* Owner's user ID.  */
        __gid_t gid;                /* Owner's group ID.  */
        __uid_t cuid;        /* Creator's user ID.  */
        __gid_t cgid;        /* Creator's group ID.  */
        unsigned short int mode;/* Read/write permission.  */
        unsigned short int __seq;/* Sequence number.  */
    };
    

    每一个ipc对象都有一个32位的ipc键和一个ipc标识符,ipc标识符由内核分配给ipc对象,在系统内部惟一,IPC机制提供了相应的编程接口根据IPC键值获取标识符。对于一个IPC对象,在打开时返回一个标识符,而关闭后再次打开同一个IPC对象时,该标识符将顺序加1,而键是ipc对象的外部表示,可由程序员选择。不同的进程直接使用key去创建IPC对象容易引起混淆,并且不同的应用之间可能会因为使用同一个key而产生冲突。为此,Linux系统提供了如下机制产生惟一的关键字。
    1)创建IPC对象时,指定关键字为IPC_PRIVATE。通过该参数创建的IPC对象的关键字值是0,所以无法在其他进程中通过关键字对该对象进行访问,只能通过返回的标识符进行访问。
    2)调用函数ftok()产生一个惟一的关键字值。通过IPC进行通信的进程,只需要按照相同的参数调用ftok即可产生惟一的参数。通过该参数可有效解决关键字的产生及惟一性问题。

    1.2. Linux提供的ipc函数
    (1)ipc对象的创建函数
    semget():获得信号量的ipc标识符
    msgget():获得消息队列的ipc标识符
    shmget():获得共享主存的ipc标识符
    (2)ipc资源控制函数
    创建ipc对象后,用户可以通过下面的库函数对ipc对象进行控制
    semctl():对信号量资源进行控制
    msgctl():对消息队列进行控制
    shmctl():对共享主存进行控制
    (3)资源操作函数
    semop():用于对信号量资源进行操作,获得或释放一个ipc信号量
    msgsnd()及msgrcv():发送和接收一个ipc消息
    shmat()及shmdt():分别将一个ipc共享主存区附加到进程的虚拟地址空间,以及把共享主存区从进程的虚拟地址空间剥离出去

    1. 消息队列
      Linux中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC的标识号唯一的标识。Linux 为系统中所有的消息队列维护一个 msg_queue 链表,该链表中的每个指针指向一个 msgid_ds 结构,该结构完整描述对应的一个消息队列。
      2.1、消息队列结构(msgid_ds)
    struct msqid_ds { 
    	struct ipc_perm msg_perm; 
    	struct msg *msg_first; /* 队列上第一条消息,即链表头*/ 
    	struct msg *msg_last; /* 队列中的最后一条消息,即链表尾 */ 
    	time_t msg_stime; /* 发送给队列的最后一条消息的时间 */ 
    	time_t msg_rtime; /* 从消息队列接收到的最后一条消息的时间 */ 
    	time_t msg_ctime; /* 最后修改队列的时间*/ 
    	ushort msg_cbytes; /*队列上所有消息总的字节数 */ 
    	ushort msg_qnum; /*在当前队列上消息的个数 */ 
    	ushort msg_qbytes; /* 队列最大的字节数 */ 
    	ushort msg_lspid; /* 发送最后一条消息的进程的pid */ 
    	ushort msg_lrpid; /* 接收最后一条消息的进程的pid */ 
    	};
    

    2.2、消息结构(msg)
    内核把每一条消息存储在以msg结构为框架的队列中

    struct msg { 
    	struct msg *msg_next; /* 队列上的下一条消息 */ 
    	long msg_type; /*消息类型*/ 
    	char *msg_spot; /* 消息正文的地址 */ 
    	short msg_ts; /* 消息正文的大小 */ 
    	};
    

    2.3、消息缓冲区(msgbuf)

    struct msgbuf { 
    	long mtype; /* 消息的类型,必须为正数 */ 
    	char mtext[1]; /* 消息正文 */ 
    	};
    

    对发送消息,先预设一个msgbuf缓冲区并写入消息类型何内容,调用相应的发送函数。对读取消息,先分配一个msgbuf缓冲区。然后把消息读入该缓冲区
    2.4、消息队列基本操作
    (1)相应头文件

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    

    (2)打开或创建消息队列
    为了创建一个新的消息队列,或存取一个已经存在的队列,要使用msgget()系统调用

    int msgget(key_t kye,int flag);
    函数key是创建/打开队列的键值,直接用常量指定或由ftok()函数产生,flag参数指定创建/打开方式。
    可以是ipc_create、ipc_excl、ipc_nowait或三者的或结果。
    (3)发送消息

    int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz,int msgflg ); 
    

    第一个参数是队列标识符,由msgget()调用返回。第二个参数msgp是一个指针,指向我们重新声明和装载的消息缓冲区。msgsz参数包含了消息以字节为单位的长度,其中包括了消息类型的4个字节。msgflg参数可以设置成0(忽略),或者:
    IPC_NOWAIT :如果消息队列满,消息不写到队列中,并且控制权返回给调用进程(继续执行)。如果不指定
    IPC_NOWAIT,调用进程将挂起(阻塞)直到消息被写到队列中。
    (4)读取消息

    int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz,long mtype, int msgflg );
    

    第一个参数用来指定要检索的队列(必须由msgget()调用返回),第二个参数(msgp)是存放检索到消息的缓冲区的地址,第三个参数(msgsz)是消息缓冲区的大小,包括消息类型的长度(4字节),第四个参数(mtype)指定了消息的类型,如果传递给mytype参数的值为0,就可以不管类型,只返回队列中最早的消息。
    如果传递给参数msgflg的值为IPC_NOWAIT,并且没有可取的消息,那么给调用进程返回ENOMSG错误消息,否则,调用进程阻塞,直到一条消息到达队列并且满足msgrcv()的参数。
    (5)消息队列属性操作

    int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
    

    msgqid是打开的消息队列id,buf是用户缓冲区,供用户存放控制参数何查询结果,cmd是规定的命令:
    IPC_STAT 读取消息队列的数据结构msqid_ds,并将其存储在b u f指定的地址中。
    IPC_SET 设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
    IPC_RMID 从系统内核中移走消息队列。
    (6)消息队列的应用
    见试验内容1

    1. 信号量
      system v支持的信号量其实是一个信号量集合,由多个单独的信号量组成,当创建一个信号量集合时,必须指定该集合中信号量的数量。Linux内核为每个信号量集合定义一个semid_ds的描述结构,系统中所有的信号量集组成一个sem_array的链表,该链表中的每个节点都指向一个semid_ds结构,每个sem_ds
      结构中有一个指向sem结构类型数组的指针,这个指针所指向的数组中的每一个元素代表一个信号量。
      3.1、信号量数据结构

    struct sem{
    int semval; /信号量的当前值/
    int sempid; /在信号量上最后一次操作的进程识别号/
    };

    3.2、信号量集合数据结构

    struct semid_ds {
    	struct ipc_perm sem_perm;	/*IPC权限 */
    	long	sem_otime; 		/* 最后一次对信号量操作(semop)的时间 */
    	long	sem_ctime;		/* 对这个结构最后一次修改的时间 */
    	struct sem		/*sem_base;		/* 在信号量数组中指向第一个信号量的指针 */
    	struct sem_queue *sem_pending;		/* 待处理的挂起操作*/
    	struct sem_queue **sem_pending_last;	 /	* 最后一个挂起操作 */
    	struct sem_undo *undo; 		 /* 在这个数组上的undo 请求 */
    	ushort	sem_nsems;	 	/* 在信号量数组上的信号量号 */
    	};
    

    3.3、信号量集中的队列结构

    struct sem_queue {	
    		struct sem_queue * next;   /* 队列中下一个节点 */
    		struct sem_queue **    prev;   /* 队列中前一个节点, *(q->prev) == q */
    		struct wait_queue *    sleeper;    /* 正在睡眠的进程 */
    		struct sem_undo *  undo;   /* undo 结构*/
    		int            pid;    /* 请求进程的进程识别号 */
    		int            status; /* 操作的完成状态 */
    		struct semid_ds *  sma;    /*有操作的信号量集合数组 */
    		struct sembuf *    sops;   /* 挂起操作的数组 */
    		 int    nsops;  /* 操作的个数 */
    };
    

    3.4、信号量基本操作
    (1)头文件

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>

    (2)打开或创建信号量
    使用segget()系统调用创建一个新的信号量集合,或者存取一个已存在的集合。

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

    参数key和semflg取值与msgget()函数对应参数相同,nsems指定要创建或打开的信号量集合中包含的信号量数目。
    (3)信号量值操作

    int semop ( int semid, struct sembuf *sops, unsigned nsops);
    

    参数sops是一个指针,它指向在集合上执行操作的数组,数组类型为sembuf,而参数nsops是在那个数组上操作的个数。

    struct sembuf {
                ushort  sem_num;        /* 在数组中信号量的索引值 */
                short   sem_op;         /* 信号量操作值(正数、负数或0) */
                short   sem_flg;        /* 操作标志,为IPC_NOWAIT或SEM_UNDO*/
        };
    

    如果sem_op为负数,那么就从信号量的值中减去sem_op的绝对值,这意味着进程要获取资源,这些资源是由信号量控制或监控来存取的。如果没有指定IPC_NOWAIT,那么调用进程睡眠到请求的资源数得到满足(其它的进程可能释放一些资源),如果sem_op是正数,把它的值加到信号量,这意味着把资源归还给应用程序的集合。
    Linux 按如下的规则判断是否所有的操作都可以成功:操作值和信号量的当前值相加大于 0,或操作值和当前值均为 0,则操作成功。
    (4)信号量属性操作

    int semctl ( int semid, int semnum, int cmd, union semun arg );
    

    semnum是将要操作的信号量个数,cmd参数表示在集合上执行的命令,arg参数的类型为semun

    union semun {
                    int val;                /* value for SETVAL */
                    struct semid_ds *buf;   /* buffer for IPC_STAT & IPC_SET */
                    ushort *array;          /* array for GETALL & SETALL */
                    struct seminfo *__buf;  /* buffer for IPC_INFO */
                    void *__pad;
            };
    

    cmd命令及解释

    命令
    解 释
    IPC_STAT
    从信号量集合上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
    IPC_SET
    设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
    IPC_RMID
    从内核中删除信号量集合
    GETALL
    从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中
    GETNCNT
    返回当前等待资源的进程个数
    GETPID
    返回最后一个执行系统调用semop()进程的PID
    GETVAL
    返回信号量集合内单个信号量的值
    GETZCNT
    返回当前等待100%资源利用的进程个数
    SETALL
    与GETALL正好相反
    SETVAL
    用联合体中val成员的值设置信号量集合中单个信号量的值

    3.5、信号量应用
    见试验内容2
    4、共享主存
    由于进程的虚拟地址可以映射到任意一处物理地址,因此两个进程的虚拟地址映射到相同的物理地址上,则这两个进程就可以利用该物理地址单元进行通信,system v共享主存将需要共享的主存放在ipc共享主存区,所有需要访问该共享区的进程都要把该共享区映射到本进程的虚拟地址空间。由于多个进程共享同一块主存区,对共享主存的访问必然要使用到某种同步机制。
    共享主存的基本步骤:
    (1)对线程或进程使用shmget()分配主存区;
    (2)使用shmat()连接一个或多个进程或线程到共享主存区中;
    (3)使用shmdt()从共享区中分离;
    (4)使用shmctl()解除分配空间。

    4.1、基本操作
    (1)头文件

    #include <sys/ipc,h>
    #include <sys/shm.h>
    

    (2)创建或获得一个共享主存区

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

    size: 要建立共享内存的长度(打开已有共享内存时置 0) ,参数key与shmflg同msgget()函数
    (3)连接共享主存区到进程虚拟空间

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

    shmaddr:共享内存的起始地址 ,shmflag:本进程对该内存的操作模式。
    (4)从指定进程虚拟空间断开共享主存区

    int shmdt(const v oid *shmaddr) ;
    

    (5共享主存区属性控制

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    buf:保存内存模式状态和访问权限的数据结构,通常为 0。cmd:允许的操作,包括以下几种类型:
    IPC_STAT,IPC_SET,IPC_RMID含义和消息队列属性控制函数对应一样
    

    4.2、共享主存区应用
    见试验内容3

    三、实验内容和步骤

    1. 编译并运行下述程序,观察并记录程序运行的结果。
    #include<sys/types.h>
    #include<sys/msg.h>
    #include<unistd.h>
    #include<sys/ipc.h>
    #include<stdio.h>
    void msg_stat(int,struct msqid_ds);
    
    main()
    {
     int gflags,sflags,rflags;
    key_t key;
    int msgid;
    int reval;
    struct msgsbuf{
     int mtype;
     char mtext[1];
    }msg_sbuf;
    
    struct msgmbuf{
     int mtype;
     char mtext[10];
    }msg_rbuf;
    struct msqid_ds msg_ginfo,msg_sinfo;
    char *msgpath="/home/msgqueue";
    key=ftok(msgpath,'a');
    gflags=IPC_CREAT|IPC_EXCL;
    msgid=msgget(key,gflags|00666);
    if(msgid==-1){
     printf("msg create error\n");
     return;
    }
    
    msg_stat(msgid,msg_ginfo);
    sflags=IPC_NOWAIT;
    msg_sbuf.mtype=10;
    msg_sbuf.mtext[0]='a';
    reval=msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);
    if(reval==-1){
     printf("message send error\n");
    }
    
    msg_stat(msgid,msg_ginfo);
    rflags=IPC_NOWAIT|MSG_NOERROR;
    reval=msgrcv(msgid,&msg_rbuf,4,10,rflags);
    if(reval==-1){
     printf("read msg error\n");
    }
    else
     printf("read from msg queue %d bytes\n",reval);
    
    msg_stat(msgid,msg_ginfo);
    msg_sinfo.msg_perm.uid=8;
    msg_sinfo.msg_perm.gid=8;
    msg_sinfo.msg_qbytes=16388;
    
    reval=msgctl(msgid,IPC_SET,&msg_sinfo);
    if(reval==-1){
     printf("msg set info error\n");
     return;
    }
    msg_stat(msgid,msg_ginfo);
    reval=msgctl(msgid,IPC_RMID,NULL);
    if(reval==-1){
     printf("unlink msg queue error\n");
     return;
    }
    }
    
    
    void msg_stat(int msgid,struct msqid_ds msg_info){
     int reval;
     sleep(1);
     reval=msgctl(msgid,IPC_STAT,&msg_info);
     if(reval==-1){
      printf("get msg info error\n");
      return;
     }
     printf("\n");
     printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
     printf("number of messages in queue is %d\n",msg_info.msg_qnum);
     printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes);
     printf("pid of last msgsnd is %d\n",msg_info.msg_lspid);
     printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid);
     printf("last msgsnd time is%s",ctime(&(msg_info.msg_stime)));
     printf("last msgrcv time is%s",ctime(&(msg_info.msg_rtime)));
     printf("last change time is%s",ctime(&(msg_info.msg_ctime)));
     printf("msg uid is%d\n",msg_info.msg_perm.uid);
     printf("msg gid is%d\n",msg_info.msg_perm.gid);
     
    }
    

    程序运行结果:
    current number of bytes on queue is 0
    number of messages in queue is 0
    max number of bytes on queue is 65536
    pid of last msgsnd is 0
    pid of last msgrcv is 0
    Segmentation fault (core dumped)

    1. 编译并运行下述程序,观察并记录程序运行的结果。
    #include <Linux/sem.h>
    #include <stdio.h>
    #include <errno.h>
    #define SEM_PATH "/home/hx/my_sem"
    #define max_tries 3
    
    int semid;
    int main()
    {
    int flag1,flag2,key,i,init_ok,tmperrno;
    struct semid_ds sem_info;
    struct seminfo sem_info2;
    union semun arg;    //union semun: 请参考附录2
    struct sembuf askfor_res, free_res;
    flag1=IPC_CREAT|IPC_EXCL|00666;
    flag2=IPC_CREAT|00666;
    key=ftok(SEM_PATH,'a');
    //error handling for ftok here;
    init_ok=0;
    semid=semget(key,1,flag1);//create a semaphore set that only includes one semphore.
    if(semid<0)
    {
     tmperrno=errno;
     perror("semget");
    if(tmperrno==EEXIST)
    //errno is undefined after a successful library call( including perror call) so it is saved //in tmperrno.
      {
      semid=semget(key,1,flag2);
    //flag2 只包含了IPC_CREAT标志, 参数nsems(这里为1)必须与原来的信号灯数目一致
      arg.buf=&sem_info;
      for(i=0; i<max_tries; i++)
      {
       if(semctl(semid, 0, IPC_STAT, arg)==-1)
       { perror("semctl error"); i=max_tries;}
       else
       {
        if(arg.buf->sem_otime!=0){ i=max_tries;  init_ok=1;}
        else  sleep(1); 
       }
      }
      if(!init_ok)
      // do some initializing, here we assume that the first process that creates the sem will
      // finish initialize the sem and run semop in max_tries*1 seconds. else it will not run
      // semop any more.
      {
       arg.val=1;
       if(semctl(semid,0,SETVAL,arg)==-1) perror("semctl setval error");
      }
     }
     else
     {perror("semget error, process exit"); 
     exit(1); 
     }
    }
    else //semid>=0; do some initializing  
    {
     arg.val=1;
     if(semctl(semid,0,SETVAL,arg)==-1)
      perror("semctl setval error");
    }
    //get some information about the semaphore and the limit of semaphore in redhat8.0
     arg.buf=&sem_info;
     if(semctl(semid, 0, IPC_STAT, arg)==-1)
      perror("semctl IPC STAT");  
     printf("owner's uid is %d\n",  arg.buf->sem_perm.uid);
     printf("owner's gid is %d\n",  arg.buf->sem_perm.gid);
     printf("creater's uid is %d\n",  arg.buf->sem_perm.cuid);
     printf("creater's gid is %d\n",  arg.buf->sem_perm.cgid);
    
     arg.__buf=&sem_info2;
     if(semctl(semid,0,IPC_INFO,arg)==-1)
      perror("semctl IPC_INFO");
     printf("the number of entries in semaphore map is %d \n",    arg.__buf->semmap);
     printf("max number of semaphore identifiers is %d \n",     arg.__buf->semmni);
     printf("mas number of semaphores in system is %d \n",     arg.__buf->semmns);
     printf("the number of undo structures system wide is %d \n",   arg.__buf->semmnu);
     printf("max number of semaphores per semid is %d \n",     arg.__buf->semmsl);
     printf("max number of ops per semop call is %d \n",     arg.__buf->semopm);
     printf("max number of undo entries per process is %d \n",     arg.__buf->semume);
     printf("the sizeof of struct sem_undo is %d \n",         arg.__buf->semusz);
     printf("the maximum semaphore value is %d \n",      arg.__buf->semvmx);
     
    //now ask for available resource: 
     askfor_res.sem_num=0;
     askfor_res.sem_op=-1;
     askfor_res.sem_flg=SEM_UNDO;  
      
      if(semop(semid,&askfor_res,1)==-1)//ask for resource
       perror("semop error");
     
     sleep(3); //do some handling on the sharing resource here, just sleep on it 3 seconds
     printf("now free the resource\n"); 
     
    //now free resource 
     free_res.sem_num=0;
     free_res.sem_op=1;
     free_res.sem_flg=SEM_UNDO;
    
     if(semop(semid,&free_res,1)==-1)//free the resource.
      if(errno==EIDRM)
       printf("the semaphore set was removed\n");
    //you can comment out the codes below to compile a different version:   
     if(semctl(semid, 0, IPC_RMID)==-1)
      perror("semctl IPC_RMID");
     else printf("remove sem ok\n");
    } 
    

    程序的运行结果(操作系统redhat8.0):
    owner’s uid is 0
    owner’s gid is 0
    creater’s uid is 0
    creater’s gid is 0
    the number of entries in semaphore map is 32000
    max number of semaphore identifiers is 128
    mas number of semaphores in system is 32000
    the number of undo structures system wide is 32000
    max number of semaphores per semid is 250
    max number of ops per semop call is 32
    max number of undo entries per process is 32
    the sizeof of struct sem_undo is 20
    the maximum semaphore value is 32767
    now free the resource
    remove sem ok

    Summary:信号灯与其它进程间通信方式有所不同,它主要用于进程间同步。通常所说的系统V信号灯实际上是一个信号灯的集合,可用于多种共享资源的进程间同步。每个信号灯都有一个值,可以用来表示当前该信号灯代表的共享资源可用(available)数量,如果一个进程要申请共享资源,那么就从信号灯值中减去要申请的数目,如果当前没有足够的可用资源,进程可以睡眠等待,也可以立即返回。当进程要申请多种共享资源时,Linux可以保证操作的原子性,即要么申请到所有的共享资源,要么放弃所有资源,这样能够保证多个进程不会造成互锁。Linux对信号灯有各种各样的限制,程序中给出了输出结果。另外,如果读者想对信号灯作进一步的理解,建议阅读sem.h源代码,该文件不长,但给出了信号灯相关的重要数据结构。

    1. 编译并运行下述程序,观察并记录程序运行的结果。
    /***shmwritetest.c**/
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    typedef struct{
        char name[4];
        int age;
    } people;
    main(int argc, char** argv)
    {
        int shm_id,i;
        key_t key;
        char temp;
        people *p_map;
        char* name = "/home";
        key = ftok(name,0);
        if(key==-1)
            perror("ftok error");
        shm_id=shmget(key,4096,IPC_CREAT);    
        if(shm_id==-1)
        {
            perror("shmget error");
            return;
        }
        p_map=(people*)shmat(shm_id,NULL,0);
        temp='a';
        for(i = 0;i<10;i++)
        {
            temp+=1;
            memcpy((*(p_map+i)).name,&temp,1);
            (*(p_map+i)).age=20+i;
        }
        if(shmdt(p_map)==-1)
            perror(" detach error ");
    } 
    
     /****shmreadtest.c*****/
    
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    typedef struct{
        char name[4];
        int age;
    } people;
    main(int argc, char** argv)
    {
        int shm_id,i;
        key_t key;
        people *p_map;
        char* name = "/home";
        key = ftok(name,0);
        if(key == -1)
            perror("ftok error");
        shm_id = shmget(key,4096,IPC_CREAT);   
        if(shm_id == -1)
        {
            perror("shmget error");
            return;
        }
        p_map = (people*)shmat(shm_id,NULL,0);
        for(i = 0;i<10;i++)
        {
        printf( "name:%s\n",(*(p_map+i)).name );
        printf( "age %d\n",(*(p_map+i)).age );
        }
        if(shmdt(p_map) == -1)
            perror(" detach error ");
    }
    

    shmwritetest.c创建一个系统V共享内存区,并在其中写入格式化数据;shmreadtest.c访问同一个系统V共享内存区,读出其中的格式化数据。分别把两个程序编译为shmwritetest及shmreadtest,先后执行./shmwritetest及./shmreadtest 则./shmreadtest输出结果如下:
    name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;
    name: g age 25; name: h age 26; name: I age 27; name: j age 28; name: k age 29;
    通过对试验结果分析,对比系统V与mmap()映射普通文件实现共享内存通信,可以得出如下结论:

    1. 系统V共享内存中的数据,从来不写入到实际磁盘文件中去;而通过mmap()映射普通文件实现的共享内存通信可以指定何时将数据写入磁盘文件中。注:前面讲到,系统V共享内存机制实际是通过映射特殊文件系统shm中的文件实现的,文件系统shm的安装点在交换分区上,系统重新引导后,所有的内容都丢失。
    2. 系统V共享内存是随内核持续的,即使所有访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核重新引导之前,对该共享内存区域的任何改写操作都将一直保留。
    3. 通过调用mmap()映射普通文件进行进程间通信时,一定要注意考虑进程何时终止对通信的影响。而通过系统V共享内存实现通信的进程则不然。 注:这里没有给出shmctl的使用范例,原理与消息队列大同小异。

    五、实验代码及步骤截图

    #include<sys/types.h>
    #include<sys/msg.h>
    #include<unistd.h>
    #include<sys/ipc.h>
    #include<stdio.h>
    void msg_stat(int,struct msqid_ds);
    
    main()
    {
     int gflags,sflags,rflags;
    key_t key;
    int msgid;
    int reval;
    struct msgsbuf{
     int mtype;
     char mtext[1];
    }msg_sbuf;
    
    struct msgmbuf{
     int mtype;
     char mtext[10];
    }msg_rbuf;
    struct msqid_ds msg_ginfo,msg_sinfo;
    char *msgpath="/home/msgqueue";
    key=ftok(msgpath,'a');
    gflags=IPC_CREAT|IPC_EXCL;
    msgid=msgget(key,gflags|00666);
    if(msgid==-1){
     printf("msg create error\n");
     return;
    }
    
    msg_stat(msgid,msg_ginfo);
    sflags=IPC_NOWAIT;
    msg_sbuf.mtype=10;
    msg_sbuf.mtext[0]='a';
    reval=msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);
    if(reval==-1){
     printf("message send error\n");
    }
    
    msg_stat(msgid,msg_ginfo);
    rflags=IPC_NOWAIT|MSG_NOERROR;
    reval=msgrcv(msgid,&msg_rbuf,4,10,rflags);
    if(reval==-1){
     printf("read msg error\n");
    }
    else
     printf("read from msg queue %d bytes\n",reval);
    
    msg_stat(msgid,msg_ginfo);
    msg_sinfo.msg_perm.uid=8;
    msg_sinfo.msg_perm.gid=8;
    msg_sinfo.msg_qbytes=16388;
    
    reval=msgctl(msgid,IPC_SET,&msg_sinfo);
    if(reval==-1){
     printf("msg set info error\n");
     return;
    }
    msg_stat(msgid,msg_ginfo);
    reval=msgctl(msgid,IPC_RMID,NULL);
    if(reval==-1){
     printf("unlink msg queue error\n");
     return;
    }
    }
    
    
    void msg_stat(int msgid,struct msqid_ds msg_info){
     int reval;
     sleep(1);
     reval=msgctl(msgid,IPC_STAT,&msg_info);
     if(reval==-1){
      printf("get msg info error\n");
      return;
     }
     printf("\n");
     printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);
     printf("number of messages in queue is %d\n",msg_info.msg_qnum);
     printf("max number of bytes on queue is %d\n",msg_info.msg_qbytes);
     printf("pid of last msgsnd is %d\n",msg_info.msg_lspid);
     printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid);
     printf("last msgsnd time is%s",ctime(&(msg_info.msg_stime)));
     printf("last msgrcv time is%s",ctime(&(msg_info.msg_rtime)));
     printf("last change time is%s",ctime(&(msg_info.msg_ctime)));
     printf("msg uid is%d\n",msg_info.msg_perm.uid);
     printf("msg gid is%d\n",msg_info.msg_perm.gid);
     
    }
    

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

    #include <Linux/sem.h>
    #include <stdio.h>
    #include <errno.h>
    #define SEM_PATH "/home/hx/my_sem"
    #define max_tries 3
    
    int semid;
    int main()
    {
    int flag1,flag2,key,i,init_ok,tmperrno;
    struct semid_ds sem_info;
    struct seminfo sem_info2;
    union semun arg;    //union semun: 请参考附录2
    struct sembuf askfor_res, free_res;
    flag1=IPC_CREAT|IPC_EXCL|00666;
    flag2=IPC_CREAT|00666;
    key=ftok(SEM_PATH,'a');
    //error handling for ftok here;
    init_ok=0;
    semid=semget(key,1,flag1);//create a semaphore set that only includes one semphore.
    if(semid<0)
    {
     tmperrno=errno;
     perror("semget");
    if(tmperrno==EEXIST)
    //errno is undefined after a successful library call( including perror call) so it is saved //in tmperrno.
      {
      semid=semget(key,1,flag2);
    //flag2 只包含了IPC_CREAT标志, 参数nsems(这里为1)必须与原来的信号灯数目一致
      arg.buf=&sem_info;
      for(i=0; i<max_tries; i++)
      {
       if(semctl(semid, 0, IPC_STAT, arg)==-1)
       { perror("semctl error"); i=max_tries;}
       else
       {
        if(arg.buf->sem_otime!=0){ i=max_tries;  init_ok=1;}
        else  sleep(1); 
       }
      }
      if(!init_ok)
      // do some initializing, here we assume that the first process that creates the sem will
      // finish initialize the sem and run semop in max_tries*1 seconds. else it will not run
      // semop any more.
      {
       arg.val=1;
       if(semctl(semid,0,SETVAL,arg)==-1) perror("semctl setval error");
      }
     }
     else
     {perror("semget error, process exit"); 
     exit(1); 
     }
    }
    else //semid>=0; do some initializing  
    {
     arg.val=1;
     if(semctl(semid,0,SETVAL,arg)==-1)
      perror("semctl setval error");
    }
    //get some information about the semaphore and the limit of semaphore in redhat8.0
     arg.buf=&sem_info;
     if(semctl(semid, 0, IPC_STAT, arg)==-1)
      perror("semctl IPC STAT");  
     printf("owner's uid is %d\n",  arg.buf->sem_perm.uid);
     printf("owner's gid is %d\n",  arg.buf->sem_perm.gid);
     printf("creater's uid is %d\n",  arg.buf->sem_perm.cuid);
     printf("creater's gid is %d\n",  arg.buf->sem_perm.cgid);
    
     arg.__buf=&sem_info2;
     if(semctl(semid,0,IPC_INFO,arg)==-1)
      perror("semctl IPC_INFO");
     printf("the number of entries in semaphore map is %d \n",    arg.__buf->semmap);
     printf("max number of semaphore identifiers is %d \n",     arg.__buf->semmni);
     printf("mas number of semaphores in system is %d \n",     arg.__buf->semmns);
     printf("the number of undo structures system wide is %d \n",   arg.__buf->semmnu);
     printf("max number of semaphores per semid is %d \n",     arg.__buf->semmsl);
     printf("max number of ops per semop call is %d \n",     arg.__buf->semopm);
     printf("max number of undo entries per process is %d \n",     arg.__buf->semume);
     printf("the sizeof of struct sem_undo is %d \n",         arg.__buf->semusz);
     printf("the maximum semaphore value is %d \n",      arg.__buf->semvmx);
     
    //now ask for available resource: 
     askfor_res.sem_num=0;
     askfor_res.sem_op=-1;
     askfor_res.sem_flg=SEM_UNDO;  
      
      if(semop(semid,&askfor_res,1)==-1)//ask for resource
       perror("semop error");
     
     sleep(3); //do some handling on the sharing resource here, just sleep on it 3 seconds
     printf("now free the resource\n"); 
     
    //now free resource 
     free_res.sem_num=0;
     free_res.sem_op=1;
     free_res.sem_flg=SEM_UNDO;
    
     if(semop(semid,&free_res,1)==-1)//free the resource.
      if(errno==EIDRM)
       printf("the semaphore set was removed\n");
    //you can comment out the codes below to compile a different version:   
     if(semctl(semid, 0, IPC_RMID)==-1)
      perror("semctl IPC_RMID");
     else printf("remove sem ok\n");
    } 
    

    程序运行结果:
    以用户身份运行:(uid有别)
    在这里插入图片描述

    以管理员身份运行:(uid有别)
    在这里插入图片描述

    /***shmwritetest.c**/
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    typedef struct{
        char name[4];
        int age;
    } people;
    main(int argc, char** argv)
    {
        int shm_id,i;
        key_t key;
        char temp;
        people *p_map;
        char* name = "/home";
        key = ftok(name,0);
        if(key==-1)
            perror("ftok error");
        shm_id=shmget(key,4096,IPC_CREAT);    
        if(shm_id==-1)
        {
            perror("shmget error");
            return;
        }
        p_map=(people*)shmat(shm_id,NULL,0);
        temp='a';
        for(i = 0;i<10;i++)
        {
            temp+=1;
            memcpy((*(p_map+i)).name,&temp,1);
            (*(p_map+i)).age=20+i;
        }
        if(shmdt(p_map)==-1)
            perror(" detach error ");
    } 
     /****shmreadtest.c*****/
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    typedef struct{
        char name[4];
        int age;
    } people;
    main(int argc, char** argv)
    {
        int shm_id,i;
        key_t key;
        people *p_map;
        char* name = "/home";
        key = ftok(name,0);
        if(key == -1)
            perror("ftok error");
        shm_id = shmget(key,4096,IPC_CREAT);   
        if(shm_id == -1)
        {
            perror("shmget error");
            return;
        }
        p_map = (people*)shmat(shm_id,NULL,0);
        for(i = 0;i<10;i++)
        {
        printf( "name:%s\n",(*(p_map+i)).name );
        printf( "age %d\n",(*(p_map+i)).age );
        }
        if(shmdt(p_map) == -1)
            perror(" detach error ");
    }
    

    实验执行结果:
    在这里插入图片描述

    六、实验总结

    1. 写出实验报告。
    2. 思考:不同进程间通信方式对比分析?
      答:系统V共享内存是以文件的形式组织在特殊文件系统shm中的。通过shmget可以创建或获得共享内存的标识符。取得共享内存标识符后,要通过shmat将这个内存区映射到本进程的虚拟地址空间(本进程内通过开辟一个内存进行映射)。
      其还包括:
      struct shmid_ds buf;
      shmctl( shmid , IPC_STAT , &buf ); // 取得共享内存的状态
      shmctl( shmid , IPC_RMID , &buf ); // 删除共享内存
      要使用共享内存,应该有如下步骤:
      1.开辟一块共享内存 shmget()
      2.允许本进程使用共某块共享内存 shmat()
      3.写入/读出
      4.禁止本进程使用这块共享内存 shmdt()
      5.删除这块共享内存 shmctl()或者命令行下ipcrm
      共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
      uid=0(root) gid=0(root) 组=0(root)
      uid=1000(schumacher) gid=1000(schumacher) 组=1000(schumacher),10(wheel)
    展开全文
  • 操作系统实验四:Linux进程管道通信 任务1:使用Pipe创建管道,创建一个子进程,子进程向父进程发送消息“I am your son!”,父进程接收到子进程的消息后,显示在屏幕上,并向子进程发送“I am your father!”。子...
  • 用进程模拟车辆过桥,利用linux进程间通信知识
  • Linux系统中使用系统调用fork()创建两个子进程,使用系统调用pipe()建立一个管道,两个子进程分别向管道各写一句话: Child process 1 is sending a message! Child process 2 is sending a message! 而父进程则...
  • 主要介绍了Linux通过匿名管道进行进程间通信,介绍了什么是管道,popen函数,pipe调用等相关内容,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
  • Linux进程间通信总结

    2018-11-13 12:52:44
    Linux下的进程间通信(Interprocess Communication,IPC)方式基本上是从Unix继承而来的。对Unix发展做出重大贡献的两个实验室:AT&amp;T的贝尔实验室、BSD在IPC方面的侧重点有所不同。前者...
  • 操作系统实验报告(LINUX进程间通信)

    热门讨论 2008-12-30 21:32:51
    操作系统实验报告(LINUX进程间通信)
  • 操作系统 实验四 进程间通信实验

    千次阅读 2021-11-26 11:26:02
    实验四 进程间通信实验 一、实验目的 1、了解什么是信号。 2、熟悉LINUX系统中进程之间软中断通信的基本原理。 3、理解进程的同步关系。 4、了解什么是管道。 5、熟悉UNIX/LINUX支持的管道通信方式。 二、...
  • Linux实验报告

    2021-05-10 08:13:27
    课程编号:B080103040Linux操作系统 实验报告姓 名 班 级 实验名称 邵春晓 软工1201 学 号 指 导 教 师 Linux操作系统实验 20124670 石 凯 开 设 学 期 实 验 时 2014-2015第一学期 第11周——第18周 评定人签字 ...
  • 实验2 Linux软中断通信 1.实验目的 通过本实验掌握软中断的基本原理掌握中断信号的使用进程的创建以及系统计时器的使用 2.实验内容上交的实验2统一取名为test2) 由父进程创建两个子进程通过终端输入Crtl+\组合键向父...
  • Linux软中断通信实验报告实验2 Linux软中断通信1.实验目的通过本实验,掌握软中断的基本原理;掌握中断信号的使用、进程的创建以及系统计时器的使用。2.实验内容(上交的实验2统一取名为:test2由父进程创建两个子...
  • 实验Linux进程间通信24课时 实验目的 理解进程通信原理掌握进程号量共享存消息队列相关的函数的使用 实验原理 Linux下进程通信相关函数除上次实验所用的几个还有 信号量 信号量又称为信号灯它是用来协调不同进程间...
  • Linux 作为一个多任务多进程的操作系统,各个进程间信息交互不可避免,进程间通信可分为本地进程间通信和远程进程间通信。本地进程间通信主要包括信号,管道,消息队列,信号量,共享内存等通信方式。 【实验预备...
  • 实验Linux进程的创建及进程间通信一、实验目的掌握linux进程的创建以及进程间通信的基本原理。二、预备知识1. 有C语言基础。2. 掌握在Linux下常用编辑器的使用。三、实验要求1.创建一个进程2.使用管道实现父子...
  • linux进程间通信

    2015-05-14 09:10:10
    用于介绍linux进程间通信基础,并举例说明
  • 代码实现了共享内存和信号量的结合,实现进程间通信及其同步问题。通过此代码可以理解共享内存及信号量基本函数的使用及实现原理。
  • Linux内核通信相关视频讲解:Linux内核,进程间通信组件的实现 linux内核,进程调度器的实现,内核源码分析 进程通信的目的 传输数据 一个进程须要将它的数据发送给还有一个进程。发送的数据量在一个字节到几M...
  • 上学期接触了linux系统,刚好操作系统课程的实验内容也都是要求在linux下完成,几个实验中个人认为对两个进程通信实验了解学习的比较好,所以这一篇和下一篇记录一下两个实验过程中的学习收获和实验成果。...
  • 里面是两个实验,一个共享内存方式的进程间通信,另一个是shell命令解释器。两个都是linux版本的。
  • Linux进程间通信

    2018-06-12 20:17:06
    一、实验目的 Linux 系统的进程通信机构(IPC)允许在任意进程间大批量地交换数据,通过本实验,理解熟悉 Linux 支持的消息通信机制、共享存储区机制及信息量机制。 二、总体设计2.1背景知识系统调用函数说明、...
  • 实验报告详细描述了进程间通信的实现原理,使用管道以及信号实现进出那个间通信,附有源代码实现。
  • Linux编程技术-实验报告

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,893
精华内容 7,157
关键字:

linux进程间通信实验