linux_信号

news2024/10/7 3:17:46

文章目录

    • 1、信号的实现机制
    • 2、发送信号
      • 2.1、发送信号的原因
      • 2.2、发送信号的机制
        • kill 函数
    • 3、接收信号
      • 3.1、处理信号
        • signal 函数
        • sigaction 函数
      • 3.2、信号阻塞 | 解除
        • sigset_t 信号集合
        • sigpending 函数
        • sigprocmask 函数
        • sigsuspend 函数
    • 4、定时器
      • 4.1、睡眠函数
        • sleep 函数
        • pause 函数
        • alarm 函数
      • 4.2、定时器
        • setitimer 函数
    • 5、例:四窗口聊天

信号:消息,通知进程系统中发生了某种类型的事件,每种类型的信号对应某种系统事件。

信号是进程间通信中唯一的异步通信机制。在进程运行过程中,经常会产生一些事件,这些事件的产生和进程的执行往往是异步的,信号提供了一种在软件层面上的异步处理事件的机制。在硬件层面上的异步处理机制是中断,信号是中断的软件模拟。

每个信号用一个整型常量宏表示,以 SIG开头,在头文件<signal.h>中定义

# 查看信号列表
kill -l 
man 7 signal

1、信号的实现机制

发送信号到目的进程是由发送信号和接收信号两个步骤组成。

  • 发送信号:内核通过更新目的进程上下文中的某个状态,发送一个信号给目的进程。
  • 接收信号:目的进程被内核强迫以某种方式对信号的发送做出反应。

在这里插入图片描述

待处理信号(pending signal):一个发出而没有接收的信号,又称为挂起信号或未决信号。

在任何时候,一种类型的信号至多只会有一个待处理信号。也就是说,如果一个进程有一个类型为 k 的待处理信号,那么任何接下来发送到这个进程的类型为 k 的信号都不会排队等待,它们只是被简单丢弃。一个进程可以选择性地阻塞接收某种信号,当一种信号被阻塞,它仍可以被发送,但是产生的待处理信号不会被接收,直到进程取消对这种信号的阻塞。

一个待处理信号最多只能被接收一次。内核为每个进程,在位图 pending 中维护着待处理信号的结合,在位图 blocked 集合(信号屏蔽字)中维护着被阻塞的信号集合。只要发送了一个类型为 k 的信号,内核就会设置 pending中的第 k 位,只要接收了一个类型为 k 的信号,内核就会清楚 pending中的第 k 位。

2、发送信号

2.1、发送信号的原因

  • 内核检测到系统事件,例除零错误等
  • 进程调用 kill 函数

2.2、发送信号的机制

unix 系统提供了大量向进程发送信号的机制,这些机制都是基于进程组的。每个进程拥有自己的 pid,每个进程属于一个进程组 pgid

硬件来源:键盘。硬件触发中断,操作系统切换到内核态执行中断处理程序,中断处理程序发送信号,进程接收信号并处理。

ctrl + c: SIGINT,表示终止该进程
ctrl + z: SIGTSTP,表示挂起该进程

软件来源:/bin/kill 程序,kill 函数,定时器函数。

kill 函数

进程通过 kill 函数发送信号给其他进程(包括自己)。

/*
返回值:成功返回 0,失败返回 -1

参数
- pid: pid > 0,发送 sig 信号给进程 pid
	   pid = 0, 发送 sig 信号给调用进程所在进程组中的每个进程(包括调用进程自己)
	   pid < 0,发送 sig 信号给进程组 |pid| 中的每个进程
- sig: 信号类型
*/
int kill(pid_t pid, int sig);

3、接收信号

进程接收到信号,有三种处理方法:

  • 默认处理:signal(SIGINT,SIG_DFL)

    - Term:终止当前进程
    - Ign:忽略该信号
    - Core:终止当前进程,并且产生core dump
    - Stop:停止(挂起)一个进程
    - Cont:使当前停止的进程,继续运行
    
  • 忽略信号:signal(SIGSEGV,SIG_IGN)

  • 捕捉信号:自定义信号处理函数。用户自定义信号处理函数的目的就是实现进程的有序退出。

