Swoole 源碼分析——Server模塊之初始化

前言

本節主要介紹 server 模塊進行初始化的代碼,關于初始化過程中,各個屬性的意義,可以參考官方文檔:

SERVER 配置選項

關于初始化過程中,用于監聽的 socket 綁定問題,可以參考:

UNP 學習筆記——基本 TCP 套接字編程

UNP 學習筆記——套接字選項

構造 server 對象

構造 server 對象最重要的是兩件事:swServer_init 初始化 server、為 server 添加端口:

PHP_METHOD(swoole_server, __construct)
{zend_size_t host_len = 0;char *serv_host;long sock_type = SW_SOCK_TCP;long serv_port = 0;long serv_mode = SW_MODE_PROCESS;swServer *serv = sw_malloc(sizeof (swServer));swServer_init(serv);serv->factory_mode = serv_mode;if (serv_port == 0 && strcasecmp(serv_host, "SYSTEMD") == 0){if (swserver_add_systemd_socket(serv) <= 0){swoole_php_fatal_error(E_ERROR, "failed to add systemd socket.");return;}}else{swListenPort *port = swServer_add_port(serv, sock_type, serv_host, serv_port);}
}

swServer_init 函數

  • swServer_init 函數主要為 serv 對象賦值初值,如果想要更改 serv 對象各個屬性,可以調用 set 函數
  • serv->gs 是全局共享內存
void swServer_init(swServer *serv)
{swoole_init();bzero(serv, sizeof(swServer));serv->factory_mode = SW_MODE_BASE;serv->reactor_num = SW_REACTOR_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_REACTOR_NUM;serv->dispatch_mode = SW_DISPATCH_FDMOD;serv->worker_num = SW_CPU_NUM;serv->max_connection = SwooleG.max_sockets < SW_SESSION_LIST_SIZE ? SwooleG.max_sockets : SW_SESSION_LIST_SIZE;serv->max_request = 0;serv->max_wait_time = SW_WORKER_MAX_WAIT_TIME;//http serverserv->http_parse_post = 1;serv->upload_tmp_dir = sw_strdup("/tmp");//heartbeat checkserv->heartbeat_idle_time = SW_HEARTBEAT_IDLE;serv->heartbeat_check_interval = SW_HEARTBEAT_CHECK;serv->buffer_input_size = SW_BUFFER_INPUT_SIZE;serv->buffer_output_size = SW_BUFFER_OUTPUT_SIZE;serv->task_ipc_mode = SW_TASK_IPC_UNIXSOCK;/*** alloc shared memory*/serv->stats = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swServerStats));if (serv->stats == NULL){swError("[Master] Fatal Error: failed to allocate memory for swServer->stats.");}serv->gs = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swServerGS));if (serv->gs == NULL){swError("[Master] Fatal Error: failed to allocate memory for swServer->gs.");}SwooleG.serv = serv;
}

swoole_init 函數

  • swoole_init 函數用于初始化全局變量 SwooleG 的各個屬性
  • SwooleGS 是全局的共享內存
  • SwooleTG 是線程特有數據,每個線程都有自己獨特的數據
