nginx系列第六篇:结合nginx梳理linux中信号的使用

news/2025/1/13 6:09:57/

         nginx中master进程来管理整个nginx工作的全过程,运行时其通过接收外部信号输入的方式来对内部进行相关调整。本文对照nginx来梳理一下linux中信号常用API的使用。

目录

1.函数sigaction和signal

2.关于信号集sigset_t

2.1 测试程序1

2.2 测试程序1

3.信号屏蔽和不屏蔽的效果

3.1信号不屏蔽

3.2信号屏蔽

4.nginx中master进程信号处理分析

5.模拟nginx信号捕获处理


信号的本质:内核实现的一种软中断

1.函数sigaction和signal

sigaction是比signal功能更加强大的信号捕捉函数,其依赖于结构体struct sigaction,定义如下:

/* Type of a signal handler.  */
typedef void (*__sighandler_t) (int);

struct sigaction
  {
    /* Signal handler.  */
#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
    union
      {
    /* Used if SA_SIGINFO is not set.  */
    __sighandler_t sa_handler;
    /* Used if SA_SIGINFO is set.  */
    void (*sa_sigaction) (int, siginfo_t *, void *);
      }
    __sigaction_handler;
# define sa_handler    __sigaction_handler.sa_handler
# define sa_sigaction    __sigaction_handler.sa_sigaction
#else
    __sighandler_t sa_handler;
#endif

    /* Additional set of signals to be blocked.  */
    __sigset_t sa_mask;

    /* Special flags.  */
    int sa_flags;

    /* Restore handler.  */
    void (*sa_restorer) (void);
  };

sigaction可以定义信号处理函数执行时别的信号到来后的动作,结构sigaction中sa_flags赋值为SA_SIGINFO时,信号处理函数为sa_sigaction,否则信号处理函数为(此时退化为和signal函数一样的功能)sa_handler。

2.关于信号集sigset_t

信号集sigset_t定义如下:

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

typedef __sigset_t sigset_t;

信号集sigset_t处理是位图操作,需要通过其api进行操作,常用api如下:
sigemptyset:清空信号集合
sigaddset:信号集中增加某个信号
sigprocmask:用于改变进程的当前阻塞信号集,也可以用来检测当前进程的信号掩码。
    每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
    备注:每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
        int sigprocmask(int how,const sigset_t *set,sigset_t * oldset);
        sigprocmask可以用来改变目前的信号掩码,其操作依参数how来决定:
        SIG_BLOCK 新的信号掩码由目前的信号掩码和参数set 指定的信号掩码作并集,也就是将set中的信号掩码添加到现有的信号掩码集合中
        SIG_UNBLOCK 将目前的信号=掩码)=删除掉参数set指定的信号掩码,也就是在现有的信号掩码集合中删去set制定的信号掩码
        SIG_SETMASK 将目前的信号掩码设成参数set指定的信号掩码。
        (SIG_BLOCK是添加,SIG_UNBLOCK是删除,SIG_SETMASK是直接赋值)
        如果参数oldset不是NULL指针,oldset指向目前的信号掩码
    注意事项:sigprocmask()函数只为单线程的进程定义的,在多线程中要使用pthread_sigmask变量,在使用之前需要声明和初始化。
sigsuspend: 信号挂起,等待有信号来,类似于条件变量的功能
    备注:sigsuspend是一个原子操作,包含4个步骤:
    (1) 设置新的mask阻塞当前进程;
    (2) 收到信号,恢复原先mask;
    (3) 调用该进程设置的信号处理函数;
    (4) 待信号处理函数返回后,sigsuspend返回。
sigaction:设置信号处理函数
sigfillset:填充信号集
sigdelset:信号集中删除信号
sigismember:信号在信号集中
sigpending: 检查悬而未决的信号集

