进程通信

进程之间的通信方式

①管道(Pipe)及有名管道(Named Pipe): 管道可用于具有"血缘"关系进程间(也就是父子进程或者兄弟进程)的通信。有名管道除具有管道所具有的功能外,还允许无"血缘"关系进程间的通信。
②信号(Signal): 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生。应该学过ARM或单片机中断吧,其实一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的(注意是效果噢!)。
③信号量(Semaphore): 主要作为进程之间及同一进程的不同线程之间的同步和互斥手段。
④共享内存(Shared Memory): 可以说这是最有效的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互此锁和信号量等。
⑤消息队列(Messge Queue): 消息队列是消息的链表,包括 Posix 消息队列和 System V 消息队列。它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以按照一定的规则向消息队列中添加消息;对消息队列具有读权限的进程则可以从消息队列中读取消息。
⑥套接字(Socket): 这个绝对是一种更为一般的进程间通信机制,它可用于网络中不同机器之间的进程间通。

1.管道

1089626-20190725095855067-1982479581.png

无名管道

创建管道可以调用 pipe() 来实现,如下表
1089626-20190725100044240-1459847938.png

详细链接:Linux进程间通信(二)---管道通信之无名管道及其基础实验

有名管道

mkfifo()函数
1089626-20190725100318598-2133639148.png

FIFO相关的出错信息进行归纳
1089626-20190725100327452-2009571787.png

access()函数
1089626-20190725100336348-1937207823.png
access()函数的功能是确定文件或文件夹的访问权限,即检查某个文件的存取方式,比如说是只读方式、只写方式等,如果指定的存取方式有效,则函数返回0,否则函数返回-1。

如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

Linux管道读写阻塞

详细链接:Linux进程间通信(三)---管道通信之有名管道及其基础实验


2.信号

信号是在软件层次上对中断机制的一种模拟,它也是异步的,一个进程不必通过任何操作来等待信号的到达。

信号事件发生的来源有两种:

     ① 硬件来源:例如按下了键盘上的按钮 或者出现其他硬件故障;

     ② 软件来源:最常用发送信号的系统函数有kill()、raise()、alarm()、setitimer()和sigqueue(),软件来源还包括一些非法运算等操作。

1.除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号
2.一般的进程只能向具有相同 uid 和 gid 的进程发送信号,或向相同进程组中的其他进程发送信号

关于setitimer()的使用可以看链接:Linux 下setitimer函数的使用

Linux中的常见信号
查看信号命令:kill -l

xzj@xzj-VirtualBox:~/development_test/process/signal$ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

编号小于SIGRTMIN的信号的说明

信号说明
①SIGHUP本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。
SIGINT程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。
SIGQUIT和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
SIGILL执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
SIGTRAP由断点指令或其它trap指令产生. 由debugger使用。
SIGABRT调用abort函数生成的信号。
SIGBUS非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
SIGFPE在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
SIGKILL用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
SIGUSR1留给用户使用
SIGSEGV试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
SIGUSR2留给用户使用
SIGPIPE管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
SIGALRM时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
SIGTERM程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。
SIGCHLD子进程结束时, 父进程会收到这个信号。如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。
这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。
SIGCONT让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
SIGSTOP停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
SIGTSTP停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
SIGTTIN当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
SIGTTOU类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
SIGURG有"紧急"数据或out-of-band数据到达socket时产生.
SIGXCPU超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。
SIGXFSZ当进程企图扩大文件以至于超过文件大小资源限制。
SIGVTALRM虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
SIGPROF类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
SIGWINCH窗口大小改变时发出.
SIGIO文件描述符准备就绪, 可以开始进行输入/输出操作.
SIGPWRPower failure
SIGSYS非法的系统调用

kill()函数语法要点

说明详细
所需头文件#include <signal.h>
#include <sys/types.h>
函数原型int kill(pid_t pid, int sig)
函数传入值pid 正数:要发送信号的进程号
0:信号被发送到所有和当前进程在同一个进程组的进程
1:信号发给所有的进程表中的进程(除了进程号大的进程外)
<-1:信号发送给进程组号为-pid的每一个进程
sig:信号
函数返回值成功:0
出错:-1

raise()函数的语法要点

说明详细
所需头文件#include <signal.h>
#include <sys/types.h>
函数原型int raise(int sig)
函数传入值sig:信号
函数返回值成功:0
出错:-1
/*信号发送函数,kill_raise.cpp*/
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char const *argv[])
{
    /* code */
    pid_t pid;
    int res;
    pid = fork();//创建一个子进程
    if (pid < 0) {
        printf("fork has error\n");
        exit(1);
    }else if (0 == pid) {
        //子进程
        printf("I am son_process,my PID is %d\n", getpid());
        raise(SIGSTOP);//子进程给自己发送停止信号
        printf("I am son_process,my pid is %d,I am killed by my father_process(pid= %d)\n", getpid(),getppid());
        exit(0);
    }else {
        //父进程
        printf("I am father_process,my pid is %d\n",getpid());
        sleep(3);//让子进程执行一段时间,不然父进程直接就把子进程杀死了
        res = waitpid(pid,NULL,WNOHANG);//如果子进程没有退出,则父进程不阻塞,返回为0;
        if (0 == res) {
            if (0 == (kill(pid,SIGKILL))) {
                printf("I am father_process(pid=%d),I killed my son_process(pid=%d)\n",getpid(),pid);
            }
        }
        waitpid(pid,NULL,0);//等待子进程退出,否则父进程就一直等待。
        exit(0);
    }

    return 0;
}

信号处理有两种:

1.使用 signal() 函数;

使用signal()函数处理时,只需指出要处理的信号和处理函数即可。它主要用于前32种非实时信号的处理,不支持信号传递信息,但是由于使用简单、易于理解,因此也受到很多程序员的欢迎。Linux还支持一个更健壮更新的信号处理函数sigaction(),推荐使用该函数。

