Linux进程通信

         不外乎:  1. 信号;  2. 管道;   (System V IPC->)3. 消息队列; 4. 信号量; 5. 共享内存;

         网络程序设计中通常包括两个或更多的需要互相对话的进程,因此进程通信的方法在网络程序设计中

    是极为重要的.
         网络程序设计的一个重要目标是保证进程间不互相干涉,否则系统可能被挂起或自锁,因此,进程间
    必须使用简洁有效地方法进行通信. 管道, 队列.
    
    1。 进程阻塞, 共享资源, 锁定.
    
    2。 信号: 用它来通知一个或多个进程异步事件的发生,比如键盘上某个键按下. 处理某种严重的错误.
         2.1 可用 kill 把SIGTERM 信号发送给这个进程.
         2.2 信号可以中断一个进程.
         2.3 bits/signum.h 对某些特定的信号名作了定义.
         2.4 P57 "SIGUP" "SIGINT" "SIGQUIT" "SIGILL" "SIGTRAP" "SIGFPE" "SIGTERM"-终止进程
                 "SIGCHILD"--实现exit和wait. 
         2.5 void abort(void); -- stdlib.h
     3. 信号的处理 [unix系统调用signal()用于接收一个指定类型的信号,并可以指定相应的方法.]
        int signal(int sig, __sighandler_t handler);
        1. func 2. SIG_IGN SIG_DFL
     4. 在进程间发送信号
        int kill(pid_t pid, int sig);   --一般用于父子进程之间
        if(pid==0), 则信号被发送到当前进程所在的进程组的所有进程;
        -1,         则信号进程标识符从高到低的顺序发送给全部的进程(搜,当前进程本身权限的限制).
        <-1,        则信号被发送给标识符为pid绝对值的进程组里的所有进程.
        
        kill 默认信号为: SIGTERM. 
        kill -s SIGINT 1234
     5. 系统调用 alarm() 和 pause()
        5.1  unsigned int alarm(unsigned int secondes)-- unistd.h
             alarm(60); -- 60s后发送SIGALRM信号
             alarm(0);--使报警时钟失效.
             当需要对某些工作设置时间限制时
             
        5.2 pause()能使调用进程暂停执行,直至接收到某种信号为止.
            int pause(void);-- unistd.h
     6. 系统调用setjmp() 和 longjmp()
        有时候,当接收到一个信号时,希望能跳回程序中以前的一个位置执行.
        setjmp(), 保存程序中的当前位置(是通过保存堆栈环境实现的).
        longjmp(), 能把控制转会被保存的位置.
        int setjmp(jmp_buf env);             --<setjmp.h
        void longjmp(jmp_buf env, int val);
   
   3。 管道, 就是将一个程序的输出和另外一个程序的输入连接起来的"单向通道".--shell[P71]
       #ls -l|more   ----> 流向
           在Linux系统内核里, 每个管道都是用一个inode节点来表示的.(当然,你是不会看到这个节点的,它
       只存在于系统的内核中.) 管道的I/O处理.  父子两个进程同时拥有对同一个管道的读写句柄.因为管道
       必须是单向的(因为它没有提供锁定的保护机制),所以我们必须决定数据的流动方向(从父到资,还是从
       子到父?), 然后在每个进程中关闭不需要的句柄.
        pipe(), dup(), dup2(), popen()/pclose()
        popen("ls ~roy", "r");
        popen("sort > /tmp/zixia","w");
        
        1. pipe()的调用必须在fork()之前;
        2. 及时关闭不需要的管道句柄;
        3. 使用dup()之前确定定向的目标是最小的文件句柄;
        4. 管道只能实现父子进程间的通信,如果两个进程之间没有fork()关系,就必须考虑其他的进程
           通信方法.
           
      3. 有名管道
           --用来解决管道不能提供非父/子关系进程间通信的缺陷,在管道的基础上发展了有名管道(FIFOs).
           --尽管Linux系统内部是以文件节点(inode)的形式存在的,但是由于其对外的不可见性("无名行"),
             我们无法创建新的句柄对其进行访问.而有名管道在Linux系统中以一种特殊的设备文件的形式
             存在于文件系统中.这样, 它不仅具有管道的通信功能,也具有了普通文件的优点.
             (可以同时被多个进程共享, 可以长期存在等等),有效地解决了管道通信的缺点.
           --因为有名管道存在于"文件系统中的文件节点(inode)",可用文件节点的方式来建立有名管道.
             #mknod sampleFIFO p  或     --使用chmod改变有名管道的存取权限
             #mkfifo -m 0666 sampleFIFO  --有存取权限
             在当前的文件系统中建立一个名字为sampleFIFO的有名管道.
               通过文件列表信息中的p指示符我们可以迅速的辨认出有名管道.  #ls -l
               prw-r--r-- l root root 0 May 14 16:25 sampleFIFO
           -- mknod() ; mknod("/tmp/sampleFIFO",s_IFIFO|0666,0);
              建立了名为"/tmp/sampleFIFO"的有名管道,其读写权限是0666(当然,最终的权限还和你的umask
              值有关).mknod的第三个参数在创建有名管道时被胡烈,一般填0.
              
           3.1 有名管道的I/o使用
              有名管道和管道的操作是相同的,只要注意,在引用已经存在的有名管道时,首先要用系统中的文件
              函数来打开它,才能接下来进行其他的操作.
         
     4. 文件和记录锁定
            "共享资源的保护问题"是多进程操作系统中一个非常重要的问题.
            文件和记录锁定可分为"咨询式锁定"和"强制锁定"两种.
            System V 和BSD提供的咨询式锁定方式, Linux都支持.
              int lockf(int fd, int function, long size); --#include <unistd.h>
              //记录锁定, 可以指定锁定的范围. 每个进程独有,用于父子进程间的共享锁定
            BSD -- 对指定文件的咨询式锁定和解锁.
              int flock(int fd, int operation); --#include <sys/file.h>
              //文件多顶. 可以继承,父子进程间使用的是同一锁定,不能用于父子进程间的.
            
     5. Linux的其它上锁技术
          创建和使用一个辅助文件, 为了实现文件的锁操作, 采用的第一个技巧用到这样一个事实:
        如果文件的"新链接名"已近存在,系统调用link()便会出错,在全程变量ermo中返回EEXIST.
        int link(char* existingpath, char* newpath) --#include<unistd.h>
           采取的技巧是: 创建一个独立独特临时文件的另一个链接. 如果链接成功, 进程便把文件锁定
        了.这时有两个路径指向锁定文件(基于进程号的临时文件和锁文件).然后我们用unlink()系统调用
        ,把临时文件删除,只剩下一个指向该文件的链接. 当需要解除锁定时,我们就用unlink()删除解除
        对该文件的链接.
 
