Linux信號

来源:http://www.cnblogs.com/xiaojiang1025/archive/2016/10/06/5935200.html
-Advertisement-
Play Games

信號本質上就是一個軟體中斷,它既可以作為兩個進程間的通信的方式, 更重要的是, 信號可以終止一個正常程式的執行, 通常被用於處理意外情況 , 信號是非同步的, 也就是進程並不知道信號何時會到達 $kill 9 3390 向PID為3390的進程發送編號為9的信號= 一個兩個進程間通信的方式之一 一共6 ...


  • 信號本質上就是一個軟體中斷,它既可以作為兩個進程間的通信的方式, 更重要的是, 信號可以終止一個正常程式的執行, 通常被用於處理意外情況 ,* 信號是非同步的, 也就是進程並不知道信號何時會到達
  • $kill -9 3390 #向PID為3390的進程發送編號為9的信號=>一個兩個進程間通信的方式之一
 1) SIGHUP      2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT     7) SIGBUS       8) SIGFPE       9) SIGKILL      10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    
  • 一共62個,不是64個, 歷史原因, 沒有32,33
  • 1~31之間的信號叫做不可靠信號, 不支持排隊, 信號可能會丟失, 也叫做非實時信號
  • 34~64之間的信號叫做可靠信號, 支持排隊, 信號不會丟失, 也叫做實時信號

絕大多數信號的預設處理方式都是終止進程, 另外兩種預設處理方式: 忽略處理, 自定義處理

1) SIGHUP 連接掛斷 終止(預設處理)
2) SIGINT 終端中斷,Ctrl+c產生該信號 終止(terminate)
3) SIGQUIT 終端退出,Ctrl+\ 終止+轉儲
4) SIGILL *進程試圖執行非法指令 終止+轉儲
5) SIGTRAP 進入斷點 終止+轉儲
6) SIGABRT *進程異常終止,abort()產生 終止+轉儲
7) SIGBUS 硬體或對齊錯誤 終止+轉儲
8) SIGFPE *浮點運算異常 終止+轉儲
9) SIGKILL 不可以被捕獲或忽略的終止信號 終止
10) SIGUSR1 用戶定義信號1 終止
11) SIGSEGV *無效的記憶體段訪問=>Segmentation error 終止+轉儲
12) SIGUSR2 用戶定義信號2 終止
13) SIGPIPE 向讀端已關閉的管道寫入 終止
14) SIGALRM 真實定時器到期,alarm()產生 終止
15) SIGTERM 可以被捕獲或忽略的終止信號 終止
16) SIGSTKFLT 協處理器棧錯誤 終止
17) SIGCHLD 子進程已經停止, 對於管理子進程很有用 忽略
18) SIGCONT 繼續執行暫停進程(用戶一般不用) 忽略
19) SIGSTOP 不能被捕獲或忽略的停止信號 停止(stop)
20) SIGTSTP 終端掛起,用戶產生停止符(Ctrl+Z) 停止
21) SIGTTIN 後臺進程讀控制終端 停止
22) SIGTTOU 後臺進程寫控制終端 停止
23) SIGURG 緊急I/O未處理 忽略
24) SIGXCPU 進程資源超限 終止+轉儲
25) SIGXFSZ 文件資源超限 終止+轉儲
26) SIGVTALRM 虛擬定時器到期 終止
27) SIGPROF 實用定時器到期 終止
28) SIGWINCH 控制終端視窗大小改變 忽略
29) SIGIO 非同步I/O事件 終止
30) SIGPWR 斷電 終止
31) SIGSYS 進程試圖執行無效系統調用 終止+轉儲

*系統對信號響應應視具體情況而定

發送信號的主要方式:

  1. 鍵盤 //只能發送部分特殊的信號 eg:Ctrl+C可以發送SIGINT
  2. 程式出錯 //只能發送部分特殊的信號 eg: 出現段錯誤, 可以發送SIGSEGV
  3. $kill -Signal PID #能發所有信號
  4. 系統函數kill()/raise()/alarm()/sigqueue()

kill()

//給任何進程或進程組發任何信號,成功返回0,失敗返回-1設errno

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

pid

  • pid>0 //指定pid
  • pid=0 //GID是調用進程PID的子進程
  • pid=-1 //任何子進程
  • pid<-1 //GID是PID的子進程

sig

  • sig == 0 //不發任何信號,但錯誤檢查還是會做,可以檢查PID是否存在