自定义信号处理函数设置原则

  • 程序尽可能简单。例:处理程序简单设置全局标志并返回,所有与接收信号相关的处理由主程序执行。
  • 函数内部只调用信号异步安全函数

安全函数:可重入的(例只访问局部变量),不能被信号处理程序中断。

3.1、处理信号

signal 函数

进程可以通过signal函数修改和信号相关联的默认行为。注意:SIGKILLSIGSTOP这两个信号既不能被忽略也不能被捕捉,即进程接收到这两个信号后,只能接受系统的默认处理,即终止进程。

# include <signal.h> 
// 信号回调函数,信号处理
typedef void (*sighandler_t)(int);

/*
功能:捕获信号
返回值:成功返回前次处理程序的指针,失败返回 SIG_ERR
参数:
- signum:要捕捉的信号值,
- handler:函数处理函数。SIG_DFL 默认处理; SIG_IGN,忽略信号; 其他,执行信号回调函数
*/ 
sighandler_t signal(int signum, sighandler_t handler); 

sigaction 函数

signal函数处理机制在多信号处理的场景下

  • 收到不同类型的信号:中断当前信号处理,优先处理新的信号,处理完后返回继续处理当前信号。
  • 收到相同类型的信号:执行完当前信号处理,然后只执行新的相同信号一次,重复的信号被忽略。
  • 当前进程阻塞在系统调用上,收到一个信号后,中断系统调用,执行信号处理函数。

不同的系统有不同的信号处理语义,但 signal函数的处理过程是固定的,无法调整。因此,Posix 标准定义了sigaction函数,允许用户自定义这些场景下进程的行为。

/*
返回值:成功返回0,失败返回-1。
参数:
- signum:要捕捉的信号值 
- act:自定义行为
- oldact:保存原来信号的回调函数,通常传入空指针
*/
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) 

其中 sigaction 结构体

struct sigaction {    
    // 联合体,新旧处理函数只有一个可以生效  
    union { 
        // 旧类型的信号处理函数 
        __sighandler_t _sa_handler; 
        // 新类型的信号处理函数,siginfo_t 保存信号的相关信息
        void (*_sa_sigaction)(int, struct siginfo *, void *);
    } _u;

    // 阻塞信号的结合 
    sigset_t sa_mask; 

    // 信号处理的方式,旧类型填 0。其他例如:
    // SA_SIGINFO,选择回调函数 sa_sigaction; 
    unsigned long sa_flags; 

    // 保留不用
    void (*sa_restorer)(void);
};

3.2、信号阻塞 | 解除

Linux 提供阻塞信号的机制

  • 隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类型的待处理信号。
  • 显式阻塞机制: sigprocmask 函数

sigset_t 信号集合

#include <signal.h> 

typedef struct { 
    unsigned long int __val[(1024/(8*sizeof(unsigned long int)))]; 
} __sigset_t; 

// sigset_t的本质就是一个位图,共有1024位
typedef __sigset_t sigset_t; 

// 初始化信号集,清除所有信号 
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(const sigset_t *set, int signum); 

sigpending 函数

获取当前待处理信号的集合。通常在回调函数当中使用的,用于检查当前是否阻塞了某个信号

/*
返回值:成功返回0,失败返回-1
参数 set: 要检测的信号
*/
int sigpending(sigset_t *set);

sigprocmask 函数

