zoukankan      html  css  js  c++  java
  • # 2017-2018-1 20155302 课下实践IPC及课上补充

    课上实践补交

    题目二要求:

    学习使用stat(1),并用C语言实现

    1. 提交学习stat(1)的截图

    2. man -k ,grep -r的使用

    3. 伪代码

    4. 产品代码 mystate.c,提交码云链接

    5. 测试代码,mystat 与stat(1)对比,提交截图

    问题探索与解决

    首先学习stat(1)指令:使用指令man 1 stat

    使用指令man -k stat

    使用指令man -k stat | grep 2

    伪代码:就是获取stat结构然后打印。

    产品代码:

    #include <sys/types.h>
    
    #include <sys/stat.h>
    
    #include <time.h>
    
    #include <stdio.h>
    
    #include <stdlib.h>
    
    
    
    int main(int argc, char *argv[])
    
           {
    
               struct stat sb;
    
    
    
               if (argc != 2) {
    
                   fprintf(stderr, "Usage: %s <pathname>
    ", argv[0]);
    
                   exit(EXIT_FAILURE);
    
               }
    
    
    
               if (stat(argv[1], &sb) == -1) {
    
                   perror("stat");
    
                   exit(EXIT_FAILURE);
    
               }
    
    
    
               printf("File type:                ");
    
    
    
               switch (sb.st_mode & S_IFMT) {
    
               case S_IFBLK:  printf("block device
    ");            break;
    
               case S_IFCHR:  printf("character device
    ");        break;
    
               case S_IFDIR:  printf("directory
    ");               break;
    
               case S_IFIFO:  printf("FIFO/pipe
    ");               break;
    
               case S_IFLNK:  printf("symlink
    ");                 break;
    
               case S_IFREG:  printf("regular file
    ");            break;
    
               case S_IFSOCK: printf("socket
    ");                  break;
    
               default:       printf("unknown?
    ");                break;
    
               }
    
    
    
               printf("I-node number:            %ld
    ", (long) sb.st_ino);
    
    
    
               printf("Mode:                     %lo (octal)
    ",
    
                       (unsigned long) sb.st_mode);
    
    
    
               printf("Link count:               %ld
    ", (long) sb.st_nlink);
    
               printf("Ownership:                UID=%ld   GID=%ld
    ",
    
                       (long) sb.st_uid, (long) sb.st_gid);
    
    
    
               printf("Preferred I/O block size: %ld bytes
    ",
    
                       (long) sb.st_blksize);
    
               printf("File size:                %lld bytes
    ",
    
                       (long long) sb.st_size);
    
               printf("Blocks allocated:         %lld
    ",
    
                       (long long) sb.st_blocks);
    
    
    
               printf("Last status change:       %s", ctime(&sb.st_ctime));
    
               printf("Last file access:         %s", ctime(&sb.st_atime));
    
               printf("Last file modification:   %s", ctime(&sb.st_mtime));
    
    
    
               exit(EXIT_SUCCESS);
    
           }
    

    stat指令与mystat测试比较:

    2017-2018-1 20155302 课下实践IPC

    共享内存

    共享内存允许两个或多个进程共享一定的存储区,因为不需要拷贝数据,所以这是最快的一种IPC。
    共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间(这里的地址空间具体是哪个地方?)中。其他进程可以将同一段共享内存连接到自己的地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是malloc分配的一样。如果一个进程向共享内存中写入了数据,所做的改动将立刻被其他进程看到。
    共享内存是IPC最快捷的方式,因为共享内存方式的通信没有中间过程,而管道、消息队列等方式则是需要将数据通过中间机制进行转换。共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是同一块的物理空间,仅仅映射到各进程的地址不同而已,因此不需要进行复制,可以直接使用此段空间。

    #include <sys/ipc.h>
    #include <sys/shm.h>
    (1)创建或访问共享内存
        * int shmget(key_t key,size_t size,int shmflg);
    
    (2)附加共享内存到进程的地址空间
        * void *shmat(int shmid,const void *shmaddr,int shmflg);//shmaddr通常为NULL,由系统选择共享内存附加的地址;shmflg可以为SHM_RDONLY
    
    (3)从进程的地址空间分离共享内存
        * int shmdt(const void *shmaddr); //shmaddr是shmat()函数的返回值
    
    (4)控制共享内存
        * int shmctl(int shmid,int cmd,struct shmid_ds *buf);
        * struct shmid_ds{
              struct ipc_perm shm_perm;
              …
          }; 
    cmd的常用取值有:(a)IPC_STAT获取当前共享内存的shmid_ds结构并保存在buf中(2)IPC_SET使用buf中的值设置当前共享内存的shmid_ds结构(3)IPC_RMID删除当前共享内存
    

    代码实例:
    建立共享内存并写入数据的程序

    #include <stdio.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdlib.h>
    #include <errno.h>
    void get_buf(char *buf)
    {
        int i=0;
        while((buf[i]=getchar())!='
    '&&i<1024)
            i++;
    }
    int main(void)
    {
        int shmid;
        shmid=shmget(IPC_PRIVATE,sizeof(char)*1024,IPC_CREAT|0666);
        if(shmid==-1)
        {
            perror("shmget");
        }
        char *buf;
        if((int)(buf=shmat(shmid,NULL,0))==-1)
        {
            perror("shmat");
            exit(1);
        }
        get_buf(buf);
        printf("%d
    ",shmid);
        return 0;
    }
    

    读取数据的程序

    #include <stdio.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdlib.h>
    int main(int argc,char **argv)
    {
        int shmid;
        shmid=atoi(argv[1]);
        char *buf;
        if((int)(buf=shmat(shmid,NULL,0))==-1)
        {
            perror("shmat");
            exit(1);
        }
        printf("%s
    ",buf);
        shmdt(buf);
        return 0;
    }
    

    管道

    管道的特点:
    1、管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
    2、只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。当父进程与使用fork创建的子进程直接通信时,发送数据的进程关闭读端,接受数据的进程关闭写端。
    3、单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
    4、数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

    (1)在两个程序之间传递数据的最简单的方法是使用popen()和pclose()函数:
        #include <stdio.h>
        FILE *popen(const char *command, const char *open_mode);
        int pclose(FILE *stream);
    popen()函数首先调用一个shell,然后把command作为参数传递给shell。这样每次调用popen()函数都需要启动两个进程;但是由于在Linux中,所有的参数扩展(parameter expansion)都是由shell执行的,这样command中包含的所有参数扩展都可以在command程序启动之前完成。
    
    (2)pipe()函数:
        #include <unistd.h>
        int pipe(int pipefd[2]);
    popen()函数只能返回一个管道描述符,并且返回的是文件流(file stream),可以使用函数fread()和fwrite()来访问。pipe()函数可以返回两个管道描述符:pipefd[0]和pipefd[1],任何写入pipefd[1]的数据都可以从pipefd[0]读回;pipe()函数返回的是文件描述符(file descriptor),因此只能使用底层的read()和write()系统调用来访问。pipe()函数通常用来实现父子进程之间的通信。
    
    (3)命名管道:FIFO
        #include <sys/types.h>
        #include <sys/stat.h>
        int mkfifo(const char *fifo_name, mode_t mode);
    

    代码实例:

    read端

    #include<stdlib.h> 
    #include<stdio.h> 
    #include<sys/types.h> 
    #include<sys/stat.h> 
    #include<fcntl.h> 
    #include<errno.h> 
    #define PATH "./fifo" 
    #define SIZE 128 
    int main() 
    { 
     umask(0); 
     if (mkfifo (PATH,0666|S_IFIFO) == -1) 
     { 
     perror ("mkefifo error"); 
     exit(0); 
     } 
     int fd = open (PATH,O_RDONLY); 
     if (fd<0) 
     { 
      printf("open fd is error
    "); 
      return 0; 
     } 
     
     char Buf[SIZE]; 
     while(1){ 
     ssize_t s = read(fd,Buf,sizeof(Buf)); 
     if (s<0) 
     { 
      perror("read error"); 
      exit(1); 
     } 
     else if (s == 0) 
     { 
      printf("client quit! i shoud quit!
    "); 
      break; 
     } 
     else 
     { 
      Buf[s] = ''; 
      printf("client# %s ",Buf); 
      fflush(stdout); 
     } 
     } 
     close (fd); 
     return 3; 
    } 
    

    write端

    #include<stdlib.h> 
    #include<stdio.h> 
    #include<unistd.h> 
    #include<sys/types.h> 
    #include<sys/stat.h> 
    #include<string.h> 
    #include<errno.h> 
    #include<fcntl.h> 
     
    #define PATH "./fifo" 
    #define SIZE 128 
    int main() 
    { 
     int fd = open(PATH,O_WRONLY); 
     if (fd < 0) 
     { 
      perror("open error"); 
      exit(0); 
     } 
     
     char Buf[SIZE]; 
     while(1) 
     { 
      printf("please Enter#:"); 
      fflush(stdout); 
      ssize_t s = read(0,Buf,sizeof(Buf)); 
      if (s<0) 
      { 
       perror("read is failed"); 
       exit(1); 
      } 
      else if(s==0) 
      { 
       printf("read is closed!"); 
       return 1; 
      } 
      else{ 
       Buf[s]= ''; 
       write(fd,Buf,strlen(Buf)); 
      } 
     } 
     return 0; 
    } 
    

    FIFO(命名管道)

    管道和命名管道的区别:

    对于命名管道FIFO来说,IO操作和普通管道IO操作基本一样,但是两者有一个主要的区别,在命名管道中,管道可以是事先已经创建好的,比如我们在命令行下执行
    mkfifo myfifo
    就是创建一个命名通道,我们必须用open函数来显示地建立连接到管道的通道,而在管道中,管道已经在主进程里创建好了,然后在fork时直接复制相关数据或者是用exec创建的新进程时把管道的文件描述符当参数传递进去。
    一般来说FIFO和PIPE一样总是处于阻塞状态。也就是说如果命名管道FIFO打开时设置了读权限,则读进程将一直阻塞,一直到其他进程打开该FIFO并向管道写入数据。这个阻塞动作反过来也是成立的。如果不希望命名管道操作的时候发生阻塞,可以在open的时候使用O_NONBLOCK标志,以关闭默认的阻塞操作。

    FIFO可以说是管道的推广,克服了管道无名字的限制,使得无亲缘关系的进程同样可以采用先进先出的通信机制进行通信。
    管道和FIFO的数据是字节流,应用程序之间必须事先确定特定的传输"协议",采用传播具有特定意义的消息。
    要灵活应用管道及FIFO,理解它们的读写规则是关键。

    代码实例:

    接收消息:

      #include                 <stdio.h>   
      #include                 <sys/types.h>   
      #include                 <sys/stat.h>   
      #include                 <string.h>   
      #include                 <fcntl.h>   
      #include                 <errno.h>   
      #include                 <sys/time.h>   
      #include                 <unistd.h>   
        
      #define   FIFO         "/tmp/fifo.temp1"   
      #define   MAXLINE   1024   
        
      int   main(void)   
      {   
           int           fifo,   fd;   
           char         buf[MAXLINE];   
           int           len;   
           fd_set     set;   
           struct     timeval   tv;   
           int           i   =   0;   
        
           unlink(FIFO);   //如果FIFO存在,就先删除   
           if   ((fifo   =   mkfifo(FIFO,   O_RDWR))   <   0)       //产生一个有名管道   
           {   
                printf("mkfifo   error:   %s/n",   strerror(errno));   
                return(0);   
           }   
           if   ((fd   =   open(FIFO,   O_RDWR))   <   0)               //读写打开有名管道   
           {   
                printf("open   error:   %s/n",   strerror(errno));   
                return(0);   
           }   
           FD_ZERO(&set);   
           FD_SET(fd,   &set);   
           tv.tv_sec   =   5;   
           tv.tv_usec   =   0;   //超时设置,超过5秒没有信息,就打印超时   
           while   (1)   
           {   
                FD_SET(fd,   &set);   
                if   ((i   =   select(fd   +   1,   &set,   NULL,   NULL,   &tv))   >   0)//检测管道是否信息   
                {   
                    printf("receive   data/n");   
                    if   (FD_ISSET(fd,   &set))   
                    {   
                                    len   =   read(fd,   buf,   MAXLINE);//读取信息   
                                    buf[len]   =   '/0';   
                                    printf("buf   =   %s/n",   buf);   
                                    tv.tv_sec   =   atoi(buf);   
                                    tv.tv_usec   =   0;   
                    }   
                }   
                else   if   (i   ==   0)   
                {   
                     tv.tv_sec   =   5;   
                     tv.tv_usec   =   0;   
                     printf("chaoshi/n");   
                }   
                else   
                     printf("error/n");   
           }   
        
           unlink(FIFO);     //删除有名管道   
           return(0);   
      }  
    

    发消息:

      #include                 <stdio.h>   
      #include                 <sys/types.h>   
      #include                 <sys/stat.h>   
      #include                 <string.h>   
      #include                 <fcntl.h>   
      #include                 <errno.h>   
        
      #define   FIFO         "/tmp/fifo.temp1"   
      #define   MAXLINE   1024   
        
      int   main(void)   
      {   
            int           fifo;   
            char         buf[MAXLINE];   
            int           len;   
            int           i   =   0;   
        
            strcpy(buf,   "10");   
            if   ((fifo   =   open(FIFO,   O_RDWR))   <   0)                   //读写打开有名管道   
            {   
                 printf("mkfifo   error:   %s/n",   strerror(errno));   
                 return(0);   
            }   
            while   (i   <   10)   
            {   
                 sprintf(buf,   "%d",   i   +   1);   
                 len   =   write(fifo,   buf,   strlen(buf));       //写入信息到管道中   
                 printf("send   len   =   %d/n",   len);   
                 sleep(i);   
                 i++;   
            }   
        
            return(0);   
      }   
    

    信号

    信号是Unix/Linux系统在一定条件下生成的事件。信号是一种异步通信机制,进程不需要执行任何操作来等待信号的到达。信号异步通知接收信号的进程发生了某个事件,然后操作系统将会中断接收到信号的进程的执行,转而去执行相应的信号处理程序。

    (1)注册信号处理函数
        #include <signal.h>
        /*typedef void (*sighandler_t)(int);  sighandler_t signal(int signum,sighandler_t handler);*/
        * void (*signal(int signum, void (*handler)(int)))(int);  //SIG_IGN && SIG_DFL
        * int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
    
    (2)发送信号
        #include <signal.h>
        * int kill(pid_t pid,int sig); //#include <sys/types.h> 
        * int raise(int sig);            //kill(getpid(),sig);
        * unsigned int alarm(unsigned int seconds); //(#include <unistd.h>) seconds秒后,向进程本身发送SIGALRM信号。
    
    (3)信号集
        信号集被定义为:typedef struct {unsigned long sig[_NSIG_WORDS];} sigset_t;
        * int sigaddset(sigset_t *set,int sig);
        * int sigemptyset(sigset_t *set);
    

    信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

    代码实例:

    #include <signal.h>
    #include <stdio.h>
    
    void int_handler(int signum)
    {
     printf("
    SIGINT signal handler.
    ");
     printf("exit.
    ");
     exit(-1);
    }
    
    int main()
    {
     signal(SIGINT, int_handler);
     printf("int_handler set for SIGINT
    ");
    
     while(1)
     {
      printf("go to sleep.
    ");
      sleep(60);
     }
    
     return 0;
    }
    

    消息队列

    消息队列是内核地址空间中的内部链表,通过linux内核在各个进程直接传递内容,消息顺序地发送到消息队列中,并以几种不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地进行识别。内核中的消息队列是通过IPC的标识符来区别,不同的消息队列直接是相互独立的。每个消息队列中的消息,又构成一个独立的链表。
    消息队列克服了信号承载信息量少,管道只能承载无格式字符流。

    消息队列保存在内核中,是一个由消息组成的链表。

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    (1)创建或访问消息队列
    * int msgget(key_t key,int msgflg);
    
    (2)操作消息队列
        * int msgsnd(int msqid,const void *msg,size_t nbytes,int msgflg);
    msg指向的结构体必须以一个long int成员开头,作为msgrcv()的消息类型,必须大于0。nbytes指的是msg指向结构体的大小,但不包括long int部分的大小
        * ssize_t msgrcv(int msqid,void *msg,size_t nbytes,long msgtype,int msgflg);
    如果msgtype是0,就返回消息队列中的第一个消息;如果是正整数,就返回队列中的第一个该类型的消息;如果是负数,就返回队列中具有最小值的第一个消息,并且该最小值要小于等于msgtype的绝对值。
    
    (3)控制消息队列
        * int msgctl(int msqid,int cmd,struct msqid_ds *buf);
        * struct msqid_ds{
              struct ipc_perm msg_perm;
              …
           };
    

    代码实例:

    #include <sys/types.h>   
    #include <sys/ipc.h>   
    #include <sys/msg.h>   
    #include <stdio.h>   
    #define MAX_LINE 80   
    #define MY_MQ_ID 1233   
    /*消息结构体的一般形式如下:  
      typedef struct  
      {  
        long type;   //用于存放消息代码,必须位于首位  
        char message[ LENGHT+1 ];  
      }MSG_TYPE_T;  
     */  
    typedef struct  
    {   
        long type;     
        float fval;   
        unsigned int uival;   
        char strval[ MAX_LINE+1 ];   
    }MY_TYPE_T;   
    int main(  )   
        {   
            int msgid,ret;   
            //create the message queue with the id MY_MQ_ID   
            msgid=msgget( MY_MQ_ID,0666|IPC_CREAT );   
            if( msgid>=0 )   
                 printf( "Created a Message Queue,message queue identifier is %d
    ",msgid );   
            //modify the size of message queue   
            struct msqid_ds buf;   
            ret=msgctl( msgid,IPC_STAT,&buf );   
            printf( "The origianl size of queue is %d
    ",buf.msg_qbytes );   
               
            buf.msg_qbytes=4096;   
            ret=msgctl( msgid,IPC_SET,&buf );   
            if( ret==0 )   
                printf( "Size sucessfully changed for queue,message queue identifier is %d
    ",msgid );   
            //send a message   
            MY_TYPE_T myMessage;   
            myMessage.type=1L;   //消息的类型,msgrcv会用到   
            myMessage.fval=128.256;   
            myMessage.uival=512;   
            strncpy( myMessage.strval,"This is a test.
    ",MAX_LINE );   
            ret=msgsnd( msgid,( struct msgbuf* )&myMessage,sizeof( MY_TYPE_T ),0 ); //0是消息旗标   
            if( ret!=-1 )   
                printf( "Message send successfully.
    " );   
            //read a message   
            MY_TYPE_T recMessage;   
            ret=msgrcv( msgid,( struct msgbuf* )&recMessage,sizeof(MY_TYPE_T),1,0 );//这个地方Message Type要和欲接受的消息类型相同   
            if( ret!=-1 )   
                {   
                    printf( "
    Read a message from the queue
    " );   
                    printf( "Message Type:%ld
    ",recMessage.type );   
                    printf( "Float value:%f
    ",recMessage.fval );   
                    printf( "Uint value:%d
    ",recMessage.uival );   
                    printf( "String value:%s
    ",recMessage.strval );   
                }   
            //destroy a message queue   
            ret=msgctl( msgid,IPC_RMID,NULL );   
            if( ret!=-1 )   
                printf( "Message queue %d sucessfully removed.
    ",msgid );   
               
            return 0;   
        }
  • 相关阅读:
    设置qt插件路径
    实现js与Qt程序的交互(使用QtWebkit)
    QT4与QT3的不同
    Qt5 结构及模块组成?
    最简单搜索引擎代码
    使用Bootstrap
    Lucene.Net简介和分词
    程序优化
    Host和Server的开发
    jQuery的ajax对WebApi和OData的封装
  • 原文地址:https://www.cnblogs.com/STILLlover521/p/7898691.html
Copyright © 2011-2022 走看看