extern swServerG SwooleG;              //Local Global Variable
extern SwooleGS_t *SwooleGS;           //Share Memory Global Variable
extern __thread swThreadG SwooleTG;   //Thread Global Variabletypedef struct
{swLock lock;swLock lock_2;
} SwooleGS_t;void swoole_init(void)
{struct rlimit rlmt;if (SwooleG.running){return;}bzero(&SwooleG, sizeof(SwooleG));bzero(&SwooleWG, sizeof(SwooleWG));bzero(sw_error, SW_ERROR_MSG_SIZE);SwooleG.running = 1;SwooleG.enable_coroutine = 1;sw_errno = 0;SwooleG.log_fd = STDOUT_FILENO;SwooleG.cpu_num = sysconf(_SC_NPROCESSORS_ONLN);SwooleG.pagesize = getpagesize();SwooleG.pid = getpid();SwooleG.socket_buffer_size = SW_SOCKET_BUFFER_SIZE;#ifdef SW_DEBUGSwooleG.log_level = 0;
#elseSwooleG.log_level = SW_LOG_INFO;
#endif//get system unameuname(&SwooleG.uname);//random seedsrandom(time(NULL));//init global shared memorySwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1);if (SwooleG.memory_pool == NULL){printf("[Master] Fatal Error: global memory allocation failure.");exit(1);}SwooleGS = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(SwooleGS_t));if (SwooleGS == NULL){printf("[Master] Fatal Error: failed to allocate memory for SwooleGS.");exit(2);}//init global lockswMutex_create(&SwooleGS->lock, 1);swMutex_create(&SwooleGS->lock_2, 1);swMutex_create(&SwooleG.lock, 0);if (getrlimit(RLIMIT_NOFILE, &rlmt) < 0){swWarn("getrlimit() failed. Error: %s[%d]", strerror(errno), errno);SwooleG.max_sockets = 1024;}else{SwooleG.max_sockets = (uint32_t) rlmt.rlim_cur;}SwooleTG.buffer_stack = swString_new(8192);if (SwooleTG.buffer_stack == NULL){exit(3);}if (!SwooleG.task_tmpdir){SwooleG.task_tmpdir = sw_strndup(SW_TASK_TMP_FILE, sizeof(SW_TASK_TMP_FILE));SwooleG.task_tmpdir_len = sizeof(SW_TASK_TMP_FILE);}char *tmp_dir = swoole_dirname(SwooleG.task_tmpdir);//create tmp dirif (access(tmp_dir, R_OK) < 0 && swoole_mkdir_recursive(tmp_dir) < 0){swWarn("create task tmp dir(%s) failed.", tmp_dir);}if (tmp_dir){sw_free(tmp_dir);}//init signalfd
#ifdef HAVE_SIGNALFDswSignalfd_init();SwooleG.use_signalfd = 1;SwooleG.enable_signalfd = 1;
#endif//timerfd
#ifdef HAVE_TIMERFDSwooleG.use_timerfd = 1;
#endifSwooleG.use_timer_pipe = 1;
}

swServer_add_port 函數

  • swServer_add_port 函數為服務端添加監聽的端口
  • 首先需要檢測 listen_port_num 已監聽的端口不能大于 SW_MAX_LISTEN_PORT(默認為 60000)
  • 如果 socket 的類型不是 unix sock,那么端口號必須大于等于 0,小于 65535
  • host 主域名長度也不能大于 SW_HOST_MAXSIZE(104)
  • 然后從共享內存池中申請一個 swListenPort 類型的對象,然后調用 swPort_init 對端口對象進行初始化
  • 利用函數 swSocket_create 創建一個 socket 對象,并返回其文件描述符
  • 調用 swSocket_bind 函數將 socket 綁定到對應的主域與端口上來
  • 如果協議是數據報(UDP),而不是數據流時,需要設置 socket 的發送緩存與接收緩存為 socket_buffer_size
  • 設置 socket 為非阻塞、O_CLOEXEC(exec 之后文件描述符自動關閉)
  • 根據協議類型設置 have_udp_sockhave_tcp_sockudp_socket_ipv4/udp_socket_ipv6 等等屬性
  • 遞增 listen_port_num ,向單鏈表 listen_list 中添加 swListenPort 對象
