flock分析與文件權(quán)限問題

real uid, effective uid

So, the real user id is who you really are (the one who owns the process), and the effective user id is what the operating system looks at to make a decision whether or not you are allowed to do something (most of the time, there are some exceptions).

貼一個(gè)試驗(yàn)代碼, 子進(jìn)程直接獲取鎖, 若獲取不到則輸出錯(cuò)誤; 父進(jìn)程睡3秒后退出.

  • 如果該文件是自己創(chuàng)建的, 無法獲取鎖, 且錯(cuò)誤為Resource temporarily unavailable.
  • 如果該文件是進(jìn)程創(chuàng)建的, 可以獲取到鎖.
  • 在本例中, 我們讓aa.txt由進(jìn)程創(chuàng)建, 讓a.txt由用戶創(chuàng)建. 則前者可以獲取鎖, 后者不能, 本文將分析原因.
#include<unistd.h>
#include<stdio.h>
#include<sys/file.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <errno.h>
#include<string.h>


extern int errno ;

int main() {
  int fd = open("a.txt", O_CREAT| O_RDWR);
  pid_t ret = fork();

  if (ret == 0) {
    // child lock ex.
    printf("child try get ex lock.\n");
    uid_t uid = getuid();
    uid_t euid = geteuid();
    printf("%d %d\n", uid, euid);

    if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
        printf("lock ex failed.\n");
    } else {
        printf("child get ex lock.\n");
    }
    int err = errno;
    printf("error no. %d\n",  err);
    printf("err msg: %s.\n",  strerror( err ));
    // fprintf(stdout, "Error opening file: %s\n", strerror( errno ));
    
    while (1) {
        printf("child sleep 10s.\n");
        sleep(10);
    }
    return 0;  // 直接退出, 此時(shí)父進(jìn)程仍持有鎖.
  } else {
    // parent lock shared.
    sleep(3);
    return 0;
    // if (flock(fd, LOCK_SH) != 0) {
    //     printf("lock sh failed.\n");
    //     return 0;
    // }

    // printf("parent get shared lock.");
    // fflush(stdout);
    // while(1) {
    //     printf("parent sleep 10s\n");
    //     sleep(20);
    // }
  }

}

輸出為:

child try get ex lock.
1000 1000
lock ex failed.
error no. 11
err msg: Resource temporarily unavailable.
child sleep 10s.

查看可知進(jìn)程用戶是自己:

[~/codes]$ id rasak
uid=1000(rasak) gid=1000(rasak) 組=1000(rasak),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),106(input),113(lpadmin),128(sambashare),999(docker)

權(quán)限問題?

我一度懷疑是權(quán)限問題, 導(dǎo)致無法獲取鎖, 于是查閱了不少關(guān)于權(quán)限的資料.
查看文件權(quán)限, 值得在意的是s, 和T. 查閱文獻(xiàn)得知s是指setuid, T指sticky bit:
Real, Effective and Saved UserID in Linux 詳細(xì)講了三者的作用.

setuid
當(dāng)執(zhí)行該文件時(shí), 執(zhí)行者會(huì)擁有root權(quán)限. 如果讓該文件能被所有用戶執(zhí)行, 就可以讓所有用戶以root身份去執(zhí)行該文件的指令. 比如sudo:

root@host [~]# ls -l /usr/bin/sudo
-rwsr-xr-x 1 root root 136808 Jan 31 13:37 /usr/bin/sudo

它的執(zhí)行內(nèi)容需要root權(quán)限, 但它能被所有人執(zhí)行.

setuid是否有權(quán)限:

If uid is the same as the real UID or the saved set-user-ID of the process, setuid() always succeeds and sets the effective UID. the real user ID and saved set-user-ID will remain unchanged.

Sticky BIT 權(quán)限: 總結(jié)一句話作用, 就是在文件上設(shè)置, 防止被文件夾寫權(quán)限者誤刪.

