title: linux/unix程式設計手冊-16_20 date: 2018-06-06 11:53:07 categories: programming tags: tips
linux/unix程式設計手冊-16(擴充套件屬性EA)
EA的名稱空間
- user EA 只適用於檔案或目錄 (ext2~4上建立時需要:
mount -o user_xattr device dir
)- trusted
- system
- security 略
linux/unix程式設計手冊-17(訪問控制表)
ACL
- 檔案系統支援ACL需要
mount -o acl
- 簡單來說ACL是對原有檔案系統許可權的擴充套件
- 原有檔案系統只針對owner,group和other, ACL支援對單獨使用者進行許可權設定
- ACL由一系列ACE組成,ACE由三部分組成
- 標記型別
- 標記限定符(可選,一般為使用者ID或組ID,下圖帶-表示不可選,同時只存在一條此型別)
- 許可權集合
- 示例
- 最小ACL:和普通檔案許可權集合一樣只包含ACL_XX_OBJ, ACL_OTHER
ACL檢查演算法
- 程式具有特權,執行許可權需要至少一條ACL匹配才有,其他同普通檔案許可權
- 程式的有效使用者ID(準確是檔案ID下面略)匹配檔案的屬主,授予ACL_USER_OBJ的ACE指定許可權
- 程式的有效使用者ID匹配了一條ACL_USER,授予許可權為ACL_MASK & ACE指定
- 程式組匹配類似(有效組ID和輔助組任意一匹配,只是匹配了ACL_GROUP_ACE後許可權需要& ACL_MASK)
- ACL_OTHER 授予的許可權
chmod之後ACL_MASK的作用及其他api呼叫 略太雜了之後有使用再記吧
linux/unix程式設計手冊-18(目錄和(硬)連結)
目錄和硬鏈
目錄的inode示例
- inode可能會有多個檔名(多個硬連結),無法通過描述符找到檔名
- inode僅在同一檔案系統中唯一,硬鏈得在同一檔案系統
- inode目錄無法硬鏈
軟連線(符號連結)
- _POSIX_SYMLOOP_MAX_ 解引用操作限制<limits.h>
- 軟鏈儲存的優化,路徑名小於60時可以直接存在存data指標的位置
- l開頭的系統呼叫往往操作的符號連結檔案本身(但是檔案的目錄如果是軟鏈,則都會解引用)
- 除了sticky設定的目錄下的軟鏈檔案(/tmp)進行刪除和重新命名的情況外,檔案的操作的許可權為解引用之後的檔案許可權。
- 硬鏈了一個軟鏈,linux不會解引用,MAC會。
檔案的刪除
即使所有指向這個inode的硬鏈都解除了,但是又程式持有改檔案的描述符(因為可以通過開啟檔案的表找到對應的inode),在關閉描述符之前,系統實際不會刪除改檔案
- tmpfile 的實現大致應該類似
f = open("tmp_file", O_CREATE|O_EXCL) err =unlink("tmp_file") 複製程式碼
#include<unistd.h>
int remove(const char *pathname);
// pathname為檔案呼叫unlink, 否則呼叫rmdir,均不解引用。
複製程式碼
目錄的操作略
程式目錄的操作略
linux/unix程式設計手冊-19(監控檔案事件)
inotify API
#include<sys/inotify.h>
// return inotify fd
int inotify_init(void);
// return wd; need read privellege on pathname
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
// 刪除fd裡面的wd
int inotify_rm_watch(int fd, uint32_t wd);
複製程式碼
-
fd,wd一對多
-
每次呼叫read 讀取fd ,如果不傳O_NONBLOCK會阻塞,傳參的話read會立刻失敗並報錯EAGAIN
struct inotify_event {
int wd;
uint32_t mask;
uint_32 cookie; // cookie for relate events(ex:rename())
uint32_t len; //sizeof name
char name[]; // 監控為目錄時返回檔名,為檔案返回空,len為0
}
複製程式碼
- 在事件佇列末尾追加一個新事件會和當前對尾比較,如果struct一致,則合併,忽略多次。
/proc/sys/fs/inotify/
儲存inotify的各種限制
- max_queue_events
- max_user_instances
- max_user_watches
linux/unix程式設計手冊-20(訊號:基本概念)
訊號是事件發生時對程式的通知機制:也叫軟體中斷
訊號通常源於核心
- 硬體發生異常:被0除,記憶體訪問錯誤等
- 使用者鍵入產生異常特殊字元:Ctrl+c,Ctrl+z
- 軟體事件:CPU時間超時等等
訊號的分類
- 標準訊號(核心像程式通知事件,通常是1-31)
- 實時訊號
程式對訊號的反饋
- 忽略訊號
- 殺死程式(非呼叫exit終止)
- 產生核心轉儲檔案並終止
- 停止程式
- 恢復停止
- 採取預設(用於恢復對預設的修改)
- 忽略預設終止訊號
- 執行編寫的訊號處理程式(如Ctrl+c, 無法直接設定處理程式殺死程式或產生核心轉儲檔案,可以通過間接呼叫abort來產生新的訊號)
訊號型別和預設行為
SIGABRT
: 呼叫abort()
會產生,會殺死程式併產生核心轉儲檔案供除錯SIGALARM
: 呼叫alarm()
或setitimer()
設定定時器到時間,核心會產生該訊號SIGBUS
: 記憶體訪問錯誤SICHLD|SIGCLD
: 子程式終止時發給父程式SIGCONT
: 恢復停止程式- .......
SIGTERM
:標準殺死程式訊號kill|killall
會發起SIGKILL
: 必殺訊號,處理程式無法將其阻塞,忽略或 捕獲(測試下);kill -9|kill -KILL
會呼叫
改變訊號
#include<signal.h>
void ( *signal(int sig, void (*handler)(int))) (int);
//成功返回之前的呼叫函式地址或者SIG_DFL(設為預設值)或SIF_IGN(忽略),失敗時返回SIG_ERR
//正常開發不要使用signal,相容性不好
複製程式碼
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
static void sigHandler(int sig){
printf("Ouch\n");
}
int main(void){
if (signal(SIGINT, sigHandler)==SIG_ERR){
printf("error");
exit(-1);
}
for (;;){
printf("*****************************");
sleep(3);
}
}
複製程式碼
傳送訊號
#include<signal.h>
int kill(pid_t pid, int sig);
複製程式碼
- pid>0, 傳送給pid程式
- pid=0, 傳送給同組所有程式
- pid<-1, 傳送給pid絕對值程式組內所有程式
- pid=-1 傳送給除了init程式和自身之外的所有有權程式
傳送的許可權
- 特權程式可以發給所有
- root使用者和組執行的init程式,僅接受安裝了處理器函式的訊號
- 非特權程式
- SIGCONT訊號忽視使用者ID檢測,非特權程式可以向會話內任意程式傳送這一訊號
sig=0,僅報告程式是否存在
其他傳送訊號呼叫
#include<signal.h>
int raise(int sig);
//單執行緒下等於
kill(getpid(), sig)
//支援執行緒系統下
pthread_kill(pthread_self(),sig)
int killgp(pid_t pgrp, int sig);
//等同於
kill(-pgrp, sig)
複製程式碼
訊號集
#include<signal.h>
// 初始化空的set
int sigemptyset(sigset_t *set);
// 初始化包含所有訊號(包括實時訊號)的set
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
int sigismember(const sigset_t *set, int sig);
複製程式碼
#define _GNU_SOURCE
#include<signal.h>
int sigandset(sigset_t *dest, sigset_t *left, sigset_t *right);
int sigorset(sigset_t *dest, sigset_t *left, sigset_t *right);
int sigisemptyset(const sigset_t *set);
複製程式碼
訊號掩碼
核心會為每個程式維護一個訊號掩碼,即一組訊號,並阻塞這些訊號對程式的傳遞,一直延遲直至解除(每個執行緒可單獨控制)
#include<signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *old_set);
// how=SIG_BLOCK, 當前程式和set取並集
// how=SIG_UNBLOCK, 移除set內訊號
// how=SIG_SETMASK, 賦值為set內訊號
// oldset不為空則指向之前的訊號掩碼
// set為空不做改動
複製程式碼
#include<signal.h>
#include<stdlib.h>
int main(void){
sigset_t blockSet, prevMask;
sigemptyset(&blockSet);
sigaddset(&blockSet, SIGINT);
if (sigprocmask(SIG_BLOCK, &blockSet, &prevMask)==-1){
exit(-1);
}
if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1){
exit(-1);
}
retunn 0;
}
複製程式碼
獲取等待訊號
#include<signal.h>
int sigpending(sigset_t *set);
// 標準訊號同一訊號只會記錄一次,實時訊號之後會有排隊處理見22章
複製程式碼
改變訊號二
#include<signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
//act 新處置的資料結構,為NULL則忽略,oldact置位將當前處置
struct sigaction{
void (*sa_handler)(int); // 呼叫函式地址或者SIG_DFL,SIF_IGN, 為函式地址時sa_mask和sa_flags才有效
sigset_t sa_mask; //呼叫函式時可額外阻塞一組訊號
int sa_flags; //略....太多標記了
void (*sa_restorer)(void); //系統內部使用,供恢復上下文
}
複製程式碼
- 執行處理器程式時,同一訊號第二次到達,如果沒有設為阻塞會忽略,設為阻塞的話會保留一次
#include<unistd.h>
int pause(void);
// 程式一直停止直到有處理函式的訊號,或者一個未處理訊號終止程式
複製程式碼