signal()函数语法要点
说明详细
所需头文件#include <signal.h>
函数原型typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
函数传入值signum:指定信号代码
handler SIG_IGN:忽略该信号
SIG_DFL:采用系统默认方式处理信号
自定义的信号处理函数指针
函数返回值成功:以前的信号处理配置
出错:-1
这里需要对该函数原型进行说明。这个函数原型有点复杂:首先该函数原型整体指向一个无返回值并且带一个整型参数的函数指针,也就是信号的原始配置函数;接着该原型又带有两个参数,其中第2个参数可以是用户自定义的信号处理函数的函数指针。
/*signal.cpp*/
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

void my_func(int sig_no)
{
    if (sig_no == SIGALRM) {
        printf("I want sleep 3 seconds\n");
    }else if (sig_no == SIGQUIT) {
        printf("I have get a SIGQUIT\n");
    }else if (sig_no == SIGINT) {
        printf("I have get a SIGINT\n");
        signal(SIGINT,SIG_DFL);
    }
        
}

int main(int argc, char const *argv[])
{
    struct itimerval tv, otv;
    //how long to run the first time
    tv.it_value.tv_sec = 1;
    tv.it_value.tv_usec = 0;
    //after the first time, how long to run next time
    tv.it_interval.tv_sec = 3;
    tv.it_interval.tv_usec = 0;
    signal(SIGALRM,my_func);
    signal(SIGQUIT,my_func);/*ctrl+\*/
    signal(SIGINT,my_func);//ctrl+c
    setitimer(ITIMER_REAL, &tv, NULL);
    while(1){
        sleep(1);
    }
    return 0;
}

2.使用信号集函数组;

sigaction()函数的语法要点
说明详细
所需头文件#include <signal.h>
函数原型int sigaction(int signum, const struct sigaction * act, struct sigaction * oldact)
函数传入值signum:信号代码,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号
act:指向结构sigaction的一个实例的指针,指定对特定信号的处理
oldact:保存原来对相应信号的处理
函数返回值成功:0
出错:-1

这里要说明的是sigaction()函数中第2和第3个参数用到的sigaction结构

    struct sigaction 
    { 
        void (*sa_handler)(int signo); 
        sigset_t sa_mask; 
        int sa_flags; 
        void (*sa_restore)(void); 
    }

①sa_handler是一个函数指针,指定信号处理函数,这里除可以是用户自定义的处理函数外,还可以为SIG_DFL(采用默认的处理方式)或SIG_IGN(忽略信号)。它的处理函数只有一个参数,即信号值。

②sa_mask是一个信号集,它可以指定在信号处理程序执行过程中哪些信号应当被屏蔽,在调用信号捕获函数前,该信号集要加入到信号的信号屏蔽字中。

③ sa_flags中包含了许多标志位,是对信号进行处理的各个选择项,见下表

常见信号的含义及其默认操作

信 号含 义
SA_NODEFER / SA_NOMASK当捕捉到此信号时,在执行其信号捕捉函数时,系统不会自动屏蔽此信号
SA_NOCLDSTOP进程忽略子进程产生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信号
SA_RESTART令重启的系统调用起作用
SA_ONESHOT / SA_RESETHAND自定义信号只执行一次,在执行完毕后恢复信号的系统默认动作

使用信号集函数组处理信号时涉及一系列的函数,这些函数按照调用的先后次序可分为以下几大功能模块:创建信号集、注册信号处理函数及检测信号。

其中,创建信号集主要用于处理用户感兴趣的一些信号,其函数包括以下几个。

● sigemptyset():将信号集初始化为空。

● sigfillset():将信号集初始化为包含所有已定义的信号集。

● sigaddset():将指定信号加入到信号集中。

● sigdelset():将指定信号从信号集中删除。

● sigismember():查询指定信号是否在信号集中。

注册信号处理函数主要用于决定进程如何处理信号。这里要注意的是,信号集里的信号并不是真正可以处理的信号,只有当信号的状态处于非阻塞状态时才会真正起作用。因此,首先使用sigprocmask()函数检测并更改信号屏蔽字(信号屏蔽字是用来指定当前被阻塞的一组信号,它们不会被进程接收),然后使用sigaction()函数来定义进程接收到特定信号后的行为。

检测信号是信号处理的后续步骤,因为被阻塞的信号不会传递给进程,所以这些信号就处于“未处理”状态(也就是进程不清楚它的存在)。sigpending()函数允许进程检测“未处理”信号,并进一步决定对它们做何处理。

首先介绍创建信号集的函数格式,下表列举了这一组函数的语法要点。

###创建信号集函数语法要点

说明详细
所需头文件#include <signal.h>
函数原型int sigemptyset(sigset_t * set)
int sigfillset(sigset_t * set)
int sigaddset(sigset_t * set, int signum)
int sigdelset(sigset_t * set, int signum)
int sigismember(sigset_t * set, int signum)
函数传入值set:信号集
signum:指定信号代码
函数返回值成功:0(sigismember成功返回1,失败返回0)
出错:-1

sigprocmask()函数语法要点

说明详细
所需头文件#include <signal.h>
函数原型int sigprocmask(int how, const sigset_t * set, sigset_t * oset)
函数传入值how:决定函数的操作方式
SIG_BLOCK:增加一个信号集到当前进程的阻塞集中
SIG_UNBLOCK:从当前的阻塞集中删除一个信号集
SIG_SETMASK:将当前的信号集设置为信号阻塞集
set:指定信号集
oset:信号屏蔽字
函数返回值成功:0
出错:-1

此处,若set是一个非空指针,则参数how表示函数的操作方式;若how为空,则表示忽略此操作。

sigpending()函数语法要点

