From 2336f88407f61552008327595aeaf0644059993a Mon Sep 17 00:00:00 2001 From: lzy Date: Mon, 27 May 2024 02:28:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=A1=E5=8F=B7=E5=AE=8C=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- C16-并发/C16-并发.md | 99 +++++++++- C16-并发/parallel/signal/block.c | 45 +++++ C16-并发/parallel/signal/mydaemon.c | 112 ++++++++++++ C16-并发/parallel/signal/mytbf_sa/Makefile | 35 ++++ C16-并发/parallel/signal/mytbf_sa/main.c | 99 ++++++++++ C16-并发/parallel/signal/mytbf_sa/mytbf.c | 201 +++++++++++++++++++++ C16-并发/parallel/signal/mytbf_sa/mytbf.h | 26 +++ C16-并发/parallel/signal/susp.c | 48 +++++ C16-并发/parallel/signal/susp_rt.c | 46 +++++ 9 files changed, 703 insertions(+), 8 deletions(-) create mode 100644 C16-并发/parallel/signal/block.c create mode 100644 C16-并发/parallel/signal/mydaemon.c create mode 100644 C16-并发/parallel/signal/mytbf_sa/Makefile create mode 100644 C16-并发/parallel/signal/mytbf_sa/main.c create mode 100644 C16-并发/parallel/signal/mytbf_sa/mytbf.c create mode 100644 C16-并发/parallel/signal/mytbf_sa/mytbf.h create mode 100644 C16-并发/parallel/signal/susp.c create mode 100644 C16-并发/parallel/signal/susp_rt.c diff --git a/C16-并发/C16-并发.md b/C16-并发/C16-并发.md index 6bc70e0..0e6185d 100644 --- a/C16-并发/C16-并发.md +++ b/C16-并发/C16-并发.md @@ -15,6 +15,9 @@ > 信号是软件中断。 信号的响应依赖于中断。 +信号的处理函数应该尽量短小,注意是否可重入问题。 +使用系统调用,不能轻易使用IO。 + ```bash kill -l # 查看信号列表 ``` @@ -31,8 +34,8 @@ typedef void (*sighandler_t)(int); /** * signal - 设置信号处理函数 - * @signum: 信号编号 - * @handler: 信号处理函数 + * @param: signum 信号编号 + * @param: handler 信号处理函数 * * 返回值:原信号处理函数 */ @@ -91,6 +94,10 @@ M P 有则重复,无则将M置1,回到常规状态。 1 0 +不能从信号处理函数中随意的往外跳。(setjmp, longjmp) +这样错过了将M置1的机会。 +标准:sigsetjmp, siglongjmp, 推出这俩来保存掩码情况。 + ### 常用函数 ```c @@ -119,7 +126,7 @@ int kill(pid_t pid, int sig); /** * raise - 发送信号给当前进程 - * @sig: 信号编号 + * @param: sig 信号编号 * * 返回值:成功返回0,失败返回非零 */ @@ -168,22 +175,88 @@ void abort(void); */ int system(const char *command); -sleep(); - +/** + * sleep - 休眠进程 + * @param: seconds: 休眠时间,单位为秒 + * + * @note: 休眠当前进程,直到指定的时间段结束 + * + * @return: 剩余时间,单位为秒 + */ +unsigned int sleep(unsigned int seconds); +// 可以封装出sleep函数 +int nanosleep(const struct timespec *req, struct timespec *rem); +int usleep(useconds_t usec); +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` ### 信号集 +```c +struct 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); +``` + ### 信号屏蔽字/`pending`的处理 +```c +/** + * sigprocmask - 设置信号掩码 + * @param: how: 掩码操作类型 + * @param: set: 信号集指针 + * @param: oldset: 旧的信号集指针 + */ +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); + +/** + * sigpending - 获取未决信号集 + * @param: set: 信号集指针 + * @note: 该函数获取当前进程的未决信号集 + * 该函数会清空pending信号集,并将其复制到set指向的信号集中。 + * 该函数不修改信号掩码。 + * + * !!!: 没有使用场景,不推荐使用 + * 取到的信号是响应之前的信号,等取到的时候,情况可能已经变化。 + */ +int sigpending(sigset_t *set); +``` + ### 扩展 ```c -sigsuspend(); -sigaction(); +/** + * sigwait - 等待信号 + */ +int sigsuspend(const sigset_t *mask); /** - * setitimer - 设置定时器 + * sigaction - 设置信号处理函数,替代signal + * @param: signum: 信号编号 + * @param: act: 信号处理函数 + * @param: oldact: 旧的信号处理函数 + * + * @note: signal无法区分信号来自user还是kernel + * sigaction可以区分 + * 看siginfo_t.si_code可以区分 + */ +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); + +struct sigaction { + //!在某些结构, sa_handler和sa_sigaction只能有一个被设置 + //!因为他们是共用体 + void (*sa_handler)(int); /* 信号处理函数 */ + void (*sa_sigaction)(int, siginfo_t *, void *); /* 信号处理函数 */ + sigset_t sa_mask; /* 信号掩码 */ + int sa_flags; /* 信号标志 */ + void (*sa_restorer)(void); /* 信号状态恢复函数 */ +}; + +/** + * setitimer - 设置定时器,替代alarm * @param: which: 定时器类型 * ITIMER_REAL : 实时定时器 * ITIMER_VIRTUAL : 虚拟定时器 @@ -216,4 +289,14 @@ struct timeval { ### 实时信号 +查看信号 + +- `kill -l` +- `cat /usr/include/bits/signum-generic.h` + +1. 排队 +2. 不丢失 + +此外都和标准信号一样 + ## 线程(强烈异步) diff --git a/C16-并发/parallel/signal/block.c b/C16-并发/parallel/signal/block.c new file mode 100644 index 0000000..f59e1e2 --- /dev/null +++ b/C16-并发/parallel/signal/block.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +static void int_hander(int s) +{ + write(1, "!", 1); +} + +int main(int argc, char** argv) +{ + int i; + int j; + sigset_t set; + sigset_t oldset; + sigset_t saveset; + + //! 要考虑原来是否阻塞的 + signal(SIGINT, int_hander); + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigprocmask(SIG_UNBLOCK, &set, &saveset); + + for (j = 0; j < 1000; j++) + { + sigprocmask(SIG_BLOCK, &set, &oldset); + + for (i = 0; i < 5; i++) + { + write(1, "*", 1); + sleep(1); + } + write(1, "\n", 1); + + // sigprocmask(SIG_UNBLOCK, &set, NULL); + sigprocmask(SIG_SETMASK, &oldset, NULL); + } + // ctrl+\ : quit信号快捷方式 + + //!!! 还原原来的阻塞状态 + sigprocmask(SIG_SETMASK, &saveset, NULL); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/mydaemon.c b/C16-并发/parallel/signal/mydaemon.c new file mode 100644 index 0000000..a57194a --- /dev/null +++ b/C16-并发/parallel/signal/mydaemon.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FNAME "/tmp/out" + +static FILE* fp; + +static int deamonize(void) +{ + pid_t pid; + int fd; + + pid = fork( ); + if (pid < 0) + return -1; + + if (pid > 0) // parent process + exit(0); + + fd = open("/dev/null", O_RDWR); + if (fd < 0) + return -1; + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); + + setsid( ); + + chdir("/"); // 防止一直占用某设备 + // umask(0); + + return 0; +} + +static void daemon_exit(int s) +{ + // if(SIGINT == s) 可以进行区分处理 + + fclose(fp); // 当多个信号响应同一个函数,可能多次free + closelog( ); + exit(0); +} + +int main(int argc, char** argv) +{ + int i; + struct sigaction sa; + + sa.sa_handler = daemon_exit; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGQUIT); + sigaddset(&sa.sa_mask, SIGTERM); + sigaddset(&sa.sa_mask, SIGINT); + sa.sa_flags = 0; + // !! 为了防止重入,对其中一个信号注册时要阻塞另外两个信号 + // 干脆就三个一起阻塞 + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + // signal(SIGINT, daemon_exit); + // signal(SIGQUIT, daemon_exit); + // signal(SIGTERM, daemon_exit); + + openlog("mydaemon", LOG_PID, LOG_DAEMON); + + if (deamonize( )) + { + syslog(LOG_ERR, "daemonize() failed!"); + exit(1); + } + else + { + syslog(LOG_INFO, "daemonize() success!"); + } + + fp = fopen(FNAME, "w"); + if (fp == NULL) + { + syslog(LOG_ERR, "fopen() failed!: %s", strerror(errno)); + exit(1); + } + + syslog(LOG_INFO, "%s was opened successfully!", FNAME); + + for (i = 0;; i++) + { + fprintf(fp, "%d\n", i); + fflush(fp); //! 刷新缓冲区 + syslog(LOG_DEBUG, "%d is printed", i); + sleep(1); + } + + // 目前守护进程只能异常终止,下面其实无法执行到 + // fclose(fp); + // closelog( ); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/mytbf_sa/Makefile b/C16-并发/parallel/signal/mytbf_sa/Makefile new file mode 100644 index 0000000..027145c --- /dev/null +++ b/C16-并发/parallel/signal/mytbf_sa/Makefile @@ -0,0 +1,35 @@ +# 方便起见一般都会先定义编译器链接器 +CC = gcc +LD = gcc + +# 正则表达式表示目录下所有.c文件,相当于:SRCS = main.c a.c b.c +SRCS = $(wildcard *.c) + +# OBJS表示SRCS中把列表中的.c全部替换为.o,相当于:OBJS = main.o a.o b.o +OBJS = $(patsubst %c, %o, $(SRCS)) + +# 可执行文件的名字 +TARGET = main + +# .PHONE伪目标,具体含义百度一下一大堆介绍 +.PHONY:all clean + +# 要生成的目标文件 +all: $(TARGET) + +LDFLAGS=-L/usr/local/lib + +# LDLIBS=-lqueue -lllist + +# 第一行依赖关系:冒号后面为依赖的文件,相当于Hello: main.o a.o b.o +# 第二行规则:$@表示目标文件,$^表示所有依赖文件,$<表示第一个依赖文件 +$(TARGET): $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +# 上一句目标文件依赖一大堆.o文件,这句表示所有.o都由相应名字的.c文件自动生成 +%.o:%.c + $(CC) -c $^ + +# make clean删除所有.o和目标文件 +clean: + rm -f $(OBJS) $(TARGET) diff --git a/C16-并发/parallel/signal/mytbf_sa/main.c b/C16-并发/parallel/signal/mytbf_sa/main.c new file mode 100644 index 0000000..3837418 --- /dev/null +++ b/C16-并发/parallel/signal/mytbf_sa/main.c @@ -0,0 +1,99 @@ +#include "mytbf.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPS 10 +#define BUFSIZE 1024 +#define BURST 100 //!! token上限 + + +int main(int argc, char** argv) +{ + int sfd, dfd = 1; + char buf[BUFSIZE]; + int len, ret, pos; + int size; + mytbf_t* tbf; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + tbf = mytbf_init(CPS, BURST); + if (NULL == tbf) + { + fprintf(stderr, "mytbf_init failed\n"); + exit(1); + } + + do //!!! + { + sfd = open(argv[1], O_RDONLY); + if (sfd < 0) + { + //!!! + if (errno != EINTR) + { + perror("open"); + exit(1); + } + } + } while (sfd < 0); + + while (1) + { + size = mytbf_fetchtoken(tbf, BUFSIZE); + if (size < 0) + { + fprintf(stderr, "mytbf_fetchtoken failed: %s\n", strerror(errno)); + } + + //* 阻塞在这里的时候,token会自增 + while ((len = read(sfd, buf, size)) < 0) + { + //!!! + if (EINTR == errno) + continue; + + perror("read"); + break; + } + if (len == 0) + break; + + if (size - len > 0) + mytbf_returntoken(tbf, size - len); + + pos = 0; + while (len > 0) + { + /* 可能没写完被别的中断打断 */ + ret = write(dfd, buf + pos, len); + if (ret < 0) + { + //!!! + if (EINTR == errno) + continue; + perror("write"); + exit(1); + } + pos += ret; + len -= ret; + } + + // sleep(1); //! 能实现,但是移植性不好 + } + + close(sfd); + mytbf_destroy(tbf); + exit(0); +} diff --git a/C16-并发/parallel/signal/mytbf_sa/mytbf.c b/C16-并发/parallel/signal/mytbf_sa/mytbf.c new file mode 100644 index 0000000..ba91f7b --- /dev/null +++ b/C16-并发/parallel/signal/mytbf_sa/mytbf.c @@ -0,0 +1,201 @@ +/*********************************************************************** + * @file mytbf.c + * @brief + * 令牌桶库实现 + * + * @author lzy (lllzzzyyy@buaa.edu.cn) + * @url https://lzyyyyyy.fun + * + * @date 2024-05-26 + * + ***********************************************************************/ + +#include "mytbf.h" +#include +#include +#include +#include +#include +#include +#include + + +struct mytbf_st +{ + int cps; + int burst; + int token; + int pos; +}; + +static struct mytbf_st* job[MYTBF_MAX]; +static int inited = 0; +static struct sigaction sa, alrm_sa_save; + + +static int get_free_pos( ) +{ + int i; + + for (i = 0; i < MYTBF_MAX; i++) + { + if (NULL == job[i]) + return i; + } + + return -1; +} + +static void alrm_action(int s, siginfo_t* infop, void* unused) +{ + int i; + + //* 判断信号来源 + if (SI_KERNEL != infop->si_code) + return; + + for (i = 0; i < MYTBF_MAX; i++) + { + if (NULL != job[i]) + { + job[i]->token += job[i]->cps; + if (job[i]->token > job[i]->burst) + job[i]->token = job[i]->burst; + } + } +} + +static void module_unload( ) +{ + int i; + struct itimerval itv; + + sigaction(SIGALRM, &alrm_sa_save, NULL); + if (errno != 0) + { + perror("sigaction"); + exit(1); + } + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 0; + + setitimer(ITIMER_REAL, &itv, NULL); + if (errno != 0) + { + perror("setitimer"); + exit(1); + } + + for (i = 0; i < MYTBF_MAX; i++) + free(job[i]); +} + +static void module_load( ) +{ + struct sigaction sa; + struct itimerval itv; + + sa.sa_sigaction = alrm_action; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; // 表示使用sigaction三参数的形式 + + sigaction(SIGALRM, &sa, &alrm_sa_save); + if (errno != 0) + { + perror("sigaction"); + exit(1); + } + + itv.it_interval.tv_sec = 1; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 1; + itv.it_value.tv_usec = 0; + + setitimer(ITIMER_REAL, &itv, NULL); + if (errno != 0) + { + perror("setitimer"); + exit(1); + } + + atexit(module_unload); //!!! atexit() +} + +mytbf_t* mytbf_init(int cps, int burst) +{ + struct mytbf_st* me; + int pos; + + if (!inited) + { + module_load( ); + inited = 1; + } + + pos = get_free_pos( ); + if (pos < 0) + return NULL; + + me = malloc(sizeof(*me)); + if (NULL == me) + return NULL; + + me->cps = cps; + me->burst = burst; + me->token = 0; + me->pos = pos; + + job[pos] = me; + + return me; +} + +static int min(int a, int b) +{ + return a < b ? a : b; +} + +int mytbf_fetchtoken(mytbf_t* ptr, int size) +{ + if (size <= 0) + return -EINVAL; + + struct mytbf_st* me = ptr; + int n; + + while (me->token <= 0) + pause( ); + + n = min(size, me->token); + + me->token -= n; + + return n; +} + +int mytbf_returntoken(mytbf_t* ptr, int size) +{ + if (size <= 0) + return -EINVAL; + + struct mytbf_st* me = ptr; + + me->token += size; + if (me->token > me->burst) + me->token = me->burst; + + return size; +} + +int mytbf_destroy(mytbf_t* ptr) +{ + struct mytbf_st* me = ptr; + + job[me->pos] = NULL; + + free(ptr); + return 0; +} diff --git a/C16-并发/parallel/signal/mytbf_sa/mytbf.h b/C16-并发/parallel/signal/mytbf_sa/mytbf.h new file mode 100644 index 0000000..7013646 --- /dev/null +++ b/C16-并发/parallel/signal/mytbf_sa/mytbf.h @@ -0,0 +1,26 @@ +/*********************************************************************** + * @file mytbf.h + * @brief + * 令牌桶库 + * + * @author lzy (lllzzzyyy@buaa.edu.cn) + * @url https://lzyyyyyy.fun + * + * @date 2024-05-26 + * + ***********************************************************************/ +#ifndef __MYTBF__H__ +#define __MYTBF__H__ + +#define MYTBF_MAX 1024 +typedef void mytbf_t; + +mytbf_t* mytbf_init(int cps, int burst); + +int mytbf_fetchtoken(mytbf_t*, int); + +int mytbf_returntoken(mytbf_t*, int); + +int mytbf_destroy(mytbf_t*); + +#endif //!__MYTBF__H__ \ No newline at end of file diff --git a/C16-并发/parallel/signal/susp.c b/C16-并发/parallel/signal/susp.c new file mode 100644 index 0000000..4b355dd --- /dev/null +++ b/C16-并发/parallel/signal/susp.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +static void int_hander(int s) +{ + write(1, "!", 1); +} + +int main(int argc, char** argv) +{ + int i; + int j; + sigset_t set; + sigset_t oldset; + sigset_t saveset; + + //! 要考虑原来是否阻塞的 + signal(SIGINT, int_hander); + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigprocmask(SIG_UNBLOCK, &set, &saveset); + sigprocmask(SIG_BLOCK, &set, &oldset); + for (j = 0; j < 1000; j++) + { + for (i = 0; i < 5; i++) + { + write(1, "*", 1); + sleep(1); + } + write(1, "\n", 1); + + sigsuspend(&oldset); // 相当于下面几句的原子操作 +#if 0 + sigset_t tmpset; + sigprocmask(SIG_SETMASK, &oldset, &tmpset); + pause( ); + sigprocmask(SIG_SETMASK, &tmpset, NULL); +#endif + } + // ctrl+\ : quit信号快捷方式 + + //!!! 还原原来的阻塞状态 + sigprocmask(SIG_SETMASK, &saveset, NULL); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/susp_rt.c b/C16-并发/parallel/signal/susp_rt.c new file mode 100644 index 0000000..4212d62 --- /dev/null +++ b/C16-并发/parallel/signal/susp_rt.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +// 编号40 +// kill -40 pid +#define MYRTSIG (SIGRTMIN + 6) + +static void mysig_hander(int s) +{ + write(1, "!", 1); +} + +int main(int argc, char** argv) +{ + int i; + int j; + sigset_t set; + sigset_t oldset; + sigset_t saveset; + + //! 要考虑原来是否阻塞的 + signal(MYRTSIG, mysig_hander); + sigemptyset(&set); + sigaddset(&set, MYRTSIG); + sigprocmask(SIG_UNBLOCK, &set, &saveset); + sigprocmask(SIG_BLOCK, &set, &oldset); + for (j = 0; j < 1000; j++) + { + for (i = 0; i < 5; i++) + { + write(1, "*", 1); + sleep(1); + } + write(1, "\n", 1); + + sigsuspend(&oldset); // 相当于下面几句的原子操作 + } + // ctrl+\ : quit信号快捷方式 + + //!!! 还原原来的阻塞状态 + sigprocmask(SIG_SETMASK, &saveset, NULL); + + exit(0); +} \ No newline at end of file