第十四章进程间的通信

来源:互联网 发布:淘宝亲宝贝入驻条件 编辑:程序博客网 时间:2024/04/30 05:06

第十四章进程间的通信

管道的概念

管道是Linux / UNIX 系列中比较原始的进程间通信方式,他实现数据以一种数据流的方式在进程间流动。在系统中其相当于文件系统上的一个文件,来缓存所要传输的数据。在某些特征性上有不同于文件,例如,当读出后,则管道中就没有数据了,但是文件没有这个特性。顾名思义,匿名管道在系统中是没有实名的,并不可以在文件系统中一任何方式看到管道。他只是进程的一种资源,会随进程的结束而被系统清楚。穿件一个管道时生成了两个文件描述符,但是对于管道中使用的文件描述符并没有路径名,也就是不存在任何意义上的文件,她们只是在内存中跟某一个索引节点相关的两个文件描述符。

#include<unistd.h>

Int pipe (int fd[2])

经由参数file des返回两个文件描述符: filedes[0]为读而打开,f i l ed es[ 1]为写而打开。 filedes[1 ]的输出是f i l edes[0]的输入。

wps_clip_image-29768

有两种方法来描绘一个管道,见图 。左半图显示了管道的两端在一个进程中相互连接,

右半图则说明数据通过内核在管道中流动。

f s t a t函数(见 4 . 2 节)对管道的每一端都返回一个 F I F O 类型的文件描述符,可以用

S _ISF IFO宏来测试管道。

单个进程中的管道几乎没有任何用处。通常,调用 p i pe的进程接着调用 f or k,这样就创建了从父进程到子进程或反之的 IPC通道。图1 4-2显示了这种情况。

f ork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[ 0]) ,子进程则关闭写端(fd[1]) 。图14-3显示了描述符的最后安排。

wps_clip_image-13284

对于从子进程到父进程的管道,父进程关闭 fd[1],子进程关闭f d[0]。

当管道的一端被关闭后,下列规则起作用:

(1) 当读一个写端已被关闭的管道时,在所有数据都被读取后, read返回0,以指示达到了文件结束处(从技术方面考虑,管道的写端还有进程时,就不会产生文件的结束。可以复制一个管道的描述符,使得有多个进程具有写打开文件描述符。但是,通常一个管道只有一个读进程,一个写进程。下一节介绍FIFO时,我们会看到对于一个单一的FIFO常常有多个写进程)

(2) 如果写一个读端已被关闭的管道,则产生信号 SIGPIPE。如果忽略该信号或者捕捉该信号并从其处理程序返回,则 wr ite出错返回,errno设置为EPIPE。在写管道时,常数 P IPE_BUF规定了内核中管道缓存器的大小。如果对管道进行 wr ite调用,而且要求写的字节数小于等于 P IPE_ B UF,则此操作不会与其他进程对同一管道(或 FI FO)的wr ite操作穿插进行。但是,若有多个进程同时写一个管道(或 FI FO) ,而且某个或某些进程要求写的字节数超过P IPE_BUF字节数,则数据可能会与其他写操作的数据相穿插。

管道的写入规则:

向管道中写入数据时,管道缓冲区一旦有空闲区域,写进程就会立即试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直被阻塞。

程序14-1创建了一个从父进程到子进程的管道,并且父进程经由该管道向子进程传送数据。

EG 14-1 :

#include<stdio.h>

#include<unistd.h>

#include<string.h>

#define BUFSIZE 512

void err_quit(char *msg)

{

    printf("%s",msg);

    return ;

}

int main()

{

    int fd [2] ;

    //写入管道的缓冲区

    char buf[BUFSIZE] = "Pipe Data : Hello\n";

    pid_t pid ;

    int len ;

    if((pipe(fd))<0)

        err_quit("Pipe failed\n");

    if((pid = fork())<0)

        err_quit("fork failed\n");

    else if( pid > 0 )

    {

        //父进程中关闭管道的读出端

        printf("father\n");

        close (fd[0]);

        //父进程管道写入数据

        len = write(fd[1],buf,strlen(buf));

        if(len <0 )

            printf("write error\n");

        else{

            printf("%s\n",buf);

        }

        exit(0);

    }

    else {

        //子进程关闭管道的写入端

        printf("child\n");

        close(fd[1]);

        //子进程从管道中读出数据

        memset(buf,0,sizeof(buf)*BUFSIZE);

        len = read(fd[0],buf,BUFSIZE);

        if(len < 0 )

            err_quit("process failed when read a pipe\n");

        else {

            printf("%s",buf);

        }

    }

}

EG14 -2 :这是一个只有一个进程之间的通信模式,有些地方还是要注意的

int main(void)

{

// pipe file descriptors array

int fd [2];

char str [256] ;

// printf("create the pipe failed\n");

if((pipe(fd ) ) < 0 )

{

printf("create the pipe failed\n");

return 0 ;

}

// write data into pipe writing port

    // write error after closing Read Port

///close(fd[0]);

if( write(fd[1],"create the pipe sucessfully!\n",31) < 0)

printf("write error \n");

else

printf("write Ok \n");

// read the data from pipe reading port

// Read the data isn't error after closing Writing Port

close(fd[1]);

if( read(fd[0],str,sizeof(str) )< 0 )

printf("Read Error");

else

printf("Read Ok \n");

printf("%s",str);

printf("pipe file descriptors are %d , %d \n",fd[0],fd[1]);

close(fd[0]);

close(fd[1]);

return 0 ;

}

EG 14 - 3 兄弟进程之间的管道通信

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

#include<sys/types.h>

#include<limits.h>

#define BUFSIZE PIPE_BUF //pipe_buf ,管道默认一次读写的数据长度

void err_quit(char *msg)

{

    printf("%s",msg);

    return ;

}

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