改变当前阻塞的信号集合(位图 blocked

/*
返回值:成功返回0, 失败返回-1

参数
- how:
	SIG_BLOCK: 把集合 set 中的信号加入到阻塞集合 blocked 当中 (blocked |= set)
    SIG_UNBLOCK: 把集合 set 从阻塞集合 blocked 中删除 (blocked = blocked & ~set)
    SIG_SETMASK: 把集合 set 替换阻塞集合 blocked (blocked = set)
- set:阻塞集合
- 参数3:原有的集合
*/
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

sigsuspend 函数

使用 sigprocmask 函数可以实现信号保护临界区。在临界区执行代码的时候,此时产生的信号将会被阻塞,临界区结束的位置只需要再使用 sigprocmask 即可。pause 系统调用可以唤醒一个阻塞进程,直到被一个信号唤醒。若采用 pause 函数捕捉临界区的信号,信号解除阻塞后,立即执行信号处理函数,无法捕捉信号。

为了捕捉临界区的信号,将解除阻塞和等待信号合并成一个原子操作,就是sigsuspend函数

int sigsuspend(const sigset_t *mask);

4、定时器

4.1、睡眠函数

sleep 函数

#include <unistd.h>
// 休眠 sec 秒
unsigned int sleep(unsigned int seconds);
// 休眠 usec 秒
int usleep(useconds_t usec);

pause 函数

阻塞一个进程,直到某个信号被递送时,进程会解除阻塞,然后终止进程或者执行信号处理函数

int pause(void);

alarm 函数

进程调用 alarm 函数向它自己发送 SIGALRM信号。

/*
返回值:前一次闹钟剩余的秒数,若以前没有设定闹钟,则为 0
参数:seconds: 闹钟的时间间隔
*/
unsigned int alarm(unsigned int seconds);

alarm 函数安排内核在 secs 秒后发送一个 SIGALRM 信号给调用进程。在任何情况下,对 alarm 的调用都将取消任何待处理的闹钟,并且返回待处理的闹钟在被发送前还剩下的秒数,若没有代理处理的闹钟,返回 0。

4.2、定时器

setitimer 函数

setitimer 系统调用负责调整间隔定时器。间隔定时器在创建的时候,就会设置一个时间间隔,定时器到达时间间隔时,调用进程会产生一个信号,随后定时器被重置。

定时器的分类

  • 真实计时器:程序实际运行的时间(时钟时间),时间到发送 SIGALARM信号
  • 虚拟计时器:程序在用户态模式下的 CPU 时间,时间到发送SIGVTALARM信号,
  • 实用计时器:程序在用户态和内核态所占用的 CPU 时间,时间到发送 SIGPROF信号

使用 fork 的时候子进程不会继承父进程的定时器,使用 exec 时候,定时器不会销毁。

/*
返回值:成功返回0,失败返回-1.

参数:
- which:设置定时器的种类:真实计时器 SIGALARM、虚拟计时器 SIGVTALARM、实用计时器 SIGPROF
- new_value:定时器的初始时间  
- old_value: 定时器的间隔时间    
*/
int getitimer(int which, struct itimerval *curr_value)
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) 
  
struct itimerval { 
    struct timeval it_interval; // 时间间隔 
    struct timeval it_value;    // 初始时间
}; 
struct timeval {
    time_t tv_sec;       // 秒 
    suseconds_t tv_usec; // 微秒
};

例:使用真实计时器,实用计时器,统计程序执行时间,在实用计时器及虚拟计时器设定计时后,先睡眠,再让程序处于while(1)

#include <func.h>

void sigFunc(int sigNum) {
    time_t now;
    time(&now);
    printf("now time = %s\n", ctime(&now));
}

int main(int argc,char*argv[]) {
    //真实计时器,信号SIGALRM
    signal(SIGALRM, sigFunc);

    struct itimerval  Time;
    memset(&Time, 0, sizeof(Time));
    Time.it_value.tv_sec = 1;    // 1s后启动
    Time.it_interval.tv_sec = 2; // 间隔2s

    time_t now;
    time(&now);
    printf("now time = %s", ctime(&now));
	// ITIMER_REAL
    setitimer(ITIMER_REAL, &Time, NULL);
    // ITIMER_PROF
    // setitimer(ITIMER_PROF, &Time, NULL);

    printf("before sleep\n");
    sleep(3);
    printf("after sleep\n");
    
    while(1);
    return 0;
}

5、例:四窗口聊天

