当前位置: 首页 > news >正文

Linux系统编程-信号进程间通信

目录

异步(Asynchronous)

信号

数据结构

1.kill

2.alarm

3.pause

4.setitimer

5.abort

信号集(sigset_t类型)

1.sigemptyset

2.sigfillset 

3.sigaddset 

4.sigdelset 

5.sigismember 

信号屏蔽

1.sigprocmask

2.sigpending 

3.sigsuspend 

4. sigaction

管道

匿名管道

命名管道

进程间通信

Message Queues 消息队列

1.msgget

2.msgsnd 

3.msgrcv 

4.msgctl 

 Semaphore Arrays 信号量数组

1.semget

2.semop

3.semtimeop 

4.semctl

Shared Memory Segments 共享内存段

1.shmget

2.shmat 

3.shmdt 

4.shmctl

用信号量实现令牌桶

mytbf.h

mytbf.c

main.c

信号量的PV操作实例

sem_pv.c


异步(Asynchronous)

        定义:异步操作是指在执行一个任务时,调用者不需要等待任务完成,可以继续执行其他操作。
        特点:
            非阻塞:调用者在任务执行期间不会被阻塞,可以继续执行其他任务。
            并发执行:多个任务可以并发执行,提高资源利用率。
            复杂性:由于任务是并发执行的,可能涉及到并发控制和状态管理,逻辑相对复杂。
        处理方法:查询法,通知法


信号

        信号是一种*异步*事件通知机制。它们用于通知进程发生了某些事件,从而允许进程对这些事件做出响应。信号机制是进程间通信(IPC)和进程控制的重要手段之一。信号是软件中断,信号的响应(应用层)依赖于中断(物理层)。


信号的基本概念
    异步通知:信号是异步发送的,即发送信号的进程不需要等待信号被接收和处理。
    软件中断:信号类似于软件中断,它们可以打断进程的正常执行流程,依赖于系统中断的实现。
    有限的信号类型:系统定义了一组有限的信号类型,每个信号类型对应一个特定的事件。
    默认行为:每个信号类型都有一个默认行为,例如终止进程、忽略信号等。
    可处理:进程可以通过定义信号处理函数来改变信号的默认行为。


信号处理机制
    信号处理函数:进程可以定义一个函数来处理接收到的信号。这个函数被称为信号处理函数。
    信号掩码:进程可以通过设置信号掩码来暂时屏蔽某些信号,防止在关键操作期间被打断。
    信号队列:信号会被放入操作系统内核中的信号队列中,进程需要定期检查信号队列并处理信号。
    默认行为:如果进程没有定义信号处理函数,信号将执行默认行为。


信号的局限性
    原子性问题:信号处理函数的执行可能会与进程的其他部分发生竞争条件。
    资源限制:信号处理函数不能进行复杂的操作,因为它们可能会被频繁地调用。
    栈限制:信号处理函数通常在单独的信号栈上执行,这限制了它们的可用空间。
    行为不可靠:信号处理是内核进行,假如有快速重复调用情况下,行为不可靠

信号的响应过程^[3]

        信号从受到到响应有一个不可避免的延迟,级别相近的标准信号响应没有严格顺序。信号掩码mask和信号挂起集pending存储在操作系统内核中,但与每个进程的上下文相关联,操作系统内核通过进程控制块(PCB)管理这些信息,并在进程执行过程中根据信号掩码决定信号的传递和处理。信号的两个32位的位图mask(初始值默认全为1)和pending(初始值默认全为0),进程在执行过程中,会定期检查是否有信号需要处理,或者发生中断后,进程被放入等待列表。线程从内核态切换到用户态时,内核会检查maks&pending的值,当结果为0是表示没有信号发生(默认1&0为0),这就是为什么标准信号被忽略了。

        1.当信号发生时,内核会在进程的信号 pending 位图中对应的位设置为 1,表示该信号已经到达但尚未处理。线程在由内核态切换为用户态^[1]时,会检查信号屏蔽字(mask)和 pending 位图。如果 mask 和 pending 的按位与(&)操作结果为 1,表示信号未被屏蔽且已到达,内核会将控制权转交给信号处理函数。

        2.信号处理函数执行完毕后,内核会清除信号 pending 位图中对应的位,即将其置为 0,表示该信号已经被处理。信号屏蔽字(mask)通常不会在信号处理后被置 0,而是保持不变或根据信号处理函数中的设置进行更改。这是为了防止同一信号在处理函数执行期间再次被处理,除非显式地更改了屏蔽字。恢复执行:

        3.信号处理函数返回后,内核会恢复线程的原始状态,包括程序计数器和寄存器状态,使得线程可以继续从接收到信号前的地方执行。线程的信号屏蔽字(mask)会根据需要进行调整,这通常在信号处理函数内部完成