---srwx--T  1 rasak rasak     0 2月  25 11:28 aa.txt
-rw-rw-r--  1 rasak rasak     8 2月  24 05:22 a.txt

What does directory permission 'S' mean? (not lower case, but in upper case), 大寫S代表setgid被設(shè)置, 小寫s代表它還有組執(zhí)行權(quán)限(也就是S+x). 應(yīng)該t也同理.

'S' = The directory's setgid bit is set, but the execute bit isn't set.

's' = The directory's setgid bit is set, and the execute bit is set.

chmod用法總結(jié)了一些常用做法, 這里也記錄以下:
所有人都獲取讀權(quán)限, 此處a代表所有人, r代表讀權(quán)限:

chmod a+r file1.txt

效果等于如下, 其中u代表文件擁有者, g代表同群組, o代表其它群組的人:

chmod ugo+r file1.txt

將檔案file1.txt和file2.txt設(shè)為該檔案擁有者, 與其所屬同一個(gè)群體者可寫入, 帶其它人不可寫入:

chmod ug+w,o-w file1.txt file2.txt

進(jìn)程問題

其實(shí)原因是父進(jìn)程退出時(shí), 沒有發(fā)送信號(hào)給子進(jìn)程讓其終止, 導(dǎo)致后者成為了孤兒進(jìn)程.

批量查找刪除進(jìn)程可用如下命令(另見xargs命令):

ps aux  |  grep -i process_name_to_kill  |  awk '{print $2}'  |  xargs sudo kill -9

另一種可參照Linux下批量殺掉篩選進(jìn)程

ps -ef| grep override |grep -v grep |awk '{print $2}' | xargs kill -9

問題總結(jié)

與文件的執(zhí)行權(quán)限并無關(guān)系, 之所以無法獲取鎖, 只是因?yàn)楦高M(jìn)程退出后, 沒有子進(jìn)程變?yōu)楣聝哼M(jìn)程, 且沒有退出.

實(shí)驗(yàn)

子進(jìn)程獲取ex鎖后, 父進(jìn)程獲取sh鎖會(huì)成功, 并覆蓋為sh鎖.
此時(shí)啟動(dòng)另一個(gè)進(jìn)程嘗試獲取sh鎖會(huì)成功.
override_flock.c

#include<unistd.h>
#include<stdio.h>
#include<sys/file.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <errno.h>
#include<string.h>


extern int errno ;

int main() {
  int fd = open("a.txt", O_CREAT| O_RDWR);
  pid_t ret = fork();

  if (ret == 0) {
    // child lock ex.
    printf("child try get ex lock.\n");
    if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
        printf("lock ex failed.\n");
    } else {
        printf("child get ex lock.\n");
    }
    printf("child sleep 10s.\n");
    sleep(15);
    printf("child exit.\n");
  } else {
    // parent lock shared.
    sleep(1);
    if (flock(fd, LOCK_SH) != 0) {
        printf("lock sh failed.\n");
        return 0;
    } else {
        printf("parent get shared lock.\n");
    }
    printf("parent sleep 10s\n");
    sleep(15);
  }

}

override_flock_wait.c

#include<unistd.h>
#include<stdio.h>
#include<sys/file.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<malloc.h>

int main() {
  printf("wait_process start.\n");
  int fd = open("a.txt", O_RDONLY);
  char *buf = (char *)malloc(sizeof(char) * 3);
  flock(fd, LOCK_SH);
  int ret =  read(fd, buf, 5);
  if (ret > 0) {
    printf("read success\n");
    printf(buf);
  } else {
    printf("read failed.\n");
  }


}

實(shí)驗(yàn)結(jié)論
flock的鎖視為持有人是open file description, 當(dāng)fork后持有相同open file description的進(jìn)程先后調(diào)用flock, 則都會(huì)成功, 且視為覆蓋鎖形式.
也就是說, 父進(jìn)程獲取ex鎖后, 子進(jìn)程獲取sh鎖, 則視為sh鎖.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容