在四个终端上分别启动A、A1、B、B1四个进程。

  • A 和 B 负责通信,A1和B1负责显示信息。
  • A 和 B 使用有名管道进行通信
  • A 和 A1 进程通信采用共享内存(信号量同步),B 和 B1 进程通信采用消息队列
  • 退出方式:ctrl + ckill 命令,任意进程收到信号后,给所有进程发送 10 号信号,有序退出。有序退出要做的主要有:关闭管道,解除对共享内存的映射,删除共享内存,删除信号量,删除消息队列。

showA

#include <head.h>

// 保存各个进程的pid
typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
} pidNums_t, *pPidNums_t;

typedef struct package{
    int flag;
    char buf[64];
}Package_t, *pPackage_t;

pPidNums_t pidsets; 
pPackage_t p;

// 2 信号处理函数
void sigFunc2(int signum,siginfo_t *p,void *p1){
    printf("%d is coming\n",signum);
    kill(pidsets->pidWriteA, 10);
    kill(pidsets->pidWriteB, 10);
    kill(pidsets->pidShowB, 10);
    kill(pidsets->pidShowA, 10);
}

// 10 信号处理函数:
void sigFunc10(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    shmdt(pidsets);
    shmdt(p);
    exit(0);
}

int main(int argc, char *argv[]) {
    // 2 号信号
    struct sigaction act2;
    bzero(&act2, sizeof(act2));
    act2.sa_flags = SA_SIGINFO;
    act2.sa_sigaction = sigFunc2;
    int ret = sigaction(SIGINT, &act2, NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");
	
    // 10 号信号
    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags = SA_SIGINFO;
    act10.sa_sigaction = sigFunc10;
    ret = sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction 10");

    // 创建共享内存
    int shmid = shmget(1000, 1024, IPC_CREAT|0600);
    ERROR_CHECK(shmid,-1,"shmget");

    // 映射地址空间
    p = (pPackage_t)shmat(shmid,NULL,0);
    ERROR_CHECK(p, (pPackage_t)-1, "shmat");
    int semArrId = semget(1000, 1, IPC_CREAT|0600);
    ERROR_CHECK(semArrId, -1, "semget");
	
    ret = semctl(semArrId, 0, SETVAL, 1);
    ERROR_CHECK(ret, -1, "semctl");
    struct sembuf sopp, sopv;
    sopp.sem_num = 0;
    sopp.sem_op = -1;
    sopp.sem_flg = SEM_UNDO;
    sopv.sem_num = 0;
    sopv.sem_op = 1;
    sopv.sem_flg = SEM_UNDO;

    int pid = getpid();
    int shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid,- 1, "pid shmget");
    pidsets = (pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidShowA = pid;
    while(1){
        semop(semArrId, &sopp, 1);
        if(p->flag == 1){
            puts(p->buf);
            printf("\n");
            memset(p->buf, 0, sizeof(p->buf));
            p->flag = 0;
        }
        if(p->flag == 2){
            printf("%*s%s\n", 10, "", p->buf);
            memset(p->buf, 0, sizeof(p->buf));
            p->flag = 0;
        }
        semop(semArrId, &sopv, 1);
    }
    return 0;
}

write A

#include <head.h>

typedef struct package{
    int flag;
    char buf[64];
}Package_t, *pPackage_t;

typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
} pidNums_t, *pPidNums_t;

pPidNums_t pidsets; 

int fdr = 0,fdw = 0;
int shmid = 0;
int semArrId = 0;
int shmidPid = 0;
pPackage_t p;

void sigFunc2(int signum,siginfo_t *p,void *p1){
    printf("%d is coming\n",signum);
    kill(pidsets->pidWriteB, 10);
    kill(pidsets->pidShowA, 10);
    kill(pidsets->pidShowB, 10);
    kill(pidsets->pidWriteA, 10);
}

void sigFunc10(int signum,siginfo_t *p,void *p1){
    printf("%d is coming\n", signum);
    semctl(semArrId, 0, IPC_RMID);
    shmdt(p);
    shmdt(pidsets);
    shmctl(shmid, IPC_RMID, NULL);
    shmctl(shmidPid, IPC_RMID, NULL);
    close(fdr);
    close(fdw);
    exit(0);
}