说明详细
所需头文件#include <signal.h>
函数原型int sigpending(sigset_t * set)
函数传入值set:要检测的信号集
函数返回值成功:0
出错:-1

在处理信号时,一般遵循如图所示的操作流程
1089626-20190726110436712-1223300324.png

/*sigaction.cpp*/
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void my_func(int sig_no)
{
    if (sig_no == SIGQUIT) {
        printf("I get a signal of SIGQUIT\n");
    }else if (sig_no == SIGINT) {
        printf("I get a signal of SIGINT\n");
    }
}
int main(int argc, char const *argv[])
{
    /* code */
    struct sigaction sigac;//声明sigaction结构体变量

    /*sigaction结构体变量的初始化*/
    sigac.sa_handler = my_func;//指定信号处理函数
    sigemptyset(&sigac.sa_mask);//将屏蔽信号集置为空,一定要使用引用的方式
    sigac.sa_flags = 0;
    /*通过信号跳转到处理函数中*/
    sigaction(SIGQUIT,&sigac,NULL);
    sigaction(SIGINT,&sigac,NULL);
    pause();
    return 0;
}
/*sigaction_set.cpp*/
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void my_func(int sig_no)
{
    if (sig_no == SIGQUIT) {
        printf("I get a signal of SIGQUIT,if you want to exit,please try (ctrl +c)\n");
    }else if (sig_no == SIGINT) {
        printf("I get a signal of SIGINT\n");
    }
}

int main(int argc, char const *argv[])
{
    /* code */
    struct sigaction action1,action2;//声明结构体变量
    sigset_t signal_set;//声明信号集
    if (sigemptyset(&signal_set) < 0) {//初始化信号集为空
        perror("sigemptyset:");
        exit(1);
    }
    /*添加信号到信号集*/
    if (sigaddset(&signal_set,SIGQUIT) < 0) {
        perror("sigaddset:");
        exit(1);
    }
    if (sigaddset(&signal_set,SIGINT) < 0) {
        perror("sigaddset:");
        exit(1);
    }
    /*检测信号是否存在信号集中,并且通过信号跳转到处理函数中*/
    if (sigismember(&signal_set,SIGQUIT)) {
        action1.sa_handler = SIG_DFL;//采用系统默认的处理方式
        sigemptyset(&action1.sa_mask);//初始化acton1的信号集
        action1.sa_flags = 0;
        sigaction(SIGQUIT,&action1,NULL);
    }
    if (sigismember(&signal_set,SIGINT)) {
        action2.sa_handler = my_func;//采用系统默认的处理方式
        sigemptyset(&action2.sa_mask);//初始化acton1的信号集
        action2.sa_flags = 0;
        sigaction(SIGQUIT,&action2,NULL);
    }
    /*设置信号屏蔽,增加一个信号集到当前进程的阻塞集中*/
    if (0 == sigprocmask(SIG_BLOCK,&signal_set,NULL)) {
        printf("%s\n","信号集被阻塞,请按任何键取消阻塞" );
        getchar();
    }else {
        perror("sigprocmask:");
        exit(1);
    }
    if (0 == sigprocmask(SIG_UNBLOCK,&signal_set,NULL)) {
        printf("%s\n", "信号集从阻塞集中移除");
    }else {
        perror("sigprocmask:");
        exit(1);
    }
    while(1);
    return 0;
}

自定义信号量:


#include <stdio.h>
#include <signal.h>
#include <unistd.h>

#define SIG_MY_MSG1 SIGRTMIN+10
#define SIG_MY_MSG2 SIGRTMIN+11
 
void sig_usr(int);
int main(void)
{
        pid_t ppid = getpid();
        if(signal(SIG_MY_MSG1, sig_usr) == SIG_ERR)
            printf("can not catch SIG_MY_MSG1\n");
        if(signal(SIG_MY_MSG2, sig_usr) == SIG_ERR)
            printf("can not catch SIG_MY_MSG2\n");
        //pid_t ppid = getpid();
        kill(ppid,SIG_MY_MSG1);
        kill(ppid,SIG_MY_MSG2);
        pause();
}
 
void sig_usr(int sig_no)
{
        if(sig_no == SIG_MY_MSG1)
            printf("received SIG_MY_MSG1\n");
        else if(sig_no == SIG_MY_MSG2)
            printf("received SIG_MY_MSG2\n");
        else
            printf("received signal %d\n", sig_no);
}

sigqueue函数

功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。
原型:int sigqueue(pid_t pid, int sig, const union sigval value);
参数:
 sigqueue的第一个参数是指定接收信号的进程id,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。
返回值:成功返回0,失败返回-1 

typedef union sigval
 { 
int sival_int; 
void *sival_ptr; 
}sigval_t; 

sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。

信号发送函数sigqueue和信号安装函数sigaction

信号量编程(Semaphore)

函数说明

在Linux系统中,使用信号量通常分为以下几个步骤:

(1)创建信号量或获得在系统中已存在的信号量,此时需要调用semget()函数。不同进程通过使用同一个信号量键值来获得同一个信号量。

(2)初始化信号量,此时使用semctl()函数的SETVAL操作。当使用二维信号量时,通常将信号量初始化为1。

(3)进行信号量的PV操作,此时调用semop()函数。这一步是实现进程间的同步和互斥的核心工作部分。

(4)如果不需要信号量,则从系统中删除它,此时使用semctl ()函数的IPC_RMID操作。需要注意的是,在程序中不应该出现对已经被删除的信号量的操作。

semget()函数语法要点:创建一个新的信号量或获取一个已经存在的信号量的键值