2.1 测试程序1

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>void sig_handler (int num) {printf("signal %d\n", num);
}//使用sigprocmask
void test1() {sigset_t set;signal(SIGINT, sig_handler);sigemptyset(&set);sigaddset(&set, SIGINT);sigprocmask(SIG_SETMASK, &set, NULL);//sigprocmask(SIG_BLOCK, &set, NULL);while(1) {printf("master process running ...\n");sleep(10);}
}//使用sigprocmask + sigsuspend
void test2() {sigset_t set;signal(SIGINT, sig_handler);sigemptyset(&set);sigaddset(&set, SIGINT);sigprocmask(SIG_SETMASK, &set, NULL);//sigprocmask(SIG_BLOCK, &set, NULL);sigemptyset(&set);while(1) {printf("master process running ...\n");sigsuspend(&set);sleep(10);}
}int main()
{
#ifdef TEST1test1();
#elsetest2();
#endifreturn 0;
}

(1)测试sigprocmask

g++ test.cpp -DTEST1
./a.out

运行结果如下:

 运行结果分析:

test1()中

sigaddset(&set, SIGINT);

 sigprocmask(SIG_SETMASK, &set, NULL);

屏蔽了信号SIGINT,所以多次发送ctrl + c程序没有相应。

(2)测试sigprocmask + sigsuspend

g++ test.cpp
./a.out

运行结果如下:

 运行结果分析:

test2()中虽然调用

sigaddset(&set, SIGINT);

 sigprocmask(SIG_SETMASK, &set, NULL);

屏蔽了信号SIGINT,但是调用了sigsuspend(&set);

设置新的mask(程序中为空信号集)阻塞当前进程,收到信号,恢复原先mask。执行信号处理函数,sigsuspend是一个原子操作,其包含如下步骤:
    (1) 设置新的mask阻塞当前进程;
    (2) 收到信号,恢复原先mask;
    (3) 调用该进程设置的信号处理函数;
    (4) 待信号处理函数返回后,sigsuspend返回。

2.2 测试程序1

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>//SIGQUIT -> ctrl + \产生
static void sig_quit(int signo) { printf("caught SIGQUIT : %d\n", signo); //将SIGQUIT的动作设为缺省值 signal(SIGQUIT, SIG_DFL);
} int main() { sigset_t newmask; sigset_t oldmask; sigset_t pendmask;//信号量捕捉函数,捕捉到SIGQUIT,跳转到函数指针sig_quit处执行signal(SIGQUIT, sig_quit);//初始化信号量集 sigemptyset(&newmask);//将SIGQUIT添加到信号量集中sigaddset(&newmask, SIGQUIT);//将newmask中的SIGQUIT阻塞掉,并保存当前信号屏蔽字sigprocmask(SIG_BLOCK, &newmask, &oldmask);sleep (5);//检查信号是悬而未决的 sigpending(&pendmask);//SIGQUIT是悬而未决的。所谓悬而未决,是指SIGQUIT被阻塞还没有被处理 if (sigismember(&pendmask, SIGQUIT)) { printf("SIGQUIT pending\n"); }//恢复被屏蔽的信号SIGQUITsigprocmask(SIG_SETMASK, &oldmask, NULL);printf("SIGQUIT unblocked\n");sleep(5);printf("end end\n");return 0;
}

运行结果如下:

运行结果分析:

       sigprocmask(SIG_BLOCK, &newmask, &oldmask);屏蔽信号SIGQUIT,给进程发送SIGQUIT,SIGQUIT为未决信号,5秒后 sigpending(&pendmask);获取未决信号集,sigismember(&pendmask, SIGQUIT)调用后发现SIGQUIT位于未决信号集中,输出SIGQUIT pending,恢复最原始信号屏蔽集合,输出SIGQUIT unblocked,此时发送SIGQUIT,执行信号捕捉函数,恢复其默认执行,程序立刻退出。