6. System V IPC [P91]
         几种新的进程通讯方式 : "消息队列" "信号量" "共享内存"
         显著特点: 是它的具体实例在内核中是以对象的形式出现的, 称为"IPC"对象,并有一个唯一的标识符.
                   标识符的唯一性只在每一类的IPC对象内成立.
         --标识符, 只在内核中使用, IPC对象在程序中式通过关键字(key)来访问的.
         --1. 建立IPC的首要问题: 如何构造新的关键字使之不和已有的关键字冲突.
              key_t mykey;
              mykey = ftok("/tmp/myapp", 'a');
               key_t mykey;
               mykey = ftok(".", 'a');
              只要我们保证server和client从同一个目录运行,我们就可以保证它们使用上面的代码产生的
              关键字是相同的.
         --2. ipcs命令
               ipcs -q 只显示消息队列
               ipcs -m 只显示共享内存
               ipcs -s 只显示信号量
               ipcrm <msg | sem | shm) <IPC_ID> --强制系统删除已存在的IPC对象
               
               
         --一. ////////////////////******消息队列******////////////////////
         . "消息队列" (Message Queue)
              3.1 ipc_perm : 保存每个IPC对象权限信息.  linux/ipc.h
              3.2 msgbuf : 我们可以自己定义传递给队列的消息的数据类型.  linux/msg.h
                   4B + 4056B
              3.3 msg : 消息队列在系统内核中式以消息链表的形式出现的.完成消息链表每个节点结构定义的msg就够.
              3.4 msgqid_ds : 被系统内核用来保存消息队列对象有关数据. linux/msg.h
                  有关函数
              3.a [P95]
                   int msgget(key_t key, int msgflg); --创建新的消息队列或获取已有的消息队列.
              3.b 
                   用来向消息队列发送消息的. linux/msg.h
                   int msgsnd(int msqid, struct msgbuf* msgp, int msgsz, int msgflg);
                   
                   // 消息发送函数
                   int open_queue(key_t keyval)
                   {
                      int qid; 
                      if( (qid=msgget(keyval, IPC_CREAT|0660)) == -1){
                          return -1;
                      }
                      return qid;
                   }
                   
                   int send_message(int qid, struct mymsgbuf *qbuf)
                   {
                       int result, length;
                       //The length is essentially the size of the structure minus sizeof(mytype)
                       length = sizeof(struct mymsgbuf) - sizeof(long);
                       if((result=msgsnd(qid, qbuf, length, 0)) == -1){
                          return -1;
                       }
                       return result;
                   }
                   
                   int main()
                   {
                      int qid;
                      key_t msgkey;
                      struct mymsgbuf{
                         long   mtype;   //Message Type
                         int    request; //Work request number
                         double salary;  //Employee's salary
                      }msg;
                      //Generate our IPC key value
                      msgkey = ftok(".", "m");
                      
                      //Open/create the queue
                      if( (qid=open_queue(msgkey)) == -1){
                          perror("open_queue");
                          exit(1);
                      }
                      //Load up the message with arbitrary test data
                      msg.mtype   = 1;      //>1
                      msg.request = 1;      //data element #1
                      msg.salary  = 1000.0; //data element #2
                      //Bombs awary! 
                      if( (send_message(qid, &msg)) == -1){
                           perror("send_message");
                           exit(1);
                      }
                      return 0;
                   }
               3.c
                   和msgsnd()函数对应, msgrcv()函数用来从消息队列中取出消息. linux/msg.h
                   int msgrcv(int msqid, struct msgbuf* msgp, int msgsz, long mtype, int msgflg);
                   int read_message(int qid, long type, struct mymsgbuf* qbuf)
                   {
                       int result, lenght;
                       length = sizeof(struct mymsgbuf) - sizeof(long);
                       if((result=msgrcv(qid, qbuf, length, type, 0)) == -1){
                          return -1;
                       }
                       return result;
                   }
                   //利用上面提到的msgrcv()对消息长度的处理, 我们可以使用下面的方法来检查队列内是否存在符合条件的消息:
                   int peek_message(int qid, long type)
                   {
                       int result, length;
                       //将msgp和msgsz分别设为NULL和零, 然后检查函数的返回值,如果是E2BIG则说明存在符合指定类型的消息.
                       if((result=msgrcv(qid, NULL, 0, type, IPC_NOWAIT))==-1){
                           if(ermo == E2BIG)
                             return TRUE;
                       }
                       return FALSE;
                   }
               3.d
                   控制消息队列的行为 linux/msg.h
                   int msgctl(int msgqid, int cmd, struct msqid_ds *buf); 
               //////////////////////////////////////////////////////////////////
               /*******************消息队列实例--msgtool(一个交互式的消息队列使用工具)[P100]*******************/
               1. 背景:
                    直到目前, 我们所接触的有关消息队列的实例只有几个简单的封装函数. 虽然它们也很有用, 但是还不够深入.
                  因此,我们下面将提供一个将消息队列应用于实际的例子--命令行程序msgtool.使用它我们可以在命令行上提供
                  消息队列的功能.
                  --提供的这个版本只接收数组类型的数据, 接收其他类型数据的功能请读者自行完成.
                  
      4. ////////////////////******信号量******////////////////////
           信号量, 简单说就是用来控制多个进程对共享资源使用的计数器.经常被用作一种锁定保护机制, 当某个进程在对资源
         进行操作时防止其他进程对该资源的额访问.
           System V中的信号量实际上是信号量的集合(Set), 它可以包含多个信号量,控制多个共享资源.
          
          //数据结构
          4.1 sem :  信号量对象实际是多个信号量的集合.数组的每个成员都是一个单独的信号量. 以sem结构的形式存储的. linux/sem.h
          4.2 semun : 联合在senctl()使用, 提供senctl()操作所需要的信息. linux/sem.h
          4.3 sembuf : 被semop()函数用来定义信号量对象的基本操作. linux/sem.h
              struct sembuf{
                 unsigned short sem_num;
                 //决定在信号量上的操作,正,负,零
                 //sem_op是0, 那么调用semop()函数的进程就会被阻塞直到对应的信号量值为零.
                 //这种操作的实质就是等待信号量所监控的资源被全部使用.
                 short          sem_op;  
                 short          sem_flg; 
              } 
         4.4 semid_qs: 和msgqid_ds类似, semid_qs结构被系统用来储存每个信号量对象的有关信息.
         //有关的函数
         4.a semget(); 建立新的信号量对象或者获取已有对象的标识符. linux/sem.h
               int semget(key_t key, int nsems, int semflg);
         4.b semop(); 用来改变信号量对象中各个信号了的状态.
               int semop(int semid, struct sembuf* sops, unsigned nsops);
               //操作序列
               struct sembuf sem_get = {0, -1, IPC_NOWAIT};
               if((semop(sid, &sem_get, 1)==-1)
                  perror("semop");
               struct sembuf sem_release = {0, 1, IPC_NOWAIT};
                  semop(sid, &sem_release,1);
         4.c semctl() 直接对信号量对象进行控制.
             int semctl(int semid, int semnum, int cmd, union semun arg);
             
             
             ///////////////////信号量的实例--semtool,交互式信号量的使用/[P109]////////////
             
 //////////////////********共享内存*********//////////////////////////
   简单说: 就是被多个进程共享的内存. 它在各种进程通信方法中式最快的. 因为它是将信息直接映射到内存中,
 省去了其他IPC方法的中间步骤.
   //数据结构
   1. shmid_ds
   //有关函数
   1. int shmget(key_t key, int size, int shmflg);  --linux/shm.h
   2. int shmat(int shmid, char* shmaddr, int shmflg);--将共享内存映射到进程自己的内存空间内.
   3. int shmctl(int shmqid, int cmd, struct shmid_ds* buf);
   4. int shmdt(char* shmaddr); //当一个进程不再需要某个共享内存的映射时,就应该使用shmdt()函数断开映射.
   ///////////共享内存应用举例--shmtool

你可能感兴趣的:(linux,职场,休闲)