{

    int fd [2] ;

    char buf[BUFSIZE] = "hello my brother!\n" ; //缓冲区

    pid_t pid ;

    int len ;

    if(pipe(fd) <0 )

    {

        err_quit("pipe failed\n");

    }

    if((pid = fork())<0)

{

    err_quit("fork failed");

    }

    // in child process

    else if( pid ==0 )

    {

        //关闭不使用的文件描述符

        close(fd[0]);

        // write message

        write(fd[1],buf,strlen(buf)) ;

        exit(0);

    }

    // second subprocess

    if( (pid = fork())< 0 )

    {

        err_quit("fork failed\n");

    }

    // father process

    else if( pid > 0)

    {

        close(fd[0]);

        close(fd[1]);

        exit(0);

    }

    else {

        close(fd[1]);

        len = read (fd[0],buf,BUFSIZE);

        write(STDOUT_FILENO,buf,len);

        exit(0);

    }

    return 0 ;

}

命名管道命名管道

命名管道(named pipe)也称为FIFO ,他是一种文件类型,在文件系统中可以看到它,创建一个FIFO文件类型类似于创建一个普通文件。在程序中可以通过查看文件stat结构中st_mode成员的值来判断该文件是否为FIFO

命名管道区别于管道主要在下面的两点:

1) 命名管道可以用于任何两个进程间的通信,而并不限制两个进程同源。

2) 命名管道作为一种特殊的文件存放于文件系统中,而不是像管道一样存放在内存中(使用完后消失)。当进程对命名管道的使用结束后,命名管道依然存在于文件系统中,除非对其进行删除操作,否则该命名管道不会消失。

命名管道的好处是解决了系统在应用过程中产生的大量的中间临时文件的问题。FIFO可以被shell调用使数据从一进程到另一个进程,系统不必为该中间通道而清理不必要的圾,或者去释放该通道的资源,它可以被被后来的进程使用。

命名管道的创建:

#include<sys/types.h>

#include<sys/stat.h>

Int mkfifo(const char *pathname,mode_t mode)

EG 14 -5 :

#include<stdio.h>

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

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

{

mode_t mode = 0666 ;

int error ;

if(argc!=2)

{

printf("USEMSG: create _ FIFO name!\n");

exit(0);

}

if( (error =  mkfifo(argv[1],mode))<0)

{

printf("failed to mkfifo !\n please chekck path or the fifofile is %d ...\n",error);

return 0 ;

}

else {

// uotput the FIFo file name

printf("you sucessfully create a FIFO name : %s \n",argv[1]);

}

return 0 ;

}

Program Output :

Program analysis:

当程序运行第二次是,由于前一次已经创建一个相同路径的fifo file了,所以系统会自动输出错误提示(基于Redhat的Linux ,Ubuntu 测试时没有给出提示)

FIFO FOLE OPEN

FIFO 的打开与其他文件的打开是不同的,FIFO的打开规则:

① 如果当前打开操作是为读而打开FIFO  时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回,否则,可能阻塞直到有相应进程为写而打开该FIFO (当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

分析:

1)当我们只运行读取端程序时,其运行结果

wps_clip_image-31946

一直处于阻塞状态(没设置阻塞位)

如果我们接着运行写入端程序,则马上读取数据

2)如果我们把函数 read (fd,buf,BUFS)改为  read (fd,buf,BUFS|O_NONBLOCK|)

则程序马上成功返回

② 如果当前打开操作是为写而打开FIFO时,若已经有相应进程为读而打开该FIFO,则当强打开操作将成功返回;否者,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

看下面的程序:

写入端:

EG-14 - 6

int main(void)

{

    int fd ;

    int n ,i ;

    char buf[BUFES];

    time_t tp ;

    printf("I am %d\n",getpid());//说明进程的ID

   if( (fd = open("fifotest",O_WRONLY))<0)

    {

        printf("Open error\n");

        exit(0);

    }

    for( i =0 ; i<10 ;i++ )

    {

        printf("Sleeping...\n");

        time(&tp);

        n = sprintf(buf,"Write_fifo_ %d sends %s",getpid(),ctime(&tp));

        printf("Send msg :%s\n",buf);

        if((write(fd,buf,n+1))<0)

        {

            printf("Write failed !\n");

            close(fd);

            exit(0);

        }

        printf("Sleeping...\n");

        sleep(1);

    }

    close(fd);

    return 0 ;

}

读取端:

EG 14 -7 :

int main(void)

{

    int fd ;

    int len ;

    char buf[BUFS] ;

    mode_t mode = 0666 ;

    if( ( fd = open ("fifotest",O_RDONLY))< 0 )

    {

        printf("Open failed\n");

        return 1;

    }

  while((len = read (fd,buf,BUFS)) > 0 ) // 开始进行通信

    {

        printf("Read fifo read : %s",buf);

    }

    close (fd);

    return 0;

}

总的来说我们在读或者写fifo时,就要先进行以写或者读的打开是操作

上面的列子可以看作是客户和服务器端的通信实例,write_fifo的作用类似于客户端,可以打开多个客户端像一个服务器发送信息,read_fifo 类似于服务器,它适时监控着fifo的读取端,当有数据时,读取并进处理,但是有一个关键的问题是,每一个客户端必须预先知道服务器提供的FIFO接口

wps_clip_image-18071

消息队列

消息队列是一种以链表结构组织的一组数据,存放在内核中,是由个进程通过消息队列标识符来引用的一种数据传输方式。像其他两种IPC对象一样,也是有内核来维护的。消息队列是三个IPC对象类型中具有数据操作性的数据传输方式,在消息队列中可以随意根据特定的数据类型值来检索消息。

消息队列的概念(只针对于System V类型的消息队列)

消息队列(也叫做报文队列)能够克服早期UNIX通信机制的一些缺点。比如作为早期

