linux/unix程式設計手冊-16_20

weixin_33830216發表於2018-06-13

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);

// 程式一直停止直到有處理函式的訊號,或者一個未處理訊號終止程式
複製程式碼

相關文章