说明详细
所需头文件#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数原型int semget(key_t key, int nsems, int semflg)
函数传入值key:信号量的键值,多个进程可以通过它访问同一个信号量,其中有个特殊值IPC_PRIVATE,用于创建当前进程的私有信号量,该值通常为0,键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值
nsems:需要创建的信号量数目,通常取值为1
semflg:同open()函数的权限位,也可以用八进制表示法,其中使用IPC_CREAT标志创建新的信号量,即使该信号量已经存在(具有同一个键值的信号量已在系统中存在),也不会出错。如果同时使用IPC_EXCL标志可以创建一个新的唯一的信号量,此时如果该信号量已经存在,该函数会返回出错
函数返回值成功:信号量标识符,在信号量的其他函数中都会使用该值
出错:-1

消息队列相关函数(ftok)详解

semget的使用

/*semget.cpp*/
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>

#define SEM_KEY  1314

int main(int argc, const char** argv) 
{
    int sem_id;
    sem_id = semget(SEM_KEY,1,IPC_CREAT|0666);//创建一个权限为666的信号量
    if (sem_id == -1)
    {
        /* code */
        printf("create the semaphore error");
        exit(1);
    }
    printf("sem_id = %d\n",sem_id);
    return 0;
}

我们可以用 ipcs –s 来查看是否创建成功。

xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ./semget_main 
sem_id = 0
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ipcs -s

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     
0x00000522 0          xzj        666        1         

用 ipcrm -s semid 号来删除指定的信号量。

xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ipcrm -s 0
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ipcs -s

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     


semctl()函数语法要点:控制信号量的函数,在这个函数中我们可以删除信号量或初始化信号量

说明详细
所需头文件#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数原型int semctl(int semid, int semnum, int cmd, union semun arg)
函数传入值semid:semget()函数返回的信号量标识符
semnum:信号量编号,当使用信号量集时才会被用到。通常取值为0,就是使用单个信号量(也是第一个信号量)
cmd:指定对信号量的各种操作,当使用单个信号量(而不是信号量集)时,常用的操作有以下几种。
● IPC_STAT:获得该信号量(或者信号量集)的semid_ds结构,并存放在由第4个参数arg结构变量的buf域指向的semid_ds结构中。semid_ds是在系统中描述信号量的数据结构
● IPC_SETVAL:将信号量值设置为arg的val值
● IPC_GETVAL:返回信号量的当前值
● IPC_RMID:从系统中删除信号量(或者信号量集)
arg:是union semnn结构,可能在某些系统中不给出该结构的定义,此时必须由程序员自己定义
union semun
{
int val;
struct semid_ds buf;
unsigned short
array;
}
函数返回值成功:根据cmd值的不同而返回不同的值
IPC_STAT、IPC_SETVAL、IPC_RMID:返回0
IPC_GETVAL:返回信号量的当前值
出错:-1

semop()函数语法要点:用户改变信号量的值。也就是使用资源还是释放资源使用权

1089626-20190729095507946-1522112407.png

代码:让子进程先行执行,再执行父进程

/**
 * sem_fork.cpp
 * 信号量同步互斥父子进程
 */
#include <stdlib.h>
#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <unistd.h>

#define DELAY_TIME 6  //为了表示父进程开始被阻塞,子进程先执行完后,父进程才执行,

/*define union by myself */
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};
/* 函数声明 */
int init_sem(int sem_id,int init_vlaue);//初始化信号量
int delete_sem(int sem_id);//删除信号量
int sem_p(int sem_id);//信号量P操作
int sem_v(int sem_id);//信号量V操作

int main(int argc, const char** argv) 
{
    pid_t ret_fork;
    int sem_id;
    /*创建信号量 */
    sem_id = semget(1314,1,IPC_CREAT|0666);//创建一个权限为666的信号量,信号量键值人为定义:1314,当然可以ftok()函数定义信号量的键值
    if (sem_id == -1)
    {
        /* code */
        printf("create the semaphore error");
        exit(1);
    }
    init_sem(sem_id,0);//把信号量的值初始化为0,阻塞父进程,让子进程先执行,以达到同步的效果
    /* 创建子进程 */
    ret_fork = fork();
    if (-1 == ret_fork) {
        perror("创建进程:");
        exit(1);
    }else if (0 == ret_fork) {
        /*子进程 */
        sleep(DELAY_TIME);
        printf("I am son_process,my pid is :%d\n",getpid());
        sem_v(sem_id);//执行V操作,唤醒父进程
    }else {
        /* 父进程 */
        sem_p(sem_id);//执行信号量(键值为1314)的P操作
        printf("I am father_process,my pid is :%d\n",getpid());
        sem_v(sem_id);
        delete_sem(sem_id);
    }   
    return 0;
}

/*信号量初始化(赋值)函数 */
int init_sem(int sem_id,int init_vlaue)
{
    union semun sem_union;
    sem_union.val = init_vlaue;//对信号量值赋值为init_value
    if (-1 == semctl(sem_id,0,SETVAL,sem_union)) {
        perror("初始化信号量:");
        return -1;
    }
    return 0;
}
/*从系统中删除信号量 */
int delete_sem(int sem_id)
{
    union semun sem_union;//声明结构体
    if (-1 == semctl(sem_id,0,IPC_RMID,sem_union)) {
        perror("删除信号量:");
        return -1;
    }
    return 0;
}
/*信号量-P操作 */
int sem_p(int sem_id)
{
    struct sembuf sops;
    sops.sem_num = 0;//单个信号量取值为0;
    sops.sem_op = -1;//信号量的操作标志,-1表示进行P操作
    sops.sem_flg = SEM_UNDO;//当进程在没有释放所占信号量而意外退出时,系统会自动释放该进程未释放的信号量
    if (-1 == semop(sem_id,&sops,1)) {
        perror("信号量P操作:");
        return -1;
    }
    return 0;
}
/*信号量-V操作 */
int sem_v(int sem_id)
{
    struct sembuf sops;
    sops.sem_num = 0;//单个信号量取值为0;
    sops.sem_op = 1;//信号量的操作标志,-1表示进行P操作
    sops.sem_flg = SEM_UNDO;//当进程在没有释放所占信号量而意外退出时,系统会自动释放该进程未释放的信号量
    if (-1 == semop(sem_id,&sops,1)) {
        perror("信号量V操作:");
        return -1;
    }
    return 0;
}