UNIX通信机制之一的信号,它所能够传送的信息量是很有限的.后来虽然POSIX 1003.1b在信号的实时性方面做了拓展,使得信号在传递信息量方面有了相当程度的改进.但信号更像是一种“即时”的通信方式,它要求接受信号的进程在某个时间范围内对俏号做出反应,因此该信号最多在接受信号进程的生命周期内才有意义,信号所传递的信息是接近于随进程持续( process-persistent)的概念。管道及有名管道则是典型的随进程持续IPC,井且.只能传送无格式的字节流无疑会给应用程序开发带来不便,另外,它的缓冲区大小也受到限制。

    消息队列就是一个消息的链表.可以把消息看做一个记录,具有特定的格式及特定的优先级.对消息队列有写权限的进程可以向其中按照一定的规则添加新消息:对消息队列有读权限的进程则可以从消息队列中读走消息.消息队列是随内核持续(kemel-persistent )的。

概念说明:

1) 随进程持续的定义为:IPC 一直存在.直至打开IPC对对象的最后一个进程关闭该对

象为止,如管道和有名管道.

2) 随内核持续的定义为:]PC 一直持续到内核重新自举或者显示删除该时象为止.如

消息队列,信号量及共享内存等.

3) 随丈件系统持续的定义为:IPC一直持续到显示删除该对象为止.

System V 消息队列

    System V消息队列是随内核持续的.只有在内核重起或者显示别除一个消息队列I讨.该

消息队列才会真正被删除.因此系统中记录消息队列的数据结构(struct ipc_ids msg_ids )位