[1]:进程从用户态切换到内核态时,内核会将当前进程的寄存器状态保存到其内核栈^[2]中,并将进程的状态信息保存在进程控制块(PCB)中。

[2]:内核栈是操作系统为每个进程在内核态时使用的栈。当进程从用户态切换到内核态时(例如执行系统调用或处理中断时),内核会使用这个栈来保存和恢复进程的状态。内核栈栈顶通常在进程控制块(PCB)中而是esp有所记录。

[3]:在了解过多线程后,我们知道操作系统其实是以最小线程为单位的,并且线程进程不分家,实际上进程只用pending位图,而他的线程都拥有独立的mask和pending,实际上是进程的线程在工作

数据结构

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
->
void(*signal(int signum, void (*handler)(int)))
//将信号 signum 的处理方式设置为 handler,其中 handler 可以是 SIG_IGN、SIG_DFL 或一个程序员定义的函数地址(即“信号处理函数”)。信号会打断堵塞的系统调用(open,write)

1.kill

int kill(pid_t pid, int sig);

向进程发送信号,成功返回0,失败返回-1。
        如果 pid 是正数,则向由 pid 指定的进程发送信号 sig。
        如果 pid 等于 0,则向调用进程的进程组中的每个进程发送信号 sig。
        如果 pid 等于 -1,则向调用进程有权限发送信号的每个进程发送信号 sig,除了进程 1(init)。
        如果 pid 小于 -1,则向进程组发送信号 sig,该进程组的 ID 是 -pid。
        如果 sig 是 0,则不发送信号,但仍然执行存在和权限检查;这可以用来检查调用者被允许发送信号的进程 ID 或进程组 ID 的存在。失败情况下要去看ERROR,是查询是本,没有权限,还是不存在。

2.alarm

unsigned int alarm(unsigned int seconds);

        安排在seconds秒后向调用进程发送SIGALRM信号,如果没有接收行为,SIGALRM信号默认终止程序(进程异常中止)。如果seconds为零,它会取消任何之前设置的定时器,并且不会发送 SIGALRM 信号。函数返回之前安排的警报还有多少秒将被发送,如果没有之前安排的警报,则返回零。

3.pause

int pause(void);

        使调用进程(或线程)休眠,直到接收到一个信号,该信号要么终止进程,要么导致信号捕获函数的调用。

4.setitimer

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;  /* 微秒 */
};

        使用 setitimer 函数可以设置计时器,当计时器到期时,会发送相应的信号。这可以用于实现定时任务或监控某些事件。成功时返回 0。失败时返回 -1,并设置 errno 以指示错误类型。


which:指定计时器的类型。可以是以下之一:
        ITIMER_REAL:实时计时器,计时器到期时发送 SIGALRM 信号。
        ITIMER_VIRTUAL:虚拟计时器,计时器到期时发送 SIGVTALRM 信号。
        ITIMER_PROF:统计计时器,计时器到期时发送 SIGPROF 信号。

new_value:指向 itimerval 结构的指针,定义了计时器的新值。itimerval 结构包含两个 timeval 结构,分别表示初始值和间隔值。

old_value:指向 itimerval 结构的指针,用于存储计时器的旧值。

5.abort

void abort(void);

        该函数首先解除对SIGABRT信号的阻塞,然后为调用进程引发该信号,结束进程并且产生calldown文件


信号集(sigset_t类型)

1.sigemptyset

int sigemptyset(sigset_t *set);

        把信号集set清空

2.sigfillset 

int sigfillset(sigset_t *set);

        把信号集set装满,包括所有信号

3.sigaddset 

int sigaddset(sigset_t *set, int signum);

        把signum加入信号集set

4.sigdelset 

int sigdelset(sigset_t *set, int signum);

        把signum从信号集set删除

5.sigismember 

int sigismember(const sigset_t *set, int signum);

        判断signum是否信号集set的成员


信号屏蔽

1.sigprocmask

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

        这个函数允许进程改变其信号屏蔽字,即决定哪些信号在传递给进程之前会被系统自动阻塞。人为设置mask来影响信号扫描时候响应,how是宏SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK,sigset_t是信号集

2.sigpending 

int sigpending(sigset_t *set);

         访问内核,输出信号集set的pending状态,因为这是用户态指令,而pending在内核态转用户态已经转变,因此得到的pending是之前的pending

3.sigsuspend 

int sigsuspend(const sigset_t *mask);

        解除一个信号集mask的堵塞状态,然后马上进入等待信号的状态,这个操作是原子的