enum swSocket_type
{SW_SOCK_TCP          =  1,SW_SOCK_UDP          =  2,SW_SOCK_TCP6         =  3,SW_SOCK_UDP6         =  4,SW_SOCK_UNIX_DGRAM   =  5,  //unix sock dgramSW_SOCK_UNIX_STREAM  =  6,  //unix sock stream
};swListenPort* swServer_add_port(swServer *serv, int type, char *host, int port)
{if (serv->listen_port_num >= SW_MAX_LISTEN_PORT){swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, "allows up to %d ports to listen", SW_MAX_LISTEN_PORT);return NULL;}if (!(type == SW_SOCK_UNIX_DGRAM || type == SW_SOCK_UNIX_STREAM) && (port < 0 || port > 65535)){swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_LISTEN_PORT, "invalid port [%d]", port);return NULL;}if (strlen(host) + 1  > SW_HOST_MAXSIZE){swoole_error_log(SW_LOG_ERROR, SW_ERROR_NAME_TOO_LONG, "address '%s' exceeds %d characters limit", host, SW_HOST_MAXSIZE - 1);return NULL;}swListenPort *ls = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swListenPort));if (ls == NULL){swError("alloc failed");return NULL;}swPort_init(ls);ls->type = type;ls->port = port;strncpy(ls->host, host, strlen(host) + 1);if (type & SW_SOCK_SSL){type = type & (~SW_SOCK_SSL);if (swSocket_is_stream(type)){ls->type = type;ls->ssl = 1;
#ifdef SW_USE_OPENSSLls->ssl_config.prefer_server_ciphers = 1;ls->ssl_config.session_tickets = 0;ls->ssl_config.stapling = 1;ls->ssl_config.stapling_verify = 1;ls->ssl_config.ciphers = sw_strdup(SW_SSL_CIPHER_LIST);ls->ssl_config.ecdh_curve = sw_strdup(SW_SSL_ECDH_CURVE);
#endif}}//create server socketint sock = swSocket_create(ls->type);if (sock < 0){swSysError("create socket failed.");return NULL;}//bind address and portif (swSocket_bind(sock, ls->type, ls->host, &ls->port) < 0){close(sock);return NULL;}//dgram socket, setting socket buffer sizeif (swSocket_is_dgram(ls->type)){setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &ls->socket_buffer_size, sizeof(int));setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &ls->socket_buffer_size, sizeof(int));}//O_NONBLOCK & O_CLOEXECswoole_fcntl_set_option(sock, 1, 1);ls->sock = sock;if (swSocket_is_dgram(ls->type)){serv->have_udp_sock = 1;serv->dgram_port_num++;if (ls->type == SW_SOCK_UDP){serv->udp_socket_ipv4 = sock;}else if (ls->type == SW_SOCK_UDP6){serv->udp_socket_ipv6 = sock;}}else{serv->have_tcp_sock = 1;}LL_APPEND(serv->listen_list, ls);serv->listen_port_num++;return ls;
}

swPort_init 函數

  • swPort_init 函數用于初始化 swListenPort 對象
  • backlogtcp_keepcounttcp_keepidle 等等都是相應 socket 的屬性
  • 在外網通信時,有些客戶端發送數據的速度較慢,每次只能發送一小段數據。這樣 onReceive 到的數據就不是一個完整的包。 還有些客戶端是逐字節發送數據的,如果每次回調 onReceive 會拖慢整個系統。Length_Check 和 EOF_Check 的使用。package_length_typepackage_eof 等等就是相關參數的具體參數。
#define SW_DATA_EOF                "\r\n\r\n"void swPort_init(swListenPort *port)
{port->sock = 0;port->ssl = 0;//listen backlogport->backlog = SW_BACKLOG;//tcp keepaliveport->tcp_keepcount = SW_TCP_KEEPCOUNT;port->tcp_keepinterval = SW_TCP_KEEPINTERVAL;port->tcp_keepidle = SW_TCP_KEEPIDLE;port->open_tcp_nopush = 1;port->protocol.package_length_type = 'N';port->protocol.package_length_size = 4;port->protocol.package_body_offset = 4;port->protocol.package_max_length = SW_BUFFER_INPUT_SIZE;port->socket_buffer_size = SwooleG.socket_buffer_size;char eof[] = SW_DATA_EOF;port->protocol.package_eof_len = sizeof(SW_DATA_EOF) - 1;memcpy(port->protocol.package_eof, eof, port->protocol.package_eof_len);
}
  • c:有符號、1字節
  • C:無符號、1字節
  • s :有符號、主機字節序、2字節
  • S:無符號、主機字節序、2字節
  • n:無符號、網絡字節序、2字節
  • N:無符號、網絡字節序、4字節
  • l:有符號、主機字節序、4字節(小寫L)
  • L:無符號、主機字節序、4字節(大寫L)
  • v:無符號、小端字節序、2字節
  • V:無符號、小端字節序、4字節

swSocket_create 創建 socket

swSocket_create 函數會根據 type 的類型來調用 socket 系統調用

int swSocket_create(int type)
{int _domain;int _type;switch (type){case SW_SOCK_TCP:_domain = PF_INET;_type = SOCK_STREAM;break;case SW_SOCK_TCP6:_domain = PF_INET6;_type = SOCK_STREAM;break;case SW_SOCK_UDP:_domain = PF_INET;_type = SOCK_DGRAM;break;case SW_SOCK_UDP6:_domain = PF_INET6;_type = SOCK_DGRAM;break;case SW_SOCK_UNIX_DGRAM:_domain = PF_UNIX;_type = SOCK_DGRAM;break;case SW_SOCK_UNIX_STREAM:_domain = PF_UNIX;_type = SOCK_STREAM;break;default:swWarn("unknown socket type [%d]", type);return SW_ERR;}return socket(_domain, _type, 0);
}

swSocket_bind 綁定端口

  • SO_REUSEADDR 允許啟動一個監聽服務器并捆綁眾所周知端口,即使以前建立的該端口用作它們的本地端口的連接仍存在。

    • 如果不對TCP的套接字選項進行任何限制時,如果啟動兩個進程,第二個進程就會在調用bind函數的時候出錯(Address already in use)。
    • 如果在調用bind之前我們設置了SO_REUSEADDR,但是不在第二個進程啟動前close這個套接字,那么第二個進程仍然會在調用bind函數的時候出錯(Address already in use)。
    • 如果在調用bind之前我們設置了SO_REUSEADDR,并接收了一個客戶端連接,并且在第二個進程啟動前關閉了bind的套接字,這個時候第一個進程只擁有一個套接字(與客戶端的連接),那么第二個進程則可以bind成功,符合預期。
  • 相對 SO_REUSEADDR 來說,SO_REUSEPORT 沒有那么多的限制條件,允許兩個毫無血緣關系的進程使用相同的 IP 地址同時監聽同一個端口,并且不會出現驚群效應
  • 對于 UNIX SOCKET,需要設置 sun_familysun_path
  • 對于 IPV4,需要設置 sin_familysin_portsin_addr;對于 IPV6,需要設置 sin6_familysin6_portsin6_addr,然后調用 bind 函數;
  • 如果 port 為0,說明服務器綁定的是任意端口,bind 函數會將系統所選擇的端口返回給 sockaddr 對象
int swSocket_bind(int sock, int type, char *host, int *port)
{int ret;struct sockaddr_in addr_in4;struct sockaddr_in6 addr_in6;struct sockaddr_un addr_un;socklen_t len;//SO_REUSEADDR optionint option = 1;if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int)) < 0){swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "setsockopt(%d, SO_REUSEADDR) failed.", sock);}//reuse port
#ifdef HAVE_REUSEPORTif (SwooleG.reuse_port){if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(int)) < 0){swSysError("setsockopt(SO_REUSEPORT) failed.");SwooleG.reuse_port = 0;}}
#endif//unix socketif (type == SW_SOCK_UNIX_DGRAM || type == SW_SOCK_UNIX_STREAM){bzero(&addr_un, sizeof(addr_un));unlink(host);addr_un.sun_family = AF_UNIX;strncpy(addr_un.sun_path, host, sizeof(addr_un.sun_path) - 1);ret = bind(sock, (struct sockaddr*) &addr_un, sizeof(addr_un));}//IPv6else if (type > SW_SOCK_UDP){bzero(&addr_in6, sizeof(addr_in6));inet_pton(AF_INET6, host, &(addr_in6.sin6_addr));addr_in6.sin6_port = htons(*port);addr_in6.sin6_family = AF_INET6;ret = bind(sock, (struct sockaddr *) &addr_in6, sizeof(addr_in6));if (ret == 0 && *port == 0){len = sizeof(addr_in6);if (getsockname(sock, (struct sockaddr *) &addr_in6, &len) != -1){*port = ntohs(addr_in6.sin6_port);}}}//IPv4else{bzero(&addr_in4, sizeof(addr_in4));inet_pton(AF_INET, host, &(addr_in4.sin_addr));addr_in4.sin_port = htons(*port);addr_in4.sin_family = AF_INET;ret = bind(sock, (struct sockaddr *) &addr_in4, sizeof(addr_in4));if (ret == 0 && *port == 0){len = sizeof(addr_in4);if (getsockname(sock, (struct sockaddr *) &addr_in4, &len) != -1){*port = ntohs(addr_in4.sin_port);}}}//bind failedif (ret < 0){swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "bind(%s:%d) failed. Error: %s [%d]", host, *port, strerror(errno), errno);return SW_ERR;}return ret;
}

swoole_fcntl_set_option 函數為文件描述符設置選項

  • 此函數主要是利用 fcntl 函數為文件描述符設置阻塞/非阻塞、CLOEXEC 等屬性。
void swoole_fcntl_set_option(int sock, int nonblock, int cloexec)
{int opts, ret;if (nonblock >= 0){do{opts = fcntl(sock, F_GETFL);}while (opts < 0 && errno == EINTR);if (opts < 0){swSysError("fcntl(%d, GETFL) failed.", sock);}if (nonblock){opts = opts | O_NONBLOCK;}else{opts = opts & ~O_NONBLOCK;}do{ret = fcntl(sock, F_SETFL, opts);}while (ret < 0 && errno == EINTR);if (ret < 0){swSysError("fcntl(%d, SETFL, opts) failed.", sock);}}#ifdef FD_CLOEXECif (cloexec >= 0){do{opts = fcntl(sock, F_GETFD);}while (opts < 0 && errno == EINTR);if (opts < 0){swSysError("fcntl(%d, GETFL) failed.", sock);}if (cloexec){opts = opts | FD_CLOEXEC;}else{opts = opts & ~FD_CLOEXEC;}do{ret = fcntl(sock, F_SETFD, opts);}while (ret < 0 && errno == EINTR);if (ret < 0){swSysError("fcntl(%d, SETFD, opts) failed.", sock);}}
#endif
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253811.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253811.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253811.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux下搭建git服務器

安裝 Git Linux 做為服務器端系統&#xff0c;Windows 作為客戶端系統&#xff0c;分別安裝 Git 服務器端&#xff1a; #yum install -y git 安裝完后&#xff0c;查看 Git 版本 [rootlocalhost ~]# git --version git version 1.7.1 客戶端&#xff1a; 下載 Git for Windows&…

mkcramfs 命令學習

mkcramfs :創建只讀文件系統 語 法 mkcramfs[必要參數][選擇參數][源目錄][目標文件]功 能mkcramfs 命令&#xff1a;用來創建CRAMFS只讀文件系統 類似命令: fdisk cramfsck mount 執行權限: 超級用戶 普通用戶 命令屬性: 磁盤維護 參數必要參數 -e 設置文件系…

對于Eclipse的正確用法

有時候我們剛剛修改了工程里的文件 但是啟動的時候它硬是說你有東西沒有聲明 而那個東西又明明在那里。。 這時候我們可以認為實際與它調用的工程關系文件&#xff08;我假想的&#xff09; 不同步。。 我們可以通過clean功能來同步實際情況和工程關系文件 所以說我們每次改了之…

邏輯綜合——工藝庫

一、庫文件的設置 運行DC時需要用到的庫文件有&#xff1a;目標庫&#xff08;target library&#xff09;、鏈接庫&#xff08;link library&#xff09;、符號庫&#xff08;symbol library&#xff09;、算術運算庫&#xff08;synthetic library&#xff09;。 1、目標庫…

weka 初練之 文本分類

0.注意weka的中文編碼RunWeka.ini-----》fileEncodingutf-81.首先對分詞后的 無新詞發現的分詞文件&#xff0c;轉換成arff文件 命令java weka.core.converters.TextDirectoryLoader -dir D:\weibo\catagory\data10W\nlpirSegment\noNI > D:\weibo\catagory\data10W\nlpirSe…

[COGS 0065][NOIP 2002] 字串變換

65. [NOIP2002] 字串變換 ★★ 輸入文件&#xff1a;string.in 輸出文件&#xff1a;string.out 簡單對比時間限制&#xff1a;1 s 內存限制&#xff1a;128 MB [問題描述] 已知有兩個字串A\$, B\$及一組字串變換的規則&#xff08;至多6個規則&#xff09;: A1\$ ->…

基與datatable的分頁

在進行分頁操作前&#xff0c;必須知道開啟服務器模式后會向服務器發送的參數的含義&#xff1a; length:告訴服務器每頁顯示的數據條數 start&#xff1a;第一條數據的起始位置 draw:繪制計數器&#xff0c;&#xff08;特殊&#xff1a;服務器接收到參數后&#xff0c;需要返…

linux sock_raw原始套接字編程

sock_raw原始套接字編程可以接收到本機網卡上的數據幀或者數據包,對與監聽網絡的流量和分析是很有作用的.一共可以有3種方式創建這種socket1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)發送接收ip數據包2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|E…

邏輯綜合——施加約束

Design Compiler時一個約束驅動&#xff08;constraint-driven&#xff09;的綜合工具&#xff0c;它的結果與設計者施加的約束條件密切相關。 一、面積約束 進行面積的約束&#xff0c;也就是告訴DC綜合的電路面積要在多少以內。在介紹約束命令之前&#xff0c;先了解一下面積…

[Codevs] 1004 四子連棋

1004 四子連棋 時間限制: 1 s空間限制: 128000 KB題目等級 : 黃金 Gold題目描述 Description在一個4*4的棋盤上擺放了14顆棋子&#xff0c;其中有7顆白色棋子&#xff0c;7顆黑色棋子&#xff0c;有兩個空白地帶&#xff0c;任何一顆黑白棋子都可以向上下左右四個方向移動到相鄰…

鏈接中獲取文件名

算得上是-test.pdf 獲取文件名 var str http://aaa.com/s/ddd/算得上是-test.pdf; console.log(str.match(/([^/*.])\.\w$/)) console.log(str.match(/([^/*.])\.\w$/)[0]) // 轉載于:https://www.cnblogs.com/cssfirefly/p/6163370.html

邏輯綜合——優化電路

對進行時序路徑、工作環境、設計規則等進行約束完成之后&#xff0c;DC就可以進行綜合、優化時序了&#xff0c;DC在優化過程中主要的策略將在下面進行說明。然而&#xff0c;當普通模式下不能進行優化的&#xff0c;就需要我們進行編寫腳本來改進DC的優化來達到時序要求。 DC…

DOM包裹wrap()方法

DOM包裹wrap()方法 如果要將元素用其他元素包裹起來&#xff0c;也就是給它增加一個父元素&#xff0c;針對這樣的處理&#xff0c;JQuery提供了一個wrap方法 .wrap( wrappingElement )&#xff1a;在集合中匹配的每個元素周圍包裹一個HTML結構 簡單的看一段代碼&#xff1a; &…

usleep函數

usleep功能把進程掛起一段時間&#xff0c; 單位是微秒&#xff08;百萬分之一秒&#xff09;&#xff1b; 頭文件&#xff1a; unistd.h 語法: void usleep(int micro_seconds); 返回值: 無 內容說明&#xff1a;本函數可暫時使程序停止執行。參數 micro_seconds 為要暫停的微…

限制Xamarin獲取圖片的大小

限制Xamarin獲取圖片的大小在App開發中&#xff0c;經常會使用網絡圖片。因為這樣不僅可以減少App的大小&#xff0c;還可以動態更新圖片。但是手機使用網絡環境千差萬別。當網絡環境不是理想的情況下&#xff0c;加載網絡圖片就是一個棘手的問題了。為了避免長時間加載圖片影響…

Linux應用開發自學之路

前言 在 「關于我 」那篇博文里&#xff0c;朋友們應該知道了我不是科班出身&#xff0c;是由機械強行轉行到Linux應用開發方向。下面我就詳細向大家介紹自己這一路上的轉行歷程&#xff0c;希望對大家有所啟發。 我是學機械專業的&#xff0c;對于機械專業我還是很感興趣&…

Verdi 基礎教程

一、Verdi 功能 查看設計debugVerdi不能自己產生波形 二、Verdi使用流程 1、Verdi環境配置 .bashrc中配置 export Verdi_HOME$Synopsys_Dir/Verdi2015 #export NOVAS_HOME$Synopsys_Dir/Verdi2015 export PATH$Verdi_HOME/bin:$PATH export LD_LIBRARY_PATH"/opt/Syno…

ida和idr機制分析(盤符分配機制)

內核ida和idr機制分析&#xff08;盤符分配機制&#xff09; ida和idr的機制在我個人看來&#xff0c;是內核管理整數資源的一種方法。在內核中&#xff0c;許多地方都用到了該結構&#xff08;例如class的id&#xff0c;disk的id&#xff09;&#xff0c;更直觀的說&#xff0…

MIPI CSI-2學習

CSI&#xff08;Camera Serial Interface&#xff09;定義了攝像頭外設與主機控制器之間的接口&#xff0c;旨在確定攝像頭與主機控制器在移動應用中的標準。 關鍵詞描述 縮寫解釋CCICamera Control Interface&#xff08;物理層組件&#xff0c;通常使用I2C或I3C進行通信&…

internet網絡 checksum校驗和計算方法

http://hi.baidu.com/%CE%C4%B3%AD%B9%AB/blog/item/7d9a4e08f82d72b32eddd4cb.html