于内核中.系统中的所有消息队列都可以在结构msg_ids中找到访问入口.

    消息队列就是一个消息的链表.每个消息队列都有一个队列头,用结构struct msg`queue

来描述。队列头中包含了该消息队列的大量信息,包括消息队列键值、用户ID.组 ID.消

息队列中消息数目等.甚至记录了最近对消息队列读写进程的ID.用户可以访问这些信息,

也可以设置其中的某些信息。

结构msg_queue用来描述消息队列头.存在于系统空间,定义如下:

Struct msg_queue

{

Struct ipc_perm q_perm ;

Time_t q_stime               /" last msgsnd time"/

time t q_rtimet                /" last asgrcv time "/

time_t q_ctimet                /" last change time "/

unsigned long q_cbyteas        /" current number of bytes on queue "1

unsigned long q_gnum,          /" number of messages in queue "/

unsigned long q_gbytes;        /" ms number of bytes on queue "/

pid t q_lspid:               -. /" pid of last magand "/

pid t q_lrpid;                 /" last receive pid "/

struct list_h.ad q_a...aq.at

struct list-head q_receiv*rss

struct list head q_s.nders,

}

结构msqid_ds 用来设置或返回消息队列的信息,存在于用户空间,定义如下:

struct msqid_ds

   {

     struct msqid_ds {

     struct ipc_perm msg_perm;

     struct msg *msg_first;     

     struct msg *msg_last;      

     __kernel_time_t msg_stime; 

     __kernel_time_t msg_rtime; 

     __kernel_time_t msg_ctime; 

     unsigned long  msg_lcbytes;

     unsigned long  msg_lqbytes;

     unsigned short msg_cbytes; 

     unsigned short msg_qnum;   

     unsigned short msg_qbytes; 

     __kernel_ipc_pid_t msg_lspid;  

     __kernel_ipc_pid_t msg_lrpid;  

};

图10.4说明了内核与消息队列是怎样建立起联系的.其中struct ipc_ids msgids是内核‘中记录消息队列的全局数据结构:struct msgqueue是摊个消息队列的队列头。

wps_clip_image-2212

从上图可以看出.全局数据结构structipc_ids msg ids可以访问排个消息队列头的第一个成员struct ipc_perm.而每个struct ipc_perm能够与具体的消息队列对应起来是因为在该结构中.有一个key_t 类烈成员key,而key则唯一确定一个消息队列. ipc_perm结构定义如下:

struct ipc_perm

  {

    __key_t __key;

    __uid_t uid;

    __gid_t gid;

    __uid_t cuid;

    __gid_t cgid;

    unsigned short int mode;

    unsigned short int __pad1;

    unsigned short int __seq;

    unsigned short int __pad2;

    unsigned long int __unused1;

    unsigned long int __unused2;

  };

消息队列的创建与打开

    消息队列的引用标识符也可以称为消息队列的ID号,鱿像进程ID一样,用来唯一标

识系统中的某一进程。消息队列的ID是由在系统范围内唯一的键值生成的,而键值可以

看做对应系统内的一条路径.

获得特定文件名的键值的系统调用是ftok ,函数原型如下:

#include <sys/types.h

#include <sys/ipc.h>

key_t ftok (char"pathnase, char proj);

返回:若成功返回消息队列的一个键值,若失败则返回-1

ftok返回与路径pathname相对应的一个键值。该函数并不直接对消息队列操作,但在调

用msgget来获得消息队列的标识符前,往往要调用该函数。

创建或打开一个消息队列的系统调用为msgget,函数原型如下:

includ. <sys/typs.h>

includ. <sys/ipc.h>

includ. <sys/sg.h>

int maggot (k.y_t key, int sgllg);

参数key是一个链值.由ftok获得;

msgflg参数是一些标志位.可以取以下位:IPC CREAT, IPC_EXCL, IPC_NOWAIT或三者的逻辑或结巢.该调用返回与键伙key相对应的消息队列标识符。

注意:

参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消.息队列,只意味着即将创建断的消息队列。

另外.当调用msgget函数创建一个消息队列时,它相应的msgid_ds结构被初始化。

Ipc_perrn结构中的各成员被设谈为相应值.msg_ gnum, msg _Ispid, msg_lrpid. Msges_stime.

Msg_rtime都被设置为0,msggbytes被设祝为系统限制值.msg_ctime被设置为当前时间

消息队列的读写

消息队列所传递的消息由两部分组成,即消息的类型和所传递的数据。一般用数据结构struct msgbuf来表示,通常消息类型是一个正的长桩数,而数据则根据需要设定.设定一个1024字节长度的消息可将其结构定义如下:

Struct msgbuf {

Long msgtype;

Char msgtext[1024];

}

    msgtypc成员代表消息类型,从消息队列中读取消息的一个重要依据就是消息的类型:

msgtcxt是消息内容,当然长度不一定为1024.因此,对于发送消息来说,首先预置一个msgbuf缓冲区并写入消息类型和内容,调用相应的发送函数即可:对读取消息来说.首先分配这样一个msgbuf缓冲区.然后把消息读入该缓冲区即可.

① 向消息队列发送数据

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

In msgsnd (int msgid, const void*prt,size_t nbytes, int flags);

返回值:若成功则返回0,若出错则返回-1

    msgsnd函数的作用是向一个消息队列发送一个消息,该消息被添加到队列的末尾.参数

msqid代表消息队列的引用标识符(ID).参数prt是一个void类型指针.指向要发送的消息,

参数nbytes是以字节表示的消息的数据长度.参数flags用于指定消息队列满时的处理方法.

    对发送消息来说.有意义的flags标志为IPC_NOWAIT,指明在消息队列没有足够空间

容纳要发送的消息时.msgsnd是否等待。当消息队列满时,如果设置了IPCes NOWAIT位.

则立刻出错返问,否则发送消息的进程被阻塞.直至消息队列中有空间或消息队列被删除时.

造成msgsnd等待的条件有两种:

a. 消息队列已满

b. 队列中的信息总数等于系统限制值

c. 队列中的字节数等于系统限制值

msgsnd解除阻塞的条件有3个:

u 不满足上述3个条件,即消息队列中有容纳该消息的空间。

u msqid代表的消息队列被删除.

u 调用msgsnd的进程被某个信号中断。

② 向消息队列发送数据

从消息队列接收数据系统调用的函数原型如下:

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

Int msgrcw( int msqid ,void * prt ,size_t nbytes ,long type, int flags)

返回:若成功则返回消息的数据长度.若出错则返回-1.

    此函数用于从指定的消息队列中读取一个消息数据.参数msqid代表消息队列的引用标

识符prt是一个void类型指针,指向存放消息的级冲区.nbytes是以字节表示的要接收的消

息的数据长度.flags用于指定消息队列满时的处理方法,其取值为表中所列的常值或几个常值的逻辑或。

wps_clip_image-32081

获得或设置消息队列属性

消息队列的信息基本上都保存在消息队列头中,因此,可以分配一个类似于消息队列头的结构struct msgid- ds来返网消息队列的属性:同样可以设置该数据结构。关于消息队列属性的操作的系统调用如下:

wps_clip_image-25411

EG:

    程序10.7筒先创建一个新的消息队列后,第I次调用msg_stat子函数打印出该消息队列的相关信息(默认属性):然后向消息队列发送一个消息(字符数据“a”)后,第2次调用

msg_stat打印消息队列此时的相关信息:接着再调用msgrcv从消息队列中读走消息后,第3

次调用msg_stat打印消息队列此时的相关信息;最后试图使用root权限(注意运行此程序时

请使用root用户)更改消息队列此时的相关属性,并再次调用msg_stat打印更改后的消息队

列的信息。程序的最后,调用msgctl关闭该消息队列.

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#include<unistd.h>

#include<time.h>

void msg_stat(int,struct msqid_ds);

int main(void)

{

    int gflags ,sflags ,rflags;

    key_t key ;

    int msgid ;

    int reval ;

    //发送信息缓冲区结构

    struct msgbuf{

        int mtype;

        char mtext[3];

    }msg_sbuf ;

    //接受信息缓冲区数据结构

    struct msgmbuf

    {

        int mtype;

        char mtext[10];

    }msg_rbuf  ;

    // current process ID

    printf("Current Process ID %d \n",getpid());

    struct msqid_ds msg_ginfo ,msg_sinfo ;

    //队列路径

    char * msgpath ="msgqueue";

    //获取信息键值

    key = ftok(msgpath,'a');

    gflags =  IPC_CREAT ;

   

    msgid = msgget(key , gflags|00666) ;

    if(  msgid == -1 )

    {

        printf("msg create error!\n");

        return -1;

    }

    printf("Message Queues Description ID %d\n",msgid);

   

    msg_stat(msgid,msg_ginfo);

   

    sflags = IPC_NOWAIT ;

    msg_sbuf.mtype = 11 ; //

    msg_sbuf.mtext [0] = 'c';

    msg_sbuf.mtext [1] = 'd';

    msg_sbuf.mtext [2] = '\n' ;

   

    reval = msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);

    if(reval == -1 )

    {

        printf("message send error!\n");

}

    msg_sbuf.mtype = 10 ; //

    msg_sbuf.mtext [0] = 'a';

    msg_sbuf.mtext [1] = 'b';

    msg_sbuf.mtext [2] = '\n' ;

   

    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,2,10,rflags) ;

    msg_rbuf.mtext[2] = NULL;

    if(-1 == reval)

    {

        printf("read msg error!\n");

        return 0 ;

    }

    else {

        //打印接受到数据的字节数

        printf("read from msg queue %d bytes , type is %d and the message is %s \n ",reval,msg_rbuf.mtype,msg_rbuf.mtext);

    }

   

    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 0 ;

    }

   

    msg_stat(msgid,msg_ginfo);

    //操作完毕,调用msgctl删除消息队列

    reval = msgctl(msgid,IPC_RMID,NULL);

    if(reval == -1 )

    {

        printf("unlink msg queue error!\n");

        return -1;

    }

    return 0;

}

void msg_stat(int msgid , struct msqid_ds msg_info)

{

    int reval ;

    sleep(1);

    reval = msgctl(msgid,IPC_STAT,&msg_info);

    if(-1 == reval)

    {

        printf("get msg info error\n");

        return ;

    }

    printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");

    printf("\n\n");

    printf("current number of bytes on queue is %d \n",msg_info.msg_cbytes);

    printf("number of message 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 the last msgrcv is %d\n",msg_info.msg_lrpid);

   

    printf("last msgsnd time is %s\n",ctime(&(msg_info.msg_stime)));

   

    printf("last msgrcv time is %s",ctime(&(msg_info.msg_rtime)));

   

    printf("last change time is %s\n",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);

}

Program Output :

Current Process ID 3356

Message Queues Description ID 229376

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

current number of bytes on queue is 0

number of message in queue is 0

max number of bytes on queue is 65536

pid of last msgsnd is 0

pid of the last msgrcv is 0

last msgsnd time is Thu Jan  1 07:00:00 1970

last msgrcv time is Thu Jan  1 07:00:00 1970

last change time is Mon Mar 18 08:33:52 2013

msg uid is 0

msg gid is 0

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

current number of bytes on queue is 6

number of message in queue is 2

max number of bytes on queue is 65536

pid of last msgsnd is 3356

pid of the last msgrcv is 0

last msgsnd time is Mon Mar 18 08:33:53 2013

last msgrcv time is Thu Jan  1 07:00:00 1970

last change time is Mon Mar 18 08:33:52 2013

msg uid is 0

msg gid is 0

read from msg queue 2 bytes , type is 10 and the message is ab

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

current number of bytes on queue is 3

number of message in queue is 1

max number of bytes on queue is 65536

pid of last msgsnd is 3356

pid of the last msgrcv is 3356

last msgsnd time is Mon Mar 18 08:33:53 2013

last msgrcv time is Mon Mar 18 08:33:54 2013

last change time is Mon Mar 18 08:33:52 2013

msg uid is 0

msg gid is 0

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

current number of bytes on queue is 3

number of message in queue is 1

max number of bytes on queue is 16388

pid of last msgsnd is 3356

pid of the last msgrcv is 3356

last msgsnd time is Mon Mar 18 08:33:53 2013

last msgrcv time is Mon Mar 18 08:33:54 2013

last change time is Mon Mar 18 08:33:55 2013

msg uid is 8

msg gid is 8

Program Analysis :

Note : it is different from Ubuntu to Centos

本人在测试时发现在ubuntu 12.12 里面

reval = msgrcv(msgid , &msg_rbuf,2,10,rflags) ;

如果msgrcv函数这样写是度不了数据的,至于为什么这样?。。。

把10 改为0 就可以了,只是我们无法获得指定类型的消息。

在centos下就完全没问题

说明

一般来说,不同系统对消息队列的限制还有系统范围内的最大消息队列个数,预计整个系统范围内的最大消息数是不同的,但在实际开发中,一般不会超过该限制。

共享内存

共享内存可以说是Linux下最快速、最有效的进程间通信方式。两个不同进程A, B共

享内存的意思是,同一块物理内存被映射到进程A. B各自的进程地址空间,进程A可以即

时看到进程B对共享内存中数据的更新;反之,进程B也可以即时看到进程A对共享内存

中数据的更新。

① 共享内存的概念

共享内存从字面意义解释就是多个进程可以把一段内存映射到自己的进程空间,以此来实现数据的共享及传输.这也是所有进程间通信方式中最快的一种.共享内存是存在于内核

级别的一种资游。

    在Shell中可以使用ipcs命令来查看当前系统IPC中的状态.在文件系统中/proc目录下

有对其描述的相应文件。下列是笔者在当前系统中使用ipcs命令后的愉出结果(Ubuntu):

macos@MacOs:~/program/14$ ipcs

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status     

0x00000000 0          macos      600        393216     2          dest        

0x00000000 2588673    macos      600        320000     2          dest        

0x00000000 2621442    macos      600        4          2          dest        

0x00000000 2981891    macos      777        190464     2          dest        

0x00000000 2850820    macos      600        393216     2          dest        

0x00000000 3145734    macos      600        393216     2          dest        

0x00000000 3178503    macos      600        393216     2          dest        

0x00000000 3276808    macos      600        393216     2          dest        

0x00000000 3309577    macos      700        1920000    2          dest        

0x00000000 3342346    macos      777        844560     2          dest        

0x00000000 1802255    macos      600        393216     2          dest        

0x00000000 1835025    macos      600        12288      2          dest        

0x00000000 1867794    macos      600        393216     2          dest        

0x00000000 2097172    macos      666        4096       0                      

0x51018306 2162709    macos      666        4096       1                      

0x00000000 2228248    macos      777        3961800    2          dest        

0x00000000 2424857    macos      777        95232      2          dest        

------ Semaphore Arrays --------

key        semid      owner      perms      nsems    

0xcbc384f8 0          macos      600        1        

0x51018305 98306      macos      666        1        

------ Message Queues --------

key        msqid      owner      perms      used-bytes   messages   

0xffffffff 0          macos      666        9            3          

macos@MacOs:~/program/14$

在系统内核为一个进程分配内存地址时,通过分页机制可以让一个进程的物理地址不连续.同时也可以让一段内存同时分给不同的进程。共享内存机制就是通过该原理来实现的.

共享内存机制只是提供数据的传送.如何控制服务器端和客户端的读写操作互斥.这就豁要一些其他的辅助工具,例如信号量的概念。

采用共享内存通信的一个显而易见的好处是效率高.因为进程可以直接读写内存.而不

需要任何数据的拷贝。

实际上.进程之间在共享内存时,井不总是读写少最数据后就解除映射.有新的通信时,再重新建立共享内存区城。而是保持共享区城.直到通信完毕为止,这样.数据内容一直保存在共享内存中.井没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效串是非常高的。

共享内存的最大不足之处在于,由于多个进程对同一块内存区城几有访问的权限.各个

进程之间的同步问垃显得尤为重要。必须控制同一时刻只有一个进程对共享内存区城写入数据.否则将造成数据的泥乱。

对于没一个共享存储段.内核会为其维护一个shmid ds类型的结构体(shmid ds结构体

定义在头文件<<sys/shm.h>中).shmides ds结构体定义如下:

struct shmid_ds {

struct ipc_perm shm_perm;

int shm_segsz;

__kernel_time_t shm_atime;

__kernel_time_t shm_dtime;

__kernel_time_t shm_ctime;

__kernel_ipc_pid_t shm_cpid;

__kernel_ipc_pid_t shm_lpid;

unsigned short shm_nattch;

unsigned short  shm_unused;

void  *shm_unused2;

void *shm_unused3;

};

注意不同内核版本可能不一样,上面的是linux-2.6.32.60内核的版本

② 共享内存的相关操作

1.创建或者打开共享内存

创建共享内存

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

Int shmget ( key_t key , int size , int flag )

返回:若成功则返回共享内存ID.若出错则返回-1.

shmget函数除了可以用于创建一个新的共享内存外.也可用于打开一个已存在的共享内存。其中.参数key表示所要创建或打开的共享内存的键值。size表示共享内存区城的大小.只在创建一个新的共享内存时生效。参数flag表示调用函数的操作类型.也可用于设锐共享内存的访问权限.两者通过逻辑或表示.参数key和flag决定了调用函数shmget的作用.相应的约定如下:

a. 当key为IPC_PRIVATE时,创建一个新的共享内存,此时参数flag的取位无效。

b. 当key不为IPC- PRIVATE时,且flag设置了IPC_CREAT位,而没有设置IPC_EXCL位.则执行操作由key取值决定.如果key为内核中某个已存在的共享内存的键值.则执行打开这个键的操作:反之.则执行创建共享内存的操作。

c. 当key不为IPC_PRIVATE时,且flag设置了IPC_CREAT位和IPC_ EXCL位.则只执行创建共享内存的操作。参数key的取值应与内核中已经存在的任何共享内存的键值都不相同.否则函数调用失败.返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误.如果确实返问该错误.那么只要调用打开共亨内存的函数就可以了(即将flag设悦为IPC_CREAT,而不设哭IPC_EXCL ).

另外,当调用shmget 函数创建一个新的共享内存时.此共享内存的shmid_ds结构被初始化.ipcperrn中的各个城被设置为相应的值.shm Ipid. shm_ nattch. shm_ atime, shm_ dtime被初始化为0,shmget函数创建一块共享内存.程序中在调用shmget函数时指定key参数值为IPC-PRIVATE.这个参数的食义是创建一个新的共享内存区,当创建成功后使用shell命令ipcs -m来显示目前系统下共享内存的状态。

EG:

#include<stdio.h>

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<stdlib.h>

#define BUFSZ 4096

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

{

    int shm_id ;

    shm_id = shmget(IPC_PRIVATE,BUFSZ,0666);

    if(shm_id<0)

    {

        printf("shmget failed\n");

        return -1;

    }

    printf("create a shared memory segment suceessfully : %d \n",shm_id);

    system("ipcs -m");

    exit(0);

}

Program Output :

macos@MacOs:~/program/14$ ./create_shm

create a shared memory segment suceessfully : 2621462

------ Shared Memory Segments --------

key        shmid      owner      perms      bytes      nattch     status     

0x00000000 491520     macos      600        393216     2          dest        

0x00000000 1376257    macos      600        393216     2          dest        

0x00000000 819202     macos      600        393216     2          dest        

0x00000000 851971     macos      600        393216     2          dest        

0x00000000 1409028    macos      600        12288      2          dest        

0x00000000 622597     macos      700        3355488    2          dest        

0x00000000 655366     macos      600        393216     2          dest        

0x00000000 1441799    macos      600        393216     2          dest        

0x00000000 1474568    macos      600        393216     2          dest        

0x00000000 1507337    macos      600        393216     2          dest        

0x00000000 1540106    macos      600        393216     2          dest        

0x00000000 1572875    macos      700        1920000    2          dest        

0x5101833f 1605644    macos      666        4096       1                      

0x00000000 1736717    macos      777        844560     2          dest        

0x00000000 2588686    macos      666        4096       0                      

0x00000000 2162703    macos      600        262080     2          dest        

0x00000000 2195472    macos      600        345600     2          dest        

0x00000000 2424849    macos      777        190464     2          dest        

0x00000000 2457618    macos      777        95232      2          dest        

0x00000000 2129939    macos      600        4          2          dest        

0x00000000 2326548    macos      600        393216     2          dest        

0x00000000 2523157    macos      777        4090880    2          dest        

0x00000000 2621462    macos      666        4096       0                      

macos@MacOs:~/program/14$

Program Analysis:

2.附加

当一个共享内存创建或打开后.某个进程如果要使用该共享内存则必须将此内存区域附

加到它的地址空间.附加操作的系统调用如下:

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

Void * shmat ( int shmid ,const void * addr , int flag ) ;

返网:若成功则返回指向共享内存段的指针,若出错则返回-1.

函数shmid指定要引入的共享内存,参数addr与flag组合说明要引入的地址值,通常只有两种用法.addr为0(即指向NULL).表明让内核来决定第1个可以引入的位置。addr非

0,并且 flag中指定SHM_ RND,则此段引入到addr所指向的位置(此操作不推荐使用,因

为不会只对一种硬件上运行应用程序,为了程序的通用性推荐使用第1种方法).在flag参

数中可以指定要引入的方式(读写方式指定).

shmat函数成功执行后,会将shmid所表示共享内存段的shmid_ds结构的shm_nattch计数器的值加一。

3.分离

当进程内存的操作完成后,应调用shmdt函数,作用是将制定的共享内存从当前空间中脱离出去。函数原型如下:

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

Int shmdt ( void * addr )

Return value : success 0 , fail -1

此函数仅用于共享内存区域与进程的地址空间分离,并不是删除空想内存本身。参数addr是调用shmat 函数时的返回值。Shmdt 函数成功后,将共享内存的shmid_ds结构中的

Shm_nattch 计数器减 1 。

4.共享内存的控制

由于共享内存这一特殊的资源类型,使它不同于普通的文件,因此.系统需要为其提供

专有的操作函数.而这无疑增加了程序员开发的难度(猫要记忆额外的专有函数)。使用函数shmctl可以对共享内存段进行多种操作,原型如下:

#include <sys/types.h>

#include <sys/ipc.h>

(include <sys/shm.h>

int shmctl (int shmid. int cmd, stcuct shmid ds *buf );

返回:若成功则返回0,否则返回-1.

    参数shmid为所要操作的共享内存段的标识符.struct shmid_ds型指针参数bur的作用与

函数cmd的值相关,参数cmd指明了所要进行的操作.其解释如表10.3所示。

wps_clip_image-18482

EG Write_shm.c:

#include<sys/ipc.h>

#include<sys/shm.h>

#include<sys/types.h>

#include<unistd.h>

typedef struct {

  char name [4];

  int age ;

}people ;

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

{

    int shm_id  ;

    people *p_map ;

    int i;

    if(argc!=2)

    {

      printf("USEAGE atshm <identifier>");

      return -1 ;

    }

    shm_id = atoi (argv[1]) ;

    p_map =( people*)shmat(shm_id,NULL,0);

    for( i =0 ; i< 10 ;i++)

    {

      printf("name :%s ",(*( p_map +i )).name);

      printf("age %d \n",(*(p_map + i )).age);

    }

    if(shmdt( p_map) == -1 )

    {

      printf("detch error!\n");

    }

    return 0 ;

}

RG Read_shm.c

#include<sys/ipc.h>

#include<sys/shm.h>

#include<sys/types.h>

#include<unistd.h>

typedef struct {

  char name [4 ] ;

  int age ;

} people ;

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

{

  int shm_id , i ;

  char temp ;

  people *p_map  ;

  if( argc !=2 )

  {

    printf("USAGE : atshm <identifier>") ;

    return -1 ;

  }

  shm_id = atoi(argv[1]) ;

  // 得到要引入的共享内存

  p_map = (people *) shmat ( shm_id ,NULL , 0 ) ;

  temp  = 'a' ;

  for ( i = 0 ;i < 10 ; i++ )

  {

    temp ++;

    memcpy ((* (p_map + i )).name , &temp , 1);

    (*(p_map+i)).age = 20 + i ;

  }

   if(shmdt (p_map ) == -1 )

    {

      printf("detach error!\n");

    }

}

信号量

在信号量的实际应用中,是不能单独定义一个信号量的,而只能定义一个信号量集,其中包含一组信号量,同一信号量集中的信号量使用同一个引用ID,这样的设置是为了多个资源或者同步操作的需要。每个信号量集都有一个与之对应结构,其中记录了信号集的各种信息,该结构的定义如下:

Struct semid_ds {

Struct ipc_perm sem_perm  ;

Struct sem *sem_base ;

Unsigned short sem_nsems ;

Time_t sem_ctime  ;

Time_t sem_ctimr ;

} ;

Sem 结构记录了一个信号量的信息,其定义如下:

Struct sem {

Unsigned short semval ;

Pid_t sempid  ;

Unsigned short semncent ;

Unsigned short semzcnt  ;  

}

1.信号量集的相关操作

对于System  v 信号量 , 主要有三个API : semget () , semop()及semctl()。

1. 创建或者打开信号集

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

返回:若成功则返回信号量集ID.否则 返回 -1

    此函数可以用于创建一个新的信号集.或打开己存在的信号集。其中,参数key表示所要创建或打开的信号集对应的键位。参数nsems表示创建的信号集中包含的信号量的个数.此参数只在创建一个新的信号量集时有效。参数flag表示调用函数的操作类型,也可用于设置信号量集的访问权限.两者通过逻辑或表示.调用函数semget的作用由参数key和flag决定,相应的约定与 shmget函数类似。

    另外,当semget成功创建一个新的信号量集时,它相应的semides ds结构被初始化.

Ipc_perm结构中的成员被设置为相应的值. sem_nsems设置为函数参数nsems的值. Sem_otime被设置为0. Sem_ctime设置为系统当前时间。

2.对信号量集的操

Int semop( int semid ,struct sembuf  semoparray [] ,size_t nops ) ;

Retuen value : success 0 , fail -1

函数中参数semid是一个通过scmget 函数返回的一个信号量集标识符,参数nops标明了参数semoparray所指向数组中的元紊个数.参数semoparray是一个struct sembuf结构类型的数组,其中每个元素表示一个操作,由于此函数是一个原子操作,一旦执行就将执行数组中所有的操作。

结构sembuf 用来说明要执行的操作,其定义如下:

Struct sembuf

{

Unsigned short sem_num ;/×相对应的信号量集中的某一个资源×/

Short sem_op ; /×执行的操作×/

Short sem_flag ;

}

在sembuf结构中,sem_num是相对应的信号量集中的某一个资源(即指定将要进行操作的信号量).所以其值是一个从0到相应的信号级集的资源总数(ipc_perm.sem nsems)之间的整数.sem_op指明所要执行的操作,sem_fig说明函数semop的行为。Sem_ op的值是一个整数.它的取值及所对应的操作说明如下:

1) Sem_op> 0:表示进程对资源使用完毕,释放相应的资源数,并将sem_op的值加到

信号量的值上。

2) Sem_op=0:进程阻塞直到信号量的相应值为0,当信号量已经为0,函数立即返回.如果信号最的值不为0,则依据sem_flg的IPC_NOWAIT位决定函数动作。Sem_flg指定IPC_NOWAIT,则semop函数出错返回EAGAIN. sem flg没有指定IPC_NOWAIT.则将该信号量的semncnt值加1.然后进程挂起直到下述情况发生。

信号最值为0,将信号量的semncnt值减1.函数semop成功返回:

此信号量被删除(只有超级用户或创建用户进程拥有此权限),函数smmop出错返回EIDRM:

进程捕捉到信号,并从信号处理函数返网,在此情况下将此信号最的scmncnt位减.,

函数semop出错返回EINTR.

3) sem op<0:请求sem一 op的绝对值的资源.如果相应的资源数可以满足请求.则将

该信号最的值减去sem- op的绝对值,函数成功返回.当相应的资源数不能满足请

求时.这个操作与sem fig有关.sem fig指定IPC NOWAIT.则semop I数出错

返回EAGAIN. semee flg没有指定IPC NOWAIT.则将该信号蛋的semncnt值加1.

然后进程挂起直到下述悄况发生:当相应的资源数可以满足请求,该信号的值减去

sem op的绝对值.成功返回:此信号皿被删除(只有超级用户或创建用户进程拥有

此权限).函数smeop出错返回EIDRM:进程捕捉到信号.并从信号处理函数返回,

在此情况将此信号最的scmncnt值减I,函数semop出错返回EINTR.

3.对信号集的控制

#include<types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

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

Return value : success >= 0 (当semctl 的操作为GET 操作时返回相应的值,其余返回零) , fail -1

参数semid 是信号量集的标识符 ,semnum 指定semid 的信号集中的某一个信号量,其类似于在信号集资源数组的下标 , 用来对指定资源进行操作 。 参数cmd 定义函数所要进行的操作, 其取值及表达的意义于参数arg 的设置有关 , 如稍后的表所示。最后一个参数arg 是一个联合体 (union) , 其定义如下:

Union semun {

Int val ;  

Struct semid_ds * buf ; 

Unsigned short array  ;

} ;

wps_clip_image-27909

wps_clip_image-29771

wps_clip_image-24033

Eg:

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<sys/sem.h>

#include<stdio.h>

#include<errno.h>

#define SEM_PATH "."

#define max_trices 3

int semid ;

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

{

    int flag1 , flag2 , key , i , init_ok , tmperrno ;

    struct seminfo sem_info2 ;

    union semun {

               int              val;   

               struct semid_ds *buf;   

               unsigned short  *array; 

               struct seminfo  *__buf; 

        }arg ;

   

struct semid_ds sem_info ;

    struct sembuf askfor_res , free_res ;

   

    flag1 = IPC_CREAT | IPC_EXCL | 00666 ;

    flag2 = IPC_CREAT | 00666 ;

   

    key =ftok(SEM_PATH , 1);

    if( -1 == key)

    {

        printf("ftok error\n");

        return -1 ;

    }

    init_ok = 0 ;

   

    semid = semget(key ,190 , flag1);

    if(semid < 0 )

    {

        tmperrno =errno  ;

        printf("semget ");

       

        if(tmperrno == EEXIST)

        {

           

            semid = semget(key,1,flag2) ;

           

            for( i =0 ; i < max_trices ; i++ )

            {

               

                if(semctl( semid , 0 , IPC_STAT ,arg ) == -1 )

                {

                    printf("semctl error \n ");

                    i = max_trices ;

                }

                else {

                   

                    if(arg.buf->sem_otime !=0 )

                    {

                        i = max_trices ;

                        init_ok = 1 ;

                    }

                    else sleep(1);

                }

            }

           

            if(!init_ok)

            {

                arg.val =12 ;

               

                if( semctl (semid , 0 , SETVAL ,arg ) == -1 )

                {

                    printf("semctl setval error\n") ;

                    return -1 ;

                }

            }

            else {

                printf("semget error . process exit !\n");

                return -1 ;

            }

        }

    }

   

    else {

        arg.val =12;

        if(semctl(semid , 0 , SETVAL ,arg ) == -1 )

            printf("semctl setval error\n") ;

    }

   

     arg.buf  =&sem_info ;

     if(semctl(semid , 0 , IPC_STAT ,arg) == -1 )

     {

         printf("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("creator's uid is %d \n",arg.buf->sem_perm.cuid) ;

     printf("creator's gid is %d \n",arg.buf->sem_perm.cgid) ;

     printf("在信号量集中,信号量的类型整数是多少:%d\n",arg.buf->sem_nsems);

     arg.__buf = &sem_info2;

     if( semctl(semid,0,IPC_STAT, arg ) == -1 )

     {

         printf("semctl IPC_INFO");

     }

     printf("the number of entries in semaphore map is %d \n" ,arg.__buf->semmap);

     printf("the number of semaphore identifiers  is %d \n" ,arg.__buf->semmni);

     printf("the number of semaphore in system  is %d \n" ,arg.__buf->semmns);

     printf("the number of undo struct system wide is %d \n" ,arg.__buf->semmsl);

     printf("the number of ops per semop call is  %d \n" ,arg.__buf->semopm);

     printf("the 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);

     if( semctl(semid , 0 ,GETALL, arg ) == -1 )

        printf("can't get the total  \n");

    else

        printf("信号量集中信号量的个数:%d \n",*(arg.array));

    

     askfor_res.sem_num = 0 ;

     askfor_res.sem_op = -2 ;

     askfor_res.sem_flg = SEM_UNDO ;

    

     if(semop(semid ,&askfor_res,1) == -1 )

        printf("semop error \n");

   

    sleep(3);

    printf("now free the resource \n");

   

    free_res.sem_num = 0 ;

    free_res.sem_op = 1 ;

    free_res.sem_flg = SEM_UNDO ;

   

    if(semop(semid,&free_res,1) == -1 )

        if(errno == EIDRM)

            printf("the semaphore set was remove");

   

    if(semctl(semid , 0, IPC_RMID) == -1 )

        printf("semctl IPC_RMID");

    else {

        printf("remove sem ok \n");

    }

    return 0 ;

}

Program Output :

macos@MacOs:~/program/14$ ./sem_app

owner's uid is 1000

owner's gid is 1000

creator's uid is 1000

creator's gid is 1000

在信号量集中,信号量的类型整数是多少:190

the number of entries in semaphore map is 16843690

the number of semaphore identifiers  is 1000

the number of semaphore in system  is 1000

the number of undo struct system wide is 1000

the number of ops per semop call is  438

the number of undo entries per process is 24

the sizeof of struct sem_undo is 0

the maximum semaphore value is 0

信号量集中信号量的个数:12

now free the resource

remove sem ok

Segmentation fault (core dumped)

macos@MacOs:~/program/14$

wps_clip_image-17042

本章总结:

wps_clip_image-9367

原创粉丝点击