From 6f28a3384ad6ce1f4aa194667b02af26b57c0aab Mon Sep 17 00:00:00 2001 From: lzy Date: Sun, 26 May 2024 08:24:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AD=A6=E4=B9=A0=E5=88=B0=E4=BA=86=20?= =?UTF-8?q?=E5=B8=B8=E7=94=A8=E5=87=BD=E6=95=B0=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- C16-并发/C16-并发.md | 219 +++++++++++++++++++ C16-并发/parallel/signal/5sec.c | 25 +++ C16-并发/parallel/signal/5sec_sig.c | 35 +++ C16-并发/parallel/signal/alarm.c | 20 ++ C16-并发/parallel/signal/anytimer/Makefile | 35 +++ C16-并发/parallel/signal/anytimer/anytimer.c | 0 C16-并发/parallel/signal/anytimer/anytimer.h | 47 ++++ C16-并发/parallel/signal/anytimer/main.c | 40 ++++ C16-并发/parallel/signal/mycpy.c | 94 ++++++++ C16-并发/parallel/signal/mytbf/Makefile | 35 +++ C16-并发/parallel/signal/mytbf/main.c | 99 +++++++++ C16-并发/parallel/signal/mytbf/mytbf.c | 155 +++++++++++++ C16-并发/parallel/signal/mytbf/mytbf.h | 26 +++ C16-并发/parallel/signal/slowcat.c | 108 +++++++++ C16-并发/parallel/signal/slowcat2.c | 102 +++++++++ C16-并发/parallel/signal/star.c | 26 +++ 16 files changed, 1066 insertions(+) create mode 100644 C16-并发/C16-并发.md create mode 100644 C16-并发/parallel/signal/5sec.c create mode 100644 C16-并发/parallel/signal/5sec_sig.c create mode 100644 C16-并发/parallel/signal/alarm.c create mode 100644 C16-并发/parallel/signal/anytimer/Makefile create mode 100644 C16-并发/parallel/signal/anytimer/anytimer.c create mode 100644 C16-并发/parallel/signal/anytimer/anytimer.h create mode 100644 C16-并发/parallel/signal/anytimer/main.c create mode 100644 C16-并发/parallel/signal/mycpy.c create mode 100644 C16-并发/parallel/signal/mytbf/Makefile create mode 100644 C16-并发/parallel/signal/mytbf/main.c create mode 100644 C16-并发/parallel/signal/mytbf/mytbf.c create mode 100644 C16-并发/parallel/signal/mytbf/mytbf.h create mode 100644 C16-并发/parallel/signal/slowcat.c create mode 100644 C16-并发/parallel/signal/slowcat2.c create mode 100644 C16-并发/parallel/signal/star.c diff --git a/C16-并发/C16-并发.md b/C16-并发/C16-并发.md new file mode 100644 index 0000000..6bc70e0 --- /dev/null +++ b/C16-并发/C16-并发.md @@ -0,0 +1,219 @@ +# 并发 + +- 同步 +- 异步:到来的时刻不确定 + +异步事件的处理: + +1. 查询法:事件发生的频率高,查询方式复杂 +2. 通知法:事件发生的频率低,通知方式简单 + +## 信号(初步异步) + +### 信号的概念 +> +> 信号是软件中断。 +信号的响应依赖于中断。 + +```bash +kill -l # 查看信号列表 +``` + +`core`文件:错误现场的保存。 +对于个人用户,默认`core`文件大小为0,可通过`ulimit -c`设置。 + +### `signal()`函数 + +```c +#include + +typedef void (*sighandler_t)(int); + +/** + * signal - 设置信号处理函数 + * @signum: 信号编号 + * @handler: 信号处理函数 + * + * 返回值:原信号处理函数 + */ +sighandler_t signal(int signum, sighandler_t handler); + +//* 不使用typedef, 直接声明函数原型 +void (*signal(int signum,void (*func)(int)))(int); +// void (*signal(...)):这表示signal函数的返回类型是一个指向函数的指针,该函数返回类型为void。 +// (int signum, void (*func)(int)):这是signal函数的参数列表,其中signum是一个整数类型的参数,func是一个指向参数为整数类型、返回类型为void的函数的指针。 +// (int):这个函数指针所指向的函数接受一个整数类型的参数。 +``` + +**信号会打断阻塞的系统调用。** +`write`,`read`,`open`,`fork`等系统调用都可能被信号打断。 +故要判断返回值是不是`EINTR`,如果是,则表示信号被打断,需要重新调用。 + +### 信号的不可靠 + +在 Linux 中,信号被用来通知进程发生了某些事件,例如终端用户按下了中断键(Ctrl+C),或者一个进程运行时间过长等。但是信号被称为“不可靠”的,主要是因为以下几个原因: + +1. **丢失信号**:在传统的 Unix 实现中,同一种信号类型如果在处理前多次发生,可能会被合并,只传递一次。这意味着除了第一次之外的其他信号都会被丢弃。例如,如果一个程序几乎同时收到两个 `SIGINT` 信号,它可能只能感知到一个。 + +2. **不可预测的顺序**:如果多个不同的信号几乎同时到达,它们被递送到进程的顺序可能与实际发生的顺序不同,这会使得程序的行为难以预测。 + +3. **中断系统调用**:在早期的 Unix 系统中,当信号被捕获时,正处于阻塞状态的系统调用(如 read, write, select 等)可能会被中断并提前返回,这常常需要额外的错误处理逻辑来重新发起系统调用。 + +4. **异步性**:信号是异步的,这意味着它们可以在程序执行的任何时刻到达。如果信号处理函数(signal handler)不够简单,它可能在执行程序的中间阶段被调用,而此时程序可能处于一个不一致的状态。因此,信号处理函数需要非常小心地编写,通常只能执行异步信号安全的函数。 + +为了克服信号的不可靠性,现代操作系统和库引入了新的机制,比如 `sigaction` API 允许更精细的控制信号处理,以及使用 `pselect` 或 `epoll` 等函数的组合来避免系统调用被中断的问题。此外,一些高级语言或框架提供了更高层次的抽象,使得信号处理变得更为安全和可靠。 + +### 可重入函数 + +所有的系统调用都是可重入的,一部分库函数也是可重入的,比如`memcpy` + +### 信号的响应过程 + +每个进程至少两个位图,`mask`和`pending`,一般都是32位的。 +`mask`是当前进程阻塞的信号集合,`pending`是当前进程收到的信号集合。 + +`mask && pending`得到的是当前进程需要处理的信号集合。 + +信号从收到到响应有一个不可避免的延迟。 + +标准信号的响应没有严格的顺序。 + +屏蔽信号就是通过`mask`置位来屏蔽信号。 + +M P +1 0 常规状态 + 收到信号,未处理 +1 1 + 从内核态回用户态,M & P = 1,处理信号 +0 0 + 处理信号前,M和P都置0 + 响应完后,M置1,再次对M & P进行判断。 + 有则重复,无则将M置1,回到常规状态。 +1 0 + +### 常用函数 + +```c +/** + * kill - 向进程发送信号 + * @param: pid: 进程ID: + * >0 : 进程ID + * =0 : 进程组ID (组内广播) + * =-1: 给当前进程有权限发信号的所有进程 (除了init进程) + * < -1: 发送给|pid|的进程 (eg. -5 就发送给 5号进程) + * + * @note: + * 这里的pid用法可以联系 + * pid_t waitpid(int pid, int *status, int options) + * + * @param: sig: 信号编号 + * =0 : 不发送信号,error check,用于检测一个进程或者进程组是否存在 + * + * 返回值:成功返回0,失败返回-1并设置errno + * errno: + * EINVAL : 参数错误 + * EPERM : 无权限发送信号 + * ESRCH : 进程不存在 + */ +int kill(pid_t pid, int sig); + +/** + * raise - 发送信号给当前进程 + * @sig: 信号编号 + * + * 返回值:成功返回0,失败返回非零 + */ +int raise(int sig); +kill(getpid(), SIGINT); // 等价 +pthread_kill(pthread_self(), SIGINT); // 多线程等价 + +/** + * alarm - 设置闹钟 + * @param: seconds: 闹钟时间,单位为秒 + * + * @note: + * - 闹钟时间到时,会发送SIGALRM信号给当前进程。 + * - 有的平台 sleep 是 alarm + pause 实现的 + * 这时sleep和alarm在同一个程序中会冲突 + * - 流量控制的基础 + * + * @eg: 使用单一计时器,构造一组函数,实现任意数量的定时器 + * 用到 alarm 或 setitimer + * TODO + * + * 返回值:成功返回0,失败返回非零 + */ +unsigned int alarm(unsigned int seconds); + +/** + * pause - 挂起进程 + * + * @note: 挂起进程,直到收到信号或被唤醒 + */ +int pause(void); + +/** + * abort - 终止进程 + * + * @note: 终止进程,立即退出,不执行清理函数,得到core文件 + */ +void abort(void); + +/** + * system - 执行系统命令 + * @param: command: 命令字符串 + * @note: 执行系统命令,阻塞当前进程,直到命令执行完毕 + * ! 需要 block SIGCHLD 并且 ignore SIGINT SIGQUIT + * @return: 命令执行的返回值 + */ +int system(const char *command); + +sleep(); + +``` + +### 信号集 + +### 信号屏蔽字/`pending`的处理 + +### 扩展 + +```c +sigsuspend(); +sigaction(); + +/** + * setitimer - 设置定时器 + * @param: which: 定时器类型 + * ITIMER_REAL : 实时定时器 + * ITIMER_VIRTUAL : 虚拟定时器 + * ITIMER_PROF : 进程计时器 + * @param: new_value: 新的定时器值 + * @param: old_value: 旧的定时器值 + * + * @note: 原子赋值,比 alarm 更精细 + * + * @return:成功返回0,失败返回-1并设置errno + */ +int setitimer(int which, + const struct itimerval *new_value, + struct itimerval *old_value); + +// 在it_value之后第一次启动 +// 后续以it_interval为间隔启动 +struct itimerval { + struct timeval it_interval; /* next value */ + struct timeval it_value; /* current value */ +}; + +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* microseconds */ +}; + +/** +``` + +### 实时信号 + +## 线程(强烈异步) diff --git a/C16-并发/parallel/signal/5sec.c b/C16-并发/parallel/signal/5sec.c new file mode 100644 index 0000000..3182b04 --- /dev/null +++ b/C16-并发/parallel/signal/5sec.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +/*********************************************************************** + * @brief 使用time函数实现计时功能 + * @param argc + * @param argv + * @return int + ***********************************************************************/ +int main(int argc, char **argv) +{ + time_t end; + long long count = 0; + + end = time(NULL) + 5; + + while (time(NULL) <= end) + count++; + + printf("%lld\n", count); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/5sec_sig.c b/C16-并发/parallel/signal/5sec_sig.c new file mode 100644 index 0000000..5007058 --- /dev/null +++ b/C16-并发/parallel/signal/5sec_sig.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +//!!! 不加volatile关键字,loop变量的值不会被修改,导致死循环!!! +static volatile int loop = 1; + +static void alrm_handler(int sig) +{ + loop = 0; +} + +/*********************************************************************** + * @brief 用sig实现计时 + * @param argc + * @param argv + * @return int + ***********************************************************************/ +int main(int argc, char **argv) +{ + long long count = 0; + + //!!! + signal(SIGALRM, alrm_handler); + alarm(5); + //!!! signal注册时钟信号的行为需要在alarm之前 + + while (loop) + count++; + + printf("%lld \n", count); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/alarm.c b/C16-并发/parallel/signal/alarm.c new file mode 100644 index 0000000..183743d --- /dev/null +++ b/C16-并发/parallel/signal/alarm.c @@ -0,0 +1,20 @@ +#include +#include + +int main(int argc, char **argv) +{ + alarm(10); + alarm(1); + alarm(5); + + // alarm(10); + // alarm(5); + // alarm(1); + + // 效果不一样 + + for (;;) + pause(); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/anytimer/Makefile b/C16-并发/parallel/signal/anytimer/Makefile new file mode 100644 index 0000000..027145c --- /dev/null +++ b/C16-并发/parallel/signal/anytimer/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/anytimer/anytimer.c b/C16-并发/parallel/signal/anytimer/anytimer.c new file mode 100644 index 0000000..e69de29 diff --git a/C16-并发/parallel/signal/anytimer/anytimer.h b/C16-并发/parallel/signal/anytimer/anytimer.h new file mode 100644 index 0000000..81e8e3f --- /dev/null +++ b/C16-并发/parallel/signal/anytimer/anytimer.h @@ -0,0 +1,47 @@ +#ifndef __ANYTIMER__H__ +#define __ANYTIMER__H__ + +#define JOB_MAX 1024 +typedef void at_jobfunc_t(void *); + +/******************************************************************** + * @brief 创建定时器 + * @details + * @param sec + * @param jobp + * @param arg + * @return int + * >= 0 成功,返回定时器ID + * == -EINVAL 失败,参数错误 + * == -ENOMEM 失败,内存不足 + * == -ENOSPC 失败,数组满 + ********************************************************************/ +int at_addjob(int sec, at_jobfunc_t *jobp, void *arg); + +/******************************************************************** + * @brief 取消定时器 + * @details + * @param id + * @return int + * == 0 成功,定时器已取消 + * == -EINVAL 失败,参数错误 + * == -EBUSY 失败,指定任务已完成 + * == -ECANCELED 失败,定时器重复取消 + ********************************************************************/ +int at_canceljob(int id); + +/********************************************************************* + * @brief 回收任务 + * @details + * @param id + * @return int + * == 0 成功,任务已回收 + * == -EINVAL 失败,参数错误 + ********************************************************************/ +int at_waitjob(int id); + + +int at_pausejob(int id); +int at_resumejob(int id); + +#endif //!__ANYTIMER__H__ \ No newline at end of file diff --git a/C16-并发/parallel/signal/anytimer/main.c b/C16-并发/parallel/signal/anytimer/main.c new file mode 100644 index 0000000..859f21b --- /dev/null +++ b/C16-并发/parallel/signal/anytimer/main.c @@ -0,0 +1,40 @@ +#include "anytimer.h" +#include +#include +#include +#include + +static void f1(void *p) +{ + printf("f1():%s\n", (char *)p); +} + +static void f2(void *p) +{ + printf("f1():%s\n", (char *)p); +} + +int main(int argc, char **argv) +{ + int job1; + + puts("Begin!"); + + job1 = at_addjob(5, f1, "aaa"); + if (job1 < 0) + { + fprintf(stderr, "at_addjob() failed!:%s\n", strerror(-job1)); + exit(1); + } + + puts("End!"); + + while (1) + { + write(1, ".", 1); + sleep(1); + } + + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/mycpy.c b/C16-并发/parallel/signal/mycpy.c new file mode 100644 index 0000000..3314634 --- /dev/null +++ b/C16-并发/parallel/signal/mycpy.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include + +#define BUFSIZE 1024 +/** + * 可以用 time 命令找到 BUFSIZE 最佳值 + * + * time ./mycpy /etc/services /tmp/out + */ + +int main(int argc, char **argv) +{ + int sfd, dfd; + char buf[BUFSIZE]; + int len, ret, pos; + + if (argc < 3) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + do //!!! + { + sfd = open(argv[1], O_RDONLY); + if (sfd < 0) + { + //!!! + if (errno != EINTR) + { + perror("open"); + exit(1); + } + } + } while (sfd < 0); + + + do //!!! + { + dfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (dfd < 0) + { + // !!! + if (errno != EINTR) + { + close(sfd); + perror("open"); + exit(1); + } + } + } while (dfd < 0); + + + while (1) + { + len = read(sfd, buf, BUFSIZE); + if (len < 0) + { + //!!! + if (EINTR == errno) + continue; + + perror("read"); + break; + } + if (len == 0) + break; + + 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; + } + } + + close(dfd); + close(sfd); + + exit(0); +} \ No newline at end of file diff --git a/C16-并发/parallel/signal/mytbf/Makefile b/C16-并发/parallel/signal/mytbf/Makefile new file mode 100644 index 0000000..027145c --- /dev/null +++ b/C16-并发/parallel/signal/mytbf/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/main.c b/C16-并发/parallel/signal/mytbf/main.c new file mode 100644 index 0000000..b5bd933 --- /dev/null +++ b/C16-并发/parallel/signal/mytbf/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/mytbf.c b/C16-并发/parallel/signal/mytbf/mytbf.c new file mode 100644 index 0000000..cf67fca --- /dev/null +++ b/C16-并发/parallel/signal/mytbf/mytbf.c @@ -0,0 +1,155 @@ +/*********************************************************************** + * @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 + +struct mytbf_st +{ + int cps; + int burst; + int token; + int pos; +}; + +static struct mytbf_st *job[MYTBF_MAX]; +static int inited = 0; +static __sighandler_t alrm_handler_save; //!!! 保存原来的alarm行为 + + +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_handler(int s) +{ + int i; + + alarm(1); + + 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; + + signal(SIGALRM, alrm_handler_save); + alarm(0); + for (i = 0; i < MYTBF_MAX; i++) + free(job[i]); +} + +static void module_load() +{ + alrm_handler_save = signal(SIGALRM, alrm_handler); + alarm(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/mytbf.h b/C16-并发/parallel/signal/mytbf/mytbf.h new file mode 100644 index 0000000..c85cafc --- /dev/null +++ b/C16-并发/parallel/signal/mytbf/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/slowcat.c b/C16-并发/parallel/signal/slowcat.c new file mode 100644 index 0000000..72f21b9 --- /dev/null +++ b/C16-并发/parallel/signal/slowcat.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include + +#define CPS 10 +#define BUFSIZE CPS + +static volatile int loop = 0; + +static void alrm_handler(int s) +{ + // alarm(1); //!! 产生新的alarm + loop = 1; +} + +/*********************************************************************** + * @brief slowcat + * @details 流量控制,漏筒实现 + * @param argc + * @param argv + * @return int + ***********************************************************************/ +int main(int argc, char **argv) +{ + int sfd, dfd = 1; + char buf[BUFSIZE]; + int len, ret, pos; + struct itimerval itv; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + signal(SIGALRM, alrm_handler); + // alarm(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; + if (setitimer(ITIMER_REAL, &itv, NULL) < 0) + { + perror("setitimer"); + exit(1); + } + + do //!!! + { + sfd = open(argv[1], O_RDONLY); + if (sfd < 0) + { + //!!! + if (errno != EINTR) + { + perror("open"); + exit(1); + } + } + } while (sfd < 0); + + while (1) + { + while (!loop) + pause(); //! + loop = 0; + + while ((len = read(sfd, buf, BUFSIZE)) < 0) + { + //!!! + if (EINTR == errno) + continue; + + perror("read"); + break; + } + if (len == 0) + break; + + 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); + + exit(0); +} diff --git a/C16-并发/parallel/signal/slowcat2.c b/C16-并发/parallel/signal/slowcat2.c new file mode 100644 index 0000000..741ce77 --- /dev/null +++ b/C16-并发/parallel/signal/slowcat2.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +#define CPS 10 +#define BUFSIZE CPS +#define BURST 100 //!! token上限 + +// static volatile int token = 0; +static volatile sig_atomic_t token = 0; //!!! 保证原子 + +static void alrm_handler(int s) +{ + alarm(1); //!! 产生新的alarm + token++; + if (token > BURST) + token = BURST; +} + +/*********************************************************************** + * @brief slowcat + * @details 流量控制,令牌桶实现 + * @param argc + * @param argv + * @return int + ***********************************************************************/ +int main(int argc, char **argv) +{ + int sfd, dfd = 1; + char buf[BUFSIZE]; + int len, ret, pos; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + + signal(SIGALRM, alrm_handler); + alarm(1); + + do //!!! + { + sfd = open(argv[1], O_RDONLY); + if (sfd < 0) + { + //!!! + if (errno != EINTR) + { + perror("open"); + exit(1); + } + } + } while (sfd < 0); + + while (1) + { + while (token <= 0) + pause(); //! + + token--; //!!! 这条指令不一定原子 + + //* 阻塞在这里的时候,token会自增 + while ((len = read(sfd, buf, BUFSIZE)) < 0) + { + //!!! + if (EINTR == errno) + continue; + + perror("read"); + break; + } + if (len == 0) + break; + + 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); + + exit(0); +} diff --git a/C16-并发/parallel/signal/star.c b/C16-并发/parallel/signal/star.c new file mode 100644 index 0000000..9505a42 --- /dev/null +++ b/C16-并发/parallel/signal/star.c @@ -0,0 +1,26 @@ +#include +#include +#include + +static void int_hander(int s) +{ + write(1, "!", 1); +} + +int main(int argc, char **argv) +{ + int i; + + //* ignore SIGINT + // signal(SIGINT, SIG_IGN); + + signal(SIGINT, int_hander); + + for (i = 0; i < 10; i++) + { + write(1, "*", 1); + sleep(1); + } + + exit(0); +} \ No newline at end of file