#include<sys/types.h>
#include<signal.h>
void fa(int signo){
    printf("catch signal number:%d\n",signo);
}
int main(){
    pid_t pid=fork();
    if(0==pid){
        …
        while(1);
    }
    if(0==kill(pid,0)){                         //if child exists
        printf("parent starts to raise signal\n");
        if(-1==kill(pid,40))                    //if fail to kill child
            perror("kill"),exit(-1);
    }
    return 0;
}.

raise()

//給調用的線程或進程發信號,如果發送的信號被handler處理了,會在handler返回後再返回,成功返回0,失敗返回非0
#include<signal.h>
int raise(int sig);
  • 在單線程程式中等價於 kill(getpid,sig)
  • 在多線程程式中等價於pthread_kill(pthread_self(), sig);
if(0!=raise(SIGINT))
    perror("raise success"),exit(-1);
//raise.c
#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
void fa(int signo){
    printf("catch %d success\n",signo);
}
int main(){
    //設置對SIGINT自定義處理
    if(SIG_ERR==signal(SIGINT,fa))
        perror("signal"),exit(-1);

    //5s後使用raise發送SIGINT
    unsigned int second=sleep(5);
    if(0==second)
        printf("sleep well");
    else
        printf("sleep is interrupted, there are %ds left\n",second);
    if(0!=raise(SIGINT))
        perror("raise success"),exit(-1);
    return 0;
}

alarm()

//seconds秒之後給調用進程發送SOGALRM信號,返回距離下次鬧鐘響起剩餘的描述,沒有鬧鐘返回0

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

seconds=0表示不設置新的SIGALRM的同時取消之前的鬧鐘==>專門用來取消鬧鐘

//alarm.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
void fa(int signo){
    printf("catch %d\n",signo);
    alarm(1);
}
int main(){
//  設置對信號SIGALRM進行自定義處理;
    if(SIG_ERR==signal(SIGALRM,fa))
        perror("signal"),exit(-1);
//  設置5s發送SIGALRM;
    unsigned int second=alarm(5);
    printf("second=%u\n",second);   //0
    sleep(2);
    //調整鬧鐘10s後響(發SIGALRM),second是3
    second=alarm(10);
    printf("second=%u\n",second);   //3
    //取消鬧鐘(不會發SIGALRM),second是3
//  second=alarm(0);
//  printf("second=%d\n",second);   //10
    
    while(1);
    return 0;
}

sigqueue()

//將信號和數據排好發給指定進程,成功返回0,失敗返回-1設errno

#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);

pid //進程的編號(給誰發信號)
sig //信號值/信號的名稱(發送什麼樣的信號)
value //聯合類型的附加數據(伴隨什麼樣的數據)

union sigval {
    int   sival_int;
    void *sival_ptr;
};
void fa(int signo,siginfo_t* info,void* pv){
    printf("進程%d發來的信號是:%d,附加數據是%d\n",info->si_pid,signo,info->si_value);
}
int main(){
    //設置對信號40的自定義處理
    struct sigaction action={};
    action.sa_sigaction=fa;
    action.sa_flags=SA_SIGINFO;
    int res=sigaction(40,&action,NULL);
    if(-1==res)
        perror("sigaction"),exit(-1);
    pid_t pid=fork();
    if(-1==pid)
        perror("fork"),exit(-1);
    if(0==pid){ //child開始, 發送1~100之間的數據發給parent
        printf("child%dstarts\n",getpid());
        int i=1;
        for(i=1;i<=100;i++){
            union sigval value;
            value.sival_int=i;
            sigqueue(getppid(),40,value);//這裡沒有錯誤處理
        }
        exit(0);
    }
    while(1);
    return 0;
}

sleep()

//讓調用的線程睡seconds秒,除非這期間收到了不能忽略的信號,成功返回0,失敗返回剩餘沒睡的時間

#include <unistd.h>
unsigned int sleep(unsigned int seconds);
unsigned int second=sleep(5);
if(0==second)
    printf("sleep well");
else
    printf("sleep is interrupted, there are %ds left\n",second);

pause()

//讓調用的線程一直睡直到接收到了一個終止進程或者引發捕獲信號函數的信號,當接受到這樣的信號時,返回-1設errno

int pause(void);
void ding(int signo){
}
int main(){
    (void) signal(SIGALRM, ding);   //ding是一個函數
    pause();    
}

Q:pause 放在signal-catching很遠的地方可以嗎