结果:
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ./sem_fork_main 
I am son_process,my pid is :11359
I am father_process,my pid is :11358

共享内存

共享内存是一种为高效的进程间通信方式,因为进程可以直接读写内存,不需要任何数据的复制。为了在多个进程间交换信息,内核专门留出了一块内存区,这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。因此,进程就可以直接读写这一内存区而不需要进行数据的复制,从而大大提高了效率。当然,由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量

原理如图所示:
1089626-20190729154646918-624332186.png

共享内存使用步骤:

  ①  创建共享内存。也就是从内存中获得一段共享内存区域,这里用到的函数是shmget();

  ②  映射共享内存。也就是把这段创建的共享内存映射到具体的进程空间中,这里使用的函数是shmat()。到这一步就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O读写命令对其进行操作。

  ③  撤销映射。使用完共享内存就需要撤销,用到的函数是shmdt()。

shmget()函数语法

1089626-20190729154838622-1223044768.png

shmat()函数语法

1089626-20190729154915987-559833823.png

shmdt()函数语法

1089626-20190729154949501-1764304784.png

shmctl()函数 -- ctl:control

与信号量的semctl()函数一样,用来控制共享内存,它的原型如下:

int shmctl(int shm_id, int command, struct shmid_ds *buf);

第一个参数,shm_id是shmget()函数返回的共享内存标识符。

第二个参数,command是要采取的操作,它可以取下面的三个值 :

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。

shmid_ds结构 至少包括以下成员:

struct shmid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};

共享内存与信号量结合实现进程的通信

1.头文件semaphore.h

#ifndef SEMAPHORE_H
#define SEMAPHORE_H
/*define union by myself */
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};
/* 函数声明 */
int init_sem(int sem_id,int init_vlaue);//初始化信号量
int delete_sem(int sem_id);//删除信号量
int sem_p(int sem_id);//信号量P操作
int sem_v(int sem_id);//信号量V操作
#endif // !SEMAPHORE_H

2.实现头文件的源文件sharemem.cpp

#include "semaphore.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <unistd.h>

/*信号量初始化(赋值)函数 */
int init_sem(int sem_id,int init_vlaue)
{
    union semun sem_union;
    sem_union.val = init_vlaue;//对信号量值赋值为init_value
    if (-1 == semctl(sem_id,0,SETVAL,sem_union)) {
        perror("初始化信号量:");
        return -1;
    }
    return 0;
}
/*从系统中删除信号量 */
int delete_sem(int sem_id)
{
    union semun sem_union;//声明结构体
    if (-1 == semctl(sem_id,0,IPC_RMID,sem_union)) {
        perror("删除信号量:");
        return -1;
    }
    return 0;
}
/*信号量-P操作 */
int sem_p(int sem_id)
{
    struct sembuf sops;
    sops.sem_num = 0;//单个信号量取值为0;
    sops.sem_op = -1;//信号量的操作标志,-1表示进行P操作
    sops.sem_flg = SEM_UNDO;//当进程在没有释放所占信号量而意外退出时,系统会自动释放该进程未释放的信号量
    if (-1 == semop(sem_id,&sops,1)) {
        perror("信号量P操作:");
        return -1;
    }
    return 0;
}
/*信号量-V操作 */
int sem_v(int sem_id)
{
    struct sembuf sops;
    sops.sem_num = 0;//单个信号量取值为0;
    sops.sem_op = 1;//信号量的操作标志,-1表示进行P操作
    sops.sem_flg = SEM_UNDO;//当进程在没有释放所占信号量而意外退出时,系统会自动释放该进程未释放的信号量
    if (-1 == semop(sem_id,&sops,1)) {
        perror("信号量V操作:");
        return -1;
    }
    return 0;
}

3.写进程-sheme_write.cpp

/* sheme_write.cpp */
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>
#include "semaphore.h"

#define SEM_KEY 1314    //信号量键值
#define MAX_SIZE 2048   //缓冲池大小
#define SHM_KEY 1234   //共享内存键值
#define SHM_SIZE 4096   //共享内存大小

int main(int argc, char const *argv[])
{
    int sem_id;
    int shm_id;
    char *shm_addr = NULL;//共享内存被映射的段地址
    char buffer[MAX_SIZE] = {0};//缓冲池初始化
    sem_id = semget(SEM_KEY,1,IPC_CREAT|0666);//创建一个权限为666的信号量
    if (sem_id == -1)
    {
        /* code */
        printf("create the semaphore error\n");
        exit(1);
    }else {
        printf("create semaphore successful!,sem_id is %d\n",sem_id);
    }
    init_sem(sem_id,1);//初始化信号量的值为0;让写先执行。
    /* 创建共享内存 */
    if (-1 == (shm_id =shmget(ftok(".",'m'),SHM_SIZE, IPC_CREAT | 0777))) {
    //if (-1 == (shm_id =shmget(IPC_PRIVATE,SHM_SIZE,0666))) {
        perror("共享内存创建shmget:");
        exit(1);
    }else {
        printf("create sharemem successful!,shm_id is %d\n",shm_id);
    }
    system("ipcs -m");//查看共享内存状态
    /* 共享内存映射 */
    if ((shm_addr = (char*)shmat(shm_id,0,0)) ==(char*)-1) {
        perror("write:shmat:");
        exit(1);
    }else {
        printf("write:共享内存映射成功!\n");
    }
    system("ipcs -m");//查看共享内存状态
    printf("请输入字符串文本:\n");
    scanf("%[^\n]",buffer);//字符串输入
    printf("你要将此数据%s写入到共享内存中!\n",buffer);
    sem_p(sem_id);
    strncpy(shm_addr,buffer,strlen(buffer));//把缓冲池的数据写入到共享内存
    printf("写数据成功\n");
    /* 解除共享内存的映射 */
    if (-1 == shmdt(shm_addr)) {
        perror("write:shmdt:");
        exit(1);
    }else {
        printf("write:共享内存解除映射成功!\n");
    }
    system("ipcs -m");//查看共享内存状态
    sem_v(sem_id);//进行信号量的v操作
    //获取信号量的值
    union semun sem_union;
    int sem_value;
    sem_value = semctl(sem_id,0,GETVAL,sem_union);
    printf("sem_id = %d,the sem_value is %d\n",sem_id,sem_value);
    exit(0);
    //return 0;
}