3.信号屏蔽和不屏蔽的效果

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/time.h>/*
SIGQUIT:Ctrl+\ 产生 默认行为: Quit (core dumped),程序退出  忽略:signal(SIGINT, SIG_IGN);
SIGINT:Ctrl+c 产生 默认行为:程序退出  忽略:signal(SIGQUIT, SIG_IGN);
无法捕获的信息号:
SIGKILL
*/void catch_sig_handler(int num) {printf("begin call,catch %d sig\n", num);sleep(5);printf("end call,catch %d sig\n", num);
}//发送Ctrl+\发送SIGQUIT消息时,程序立刻 Quit (core dumped)
void test1() {struct sigaction act;act.sa_flags = 0;act.sa_handler = catch_sig_handler;//清空屏蔽信号集sigemptyset(&act.sa_mask);//等待按键Ctrl+c,捕捉2号信号sigaction(SIGINT, &act, NULL);signal(SIGINT, catch_sig_handler);
}//发送Ctrl+\发送SIGQUIT消息时,SIGINT信号函数处理完毕程序才会Quit (core dumped)
void test2() {struct sigaction act;act.sa_flags = 0;act.sa_handler = catch_sig_handler;//清空屏蔽信号集sigemptyset(&act.sa_mask);//临时屏蔽Ctrl+\信号  执行SIGINT信号期间,先不处理SIGQUIT信号sigaddset(&act.sa_mask, SIGQUIT);//等待按键Ctrl+c,捕捉2号信号sigaction(SIGINT, &act, NULL);
}int main(int argc, char *argv[])
{
#ifdef TEST1test1();
#elsetest2();
#endifwhile(1) {printf("I am running ......\n");sleep(1);}return 0;
}

3.1信号不屏蔽

g++ test.cpp -DTEST1
./a.out

运行效果:

 运行结果分析:

由于没有屏蔽信号SIGQUIT(Ctrl+\),所以收到SIGQUIT后程序直接退出

3.2信号屏蔽

g++ test.cpp
./a.out

运行效果:

 运行结果分析:

由于调用sigaddset(&act.sa_mask, SIGQUIT);屏蔽信号SIGQUIT,所以在执行SIGINT信号处理函数过程中是不会被SIGQUIT所干扰的,只有信号处理函数执行完毕后才执行SIGQUIT的动作(默认是退出程序)。

4.nginx中master进程信号处理分析

nginx中信号处理在函数ngx_init_signals和ngx_master_process_cycle中,代码调用大致逻辑如下:

int ngx_cdecl
main(int argc, char *const *argv)
{
    ...
    //设置信号处理函数,初始化需要捕获的信号
    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }
    ...
    主线程捕获函数
    ngx_master_process_cycle(cycle);
    ...
    return 0;
}

ngx_init_signals定义如下:

 ngx_master_process_cycle定义如下:

 

通过 sigprocmask + sigsuspend配合在保证master主进程接收外部信号输入并及时处理。

