Linux 服务器程序规范、服务器日志、用户、进程间的关系
文章目錄
- 服務器程序規范
- 日志
- rsyslogd 守護進程
- syslog函數
- openlog函數
- setlogmask函數
- closelog函數
- 用戶
- 進程間的關系
- 進程組
- 會話
- 系統資源限制
- 改變工作目錄和根目錄
- 服務器程序后臺化
服務器程序規范
日志
rsyslogd 守護進程
既能接受用戶進程輸出的日志,也能接受內核日志。
- 用戶進程調用 syslog函數 將日志輸出到一個 UNIX 本地域 socket 類型(AF_UNIX)的文件 /dev/log 中,rsyslogd 則監聽該文件以獲得用戶進程的輸出。
- 內核日志由 printk 等函數打印至內核的 環狀緩存(ring buffer) 中,環狀緩存的內容直接映射到 /proc/kmsg 中,rsyslogd 讀取該文件以獲得內核日志。
syslog函數
應用程序通過 syslog函數 與 rsyslogd 守護進程通信:
#include<syslog.h> void syslog( int priority, const char* message, ...); // 采用可變參數(message 和 第三個參數……)來結構化輸出 // priority:設施值與日志級別的按位或,設施值的默認值是 LOG_USER。限于 LOG_USER 設施值對應的日志級別有:
#include<syslog.h> #define LOG_EMERG 0 // 系統不可用 #define LOG_ALERT 1 // 報警,需要立即采取動作 #define LOG_CRIT 2 // 非常嚴重的狀況 #define LOG_ERR 3 // 錯誤 #define LOG_WARNING 4 // 警告 #define LOG_NOTICE 5 // 通知 #define LOG_INFO 6 // 信息 #define LOG_DEBUG 7 // 調試openlog函數
改變 syslog 的默認輸出方式,進一步結構化日志內容:
#include<syslog.h> void openlog( const char* ident, int logopt, int facility ); // ident指定的字符串被添加到日志消息的日期和時間之后,通常被設置為程序的名字 // logopt對 syslog 調用的行為進行配置,為下列值的按位或: #define LOG_PID 0x01 // 在日志消息中博阿寒程序 PID #define LOG_CONS 0x02 // 如果消息不能記錄到日志文件,則打印至終端 #define LOG_ODELAY 0X04 // 延遲打開日志功能知道第一次調用 syslog #define LOG_NDELAY 0x08 // 不延遲打開日志功能 // facility用來修改 syslog 函數中的默認設施值setlogmask函數
程序在開發階段可能需要輸出很多調試信息,而發布之后又需要將這些調試信息關閉。
解決這個問題的方法并非是在發布后刪除調試代碼(日后可能還需要用到),而有更簡單的做法——設置日志掩碼,使日志級別大于掩碼的日志信息被系統忽略。
setlogmask函數 就用以設置日志掩碼:
#include<syslog.h> int setlogmask( int maskpri ); // maskpri指定日志掩碼值 // 該函數始終會成功,返回調用進程舊的日志掩碼值closelog函數
用以關閉日志功能:
#include<syslog.h> void closelog();用戶
服務器中不同的用戶有不同的權限,因此 獲取or設置 當前進程的 真實用戶ID(UID)、有效用戶ID(EUID)、真實組ID(GID)、有效組(EGID) 就尤為重要:
#include <sys/types.h> #include <unistd.h> uid_t getuid(); uid_t geteuid(); gid_t getgid(); gid_t getegid(); int setuid( uid_t uid ); int seteuid( uid_t uid ); int setgid( gid_t gid ); int setegid( gid_t gid );一個進程有兩個 用戶ID: UID 和 EUID。EUID 方便了資源訪問:使得 運行程序的用戶 擁有 該程序的有效用戶 的權限。如:任何用戶都可以通過 su程序 修改自己的賬戶信息,這就不得不訪問需要 root 權限的 /etc/passwd 文件。那么以普通用戶身份啟動的 su程序 如何能訪問/etc/passwd 文件呢?
用 ls 命令可以查看到,su程序 的所有者是 root,且被設置了 set-user-id標志 ,即任何普通用戶運行 su程序 時,su程序 的有效用戶為 root。有效用戶為 root 的進程被稱為 特權進程(privileged processes)。
類似的,EGID 為運行目標程序的組用戶提供有效組的權限。
進程間的關系
進程組
Linux 下每個進程都隸屬于一個進程組,PGID 即為它的 進程組ID。進程組將一直存在,知道其中所有進程都退出或者加入到其他線程組。每個進程組都有一個首領進程,其 PGID 和 PID 相同。
#include< unistd.h> pid_t getpgid( pid_t pid ); // 成功返回 ID,失敗返回 -1 并設置 errno int setpgid( pid_t pid, pid_t pgid ); // 將 PID 為 pid 的進程的 PGID 設置為 pgid,若 pid 和 pgid 相等,則 pid 指定的進程將被設定為進程組首領 // 若 pid=0,則表示設置 當前進程 的 PGID 為 pgid // 若 pgid=0,則使用 pid 作為目標進程的 PGID // 成功時返回 0,失敗返回 -1 并設置 errno一個進程只能設置自己或者子進程的 PGID,子進程調用 exec 系列函數后,父進程不再能設置它的 PGID。
會話
一些有關聯的進程組能形成一個會話(session),下面的函數用以創建一個會話:
#include<unistd.h> pid_t setsid( void ); // 成功時返回新的進程組的 PGID,失敗則返回 -1 并設置 errno該函數不能由進程組的首領進程調用,否則產生一個錯誤。對于非組首領的進程,創建新會話的同時且有如下額外效果:
- 調用進程成為會話的首領,此時該進程是新會話的唯一成員。
- 新建一個進程組,其 PGID 就是調用進程的 PID,調用進程成為該組的首領。
- 調用進程將甩開終端(如果有的話)。
Linux 進程并未提供所謂 會話ID(SID) 的概念,但將會話首領所在的進程組的 PGID 視為 SID,并提供了如下函數來讀取 SID:
#include<unistd.h> pid_t getsid( pid_t pid );系統資源限制
Linux 上運行的程序都會受到資源限制的影響,比如物理設備限制(CPU數量、內存數量等)、系統策略限制(CPU時間等),以及具體實現的限制(文件名的最大長度)。這些系統資源限制可以通過下面的函數來讀取和設置:
#include <sys/resource.h> int getrlimit( int resource, struct rlimit* rlim ); int setrlimit( int resource, const struct rlimit* rlim ); // 成功時返回 0,失敗時返回 -1 并設置 errno。 struct rlimit{rlim_t rlim_cur; // 指定資源的軟限制,是一個建議性的,最好不要超越的限制,若超越限制,則系統可能向進程發送信號以終止其運行。rlim_t rlim_max; // 指定資源的硬限制,一般是軟限制的上限。普通程序可以減小硬限制,只有以 root 身份運行的程序才能增加硬限制。// rlim_t是一個整數類型,描述資源級別。 };除上述外:
- 還可以使用 ulimit 命令修改當前 shell 環境下的資源限制(軟限制或/和硬限制),這種修改將對該 shell 啟動的所有后續程序有效。
- 還可以通過修改配置文件來改變系統軟限制和硬限制,這種修改是永久的。
改變工作目錄和根目錄
#include<unistd.h> /* 獲取進程當前工作目錄 */ char* getcwd( char* buf, size_t size ); // buf指向的內存用于存儲進程當前工作目錄的絕對路徑名,大小由 size 參數指定 // 如果當前工作的絕對路徑長度(加上末尾的“\0”)超過了 size,則返回 NULL 并設置 errno 為 ERANGE。 // 若 buf 為 NULL 并且 size 非 0,則 getcwd 可能在內部使用 malloc 動態分配內存,并將進程的當前工作目錄存儲在其中, // 此時我們需要自己釋放 getcwd 在內部創建的這塊內存。 // 成功返回一個指向目標存儲區(buf指向的緩存區或者getcwd在內部動態創建的緩存區)的指針,失敗返回 NULL 并設置 errno。/* 改變進程的工作目錄 */ int chdir( const char* path ); // path 指定要切換到的目標目錄 // 成功返回 0,失敗返回 -1 并設置 errno/* 改變進程根目錄 */ int chroot(const char* path); // 參數和返回值同上,調用本函數后,仍需使用 chdir("/") 來將工作目錄切換至新的根目錄。 // 改變進程的根目錄后,程序可能無法訪問處于舊路徑的文件or目錄, // 不過可以利用進程已經打開的文件描述符來訪問調用 chroot 之后不能直接訪問的文件。 // 只有特權進程才能改變根目錄服務器程序后臺化
讓一個進程以守護進程的方式運行:
#include<unistd.h> int daemon(int nochdir, int noclose); // nochdir 用于指定是否改變工作目錄,為 0 則工作目錄被設置為“/”(根目錄),否則繼續使用當前目錄 // noclose 為 0 時,標準輸入、輸出、錯誤輸出都被重定向到 /dev/null 文件,否則依然使用原來的設備 // 成功返回 0,失敗返回 -1 并設置 errno總結
以上是生活随笔為你收集整理的Linux 服务器程序规范、服务器日志、用户、进程间的关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: simple_strtoul()分析
- 下一篇: linux天气软件,类似智能手机!Lin