4.读进程-sheme_read.cpp

/* sheme_read.cpp */
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>
#include "semaphore.h"

#define SEM_KEY 1314    //信号量键值
#define MAX_SIZE 2048   //缓冲池大小
#define SHM_KEY 1234   //共享内存键值
#define SHM_SIZE 4096   //共享内存大小

int main(int argc, char const *argv[])
{
    int sem_id;
    int shm_id;
    char *shm_addr = NULL;//共享内存被映射的段地址
    char buffer[MAX_SIZE] = {0};//缓冲池初始化
    // if (argc >1){
    //     shm_id = atoi(argv[1]);
    // }else
    // {
    //     printf("无法获取shm_id\n");
    //     exit(1);
    // }
    
    sem_id = semget(SEM_KEY,1,IPC_CREAT|0666);//创建一个权限为666的信号量
    if (sem_id == -1)
    {
        /* code */
        printf("get the semaphore error\n");
        exit(1);
    }
    printf("成功获取信号量id:%d\n",sem_id);
    /* 进入临界资源区 */
    sem_p(sem_id);//对信号量进行p操作
    /* 根据SHM_KEY获取共享内存 */
    if (-1 == (shm_id =shmget(ftok(".",'m'),SHM_SIZE,IPC_CREAT | 0777))) {
        perror("共享内存获取shmget:");
        exit(1);
    }else {
        printf("get sharemem successful!,shm_id is %d\n",shm_id);
    }
    system("ipcs -m");//查看共享内存状态
    /* 共享内存映射 */
    printf("成功获取共享内存id:%d\n",shm_id);
    if ((shm_addr = (char*)shmat(shm_id,0,0)) ==(char*)-1) {
        perror("red:shmat:");
        exit(1);
    }else {
        printf("red:共享内存映射成功!\n");
    }
    /* 从共享内存里读 */
    strcpy(buffer,shm_addr);
    printf("从共享内存里读取的数据为:%s\n",buffer);
    /* 解除共享内存的映射 */
    if (-1 == shmdt(shm_addr)) {
        perror("write:shmdt:");
        exit(1);
    }else {
        printf("write:共享内存解除映射成功!\n");
    }
    //删除共享内存
    if (-1 == shmctl(shm_id,IPC_RMID,NULL)) {
        perror("delete share_emeory:");
        exit(1);
    }else {
        printf("delete shared_memory successful!\n");
    }
    system("ipcs -m");//查看共享内存状态
    sem_v(sem_id);//进行信号量的v操作
    delete_sem(sem_id);
    return 0;
}

结果:

写进程
xzj@xzj-VirtualBox:~/development_test/process/sharemem$ ./sheme_write_main 
create semaphore successful!,sem_id is 327680
create sharemem successful!,shm_id is 3407884

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 360448     xzj        600        16777216   2                       
0x00000000 458753     xzj        600        524288     2          目标       
0x00000000 622594     xzj        600        524288     2          目标       
0x00000000 294915     xzj        600        524288     2          目标       
0x00000000 1245188    xzj        700        16472      2          目标       
0x00000000 753669     xzj        600        67108864   2          目标       
0x00000000 851974     xzj        600        524288     2          目标       
0x00000000 1048583    xzj        600        524288     2          目标       
0x00000000 1146888    xzj        600        524288     2          目标       
0x00000000 1343497    xzj        600        524288     2          目标       
0x00000000 1441802    xzj        600        524288     2          目标       
0x00000000 1540107    xzj        600        524288     2          目标       
0x6d010f3c 3407884    xzj        777        4096       0                       
0x00000000 2261005    xzj        600        524288     2          目标       
0x00000000 2621454    xzj        700        161700     2          目标       
0x00000000 2293775    xzj        600        67108864   2          目标       

write:共享内存映射成功!

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 360448     xzj        600        16777216   2                       
0x00000000 458753     xzj        600        524288     2          目标       
0x00000000 622594     xzj        600        524288     2          目标       
0x00000000 294915     xzj        600        524288     2          目标       
0x00000000 1245188    xzj        700        16472      2          目标       
0x00000000 753669     xzj        600        67108864   2          目标       
0x00000000 851974     xzj        600        524288     2          目标       
0x00000000 1048583    xzj        600        524288     2          目标       
0x00000000 1146888    xzj        600        524288     2          目标       
0x00000000 1343497    xzj        600        524288     2          目标       
0x00000000 1441802    xzj        600        524288     2          目标       
0x00000000 1540107    xzj        600        524288     2          目标       
0x6d010f3c 3407884    xzj        777        4096       1                       
0x00000000 2261005    xzj        600        524288     2          目标       
0x00000000 2621454    xzj        700        161700     2          目标       
0x00000000 2293775    xzj        600        67108864   2          目标       