5.模拟nginx信号捕获处理

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>#define GROUP_NUM 2typedef struct {int     signo;char   *signame;char   *name;void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} ngx_signal_t;#define ngx_signal_helper(n)     SIG##n
#define ngx_signal_value(n)      ngx_signal_helper(n)#define ngx_value_helper(n)   #n
#define ngx_value(n)          ngx_value_helper(n)#define NGX_SHUTDOWN_SIGNAL      QUIT
#define NGX_TERMINATE_SIGNAL     TERM
#define NGX_NOACCEPT_SIGNAL      WINCH
#define NGX_RECONFIGURE_SIGNAL   HUP#if (NGX_LINUXTHREADS)
#define NGX_REOPEN_SIGNAL        INFO
#define NGX_CHANGEBIN_SIGNAL     XCPU
#else
#define NGX_REOPEN_SIGNAL        USR1
#define NGX_CHANGEBIN_SIGNAL     USR2
#endif#define ngx_errno                  errnotypedef intptr_t        ngx_int_t;
typedef int             ngx_err_t;
typedef uintptr_t       ngx_uint_t;
typedef pid_t       ngx_pid_t;typedef ngx_uint_t  ngx_rbtree_key_t;
typedef ngx_int_t   ngx_rbtree_key_int_t;typedef ngx_rbtree_key_t      ngx_msec_t;
typedef ngx_rbtree_key_int_t  ngx_msec_int_t;#define ngx_getpid   getpid
#define ngx_getppid  getppid#define ngx_gettimeofday(tp)  (void) gettimeofday(tp, NULL);
#define ngx_msleep(ms)        (void) usleep(ms * 1000)
#define ngx_sleep(s)          (void) sleep(s)#define NGX_PROCESS_SINGLE     0
#define NGX_PROCESS_MASTER     1
#define NGX_PROCESS_SIGNALLER  2
#define NGX_PROCESS_WORKER     3
#define NGX_PROCESS_HELPER     4#define  NGX_OK          0
#define  NGX_ERROR      -1
#define  NGX_AGAIN      -2
#define  NGX_BUSY       -3
#define  NGX_DONE       -4
#define  NGX_DECLINED   -5
#define  NGX_ABORT      -6ngx_uint_t    ngx_process;
ngx_uint_t    ngx_worker;
ngx_pid_t     ngx_pid;
ngx_pid_t     ngx_parent;sig_atomic_t  ngx_reap;
sig_atomic_t  ngx_sigio;
sig_atomic_t  ngx_sigalrm;
sig_atomic_t  ngx_terminate;
sig_atomic_t  ngx_quit;
sig_atomic_t  ngx_debug_quit;
ngx_uint_t    ngx_exiting;
sig_atomic_t  ngx_reconfigure;
sig_atomic_t  ngx_reopen;sig_atomic_t  ngx_change_binary;
ngx_pid_t     ngx_new_binary;
ngx_uint_t    ngx_inherited;
ngx_uint_t    ngx_daemonized;sig_atomic_t  ngx_noaccept;
ngx_uint_t    ngx_noaccepting;
ngx_uint_t    ngx_restart;void
ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext);ngx_signal_t  signals[] = {{ ngx_signal_value(NGX_RECONFIGURE_SIGNAL),"SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),"reload",ngx_signal_handler },{ ngx_signal_value(NGX_REOPEN_SIGNAL),"SIG" ngx_value(NGX_REOPEN_SIGNAL),"reopen",ngx_signal_handler },{ ngx_signal_value(NGX_NOACCEPT_SIGNAL),"SIG" ngx_value(NGX_NOACCEPT_SIGNAL),"",ngx_signal_handler },{ ngx_signal_value(NGX_TERMINATE_SIGNAL),"SIG" ngx_value(NGX_TERMINATE_SIGNAL),"stop",ngx_signal_handler },{ ngx_signal_value(NGX_SHUTDOWN_SIGNAL),"SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),"quit",ngx_signal_handler },{ ngx_signal_value(NGX_CHANGEBIN_SIGNAL),"SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),"",ngx_signal_handler },{ SIGALRM, "SIGALRM", "", ngx_signal_handler },{ SIGINT, "SIGINT", "", ngx_signal_handler },{ SIGIO, "SIGIO", "", ngx_signal_handler },{ SIGCHLD, "SIGCHLD", "", ngx_signal_handler },{ SIGSYS, "SIGSYS, SIG_IGN", "", NULL },{ SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },{ 0, NULL, "", NULL }
};void
ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{char            *action;ngx_int_t        ignore;ngx_err_t        err;ngx_signal_t    *sig;ignore = 0;err = ngx_errno;printf("signo = [%d]\n", signo);for (sig = signals; sig->signo != 0; sig++) {if (sig->signo == signo) {break;}}action = "";switch (ngx_process) {case NGX_PROCESS_MASTER:case NGX_PROCESS_SINGLE:switch (signo) {case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):ngx_quit = 1;action = ", shutting down";printf("recv signal SIGQUIT\n");sleep(5);break;case ngx_signal_value(NGX_TERMINATE_SIGNAL):case SIGINT:ngx_terminate = 1;action = ", exiting";printf("recv signal SIGINT\n");sleep(5);break;case ngx_signal_value(NGX_NOACCEPT_SIGNAL):if (ngx_daemonized) {ngx_noaccept = 1;action = ", stop accepting connections";}break;case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):ngx_reconfigure = 1;action = ", reconfiguring";break;case ngx_signal_value(NGX_REOPEN_SIGNAL):ngx_reopen = 1;action = ", reopening logs";break;case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {action = ", ignoring";ignore = 1;break;}ngx_change_binary = 1;action = ", changing binary";break;case SIGALRM:ngx_sigalrm = 1;break;case SIGIO:ngx_sigio = 1;break;case SIGCHLD:ngx_reap = 1;break;}break;case NGX_PROCESS_WORKER:case NGX_PROCESS_HELPER:switch (signo) {case ngx_signal_value(NGX_NOACCEPT_SIGNAL):if (!ngx_daemonized) {break;}ngx_debug_quit = 1;/* fall through */case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):ngx_quit = 1;action = ", shutting down";break;case ngx_signal_value(NGX_TERMINATE_SIGNAL):case SIGINT:ngx_terminate = 1;action = ", exiting";break;case ngx_signal_value(NGX_REOPEN_SIGNAL):ngx_reopen = 1;action = ", reopening logs";break;case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):case SIGIO:action = ", ignoring";break;}break;}if (siginfo && siginfo->si_pid) {// printf("signal %d (%s) received from %P%s",//              signo, sig->signame, siginfo->si_pid, action);printf("signal %d (%s)\n", signo, sig->signame);} else {/*printf("signal %d (%s) received%s",signo, sig->signame, action);*/printf("signal %d (%s)\n", signo, sig->signame);}if (ignore) {printf("the changing binary signal is ignored: ""you should shutdown or terminate ""before either old or new binary's process");}//printf("action = [%s]\n", action);
}ngx_int_t
ngx_init_signals()
{ngx_signal_t      *sig;struct sigaction   sa;for (sig = signals; sig->signo != 0; sig++) {memset(&sa, 0x00, sizeof(struct sigaction));if (sig->handler) {sa.sa_sigaction = sig->handler;sa.sa_flags = SA_SIGINFO;} else {sa.sa_handler = SIG_IGN;}sigemptyset(&sa.sa_mask);if (sigaction(sig->signo, &sa, NULL) == -1) {
#if (NGX_VALGRIND)printf("sigaction(%s) failed, ignored", sig->signame);
#elseprintf("sigaction(%s) failed", sig->signame);return NGX_ERROR;
#endif}}return NGX_OK;
}void
ngx_master_process_cycle()
{char              *title;u_char            *p;size_t             size;ngx_int_t          i;ngx_uint_t         sigio;sigset_t           set;struct itimerval   itv;ngx_uint_t         live;ngx_msec_t         delay;sigemptyset(&set);sigaddset(&set, SIGCHLD);sigaddset(&set, SIGALRM);sigaddset(&set, SIGIO);sigaddset(&set, SIGINT);sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));//如下代码的含义: 系统很忙的,没有时间及时相应信号,进程可以先把信号阻塞掉,等系统有空闲时间再去相应,这也保证了信号的可靠性。if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {printf("sigprocmask() failed, ngx_errno:%d\n", ngx_errno);}sigemptyset(&set);ngx_new_binary = 0;delay = 0;sigio = 0;live = 1;for ( ;; ) {printf("master process running ......\n");if (delay) {/*if (ngx_sigalrm) {sigio = 0;delay *= 2;ngx_sigalrm = 0;}printf("termination cycle: %M\n", delay);itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = delay / 1000;itv.it_value.tv_usec = (delay % 1000 ) * 1000;if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {printf("setitimer() failed, ngx_errno:%d\n", ngx_errno);}*/}sigsuspend(&set);if (ngx_reap) {ngx_reap = 0;live = 1;}if (!live && (ngx_terminate || ngx_quit)) {printf("master process exit\n");exit(0);}if (ngx_terminate) {if (delay == 0) {delay = 50;}if (sigio) {sigio--;continue;}continue;}if (ngx_quit) {continue;}if (ngx_reconfigure) {ngx_reconfigure = 0;if (ngx_new_binary) {ngx_noaccepting = 0;continue;}printf("reconfiguring \n");/* allow new processes to start */ngx_msleep(100);live = 1;}if (ngx_restart) {ngx_restart = 0;live = 1;}if (ngx_reopen) {ngx_reopen = 0;}if (ngx_change_binary) {ngx_change_binary = 0;}if (ngx_noaccept) {ngx_noaccept = 0;ngx_noaccepting = 1;}}
}int main()
{//信号初始化ngx_init_signals();//创建子进程for (int i = 0; i < GROUP_NUM; i++) {int pid = fork();switch (pid) {case -1: // errorreturn -1;case 0: // childsleep(2);printf("child exit \n");exit(0);default: // parentbreak;}}//主进程执行ngx_master_process_cycle();return 0;
}