4. sigaction

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);struct sigaction {void     (*sa_handler)(int); //信号处理函数的指针。可以设置为 SIG_DFL(默认处理)、SIG_IGN(忽略信号)或者指向自定义的信号处理函数。void     (*sa_sigaction)(int, siginfo_t *, void *); //可以用suguinfo_t结构体(一些系统是共用体)获取信号的信息,以此进行对型号的更进一步处理sigset_t   sa_mask;  //信号集,定义了在信号处理函数执行期间需要屏蔽的信号。int        sa_flags; //标志位,用于设置信号处理的一些选项,如 SA_RESTART(使系统调用在信号处理后自动重启),SA_NOCHLDWAIT(子进程创建后启用自行消亡,无需收尸,防止僵尸进程)。void     (*sa_restorer)(void);
};siginfo_t {int      si_signo;     /* 信号编号 */int      si_errno;     /* 一个错误号 */int      si_code;      /* 信号代码(宏值) */int      si_trapno;    /* 导致硬件生成信号的陷阱编号(在大多数架构中未使用) */pid_t    si_pid;       /* 发送进程的ID */uid_t    si_uid;       /* 发送进程的真实用户ID */int      si_status;    /* 退出值或信号 */clock_t  si_utime;     /* 消耗的用户时间 */clock_t  si_stime;     /* 消耗的系统时间 */union sigval si_value; /* 信号值 */int      si_int;       /* POSIX.1b 信号 */void    *si_ptr;       /* POSIX.1b 信号 */int      si_overrun;   /* 定时器溢出计数;POSIX.1b 定时器 */int      si_timerid;   /* 定时器ID;POSIX.1b 定时器 */void    *si_addr;      /* 导致故障的内存位置 */long     si_band;      /* 带事件(在 glibc 2.3.2 及更早版本中为 int) */int      si_fd;        /* 文件描述符 */short    si_addr_lsb;  /* 地址的最低有效位(自 Linux 2.6.32 起) */void    *si_lower;     /* 地址违规发生时的下限(自 Linux 3.19 起) */void    *si_upper;     /* 地址违规发生时的上限(自 Linux 3.19 起) */int      si_pkey;      /* 导致故障的 PTE 上的保护键(自 Linux 4.6 起) */void    *si_call_addr; /* 系统调用指令的地址(自 Linux 3.5 起) */int      si_syscall;   /* 尝试的系统调用编号(自 Linux 3.5 起) */unsigned int si_arch;  /* 尝试的系统调用的架构 */}

        为进程设置或获取信号处理的详细信息,signum指定要设置或获取的信号的宏,act和oldact是sigaction结构体,其中的sa_mask可以设置屏蔽的信号集。成功返回0,失败返回-1.


管道

匿名管道

        允许两个进程之间进行单向通信.没有文件系统中的对应实体,它不是文件系统中的一个文件,也没有与之关联的文件名。这种管道是临时的,仅存在于创建它的进程和与之通信的进程之间。一旦这些进程结束,管道也就随之消失,不会留下任何痕迹。

int pipe(int pipefd[2]);

        pipe创造一个匿名管道,可以实现进程(父子进程)间的通信.pipefd 是一个包含两个整数的数组,这两个整数分别代表管道的两个端点:pipefd[0] 是管道的读端。pipefd[1] 是管道的写端。成功0发生错误返回-1,并设置全局变量 errno 以指示错误类型

命名管道

        通常称为FIFO(First In, First Out,先进先出)或命名管道文件,是一种特殊的文件类型,它允许进程通过一个文件名进行通信。与传统的管道不同,命名管道可以在不相关的进程之间进行通信,甚至可以跨网络使用。

int mkfifo(const char *pathname, mode_t mode);

        创建一个命名管道.pathname:这是一个指向以null结尾的字符串的指针,指定了要创建的命名管道的路径和文件名。mode:这是一个模式位掩码,定义了文件的权限。


进程间通信

XSI -> SysV
IPC -> Inter-Process Communication 进程间通信,shell指令ipcs可以看到IPC详情

key是IPC的关键,消息队列,共享内存段和信号量数组都接受key来产生自身详情(id等),并且用key的唯一性进行进程间通信

key_t ftok(const char *pathname, int proj_id);

        生成一个唯一的键(key)。这个键可以用于创建或访问一个消息队列或共享内存段,确保这些资源在进程间通信(IPC)中的唯一性.pathname是一个指向以null结尾的字符串的指针,指定了一个文件的路径名,函数将基于这个文件的inode信息和项目标识符proj_id来生成键。proj_id是一个整数,用于进一步确保生成的键的唯一性。它可以是任意值,但通常选择一个非负整数。