请输入字符串文本:
I am xzj,I am working in iauto!
你要将此数据I am xzj,I am working in iauto!写入到共享内存中!
写数据成功
write:共享内存解除映射成功!

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 360448     xzj        600        16777216   2                       
0x00000000 458753     xzj        600        524288     2          目标       
0x00000000 622594     xzj        600        524288     2          目标       
0x00000000 294915     xzj        600        524288     2          目标       
0x00000000 1245188    xzj        700        16472      2          目标       
0x00000000 753669     xzj        600        67108864   2          目标       
0x00000000 851974     xzj        600        524288     2          目标       
0x00000000 1048583    xzj        600        524288     2          目标       
0x00000000 1146888    xzj        600        524288     2          目标       
0x00000000 1343497    xzj        600        524288     2          目标       
0x00000000 1441802    xzj        600        524288     2          目标       
0x00000000 1540107    xzj        600        524288     2          目标       
0x6d010f3c 3407884    xzj        777        4096       0                       
0x00000000 2261005    xzj        600        524288     2          目标       
0x00000000 2621454    xzj        700        161700     2          目标       
0x00000000 2293775    xzj        600        67108864   2          目标       

sem_id = 327680,the sem_value is 1
xzj@xzj-VirtualBox:~/development_test/process/sharemem$ 


读进程
xzj@xzj-VirtualBox:~/development_test/process/sharemem$ ./sheme_read_main
成功获取信号量id:327680
get sharemem successful!,shm_id is 3407884

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 360448     xzj        600        16777216   2                       
0x00000000 458753     xzj        600        524288     2          目标       
0x00000000 622594     xzj        600        524288     2          目标       
0x00000000 294915     xzj        600        524288     2          目标       
0x00000000 1245188    xzj        700        16472      2          目标       
0x00000000 753669     xzj        600        67108864   2          目标       
0x00000000 851974     xzj        600        524288     2          目标       
0x00000000 1048583    xzj        600        524288     2          目标       
0x00000000 1146888    xzj        600        524288     2          目标       
0x00000000 1343497    xzj        600        524288     2          目标       
0x00000000 1441802    xzj        600        524288     2          目标       
0x00000000 1540107    xzj        600        524288     2          目标       
0x6d010f3c 3407884    xzj        777        4096       0                       
0x00000000 2261005    xzj        600        524288     2          目标       
0x00000000 2621454    xzj        700        161700     2          目标       
0x00000000 2293775    xzj        600        67108864   2          目标       

成功获取共享内存id:3407884
red:共享内存映射成功!

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 360448     xzj        600        16777216   2                       
0x00000000 458753     xzj        600        524288     2          目标       
0x00000000 622594     xzj        600        524288     2          目标       
0x00000000 294915     xzj        600        524288     2          目标       
0x00000000 1245188    xzj        700        16472      2          目标       
0x00000000 753669     xzj        600        67108864   2          目标       
0x00000000 851974     xzj        600        524288     2          目标       
0x00000000 1048583    xzj        600        524288     2          目标       
0x00000000 1146888    xzj        600        524288     2          目标       
0x00000000 1343497    xzj        600        524288     2          目标       
0x00000000 1441802    xzj        600        524288     2          目标       
0x00000000 1540107    xzj        600        524288     2          目标       
0x6d010f3c 3407884    xzj        777        4096       1                       
0x00000000 2261005    xzj        600        524288     2          目标       
0x00000000 2621454    xzj        700        161700     2          目标       
0x00000000 2293775    xzj        600        67108864   2          目标       

从共享内存里读取的数据为:I am xzj,I am working in iauto!
write:共享内存解除映射成功!
delete shared_memory successful!

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 360448     xzj        600        16777216   2                       
0x00000000 458753     xzj        600        524288     2          目标       
0x00000000 622594     xzj        600        524288     2          目标       
0x00000000 294915     xzj        600        524288     2          目标       
0x00000000 1245188    xzj        700        16472      2          目标       
0x00000000 753669     xzj        600        67108864   2          目标       
0x00000000 851974     xzj        600        524288     2          目标       
0x00000000 1048583    xzj        600        524288     2          目标       
0x00000000 1146888    xzj        600        524288     2          目标       
0x00000000 1343497    xzj        600        524288     2          目标       
0x00000000 1441802    xzj        600        524288     2          目标       
0x00000000 1540107    xzj        600        524288     2          目标       
0x00000000 2261005    xzj        600        524288     2          目标       
0x00000000 2621454    xzj        700        161700     2          目标       
0x00000000 2293775    xzj        600        67108864   2          目标       

xzj@xzj-VirtualBox:~/development_test/process/sharemem$ 

可以使用以下命令查看共享内存

查看共享内存:ipcs  -m 
删除共享内存:ipcrm  -m  shm_id

消息队列

三种系统V IPC:消息队列、信号量以及共享内存(共享存储器)之间有很多相似之处。每个内核中的 I P C结构(消息队列、信号量或共享存储段)都用一个非负整数的标识符( i d e n t i f i e r )加以引用。 无论何时创建I P C结构(调用m s g g e t、 s e m g e t或s h m g e t) ,都应指定一个关键字(k e y),关键字的数据类型由系统规定为 k e y _ t,通常在头文件< s y s / t y p e s . h >中被规定为长整型。关键字由内核变换成标识符。

消息队列的实现操作

  ①  创建或打开消息队列。使用的函数是msgget(),这里创建的消息队列的数量会受到系统消息队列数量的限制。

  ②  添加消息。使用的函数是msgsnd(),它把消息添加到已打开的消息队列末尾。

  ③  读取消息。使用的函数是msgrcv(),它把消息从消息队列中取走,与FIFO不同的是,这里可以取走指定的某一条消息。

  ④  控制消息队列。使用的函数是msgctl(),它可以完成多项功能。

ftok函数

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

key_t ftok(const char *pathname, int proj_id);//“/home/linux” , 'a'
功能:生成一个key(键值)

msgget函数

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

int msgget(key_t key, int msgflg);