int main(int argc,char *argv[]) {
    ARGS_CHECK(argc, 3);
    struct sigaction act2;
    bzero(&act2, sizeof(act2));
    act2.sa_flags = SA_SIGINFO;
    act2.sa_sigaction = sigFunc2;
    int ret = sigaction(SIGINT, &act2, NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");

    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags = SA_SIGINFO;
    act10.sa_sigaction = sigFunc10;
    ret=sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction 10");

    //A和B的读写管道
    fdr=open(argv[1], O_RDONLY);
    ERROR_CHECK(fdr, -1, "open1");
    fdw=open(argv[2], O_WRONLY);
    ERROR_CHECK(fdw, -1, "open2");
    printf("i am chat1, fdr=%d fdw=%d\n",fdr,fdw);

    char buf[128] = {0};
    fd_set rdset;

    // A和showA之间的共享内存
    shmid=shmget(1000, 1024, IPC_CREAT|0600);
    ERROR_CHECK(shmid, -1, "shmget");
    p=(pPackage_t)shmat(shmid, NULL, 0);
    ERROR_CHECK(p, (pPackage_t) - 1, "shmat");
    p->flag = 0;

    // A和showA之间的互斥锁
    semArrId = semget(1000, 1, IPC_CREAT|0600);
    ERROR_CHECK(semArrId, -1, "semget");
    ret=semctl(semArrId, 0, SETVAL, 1);
    ERROR_CHECK(ret, -1, "semctl");
    struct sembuf sopp, sopv;
    sopp.sem_num = 0;
    sopp.sem_op = -1;
    sopp.sem_flg = SEM_UNDO;
    sopv.sem_num = 0;
    sopv.sem_op = 1;
    sopv.sem_flg = SEM_UNDO;

    // pid存储的共享内存
    int pid = getpid();
    shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid, -1, "pid shmget");
    pidsets=(pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidWriteA = pid;

    while(1){
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(fdr, &rdset);
        ret = select(fdw + 1, &rdset, NULL, NULL, NULL);
        ERROR_CHECK(ret, -1, "select");
        printf("ret = %d\n", ret);

        if(FD_ISSET(STDIN_FILENO,&rdset)){
            memset(buf, 0, sizeof(buf));
            ret=read(STDIN_FILENO, buf, sizeof(buf));
            //        if(0==ret){
            //            printf("主动断开\n");
            //            break;
            //        }
            if(strcmp(buf,"\n")!=0){
                write(fdw,buf,strlen(buf)-1);
                ret = semop(semArrId,&sopp,1);
                ERROR_CHECK(ret, -1, "semop1");
                p->flag=2;
                strcpy(p->buf,buf);
                ret = semop(semArrId,&sopv,1);
                ERROR_CHECK(ret, -1, "semop2");
            }
        }

        if(FD_ISSET(fdr, &rdset)){
            memset(buf, 0, sizeof(buf));
            ret=read(fdr, buf, sizeof(buf));
            ERROR_CHECK(ret, -1, "read");
            printf("ret = %d\n", ret);
            //        if(0==ret){
            //            printf("连接已断开\n");
            //            break;
            //        }
            ret = semop(semArrId, &sopp, 1);
            ERROR_CHECK(ret, -1, "semop1");
            p->flag = 1;
            strcpy(p->buf, buf);
            ret = semop(semArrId, &sopv, 1);
            ERROR_CHECK(ret, -1, "semop2");
            puts(buf);
        }
    }
    return 0;
}

showB

#include <head.h>

typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
} pidNums_t,*pPidNums_t;

typedef struct mymsgbuf{
    long mtype;
    char mtext[64];
}MSG_t;

pPidNums_t pidsets;

void sigFunc2(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    kill(pidsets->pidWriteA, 10);
    kill(pidsets->pidWriteB, 10);
    kill(pidsets->pidShowA, 10);
    kill(pidsets->pidShowB, 10);
}