- 函数的命名方式类似
    - xxxget 获取
    - xxxop 操作
    - xxxctl 控制


Message Queues 消息队列

1.msgget

int msgget(key_t key, int msgflg);

        获取消息队列标识符,创建或获取一个消息队列

2.msgsnd 

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

        向指定的消息队列发送消息

3.msgrcv 

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

        从指定的消息队列接收消息

4.msgctl 

int msgctl(int msqid, int op, struct msqid_ds *buf);

         控制消息队列,可以用于设置消息队列属性或删除消息队列


 Semaphore Arrays 信号量数组

信号量:是一种*同步*通信机制,信号量值是一个整数值,通常用于表示可用资源的数量。在互斥锁的情况下,信号量的值通常初始化为1,表示有一个资源可用。


P 操作(Proberen,测试)
    -当一个进程需要访问共享资源时,它会执行 P 操作。
    -如果信号量的值大于0,P 操作会将信号量的值减1,进程可以继续执行。
    -如果信号量的值等于0,进程将被操作系统挂起(阻塞),直到信号量的值变为大于0。


V 操作(Verhogen,增加)
    -当进程完成对共享资源的访问时,它会执行 V 操作。
    -V 操作会将信号量的值增加1。
    -如果有其他进程因为信号量的值为0而被阻塞,增加信号量的值可能会唤醒这些进程中的一个,使其可以继续执行。

1.semget

int semget(key_t key, int nsems, int semflg);

        创建或获取一个信号量集的标识符

2.semop

int semop(int semid, struct sembuf *sops, size_t nsops);

        对信号量集执行操作(如P操作或V操作),sops是结构图指针初始位置,nops是成员,结果体元素如下
            -unsigned short sem_num;  /* semaphore number */
            -short          sem_op;   /* semaphore operation */
            -short          sem_flg;  /* operation flags */

3.semtimeop 

int semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *_Nullable timeout);

         对信号量集执行操作,并可指定超时时间

4.semctl

int semctl(int semid, int semnum, int op, ...);

        控制信号量,可以用于设置信号量的值(初始化)、获取信号量的状态或删除信号量


Shared Memory Segments 共享内存段

1.shmget

int shmget(key_t key, size_t size, int shmflg);

        创建或获取一个共享内存标识符,其中key用于指定共享内存的唯一性,size是共享内存段的大小,shmflg是控制标志。

2.shmat 

void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);

        将共享内存段附加到进程地址空间,shmid是共享内存的标识符,shmaddr是指定的附加地址(可选),shmflg是控制标志。

3.shmdt 

int shmdt(const void *shmaddr);

         从进程地址空间分离共享内存段,shmaddr是共享内存段的地址。

4.shmctl

int shmctl(int shmid, int op, struct shmid_ds *buf);

        控制共享内存段,可以用于设置共享内存的状态、获取信息或删除共享内存,op是操作类型,buf是操作相关的数据结构。


用信号量实现令牌桶

mytbf.h

#ifndef MYTBF_H__
#define MYTBF_H__#define MYTBF_MAX 1024typedef void mytbf_t;mytbf_t *mytbf_init(int cps,int burst);//获取token
int mytbf_fetchtoken(mytbf_t *,int);
//归还token
int mytbf_returntoken(mytbf_t *,int);int mytbf_destroy(mytbf_t *);#endif