A:pause就相當於sleep(∞), 跟捕捉語句沒什麼關係,誰說一定要組合用的

Q: 寫了捕獲語句, 是不是就使整個進程有了捕獲某個信號的能力還是只是這一句???

A:當然不是, signal就像是全局變數, 使用sigaction()對某個信號進行重定向後, 此後的程式遇到這個信號就使用新的處理方式

signal()

//重新設置對信號的處理方式,將信號signum交給handler處理,成功返回之前的signal handler,失敗返回-1設errno

#include <signal.h>
sighandler_t signal(int signum, sighandler_t handler);

handler

  • SIG_IGN(Signal ignore)
  • SIG_DFL(Signal default)
  • User-defined fhandler with argument signum
#include<signal.h>
void fa(int signo){
    printf("catch signal:%d\n",signo);
    if(SIG_ERR==signal(signo,SIG_DFL))
        perror("signal"),exit(-1);
}
int main(){
    if(SIG_ERR==signal(3,fa))
        perror("signal"),exit(-1);
    return 0;
}

信號集

信號的集合,當前系統所支持的信號範圍:1~64(共62個),就是說每個進程可以使用信號1~64,為了對信號操作方便, 我們把一些信號放在一個集合中一起處理, 很多函數(包括sigpromask,sigpending)都是對信號集操作而不是對單個信號

sigset_t

Linux中表示信號集的數據類型, 就是一個超級大的整數(128byte, 32個unsigned long int的數組), 底層採用一個二進位位來代表一個信號, 根據該二進位位為0 or 1表示該信號是否存在

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

在當前系統中, 按照%d列印的話就是列印信號集類型中的低4個位元組的數據其實當前只需要8byte, 設計這麼大是為了未來擴展

sigemptyset() /sigfillset()/sigaddset()/sigdelset()/sigismember()

//這些都是信號集操作函數
//sigemptyset()/sigfillset()/ sigaddset()/ sigdelset() 成功返回0,失敗返回-1設errno
//如果signum是指定信號集的一員,sigismember()返回1,不是返回0,失敗返回-1設errno。

#include <signal.h>
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);   //判斷信號是否屬於信號集

sigprocmask()

//sigprocmask()只適用與單線程進程,多線程的參考pthread_sigmask()
//在某些特殊程式的執行過程中, 是不能被信號打斷的, 此時需要信號的屏蔽技術來解決該問題
//獲取並修改調用線程的屏蔽信號集
//成功返回0,失敗返回-1設errno

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how

  • SIG_BLOCK //新的屏蔽信號集是原有集合(不是oldset,oldset是用來存儲的)和set的合集
  • SIG_UNBLOCK //set內的信號被從屏蔽集合oldset中移除
  • SIG_SETMASK //屏蔽信號集就是setThe set of blocked signals is set to the argument set.

oldset

  • 如果oldset 不是NULL,則之前的信號集被存儲在其中
  • 如果set是NULL,屏蔽信號集不會變數,但現存的屏蔽信號集還是會被存儲在oldset中
//準備信號集中有:2,3
sigset_t set,oldset;
if(-1==sigemptyset(&set))
    perror("sigemptyset"),exit(-1);
if(-1==sigemptyset(&oldset))
    perror("sigemptyset"),exit(-1);
if(-1==sigaddset(&set,2))
    perror("sigaddset"),exit(-1);

//設置屏蔽的信號集
if(-1== sigprocmask(SIG_SETMASK,&set,&oldset))
    perror("sigprocmask"),exit(-1);
  • 信號屏蔽並不是刪除信號, 而是將信號單獨保存起來, 等信號的屏蔽解除之後, 信號還是會被處理的
  • 可靠信號: 支持排隊, mask期間有多少信號, mask解除之後就會處理多少
  • 不可靠信號:不支持排隊, mask期間有多個信號時, mask解除之後只處理第一個

sigpending()

//檢查mask期間捕捉到但是沒有處理的信號, 通過形參帶出結果,成功返回0,失敗返回-1設errno

#include <signal.h>
int sigpending(sigset_t *set);

sigaction()

//是前面的信號處理的集大成者且有很多擴展,是一個非常健壯的signal處理介面,建議使用,成功返回0,失敗返回-1設errno

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

signum可以代表除了SIGSTOP和SIGKILL之外的任何一個信號
如果NULL!=act,act作為針對signal的新的action。
如果NULL!=oldact, 之前的action被存儲在oldact。
act/oldact:

struct sigaction {
    void        (*sa_handler)(int);
    void        (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t    sa_mask;
    int         sa_flags;
    void        (*sa_restorer)(void);       //obsolete, should not be used.
};

如果 sa_flags 被設為SA_SIGINFO,則sa_sigaction用來作signum的handler, 否則,用sa_handler
sa_handler表明針對signum的handler,和signal()第二個參數類型一致, 都是用於設置signal信號的處理方式SIG_DFL,SIG_IGN,user-defined handler

//sa_sigaction
struct  siginfo_t {
    int         si_signo;       /* Signal number */
    int         si_errno;       /* An errno value */
    int         si_code;        /* Signal code */
    int         si_trapno;      /*Trap number that caused hardware-generated signal
    pid_t       si_pid;         /* Sending process ID */    //發送信號的進程號
    uid_t       si_uid;         /* Real user ID of sending process */
    int         si_status;      /* Exit value or signal */
    clock_t     si_utime;       /* User time consumed */
    clock_t     si_stime;       /* System time consumed */
    sigval_t    si_value;       /* Signal value */          //伴隨信號到來的附加數據
    int         si_int;         /* POSIX.1b signal */
    void*       si_ptr;         /* POSIX.1b signal */
    int         si_overrun;     /* Timer overrun count; POSIX.1b timers */
    int         si_timerid;     /* Timer ID; POSIX.1b timers */
    void*       si_addr;        /* Memory location which caused fault */
    long        si_band;        /* Band event (was int in glibc 2.3.2 and earlier) */
    int         si_fd;          /* File descriptor */
    short       si_addr_lsb;    /* Least significant bit of address(since kernel 2.6.32) */
} 

sa_mask 表明在信號handler執行期間需要屏蔽的信號,此外,出發handler的信號預設是被屏蔽的,除非flag里指定的SA_NODEFE

sa_flags(Bitwise Or)

  • SA_NOCLDSTOP //If signum is SIGCHLD, do not receive notification when child processes stop (i.e., when they receive one of SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU) or resume (i.e., they receive SIGCONT) (see wait(2)). This flag is only meaningful when establishing a handler for SIGCHLD.
  • SA_NOCLDWAIT //If signum is SIGCHLD, do not transform children into zombies when they terminate. See also waitpid(2). This flag is only meaningful when establishing a handler for SIGCHLD, or when setting that signal's disposition to SIG_DFL.If the SA_NOCLDWAIT flag is set when establishing a handler for SIGCHLD, POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated when a child process terminates. On Linux, a SIGCHLD signal is generated in this case; on some other implementations, it is not.
  • SA_NODEFER //解除對觸發信號處理函數相同信號的屏蔽
  • SA_ONSTACK //Call the signal handler on an alternate signal stack provided by sigaltstack(2). If an alternate stack is not available, the default stack will be used. This flag is only meaningful when establishing a signal handler.
  • SA_RESETHAND //一旦信號處理函數被調用之後, 信號的處理方式就會恢復為預設處理方式
  • SA_RESTART //Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals.This flag is only meaningful when establishing a signal handler. See signal(7) for a discussion of system call restarting.
  • SA_SIGINFO //使用第二個函數指針設置信號的處理方式
//sigaction.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
void fa(int signo){
    printf("fa1...\n");
    sleep(3);
    printf("fa2...\n");
}
int main(){
    printf("current pid:%d\n",getpid());
    //prepare struc var
    struct sigaction action={};
    action.sa_handler=fa;
    sigemptyset(&action.sa_mask);
    sigaddset(&action.sa_mask,3);
    action.sa_flags=SA_NODEFER; //解除對信號2的屏蔽,sigaction(2....)
    int res=sigaction(2,&action,NULL);
    if(-1==res)
        perror("sigaction"),exit(-1);
    printf("set signal successfully\n");
    while(1);
    return 0;
}

sigaction()那點事:

  1. 使用sigaction(),需要明確兩個act,一個用來存儲老act的oldact,一個用來安裝新act的act,
  2. 這兩個act都是struct sigaction
  3. 結構體有兩個函數分別指明對sig的處理方式,具體使用哪個需要看flag是否被set了SA_SIGINFO,set了,就是sa_sigaction()處理信號,沒set,就是sa_handler,前者比後者的功能更多,後者其實就是signal()
  4. 指明瞭用哪個函數處理了信號,我們還可以指定在signal handler執行的過程中哪些SIG被mask,註意這個是sigset_t類型,把要mask的信號放在一個信號集里,預設條件下the signal which triggered the handler是被mask的,除非在sa_flags中set了SA_NODEFER;
  5. 可以看到,整個act中flags主要起到了配置的作用,它決定使用哪個handler(SA_SIGINFO),也決定要不要屏蔽觸發信號(SA_NODEFER);
  6. 除了這兩個,flags還有其他選項
void fa(int signo,siginfo_t* info,void* pv){
    printf("進程%d發來了信號%d\n",info->si_pid,signo);
}
int main(){
    struct sigaction action={};
    action.sa_sigaction=fa;
    action.sa_flags=SA_SIGINFO;
    if(-1== sigaction(2,&action,NULL))
        perror("sigaction"),exit(-1);
    return 0;
}

父子進程信號

  1. 對於fork()創建的child, child完全照搬parent對信號的處理方式:
    • parent忽略=>child忽略;
    • parent預設=>child預設;
    • arent自定義=>child自定義;
  2. 對於vfork(),execl()啟動的child, 部分照搬parent的信號處理方式:
    • parent忽略=>child忽略;
    • parent預設=>child預設;
    • parent自定義=>child預設=>因為execl()已經跳出的子進程, 而process.out裡面沒有fa,所以會按預設方式處理
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
void fa(int signo){
    printf("%d\n",signo);
}
int main(){
    if(SIG_ERR==signal(2,fa))
        perror("signal"),exit(-1);
    if(SIG_ERR==signal(3,SIG_IGN))
        perror("signal"),exit(-1);
    pid_t pid=vfork();
    if(-1==pid)
        perror("vfork"),exit(-1);
    if(0==pid){
        printf("child's pid=%d\n",getpid());
        int res=execl("process.out","process.out",NULL);
        if(-1==res)
            perror("execl"),exit(-1);
        while(1);
    }
    return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 模型 1. 獲取key ftok() 2. 創建/獲取信號量集 semget() 3. 初始化信號量集 semctl() 4. 操作信號量集 semop() 3. 刪除信號量集 semctl() 使用的頭文件: ftok() pathname :文件名 proj_id : 1~255的一個數,表示p ...
  • 其實樹莓派自帶realvnc server 不需要額外tightvnc server 1.點擊左上角樹莓派圖標-->Preferences-->Raspberry Pi Configuration 2.Enable VNC 3.點擊"OK"後,其實VNCserver已經打開了: 4.點擊"More" ...
  • 模型: 1. 獲取key值 :ftok() 2. 創建/獲取消息隊列 :msgget() 3. 發消息到消息隊列/從消息隊列收信息 :msgsnd()/msgrcv() 3. 刪除消息隊列 :msgctl() 使用的頭文件 ftok() pathname :文件名 proj_id : 1~255的一 ...
  • 1.代碼 input_subsys.drv.c 在linux輸入子系統(input subsystem)之按鍵輸入和LED控制的基礎上有小改動,input_subsys_test.c不變。 input_subsys.drv.c 2. input_subsys_drv.c, input.c, evde ...
  • 通信模型: 1. 獲取key值 :ftok() 2. 創建/獲取共用記憶體 :shmget() 3. 掛接共用記憶體 :shmat() 4. 脫接共用記憶體 :shmdt() 5. 刪除共用記憶體 :shmctl() 使用的頭文件 ftok() pathname :文件名 proj_id : 1~255的一 ...
  • ipcs ipcs m 查看系統中已經存在的共用記憶體 shmid :共用記憶體的id perms :permission nattch :number attatch ipcs q 查看系統中現有的消息隊列 used byte: 隊列的大小 message : 隊列中消息的條數 ipcs s 查看系統 ...
  • 管道是Linux的十種文件類型之一,使用管道通信本質上還是以文件作為通信的媒介 有名管道+無名管道=管道 有名管道(FIFO文件):就是 有文件名的管道, 可以用於任意兩個進程間的通信 無名管道(pipe文件):就是沒有文件名的管道, 只能用於父子進程之間的通信 mkfifo 創建有名管道,管道不能 ...
  • Linux中, 系統為每個系統都維護了三種計時器,分別為: 真實計數器, 虛擬計時器以及實用計時器, 一般情況下都使用真實計時器 getitimer()/setitimer() which //具體的計時器類型 1. ITIMER_REAL :真實計時器 統計進程消耗的真實時間 通過定時產生SIGA ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...