void sigFunc10(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    shmdt(pidsets);
    exit(0);
}

int main(int argc,char *argv[]) {
    struct sigaction act2;
    bzero(&act2,sizeof(act2));
    act2.sa_flags =S A_SIGINFO;
    act2.sa_sigaction = sigFunc2;
    int ret =s igaction(SIGINT,&act2,NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");

    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags = SA_SIGINFO;
    act10.sa_sigaction = sigFunc10;
    ret=sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction");
    int msgid=msgget(1000, IPC_CREAT|0600);
    ERROR_CHECK(msgid, -1, "msgget");
   
    struct mymsgbuf msgInfo;
    
    int pid = getpid();
    int shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid, -1, "pid shmget");
    pidsets=(pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidShowB = pid;

    while(1){
        bzero(&msgInfo, sizeof(msgInfo));
        ret = msgrcv(msgid, &msgInfo, sizeof(msgInfo), 0, 0);
        ERROR_CHECK(ret, -1, "msgrcv");
        if(msgInfo.mtype == 1){
            printf("%s\n\n", msgInfo.mtext);
        }
        if(msgInfo.mtype == 2){
            printf("%*s%s\n", 10, "", msgInfo.mtext);
        }
    }
    return 0;
}

wirteB

#include <head.h>

typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
}pidNums_t, *pPidNums_t;

pPidNums_t pidsets; 
int fdr,fdw;
int msgid;

typedef struct mymsgbuf{
    long mtype;
    char mtext[64];
} Msg_t,*pMsg_t;

void sigFunc2(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    kill(pidsets->pidWriteA, 10);
    kill(pidsets->pidShowA, 10);
    kill(pidsets->pidShowB, 10);
    kill(pidsets->pidWriteB, 10);
}

void sigFunc10(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    close(fdr);
    close(fdw);
    shmdt(pidsets);
    msgctl(msgid, IPC_RMID,NULL);
    exit(0);
}

