,
8.1 概述
linux里使用较多的进程间通信方式:
管道,pipe和fifo,管道pipe没有实体文件,只能用于具有亲缘关系的进程间通信;有名管道 named pipe,也叫fifo,还允许无亲缘关系进程间通信;信号,signal,软件模拟中断的机制,很多信号是系统处理的;消息队列,messge queue,消息的链表。共享内存,shared memory,容量大,用的比较多,需要额外的同步机制,如互斥锁和信号量信号量,semaphore,主要用于进程间的同步和互斥套接字,socket,主要用于网络通信
8.2 管道
8.2.1 概述及注意事项
例如 ps -ef | grep ntp, ps命令的输出,先进入管道(在内核中),grep命令从管道中获取输入数据。
注意事项:
只能用于具有亲缘关系的进程间通信半双工,读和写端口分开能用read/write等调用,类似文件,但只存在于内核中进程创建管道,会创建fds[0]和fds[1],0固定用于读,1固定用于写
子进程继承父进程的管道文件描述符,关闭多余的描述符以后,可以构建父子进程间的通信,兄弟进程也同理。
只有读端存在时,写入才有意义,否则写入端会收到SIGPIPE信号,进程会被终止; 缓冲区有空闲,则写入,否则阻塞
8.2.2 系统调用
#include <unistd.h>int pipe( int fd[2] );参数: fd[2],管道的文件描述符,0读1写返回值: 成功:0 出错:-1
8.2.3 实例
#include <stdio.h> // printf#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <fcntl.h>
#define PIPE_FD_RD 0#define PIPE_FD_WR 1
int main(int args, char *argv[]){ pid_t pid_fork; char buf[32]="pipe data.\n"; int fd[2];
if( pipe(fd) < 0 ) { printf("pipe() err,exit.\r\n"); exit(-1); }
pid_fork = fork(); if( pid_fork < 0 ) { printf("fork err.\r\n"); } else if( pid_fork > 0 ) // father write { close(fd[PIPE_FD_RD]); while(1) { printf("Father write data.\n"); write(fd[PIPE_FD_WR],buf,strlen(buf)+1); sleep(1); } } else // child read { close(fd[PIPE_FD_WR]); memset(buf,0,sizeof(buf)); while(1) { if( read(fd[PIPE_FD_RD],buf,32 ) > 0 ) { printf("child read data."); printf("%s\r\n",buf); } } }
exit(0);}
8.2.4 标准流管道 popen/pclose
标准流把执行另一个程序的流程标准化,简化编程,具体操作如下:
创建一个管道fork一个子进程在父子进程中关闭不需要的文件描述符执行exec函数族调用执行函数中所指定的命令(另一个程序)
优缺点:
优点:大大减少代码量缺点:不灵活,只能用标准流读写,不能用read、write这类不带缓冲的IO函数
#include <stdio.h>FILE * popen( const char * command, const char * type );参数: command:以NULL结尾的字符串,包含shell命令, 会被送到shell中执行。 type:“r”,读命令的结果,即文件指针连接到命令的标准输出; “w”,设置命令的输入,即文件指针连接到命令的标准输入;返回值: 成功:文件流指针 出错:-1int pclose(FILE * stream);参数: stream:要关闭的文件流返回值: 成功:返回由popen所执行进程的
退出码 出错:-1
用popen执行ps -ef
/* 8-2,popen,pclose */
#include <stdio.h> // printf,popen/pclose#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <fcntl.h>
int main(int args, char *argv[]){ FILE * pfile; char buf[1024];
printf("start.\r\n"); pfile = popen("ps -ef","r"); if(pfile == -1) { printf("popen return err.\r\n"); exit(-1); } while( fgets(buf,1024,pfile) !=NULL) // 注意fgets的参数及返回值 { printf("%s\r\n",buf); }
rtn = pclose(pfile); // 获取ps -ef执行的退出码,有若干宏定义可以检测错误,类似wait函数用到的宏定义 printf("pclose() return 0x%x.\r\n",rtn);
exit(0);}
8.2.5 FIFO
有名管道,即FIFO,与普通文件差不多,在文件系统中有实体文件,可以用于互不相关的两个进程实现彼此通信。不能用lseek()mkfifo()创建有名管道后,可以用open、read、write等函数操作阻塞问题
读,阻塞打开,若当前FIFO内没有数据,则读进程一直阻塞到有数据写入;读,非阻塞打开,不管有没有数据,都返回,没有数据时返回0写,堵塞打开,写操作一直阻塞到可以写入(FIFO有空间了)写,非阻塞打开,若不能写入全部数据,则写进程进行部分写入或者调用失败
#include <sys/types>#include <sys/state.h>int mkfifo( const char * filename, mode_t mode ); // 创建FIFO参数: filename,要创建的管道名 mode,与open的第三个参数mode一样,表示该文件的权限返回值: 成功,0 出错,-1FIFO相关错误总结(errno): EACCESS, 参数filename的目录无可执行权限 EEXIST,参数filename指定的文件已存在 ENAMETOOLONG,参数filename的路径名称过长 ENOENT,参数filename包含的目录不存在 ENOSPC,文件系统的剩余空间不足 ENOTDIR,目录存在,但不是真正的目录 EROFS,文件存在于只读文件系统中
8.2.1 FIFO实例
/* 8-3,fifo */
#include <stdio.h> // printf,popen/pclose#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <sys/stat.h> #include <fcntl.h>
void write_fifo( int args, char *argv[] ){ int fd;
if( args < 2 ) { printf("help: app string_writing_to_fifo.\r\n"); exit(-1); }
// 创建fifo if( access("myfifo",F_OK) < 0) { if( mkfifo("myfifo",0666)<0 ) { printf("mkfifo error.\r\n"); exit(-1); } }
// open if( (fd=open("myfifo",O_WRONLY)) < 0 ) { printf("open fifo err.\r\n"); exit(-1); }
// write if( write(fd,argv[1],128) < 0 ) printf("write err.\r\n");}
void read_fifo( void ){ int fd; char buf[128];
// open if( (fd=open("myfifo",O_RDONLY)) < 0 ) { printf("open fifo err.\r\n"); exit(-1); }
// read if( read(fd,buf,128) < 0 ) printf("read err.\r\n"); else printf("read from fifo: %s.\r\n",buf);}
int main(int args, char *argv[]){
//write_fifo(args,argv); // write进程时使用此函数 read_fifo(); // read进程是使用此函数 exit(0);}
$ ./write "hello world" // 终端1,阻塞,终端2执行read以后接触阻塞,估计是fifo没有读进程时,写不进去
$ ./read // 终端2,获取fifo中的数据read from fifo: hello world.
8.3 信号
8.3.1 信号概述
用软件对中断机制的一种模拟进程未处于执行态怎么办? 内核暂时保存信号,待进程进入执行态以后再提交信号可靠信号和不可靠信号
可靠信号支持排队不可靠信号不支持排队,一个信号已经注册,又来了一个新的相同信号,操作系统直接扔掉,不排队
信号的处理方式
忽略,两个信号不可忽略,SIGKILL/SIGSTOP捕捉,自定义信号处理函数,信号发生时,执行响应的处理函数缺省,系统默认动作,大部分是终止进程linux的常用信号:
SIGHUP,终止, 终端连接结束时发出
SIGINT,终止,
Ctrl+C 时发出,终端发送此信号给每个前台进程;SIGQUIT,终止,
Ctrl+\ 时发出;
SIGILL,终止, 进程企图执行
非法指令SIGFPE,终止,
致命算数运算错误,例如浮点运算错误、溢出、除数为0等SIGKILL,终止,
结束进程,不能被阻塞、处理或忽略
SIGALRM,终止, 定时器到时
SIGSTOP,暂停,
暂停进程,不能被阻塞、处理或忽略SIGTSTP,停止,
交互停止进程,Ctrl+ZSIGCHLD,忽略,
子进程改变状态,父进程收到此信号SIGABORT,进程异常终止
8.3.2 kill()和raise()
kill()可以向进程或进程组发送信号除了SIGKILL外,还可以发送其他信号;raise()进程给自身发送信号;
#include <signal.h>#include <sys/types.h>int kill( pid_t pid, int sig );参数: pid:正数,信号发往的进程号 0,信号被发往所有与当前进程同一个进程组的进程 -1,信号发送给所有进程表中的进程 <-1,信号发送给进程组号为-pid的所有进程 sig,信号返回值: 成功,0 出错,-1int raise( int sig );参数: sig,信号返回值: 成功,0 出错,-1
/* 8-4,kill,raise */
#include <stdio.h> // printf,popen/pclose#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <fcntl.h>#include <signal.h>
int main(int args, char *argv[]){ pid_t pid;
pid = fork(); if( pid < 0 ) { printf("fork err.\r\n"); } else if( pid>0 ) // 父进程 { sleep(3); printf("kill child %d.\r\n",pid); kill(pid,SIGKILL); printf("father exit.\r\n"); } else // 子进程 { printf("child %d waiting...\r\n",getpid()); //raise(SIGSTOP); while(1) // 等待被kill { printf("child alive.\r\n"); sleep(1); } }
exit(0);}
$ ./example
child 6442 waiting...child alive.child alive.child alive.kill child 6442.father exit.
8.3.3 alarm()和pause()
alarm()函数是闹钟函数,在进程中设置一个定时器,到时后向进程发送SIGALARM信号;
【注意】一个进程只能有1个闹钟时间,如果调用alarm()前已经设置过闹钟,则新的闹钟时间取代旧的。
pause()将调用进程挂起,直至捕捉到信号为止,常用函数
#include <unistd.h>unsigned int alarm( unsigned int seconds );参数: seconds,指定定时器秒数,到时后向该进程发送SIGALARM信号返回值: 成功,如果之前调用过alarm()函数,则返回上一个闹钟的剩余时间;否则返回0; 出错,-1int pause( void )返回值:-1,并且把errno设置为EINTR.【注意】
只有执行1个信号处理函数并从其返回时, pause才返回
/* 8-5,alarm/pause */
#include <stdio.h> // printf,popen/pclose#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <fcntl.h>#include <signal.h>
int main(int args, char *argv[]){ printf("start. finish after 5 seconds. \r\n"); alarm(5); pause();
printf("finish. \r\n"); // 不会执行 fflush((FILE*)STDOUT_FILENO);
exit(0);}
$ ./example start. finish after 5 seconds. // 5秒以后结束了闹钟
$
8.3.3 信号的处理
通过接口注册信号处理函数,信号发生时,就会调用注册的信号处理函数了。signal()简单,sigaction()健壮,推荐使用sigaction()函数。
#include <signal.h>void ( *signal( int signum, void (*handler)(int) ) )(int);参数: signum,信号 handler,SIG_IGN,忽略该信号 SIG_DFL,系统默认方式 自定义的信号处理函数指针返回值: 成功,以前的信号处理配置 出错,-1,NULL【注意】signal函数形式复杂了,写成如下形式便于理解(就是返回值形式复杂了些): typedef void sign( int ); sign * signal( int , handler *);
返回值为sign类型,“无返回值并且带一个整型参数的函数”,就是信号的原始配置函数。 void (*handler)(int),信号处理函数,类似注册一个回调函数,形参int是具体的信号值int sigaction( int signum, const struct
sigaction * act, struct sigaction * oldact );参数: signum,信号,除了SIGKILL和SIGSTOP act,指定对特定信号的处理。 oldact,保存原来对相应信号的处理返回值: 成功,0 出错,-1【sigaction结构体】struct sigaction{ void (*sa_handler)(int signo); // 自定义信号处理函数指针/SIG_DEL/SIG_IGN sigset_t sa_mask; // 信号集,指定
在信号处理程序执行过程中哪些信号应当被屏蔽 int sa_flags; // 标志位,包含若干对信号进行处理的各个选择项 void (*sa_restore)(void); // }sa_flags:SA_NODEFER/SA_NOMASK,当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动屏蔽此信号SA_NOCLDSTOP,进程忽略子进程产生的任何SIGSTOP/SIGTSTP/SIGTTI/SIGTTOU信号SA_RESTART,令重启的系统调用起作用SA_ONESHOT/SA_RESETHAND,自定义信号只执行1次,执行完毕后恢复信号的系统默认动作
/* 8-6,signal */
#include <stdio.h> // printf,popen/pclose#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <fcntl.h>#include <signal.h>
void fun_sigal( int sig_no ){ if( sig_no == SIGINT ) printf("Signal SIGINT\r\n"); else if( sig_no == SIGQUIT ) printf("Signal SIGQUIT\r\n"); else printf("Other SIGQUIT\r\n");}
int main(int args, char *argv[]){ printf("start. \r\n");
signal(SIGINT,fun_sigal); signal(SIGQUIT,fun_sigal);
pause(); // 从信号处理函数返回 pause(); // 再等待一次 printf("finish. \r\n");
exit(0);}
$ ./example start. ^CSignal SIGINT^CSignal SIGINT // 相同信号,多次发生的情况finish.
$ ./example start. ^CSignal SIGINT^\Signal SIGQUITfinish.
/* 8-7,sigaction */
#include <stdio.h>
// printf,popen/pclose
#include <stdlib.h>
// exit
#include <unistd.h>
#include <sys/types.h>
// pid_t
#include <fcntl.h>
#include <signal.h>
void fun_sigal(
int sig_no )
{
if( sig_no ==
SIGINT )
printf("Signal SIGINT\r\n");
else if( sig_no ==
SIGQUIT )
printf("Signal SIGQUIT\r\n");
else
printf("Other SIGQUIT\r\n");
}
int main(
int args,
char *
argv[])
{
struct sigaction st_sigact;
printf("start. \r\n");
st_sigact.sa_handler =
fun_sigal;
st_sigact.sa_flags=
0;
sigemptyset(&
st_sigact.sa_mask);
sigaction(SIGINT,&
st_sigact,NULL);
sigaction(SIGQUIT,&
st_sigact,NULL);
pause(); // 从信号处理函数返回
pause();
// 再等待一次
printf(
"finish. \r\n");
exit(0);
}【执行效果与signal例程相同】
8.3.4 信号集
后续补充吧。
8.4 信号量
8.4.1 信号量概述
不同进程有可能争抢共享资源信号量用来解决进程之间的同步和互斥,信号量包括
信号量变量,
代表当前可用的该资源的数量,>0表示资源可用, <=0表示无可用资源;一般信号量只有0/1,叫二维信号量。在该信号量下等待资源的进程等待队列对信号量的两个原子操作,PV
P操作,占用资源,-1,如果没有可用资源,则被阻塞,直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该资源)V操作,释放资源,+1,如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程
创建信号量,semget(),不同进程用相同键值获取同一个信号量
初始化,semctl(),SETVAL
PV操作,semop()
删除,semctl(), IPC_RMID
8.4.2 函数
#include <sys/types>
#include <sys/ipc.h>
#include <sys/sem.h>int semget( key_t key, int nsems, int semflg ); // 获取或创建信号量参数: key,信号量的键值,多个进程通过同一个键值访问同一个信号量;IPC_PRIVATE用于创建本进程的私有信号量; nsems,需创建的信号量数目,通常取值为1 semflg,类似open函数的flg+mode的集合,其中权限与open相同,也可用八进制表示;IPC_CREAT,创建,已存在也不出错;IPC_EXCL,如果创建时已经存在,则返回错误;返回值: 成功,信号量标识符,类似文件描述符 出错,-1int semctl( int semid, int semnum, int cmd, union semun arg );参数: semid,信号量标识符 semnum,信号量编号,一般为0,使用信号量集时才有用 cmd,指定各种操作,常用如下: IPC_STAT,获取信号量的状态结构体semid_ds,并存放在第四个参数arg的buf中。semid_ds是系统中描述信号量的数据结构; IPC_SET,将信号量的值设置为arg的val IPC_GET,返回信号量的当前值 IPC_RMID,删除信号量 arg,union semun结构,在有些系统里需要自己定义,
linux里需要自己定义 union semun { int val; struct semid_ds * buf; unsigned short * array; }返回值: 成功:IPC_STAT/IPC_SETVAL/IPC_RMID时,返回0 IPC_GETVAL时返回信号量当前值 出错:-1int semop( int semid, struct sembuf * sops, size_t nsops );参数: semid,信号量标识符 sops,指向信号量操作结构体数组 struct sembuf { short sem_num; // 信号量编号,使用单个信号量时,通常取值为0 short sem_op; // -1为P操作,+1为V操作 short sem_flg; // 通常设置为SEM_UNDO,这样进程没有释放信号量就退出时,系统自动释放该信号量 } nsops,该数组的操作个数,通常取1返回值: 成功,0 出错,-1
8.4.3 例程
/* 8-8,sem */
#include <stdio.h>
// printf,popen/pclose
#include <stdlib.h>
// exit
#include <unistd.h>
#include <sys/types.h>
// pid_t
#include <fcntl.h>
#include <sys/sem.h>
#include <sys/ipc.h>
union semun
{
int val;
struct semid_ds *
buf;
unsigned short *
array;
};
void sem_init(
int semid,
int val )
{
union semun arg;
arg.val =
val;
semctl( semid, 0, IPC_SET, arg);
}
void sem_p(
int semid)
{
struct sembuf st_sem_buf;
st_sem_buf.sem_num =
0;
st_sem_buf.sem_op = -
1;
st_sem_buf.sem_flg =
SEM_UNDO;
semop( semid, &st_sem_buf ,
1 );
}
void sem_v(
int semid )
{
struct sembuf st_sem_buf;
st_sem_buf.sem_num =
0;
st_sem_buf.sem_op =
1;
st_sem_buf.sem_flg =
SEM_UNDO;
semop( semid, &st_sem_buf ,
1 );
}
void sem_del(
int semid )
{
union semun arg;
semctl( semid, 0, IPC_RMID, arg);
}
int main(
int args,
char *
argv[])
{
pid_t pid;
key_t key;
int semid;
printf("start. \r\n");
// creat sem
key = ftok(
".",
0);
if( (semid=semget(key,
1,
0666|IPC_CREAT)) <
0 )
{
printf("semget err.\r\n");
exit(-
1);
}
sem_init(semid,0);
pid =
fork();
if( pid >
0 )
// 父进程
{
sleep(5);
printf("father process.\r\n");
sem_v(semid);
}
else if( pid ==
0 )
{
sem_p(semid);
printf("child process.\r\n");
sem_v(semid);
}
else
{
printf("fork err.\r\n");
exit(-
1);
}
printf("finish. \r\n");
exit(0);
}
$ ./example start. // 5秒后后面打印信息出现father process. finish. child process.finish.
【注意】此例用sem实现父子进程顺序执行,属于同步的例子。 sem也可以实现互斥操作。
8.5 共享内存
8.5.1 概述
内核有专门的内存区用于进程间共享,进程可以将其映射到自己的私有地址空间后访问。数据读写高效,不需要额外复制,但是需要额外的同步和互斥机制【注意】 linux命令
ipcs可以查看共享内存、消息队列的各种进程间通信机制的情况
8.5.2 函数
#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int shmget( key_t key, int size, int shmflg ); // 从内核中获取共享内存参数: key,共享内存的键值,与信号量函数中的键值相同 size,共享内存大小 shmflg,同open的权限位,可用8进制表示返回值: 成功:共享内存段标识符 出错,-1int shmat( int shmid, const void * shmaddr, int shmflg ); // 映射从内核中获取的共享内存,映射已后进程就可以使用了参数: shmid,共享内存标识符 shmaddr,将共享内存映射到指定地址,若为0,则系统自动分配 shmflg,SHM_RDONLY,只读;默认0,可读写返回值: 成功,被映射的段地址 出错,-1int shmdt( const void * shmaddr );参数: shmaddr,被映射的共享内存段地址返回值: 成功,0 出错,-1
8.5.3 例程
/* 8-9,shm */
#include <stdio.h>
// printf,popen/pclose
#include <stdlib.h>
// exit
#include <
string.h>
#include <unistd.h>
#include <sys/types.h>
// pid_t
#include <fcntl.h>
//#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(
int args,
char *
argv[])
{
pid_t pid;
int shmid;
char *
shm_addr;
char buf[
128];
char flg[
6]=
"write";
printf("start. \r\n");
// creat sem
if( (shmid=shmget(IPC_PRIVATE,
128,
0666)) <
0 )
{
printf("semget err.\r\n");
exit(-
1);
}
else
printf("semget ok,shmid %d\r\n",shmid);
system("ipcs -m");
pid =
fork();
if( pid >
0 )
// 父进程
{
if( (shm_addr=(
char*)shmat(shmid,
0,
0) ) == ((
void*)-
1) )
{
printf("shmat err in father process,rtn 0x%x\r\n",shm_addr);
exit(-
1);
}
else
printf("shmat addr 0x%x in father process.\r\n",shm_addr);
sleep(1);
printf("input something in father.\r\n");
fgets(buf,128,stdin);
strncpy(shm_addr,flg,strlen(flg));
strncpy(shm_addr+
strlen(flg),buf,strlen(buf));
printf("father finish.write data:%s \r\n",shm_addr);
}
else if( pid ==
0 )
{
printf("child pid %d\r\n",getpid());
if( (shm_addr=(
char*)shmat(shmid,
0,
0) ) == ((
void*)-
1) )
{
printf("shmat err in child process,rtn 0x%x\r\n",shm_addr);
exit(-
1);
}
else
printf("shmat addr 0x%x in child process.\r\n",shm_addr);
printf("\r\nchild read shm:%s \r\n",shm_addr);
while(strncmp(flg,shm_addr,strlen(flg)) !=
0)
{
sleep(1);
printf("\r\nchild read shm:%s \r\n",shm_addr);
}
printf("rev from father process:%s\r\n",shm_addr+
strlen(flg));
shmdt(shm_addr);
printf("child finish. \r\n");
}
else
{
printf("fork err.\r\n");
exit(-
1);
}
exit(0);
}【运行结果】
shmat addr 0xb3cad000 in father process.child pid 10449shmat addr 0xb3cad000 in child process.
child read shm: input something in father
child read shm: 12father finish.write data:write12$ child read shm:write12rev from father process:12
child finish.
8.6 消息队列
8.6.1 概述
消息队列是消息的列表,用户可以添加和读取消息比FIFO功能多一些,用户可以随机查询消息消息存在于
内核中,由“队列ID”标识有点类似通信类接口,以包为单位
8.6.2 函数
msgget(),创建或打开消息队列msgsnd(), 添加消息队列msgrcv(),读取消息队列,
可以指定取走某一条消息msgctl(), 控制消息队列
#include <sys/types>#include <sys/ipc.h>#include <sys/shm.h>int msgget( key_t key, int msgflg ); 参数: key, 消息队列键值,多进程共享时使用,特殊键值IPC_PRIVATE表示当前进程的私有消息队列 msgflg, 权限标志位返回值: 成功,消息队列ID 出错,-1int msgsnd( int msqid, const void * msgp, size_t msgsz, int msgflg );参数: msqid,消息队列ID msgp,指向消息结构的指针,该结构通常为 struct msgbuf { long
mtype; // 消息类型,同一个消息队列,可以有多种消息类型,接收时可以按类型接收,这点比较方便 char mtext[1]; // 消息正文 }
【注意】该结构为模板,实际使用时需要自己根据数据量进行定义 msgsz,消息正文的字节数,不包括消息类型 msgflg,IPC_NOWAIT,不阻塞;0,阻塞直到发送成功为止返回值: 成功,0 出错,-1int msgrcv( int msqid, void * msgp, size_t msgsz, long int msgtyp, int msgflg );参数: msqid,消息队列ID msgsz,消息缓冲区,同msgsnd的msgp msgtyp, 0,接收消息队列中的第一个消息,不管类型了 大于0,接收第一个类型为msgtyp的消息 小于0,接收第一个类型“不小于msgtyp绝对值”且“类型值最小”的消息 msgflg,MSG_NOERROR,若返回的消息比msgsz字节多,则消息就会截短到msgsz字节,且不通知消息发送进程 IPC_NOWAIT,不阻塞 0,阻塞,直到收到一条消息为止返回值: 成功,0 出错,-1int msgctl( int ms
qid, int cmd, struct msqid_ds * buf );参数: msqid,消息队列的队列ID cmd,IPC_STAT,读取消息队列的数据结构msqid_ds,并将其保存在buf中 IPC_SET,设置消息队列数据结构msqid_ds中的ipc_perm域(IPC操作权限描述结构),该值取自buf IPC_RMID,删除消息队列 buf,描述消息队列的msqid_ds结构类型变量返回值: 成功,0 出错,-1
8.6.3 例程
转载于:https://www.cnblogs.com/liuwanpeng/p/6631833.html