编译运行:

 通过运行可以看出程序很快速的交替响应信号SIGINT和SIGQUIT。


http://www.ppmy.cn/news/248039.html

相关文章

前端基础(JavaScript)——基础语法(变量,分支...) Json对象【重要】 函数定义 事件(未完待续)

目录 引出JS是啥&#xff0c;能干啥基础语法1.变量----let2.怎么打印---console3.if条件分支--啥都可以是条件例子&#xff1a;输入框&#xff0c;打印输入和未输入4.数组push 和 splice&#xff08;2&#xff09;splice&#xff0c;3个参数&#xff0c;索引开始、删除几个&…

第二章:MySQL环境搭建

第二章&#xff1a;MySQL环境搭建 2.1&#xff1a;MySQL的下载、安装、配置 MySQL的四大版本 MySQL Community Server社区版本&#xff1a;开源免费、自由下载&#xff0c;但不提供官方技术支持&#xff0c;适用于大多数普通用户。MySQL Enterprise Edition企业版本&#xff1…

jmeter打开文件时报cannot read value = ::{20d04fe0-3aea-1069-a2d8-08002b30309d}\::{5fcd4425-ca3a-48f4-a57c-

引发问题原因未知&#xff0c;但是通过百度找到解决方法 WARNING: Cannot read value ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{5FCD4425-CA3A-48F4-A57C-B8A75C32ACB1} java.io.FileNotFoundException: File C:\apache-jmeter-5.4.1\apache-jmeter-5.4.1\bin\::{20D04F…

Java中使用A标签出现“%20d“等字符解决方法

在使用超链接时从配置文件中获取的地址,在通过${“value”}注入后多出了其他的字符,在此记录一下. 多出字符的原因是将URL中多余的空格进行了转义. 解决办法也很简单, 将url重新进行编码 URLDecoder.decode(URL,"UTF-8");提醒一下, 代码中使用decode方法的时候需要抛…

【Java-13】网络编程知识整理总结

主要内容 网络编程 TCP通信 Junit单元测试 单例设计模式 多例设计模式 工厂设计模式 1 网络编程 1.1 软件架构 C/S结构 &#xff1a;全称为Client/Server结构&#xff0c;是指客户端和服务器结构。常见程序有&#xff31;&#xff31;、迅雷等软件B/S结构 &#xff1a…

20D添加标准SPI驱动详解 ——(一)

点此进入原文链接 用过海思3520DV200的人应该都知道吧&#xff0c;由于芯片的年代比较久了&#xff0c;20Dv200的SDK里面是没有标准SPI通讯接口的驱动的&#xff0c;要用SPI通讯的话只能用官方提供的工具SSP_read和write来读写数据&#xff0c;这样非常的不方便&#xff0c;也…

20D添加标准SPI驱动详解 ——(二)

点此进入原文链接 上次跟大家说道&#xff0c;这个添加spi驱动的思路是啥&#xff0c;那么接下来我们就要具体去看代码了。   我们首先要搞清楚&#xff0c;要修改和添加哪几个文件&#xff0c;再去做具体的修改。这个怎么看呢&#xff0c;做过linux下内核驱动修改的人应该知…

20D添加标准SPI驱动详解 ——(三)

点此进入原文链接 20D添加标准SPI驱动详解 ——&#xff08;二&#xff09;中&#xff0c;我们最后告诉了大家&#xff0c;我们需要修改和添加哪些文件&#xff0c;Kconfig和Makefile我们就不跟大家说了&#xff0c;大家照着16A的文件自己去复制粘贴一下就行了&#xff0c;就是…