int main(int argc,char *argv[]) {
    ARGS_CHECK(argc, 3);

    struct sigaction act2;
    bzero(&act2, sizeof(act2));
    act2.sa_flags = SA_SIGINFO;
    act2.sa_sigaction =s igFunc2;
    int ret=sigaction(SIGINT, &act2, NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");

    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags =S A_SIGINFO;
    act10.sa_sigactio n= sigFunc10;
    ret=sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction");

    fdw = open(argv[1], O_WRONLY);
    fdr = open(argv[2], O_RDONLY);
    printf("i am chat2 , fdr=%d fdw=%d\n", fdr, fdw);
    // B 和 showB 的消息队列
    char buf[128] = {0};
    fd_set rdset;
    msgid = msgget(1000, IPC_CREAT|0600);
    ERROR_CHECK(ret, -1, "msgget");
    struct mymsgbuf msgInfo;
    // pid 共享内存
    int pid = getpid();
    int shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid, -1, "pid shmget");
    pidsets = (pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidWriteB = pid;

    while(1){
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(fdr, &rdset);
        ret=select(fdr + 1, &rdset, NULL, NULL, NULL);
        ERROR_CHECK(ret, -1, "select");
        if(FD_ISSET(STDIN_FILENO, &rdset)){
            memset(buf, 0, sizeof(buf));
            ret = read(STDIN_FILENO, buf, sizeof(buf));
            //     if(0==ret){
            //         printf("主动断开\n");
            //         break;
            //     }
            if(strcmp(buf, "\n") != 0){
                write(fdw,buf,strlen(buf) - 1);
                msgInfo.mtype = 2;
                strcpy(msgInfo.mtext, buf);
                ret = msgsnd(msgid, &msgInfo, strlen(msgInfo.mtext), 0);
                ERROR_CHECK(ret,-1,"msgsnd");
            }
        }

        if(FD_ISSET(fdr, &rdset)){
            memset(buf, 0, sizeof(buf));
            ret=read(fdr, buf, sizeof(buf));
            ERROR_CHECK(ret, -1, "read");
            //     if(0==ret){
            //         printf("连接已断开\n");
            //         break;
            //     }
            if(strlen(buf) != 0){
                puts(buf);
                msgInfo.mtype = 1;
                strcpy(msgInfo.mtext, buf);
                ret = msgsnd(msgid, &msgInfo, strlen(msgInfo.mtext), 0);
                ERROR_CHECK(ret, -1, "msgsnd");
            }
        }
    }
    close(fdr);
    close(fdw);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194443.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 学习笔记_1前言;软件安装

前言 学习自江科大自动协 b站课程。 呜呼&#xff01;今朝有坑今朝开&#xff0c;管他明朝埋不埋&#xff01;开新坑的过程是最爽的。 STM32 是 ST 公司基于 ARM CORE-M 芯片&#xff08;类似 CPU&#xff09;开发的32位的单片机&#xff0c;相比8位的51单片机性能更强。&am…

【数据结构】哈希表的原理及实现

1.什么是哈希表 哈希表又称为散列表&#xff0c;它是一种以键值对形式来存储数据的结构&#xff0c;只要输入待查找的key&#xff0c;就可以通过该key寻找到对应的值。对应函数&#xff1a;y f(key)通过把关键码映射到表中的对应位置来访问对应信息&#xff0c;来加快查找速度…

机械设备行业ERP在企业中如何发挥作用?

对机械设备制造企业而言&#xff0c;一方面&#xff0c;大部分销售额都集中在少数几个客户&#xff0c;很难实时了解市场和用户真实需求&#xff0c;订单修改、取消&#xff0c;销售、生产预测不准&#xff0c;原料积压、作废等是常有的事&#xff0c;日积月累给企业造成极大的…

【官方 | 计算机二级Python教程】第一章:程序设计基本方法

【官方 | 计算机二级Python教程】第一章&#xff1a;程序设计基本方法参考书目第一章&#xff1a;程序设计基本方法本章知识导图1.1 程序设计语言1.1.1 程序设计语言概述1.1.2 编译和解释1.2 Python语言概述1.2.1 Python语言的发展1.2.2 Python最小程序1.3 Python开发环境配置1…

iptables端口复用_远程操控

目录 方式一&#xff1a;利用 ICMP 做遥控开关 一、创建端口复用链 二、创建端口复用规则 三、设置开启开关 四、设置关闭开关 五、将发现的数据包转到HTTP_SSH_PORT链上进行处理 六、开启复用 七、关闭复用 方式二&#xff1a;利用tcp数据包中的关键字做遥控开关 一…

【Java】GET 和 POST 请求的区别

GET 和 POST 请求的区别 GET 和 POST请求是最常用的两种请求方法&#xff0c;写了几个Servlet项目&#xff0c;发现这两种请求用的实在是多&#xff0c;给我的感觉就是这两个请求仿佛只有一个名字不同而已。但是通过查询资料发现&#xff0c;里面大有文章。HTTP协议定义的方法…

从0开始学python -18

Python3 元组 Python 的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。 元组使用小括号 ( )&#xff0c;列表使用方括号 [ ]。 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可。 实例(Python 3.0) >>> tup1 (Go…

研发能力加码!维视智造团队入选“科学家+工程师”队伍!

一、维视智造成功入选2023年度秦创原“科学家工程师”队伍近日&#xff0c;陕西省科学技术厅公布了2023年度秦创原“科学家工程师”队伍入选名单&#xff0c;维视智造旗下欣维视觉工程师团队联合西北工业大学马志强副教授团队&#xff0c;申报的“大口径光学元件形性误差检测方…

第9章 Idea集成gitee(码云)

第一节 码云简介 众所周知&#xff0c;GitHub服务器在国外&#xff0c;使用GitHub作为项目托管网站&#xff0c;如果网速不好的话&#xff0c;严重影响使用体验&#xff0c;甚至会出现登录不上的情况。针对这个情况&#xff0c;大家也可以使用国内的项目托管网站-码云。 码云…

golang 协程关闭——谁敢说没踩过坑

Go语言中&#xff0c;协程创建和启动非常简单&#xff0c;但是如何才能正确关闭协程呢&#xff0c;和开车一样&#xff0c;前进总是很容易&#xff0c;但是如何正确的把车停在指定的地方总是不容易的。生产实践中&#xff0c;go常常遇到未能正确关闭协程而影响程序运行的场景&a…

Unity - TextMeshPro

TextMeshPro TextMeshPro 是 Unity 的终极文本解决方案。它是 Unity 的 UI 文本和旧版文本网格的完美替代品。 TextMeshPro&#xff08;也称为 TMP&#xff09;功能强大且易于使用&#xff0c;它使用高级文本渲染技术以及一组自定义着色器&#xff1b;提供显着的视觉质量改进&…

C++分文件编写VS Code和CMakeLists使用详解

目录一、示例代码1.1 主函数main.cpp1.2 子函数源文件1.3 子函数头文件二、VS Code编译2.1 报错2.2解决方法三、CMakeLists编译Windows 10 Ubuntu 20.04 VS Code 一、示例代码 1.1 主函数main.cpp 要用双引号包含子函数的头文件&#xff0c;第二行 #include<iostream&g…

项目经理必备的5种项目管理工具,让你的项目迅速上手

做项目管理是一条漫漫长路&#xff0c;所有的本事&#xff0c;都是靠一个个项目&#xff0c;一点点积累而来的&#xff0c;并不存在“迅速上手”的方法。 一名普通项目经理的成长&#xff0c;都要经过一定时间的修炼&#xff0c;并且要灵活使用项目管理工具&#xff0c;这里给…

跑步的人如何选择耳机、最好的跑步蓝牙耳机排名清单

相信很多人和小编一样&#xff0c;在跑步健身的时候也喜欢听点音乐&#xff0c;特别是节奏感强的音乐能让运动更加有激情。但是如果佩戴传统的有线耳机容易扯到线&#xff0c;在现代化的今天&#xff0c;当然要选择蓝牙耳机。今天就为大家介绍一下跑步用什么蓝牙耳机好&#xf…

看完这篇文章,我再也不用担心线上出现CPU性能问题了(下)

目录平均负载CPU 使用率进程上下文切换补充总结在 《看完这篇文章&#xff0c;我再也不用担心线上出现CPU性能问题了&#xff08;上&#xff09;》中&#xff0c;咸鱼给大家介绍了 CPU 常见的性能指标&#xff0c;当生产环境出现 CPU 性能瓶颈的时候&#xff0c;优先观察这些指…

论文投稿指南——中文核心期刊推荐(食品工业 2)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

写IB EE(Extended Essay)时最容易犯的五大错误

【第一大忌】用随意找来的文章做Sources&#xff01; 同学们都知道EE写作一定要做好citation&#xff0c;在文中、文末都要列出参考资料。但不是什么文章都”有资格“成为bibliography的一部分&#xff0c;选取质量高的sources是很重要的。那些百度上搜到的作者不详、自己都没有…

教你使用 Petalinux 定制 Linux

测试平台&#xff1a;黑金 Zynq7035 开发板 芯片型号&#xff1a;XC7Z035-2FFG676I 开发环境&#xff1a;Ubuntu 16.04 开发工具&#xff1a;Petalinux 2017.4 Step1 创建 Petalinux 工程 1.1 将 Vivado 工程目录下*.sdk文件夹中的*.hdf文件复制到新建的proj文件夹中 1.2 …

串级PID控制原理-2

按串级控制的基本原理&#xff0c;采用Simulink进行编程&#xff0c;在连续方式下进行仿真。在串级控制中&#xff0c;主调节器采用PI控制&#xff0c;取kp 50&#xff0c;k i5&#xff0c;副调节器采用Р控制&#xff0c;kp 200。外加干扰为正弦信号sin(50t)&#xff0c;通过切…

报表控件Stimulsoft技术答疑:如何在二维码中编码数据?

Stimulsoft Reports是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…