功能:创建或取得一个消息队列对象
返回:消息队列对象的id 同一个key得到同一个对象
格式:msgget(key,flag|mode);
flag:可以是0或者IPC_CREAT(不存在就创建) 
mode:同文件权限一样

1089626-20190730141548992-1367398319.png

msgsnd函数

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:将msgp消息写入标识为msgid的消息队列
msgp: 
struct msgbuf {
long mtype; /* message type, must be > 0 */消息的类型必须>0
char mtext[1]; /* message data */长度随意
};

msgsz:要发送的消息的大小 不包括消息的类型占用的4个字节
msgflg: 如果是0 当消息队列为满 msgsnd会阻塞
如果是IPC_NOWAIT 当消息队列为满时 不阻塞 立即返回

返回值:成功返回id 失败返回-1

1089626-20190730141637165-482576189.png

msgrcv函数

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

功能:从标识符为msgid的消息队列里接收一个指定类型的消息 并 存储于msgp中 读取后 把消息从消息队列中删除
msgtyp:为 0 表示无论什么类型 都可以接收
msgp:存放消息的结构体
msgsz:要接收的消息的大小 不包含消息类型占用的4字节
msgflg:如果是0 标识如果没有指定类型的消息 就一直等待
如果是IPC_NOWAIT 则表示不等待

1089626-20190730141702270-1470435537.png

msgctl函数

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgctl(msgid,IPC_RMID,NULL);//删除消息队列对象

1089626-20190730141722580-953969139.png

可以使用以下命令查看消息队列

查看共享内存:ipcs  -q 
删除共享内存:ipcrm  -q  msqid

代码实战:一个进程从消息队列里写,一个进程从消息队列里读(阻塞读)

头文件:messagequeue.h

#ifndef MESSAGEQUEUE_H
#define MESSAGEQUEUE_H
#define BUFFER_SIZE 1024

struct message
{
    long msg_type;
    char msg_text[BUFFER_SIZE];
};

#endif // !MESSAGEQUEUE_H

从消息队列中写:msgsnd.cpp

/* msgsnd.cpp */
#include "messagequeue.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int msgQueue_id;//消息队列标识符
    key_t key;//键值
    struct message msg;
    /* 根据不同的路径和关键字产生标准的key */
    if ((key = ftok(".", 'a')) == -1)
    {
        perror("ftok has a error:");
        exit(1);
    }
    /* 创建消息队列 */
    if ((msgQueue_id = msgget(key, IPC_CREAT|0666)) == -1)
    {
        perror("msgget");
        exit(1);
    }
    printf("打开或创建消息队列成功,消息队列ID为:%d\n",msgQueue_id);
    while(1)
    {
        printf("请输入你想输入的消息(输入exit结束):\n");
        if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL)//输入一行字符串
        {
            puts("no message");
            exit(1);
        }

        msg.msg_type = getpid();
        /* 添加消息到消息队列 */
        if ((msgsnd(msgQueue_id, &msg, strlen(msg.msg_text), 0)) < 0)
        {
            perror("message posted");
            exit(1);
        }
        if (strncmp(msg.msg_text, "exit", 4) == 0)
        {
            break;
        }
    }
    return 0;
}

从消息队列里读:msgrcv.cpp

/* msgrcv.cpp */
#include "messagequeue.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int msgQueue_id;
    key_t key;
    struct message msg;

    /* 根据不同的路径和关键字产生标准的key */
    if ((key = ftok(".", 'a')) == -1)
    {
        perror("ftok");
        exit(1);
    }
    /* 创建消息队列 */
    if ((msgQueue_id = msgget(key, IPC_CREAT|0666)) == -1)
    {
        perror("msgget");
        exit(1);
    }
    printf("打开或创建消息队列成功,消息队列ID为:%d\n",msgQueue_id);
    do
    {
        /* 读取消息队列 */
        memset(msg.msg_text, 0, BUFFER_SIZE);//清空消息数组
        if (msgrcv(msgQueue_id, (void*)&msg, BUFFER_SIZE, 0, 0) < 0)
        {
            perror("msgrcv");
            exit(1);
        }
        printf("来自进程id为:%ld 的消息为: %s", msg.msg_type, msg.msg_text); 

    } while(strncmp(msg.msg_text, "exit", 4));
    /* 从系统内核中移走消息队列 */
    if ((msgctl(msgQueue_id, IPC_RMID, NULL)) < 0)
    {
        perror("msgctl");
        exit(1);
    }   
    return 0;
}

结果

读进程
xzj@xzj-VirtualBox:~/development_test/process/messageQueue$ ./msgsnd_main 
打开或创建消息队列成功,消息队列ID为:32768
请输入你想输入的消息(输入exit结束):
my name is xzj,I am working in iauto!
请输入你想输入的消息(输入exit结束):
wuhan is a beautiful city.
请输入你想输入的消息(输入exit结束):
exit

写进程
xzj@xzj-VirtualBox:~/development_test/process/messageQueue$ ./msgrcv_main 
打开或创建消息队列成功,消息队列ID为:32768
来自进程id为:9611 的消息为: my name is xzj,I am working in iauto!
来自进程id为:9611 的消息为: wuhan is a beautiful city.
来自进程id为:9611 的消息为: exit

socket

工作流程:
1.服务端先用socket函数建立一个套接字,并调用listen函数,使服务端的这个端口和ip处于监听状态,等待客户端的链接。
2.客户端用socket函数建立一个套接字,设定远程IP和端口,并调用connect函数。
3.服务端用accept函数来接受远程计算机的链接,建立起与客户之间的通信。
4.完成通信后,最后使用close函数关闭socket连接。

未完。。。。待续!!!!

参考链接:linux系统编程之信号(五):实时信号与sigqueue函数

转载于:https://www.cnblogs.com/xzj8023tp/p/11242395.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值