mytbf.c

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>#include "mytbf.h"struct mytbf_st{int csp;int burst;int token;int pos;//任务列表的下标
};static struct mytbf_st *job[MYTBF_MAX];
static volatile int inited = 0;
static struct sigaction old_sa;
static struct itimerval old_itv;static int get_free_pos(){for (int i = 0;i < MYTBF_MAX;i++){if (job[i] == NULL)return  i;}return -1;
}//信号处理函数
static void handler(int sig,siginfo_t *infop,void *unused){struct itimerval itv;if (infop->si_code != SI_KERNEL){return ;}itv.it_interval.tv_sec = 1;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 0;if(setitimer(ITIMER_REAL,&itv,NULL) < 0){perror("setitimer()");exit(1);}for (int i = 0;i < MYTBF_MAX;i++){if (job[i] != NULL){job[i]->token += job[i]->csp;if (job[i]->token > job[i]->burst){job[i]->token = job[i]->burst;}}}
}//卸载信号处理模块 当发生异常退出时 可以将占用的资源释放 将alarm信号取消
static void mod_unload(){//signal(SIGALRM,alarm_status);sigaction(SIGALRM,&old_sa,NULL);struct itimerval itv;itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = 0;itv.it_value.tv_usec = 0;if(setitimer(ITIMER_REAL,&itv,&old_itv) < 0){perror("setitimer()");exit(1);}for (int i = 0;i < MYTBF_MAX;i++){free(job[i]);}
}//装载信号处理模块
static void mod_load(){//alarm_status = signal(SIGALRM,handler);//保存alarm信号处理函数原来的状态struct sigaction sa;sa.sa_sigaction = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_SIGINFO;if (sigaction(SIGALRM,&sa,&old_sa) < 0){perror("sigaction()");exit(1);}struct itimerval itv;itv.it_interval.tv_sec = 1;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 0;if(setitimer(ITIMER_REAL,&itv,&old_itv) < 0){perror("setitimer()");exit(1);}atexit(mod_unload);
}mytbf_t *mytbf_init(int cps,int burst){struct mytbf_st *tbf;if (!inited){mod_load();}//将新的tbf装载到任务组中int pos;pos = get_free_pos();if (pos == -1){return NULL;}tbf = malloc(sizeof(*tbf));if (tbf == NULL)return NULL;tbf->token = 0;tbf->csp = cps;tbf->burst = burst;tbf->pos = pos;job[pos] = tbf;return tbf;
}//获取token ptr是一个 void * size是用户想要获取的token数
int mytbf_fetchtoken(mytbf_t *ptr,int size){struct mytbf_st *tbf = ptr;if (size <= 0){return -EINVAL;}//有token继续while (tbf->token <= 0){pause();}int n =tbf->token<size?tbf->token:size;tbf->token -= n;//用户获取了 n 个tokenreturn n;
}//归还token ptr是一个 void *
int mytbf_returntoken(mytbf_t *ptr,int size){struct mytbf_st *tbf = ptr;if (size <= 0){return -EINVAL;}tbf->token += size;if (tbf->token > tbf->burst)tbf->token = tbf->burst;return size;
}int mytbf_destroy(mytbf_t *ptr){struct mytbf_st *tbf = ptr;job[tbf->pos] = NULL;free(tbf);return 0;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <math.h>#include "mytbf.h"static const int SIZE = 1024;
static const int CPS = 10;
static const int BURST = 100;//最大令牌数static volatile int token = 0;//持有令牌数int main(int argc,char** argv)
{if (argc < 2){fprintf(stdout,"Usage...");exit(1);}mytbf_t *tbf;tbf = mytbf_init(CPS,BURST);if (tbf == NULL){fprintf(stderr,"tbf init error");exit(1);}//打开文件int sfd,dfd = 0;do{sfd = open(argv[1],O_RDONLY);if (sfd < 0){if (errno == EINTR)continue;fprintf(stderr,"%s\n",strerror(errno));exit(1);}}while(sfd < 0);char buf[SIZE];while(1){int len,ret,pos = 0;int size = mytbf_fetchtoken(tbf,SIZE);//int i = 0;//while(i < 2){//    sleep(1);//    i++;//}if (size < 0){fprintf(stderr,"mytbf_fetchtoken()%s\n",strerror(-size));exit(1);}len = read(sfd,buf,size);while (len < 0){if (errno == EINTR)continue;strerror(errno);break;}//读取结束if (len == 0){break;}//要是读到结尾没用完tokenif (size - len > 0){mytbf_returntoken(tbf,size-len);}//以防写入不足while(len > 0){ret = write(dfd,buf+pos,len);while (ret < 0){if (errno == EINTR){continue;}printf("%s\n",strerror(errno));exit(1);}pos += ret;len -= ret;}}close(sfd);mytbf_destroy(tbf);exit(0);
}

信号量的PV操作实例

sem_pv.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/sem.h>#define THRNUM 20
#define FNAME "/tmp/out"
#define BUFSIZE 1024static int semid;//取资源量
static void P(){struct sembuf op;op.sem_num = 0;op.sem_op = -1;//取一个资源op.sem_flg = 0;//特殊要求while(semop(semid,&op,1) < 0){if (errno == EINTR||errno == EAGAIN){continue;}else{perror("semop()");exit(1);}}}//还资源量
static void V(){struct sembuf op;op.sem_num = 0;op.sem_op = 1;//取一个资源op.sem_flg = 0;//特殊要求while(semop(semid,&op,1) < 0){if (errno == EINTR||errno == EAGAIN){continue;}else{perror("semop()");exit(1);}}}static void handler(){FILE *fp = fopen(FNAME,"r+");char buf[BUFSIZE];if(fp == NULL){perror("fopen()");exit(1);}P();fgets(buf,BUFSIZE,fp);fseek(fp,0,SEEK_SET);sleep(1);fprintf(fp,"%d\n",atoi(buf)+1);fflush(fp);V();fclose(fp);
}int main()
{pid_t pid;semid = semget(IPC_PRIVATE,1,0666);//父子关系的进程通信可以使用匿名IPCif (semid < 0){perror("semget()");exit(1);}//初始化if (semctl(semid,0,SETVAL,1)){//相当于互斥量perror("semctl()");exit(1);}    for (int i = 0;i < THRNUM;i++){pid = fork() ;if (pid < 0){perror("fork()");exit(1);}if (pid == 0){handler();exit(0);}}for (int i = 0;i < THRNUM;i++){wait(NULL);}semctl(semid,0,IPC_RMID);return 0;
}

相关文章:

Linux系统编程-信号进程间通信

目录 异步&#xff08;Asynchronous&#xff09; 信号 数据结构 1.kill 2.alarm 3.pause 4.setitimer 5.abort 信号集(sigset_t类型) 1.sigemptyset 2.sigfillset 3.sigaddset 4.sigdelset 5.sigismember 信号屏蔽 1.sigprocmask 2.sigpending 3.sigsus…...

Attention Module (SAM)是什么?

SAM&#xff08;Spatial Attention Module&#xff0c;空间注意力模块&#xff09;是一种在神经网络中应用的注意力机制&#xff0c;特别是在处理图像数据时&#xff0c;它能够帮助模型更好地关注输入数据中不同空间位置的重要性。以下是关于SAM的详细解释&#xff1a; 1. 基本…...

【C语言】堆排序

堆排序即利用堆的思想来进行排序&#xff0c;总共分为两个步骤&#xff1a; 1. 建堆 升序&#xff1a;建大堆 降序&#xff1a;建小堆 原因分析&#xff1a; 若升序建小堆时间复杂度是O(N^2) 升序建大堆&#xff0c;时间复杂度O&#xff08;N*logN&#xff09; 所以升序建大堆…...

ntp服务重启报错Failed to restart ntpd.service: Unit is masked.

问题概述&#xff1a; 重启ntp服务报错Failed to restart ntpd.service: Unit is masked&#xff0c;使用systemctl unmask ntpd.service命令关闭屏蔽还是报错Failed to restart ntpd.service: Unit is masked 解决方法&#xff1a; 重装ntp服务 yum remove ntpyum install…...

面试题-每日5到

16.Files的常用方法都有哪些&#xff1f; Files.exists():检测文件路径是否存在 Files.createFile():创建文件 Files.createDirectory():创建文件夹 Files.delete():删除一个文件或目录 Files.copy():复制文件 Files.move():移动文件 Files.size():查看文件个数 Files.read():读…...

代码美学大师:打造Perl中的个性化代码格式化工具

代码美学大师&#xff1a;打造Perl中的个性化代码格式化工具 在软件开发过程中&#xff0c;代码的可读性至关重要。Perl&#xff0c;作为一种灵活的脚本语言&#xff0c;允许开发者以多种方式实现代码格式化。自定义代码格式化工具不仅能提升代码质量&#xff0c;还能加强团队…...

成为一名月薪 2 万的 web 安全工程师需要掌握哪些技能?

现在 web 安全工程师比较火&#xff0c;岗位比较稀缺&#xff0c;现在除了一些大公司对学历要求严格&#xff0c;其余公司看中的大部分是能力。 有个亲戚的儿子已经工作 2 年了……当初也是因为其他的行业要求比较高&#xff0c;所以才选择的 web 安全方向。 资料免费分享给你…...

Linux中如何添加磁盘分区

在Linux中添加分区通常涉及到几个步骤&#xff0c;包括识别磁盘、创建分区、格式化分区&#xff0c;以及挂载或将其用作特定的文件系统类型&#xff08;如LVM、RAID等&#xff09;。以下是一个基本的步骤指南&#xff0c;假设你正在使用命令行界面&#xff08;CLI&#xff09;和…...

计算机毕业设计Hadoop+Hive专利分析可视化 面向专利的大数据管理系统 专利爬虫 专利数据分析 大数据毕业设计 Spark

《Hadoop专利大数据分析可视化系统》开题报告 一、选题背景与意义 随着信息技术的飞速发展&#xff0c;全球数据量呈现爆炸式增长&#xff0c;特别是在专利领域&#xff0c;数据的积累和更新速度更是惊人。专利数据不仅包含了技术创新的详细信息&#xff0c;还反映了行业的发…...

git是什么?git和svn的区别。git的一些命令

Git是什么 Git是一个开源的分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;简称DVCS&#xff09;&#xff0c;它可以有效、高速地处理从很小到非常大的项目版本管理。版本控制系统能追踪项目从开始到结束的整个过程&#xff0c;对编程人员而言…...

RK3568平台(触摸篇)双屏异触调试

一.现象 现象&#xff1a;准备两块主屏都接触摸框&#xff0c;A屏的HDMIOUT外接B屏的HDMIIN&#xff0c;用手触摸A屏&#xff0c;发现A屏没有触摸&#xff0c;A屏幕的触摸现象在B屏那边。 现要求&#xff1a;用手触摸A屏&#xff0c;A屏要有现象&#xff0c;不能现象在B屏那边…...

angular cmd

npm uninstall -g angular/cli npm install -g angular/cli npm install -g angular/cli17 ng update angular/core17 angular/cli17 # 安装 typescript npm i -g typescript5.3.2 # 安装 Angular CLI npm install -g angular/cli17.3.8 # 或者 cnpm install -g angular/cli…...

[ACTF2020 新生赛]BackupFile1

打开题目 利用disearch扫描&#xff0c;发现源文件index.php.bak 下载下来 打开文件 代码审计&#xff0c;翻译一下 翻译代码为&#xff1a; <?php include_once "flag.php"; //这一行使用 include_once 函数来包含&#xff08;或插入&#xff09;另一个 PHP …...

Springboot学习-day16

Springboot学习-day16 Springboot是spring家族中的一个全新框架&#xff0c;用来简化spring程序的创建和开发过程。在以往我们通过SpringMVCSpringMybatis框架进行开发的时候&#xff0c;我们需要配置web.xml&#xff0c;spring配置&#xff0c;mybatis配置&#xff0c;然后整…...

Map 31

...

dfs,CF 196B - Infinite Maze

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 https://codeforces.com/problemset/problem/196/B 二、解题报告 1、思路分析 考虑如何判断一条路径可以无限走&#xff1f; 我们对朴素的网格dfs改进&#xff0c;改进为可以dfs网格外的区域 如果存在某个…...

鸿蒙应用框架开发【JS注入与执行】 Web

JS注入与执行 介绍 本示例基于H5游戏&#xff0c;通过arkui的button实现对游戏实现基本控制&#xff0c;展示webview的JS注入与执行能力&#xff0c;及native应用与H5的通信能力。 效果预览 使用说明 1.设备连接热点&#xff0c;可访问互联网。 2.打开应用&#xff0c;通过…...

AI问答:理解 DRG / Diagnosis Related Group / 按疾病诊断相关分组

DRG&#xff08;Diagnosis Related Group&#xff09;系统&#xff0c;中文译作“按疾病诊断相关分组”&#xff0c;是一种根据病情临床相似程度和资源消耗水平将住院病人进行分组的系统。以下是对DRG系统的详细理解&#xff1a; 一、定义与原理 1.1、定义&#xff1a;DRG系统…...

多个线程同时调用接口

1、线程的基本概念 线程是程序执行的最小单元。每个线程可以独立执行一段代码&#xff0c;与其他线程并行运行。Java提供Thread类和Runnable接口来创建和管理线程。 2、创建线程 1&#xff09;继承Thread类并重写run()方法&#xff1a; class MyThread extend Thread{ pub…...

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——1到手测试

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——1到手测试 ​ 大家好&#xff0c;今天给大家带来的是购买到小车或者说RDK X3之后直接快速体验&#xff0c;今天主要围绕官方的快速入门手册进行逐步测试 1.知识补充1 ​ 在这里首先要给新手小白补充几…...

2024第三届钉钉杯大学生大数据挑战赛【A题】完整分享

2024第三届钉钉杯大学生大数据挑战赛已经开赛&#xff0c;小编给大家带来非常实用的助力【A题】完整&#xff0c;&#xff08;看图片下方的说明&#xff09;&#xff0c;资料预览&#xff1a; 微信公众号...

下面关于数组排序的说明那项是错误的?

下面关于数组排序的说明那项是错误的&#xff1f; A. java.util.Arrays类提供有数组排序的支持方法&#xff1a;sort()&#xff1b; B. 通过java.util.Arrays类排序的对象所在类需要实现Comparable或Comparator接口&#xff1b; C. String数组可以进行排序&#xff0c;是因为St…...

【第二篇章】优秀的机器学习策略 超参数优化之决策树

在机器学习的浩瀚星空中&#xff0c;决策树作为一颗璀璨的星辰&#xff0c;以其直观易懂、解释性强以及高效处理分类与回归任务的能力&#xff0c;赢得了众多数据科学家与工程师的青睐。随着大数据时代的到来&#xff0c;如何从海量数据中提炼出有价值的信息&#xff0c;构建出…...

httprunner转载

基于 HttpRunner4.0 的接口自动化测试实践 测试之家 from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase # 配置数据库连接信息 config ( Config("database test") .variables( **{ "db_host": &…...

反序列化漏洞vulhub靶场serial

环境搭建 下载 https://download.vulnhub.com/serial/serial.zip 解压出来就是这种 你会得到一个这样的文件&#xff0c;这里使用VMware新建一个虚拟机&#xff0c;这里记录比较重要的几部分。 这里就是使用我们刚才下过来的。 漏洞过程详解 1.信息收集 打开靶机&#xff0…...

C++ 文件流详解

在 C 中&#xff0c;文件处理是一个常见且重要的任务。标准库提供了三种主要的文件流类来处理文件输入和输出&#xff1a;fstream、ifstream 和 ofstream。这些类都在 <fstream> 头文件中定义。 一、fstream 类 fstream 是文件流类的基类&#xff0c;既可以用于读操作&…...

docker compse简介与安装

目录 1. Docker Compose 简介 2. Docker Compose 安装 2.1 在 Ubuntu 上安装 Docker Compose 2.1.1 通过 apt 安装 2.1.2 使用官方脚本安装最新版本 2.2 在 CentOS 上安装 Docker Compose 2.2.2 使用官方脚本安装最新版本 2.2.3 使用 pip 安装 2.3 在 openEuler 上安装…...

基于深度学习的零样本学习

零样本学习&#xff08;Zero-Shot Learning, ZSL&#xff09;是深度学习中的一个前沿研究领域&#xff0c;其目标是在没有见过目标类别的样本的情况下&#xff0c;对这些新类别进行识别或分类。这种方法特别适用于在实际应用中存在大量未标注类别或新类别不断涌现的场景&#x…...

C++——list容器以及手动实现

LIST容器 list概述列表容器属性例子 list函数构造函数默认构造函数&#xff1a;带有元素个数和元素初值的构造函数&#xff1a;范围构造函数&#xff1a;拷贝构造函数&#xff1a;移动构造函数&#xff1a;示例 赋值运算符重载拷贝赋值操作符 (1)&#xff1a;移动赋值操作符 (2…...

Win11系统文件资源管理器鼠标右键卡顿解决方法

引用链接&#xff1a; Windows 11文件资源管理器崩溃怎么解决&#xff1f;看看这7个解决办法&#xff01;...

删除网站域名/交换友链是什么意思

在这个浏览器百花争鸣的时代&#xff0c;作为前端开发的我们为了我们漂亮的设计能适应各个浏览器可为煞费苦心&#xff0c;主要体现在javascript和css上面。javascript我这次就不谈了&#xff0c;先说说css。 为了适应不同浏览器不同的版本&#xff08;版本主要就ie来说&#x…...

网站宽度960/营销技巧有哪些

在用round的过程中遇到过两个比较迷惑的问题&#xff0c;记录一下。 1、round是用来实现浮点数的近似舍入的&#xff0c;一般而言四舍五入法则深入人心&#xff0c;但是这个round的行为并不完全符合四舍五入&#xff0c;而是四舍六入五成双。关于这种舍入方法PEP327中有详细的…...

做律师咨询网站/山东疫情最新情况

简介 自.NET 4.5发布已经过了差不多1年了。但是随着最近微软大多数的发布&#xff0c;与.NET开发者交流的问题显示&#xff0c;开发者仅知道一到两个特性&#xff0c;其他的特性仅仅停留在MSDN并以简单的文档形式存在着。 比如说&#xff0c;当你问一个.NET开发者.NET框架内核中…...

开发公司没有资质有什么影响/慈溪seo

第二十届中国国际高新技术成果交易会于11月14日至18日在深圳会展中心举办&#xff0c;本届展会的主题为「坚持新发展理念&#xff0c;推动高品质发展」。开幕当天&#xff0c;SAP 全球高级副总裁、SAP 中国研究院院长李瑞成博士受邀出席「2018中国高新技术论坛」&#xff0c;探…...

咸宁网站设计公司/seo网络营销推广公司深圳

alter any cluster 修改任意簇的权限alter any index 修改任意索引的权限alter any role 修改任意角色的权限alter any sequence 修改任意序列的权限alter any snapshot 修改任意快照的权限alter any table 修改任意表的权限alter any trigger 修改任意触发器的权限alter clust…...

怎么用htlm5自己做网站/谷歌搜索引擎官网

在中国&#xff0c;随着企业数字化转型的加速&#xff0c;越来越多的企业开始使用CRM&#xff08;客户关系管理&#xff09;系统来管理其客户关系。 然而&#xff0c;中国市场上有很多CRM系统供应商&#xff0c;让企业难以选择。那么&#xff0c;中国有这么多CRM系统供应商&am…...