Linux系統程式設計入門

wenmingdajin發表於2020-12-24

1.檔案IO

  • 標準C庫IO函式:標準C庫IO函式可跨平臺,帶有緩衝區,降低與磁碟的互動次數,提高了效率。在進行磁碟讀寫時,應使用標準C庫的IO函式,但在進行網路通訊時,我們更希望立即把資料發出去,所以使用沒有緩衝區的LiunxIO函式更好。
    在這裡插入圖片描述
  • 標準C庫IO與Linux系統IO
    在這裡插入圖片描述

2.虛擬地址空間

虛擬地址空間通過MMU轉換為實際的實體記憶體空間。
在這裡插入圖片描述

3.檔案描述符

在這裡插入圖片描述

4.open開啟檔案

//開啟一個已經存在檔案
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);、
	引數:
		- pathname:要開啟的檔案路徑
		- flags:對檔案的操作許可權設定以及其它設定
		  - 必選項:O_RDONLY,O_WRONLY or O_RDWR,這三個設定互斥
          - 可選項:O_CREAT等等
	返回值:返回一個新的檔案描述符,如果呼叫失敗,會返回-1
errno:屬於Linux系統函式庫,庫裡面的一個全域性變數,記錄的是最近的錯誤號
#include <stdio.h>
void perror(const char *s);
	s引數:使用者描述
//測試程式碼
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
    int fd=open("a.txt",O_RDONLY);
    if(fd==-1){
        perror("error from open");
    }
    close(fd);
    return 0;
}

在這裡插入圖片描述

5.open建立新檔案

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
	引數:
	    - pathname:要開啟的檔案路徑
	    - flags:對檔案的操作許可權設定以及其它設定,32位,每一位都是一個標誌位
	        - 必選項:O_RDONLY,O_WRONLY or O_RDWR,這三個設定互斥
	        - 可選項:O_CREAT等等
	    - mode:八進位制數,表示使用者對建立出的新的檔案的操作許可權,比如0775
              函式執行後最終的許可權是:mode & ~unmask
              例如:0777 & ~(0002)111111111 & 111111101
              umask的作用就是抹去某些許可權,使許可權更加合理
注意:-rwxrwxr-x,第1位表示檔案型別,後3位表示當前使用者對該檔案的許可權,
再後3位表示當前使用者所在組對該檔案的許可權,最後3位表示其它組使用者對該檔案的許可權
//示例程式碼
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
    int fd=open("create.txt",O_RDWR|O_CREAT,0777);
    if(fd==-1){
        perror("error from open");
    }
    close(fd);
    return 0;
}

6.read、write函式

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
    引數:
        - fd:檔案描述符,open得到,通過這個檔案描述符操作某個檔案
        - buf:儲存讀取的資料,陣列的地址(傳出引數)
        - count:指定的陣列的大小,以位元組為單位
    返回值:
        - 成功:大於0,實際讀取的位元組數;等於0,讀取完畢
        - 失敗:-1,並設定errno

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
    引數:
        - fd:檔案描述符,open得到,通過這個檔案描述符操作某個檔案
        - buf:需要往磁碟寫入的資料
        - count:要寫的資料的實際的大小,以位元組為單位
    返回值:
        成功:返回實際寫入的位元組數
        失敗:返回-1,並設定errno
//示例程式碼
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
    // 1.通過open開啟english.txt檔案
    int srcfd=open("english.txt",O_RDONLY);
    if(srcfd==-1){
        perror("open src");
        return -1;
    }
    // 2.建立一個新的檔案(拷貝檔案)
    int destfd=open("cpy.txt",O_WRONLY | O_CREAT,0664);
    if(destfd==-1){
        perror("open dest");
        return -1;
    }
    // 3.頻繁的讀寫操作
    char buf[1024]={0};
    int len=0;
    while((len=read(srcfd,buf,sizeof(buf)))>0){
        write(destfd,buf,len);
    }
    // 4.關閉檔案
    close(destfd);
    close(srcfd);
    return 0;
}

7.lseek函式

標準C庫中的
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

Linux系統函式
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
    引數:
        - fd:檔案描述符,通過open得到
        - offset:偏移量
        - whence:
            SEEK_SET:設定檔案指標的偏移量
            SEEK_CUR:設定偏移量,當前位置+offset
            SEEK_END:設定偏移量,檔案大小+offset
    返回值:返回檔案指標最終所在的位置
作用:
    1.移動檔案指標到檔案頭部
        lseek(fd,0,SEEK_SET);
    2.獲取當前檔案指標的位置
        lseek(fd,0,SEEK_CUR);
    3.獲取檔案長度
        lseek(fd,0,SEEK_END);
    4.擴充檔案的長度,當前檔案10B,擴充為110B
        lseek(fd,100,SEEK_END);
        需要寫一次資料才會管用
//示例程式
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
    int fd=open("hello.txt",O_RDWR);
    if(fd==-1){
        perror("open");
        return -1;
    }

    int ret=lseek(fd,100,SEEK_END);
    if(ret==-1){
        perror("lseek");
        return -1;
    }
    write(fd," ",1);
    close(fd);
    return 0;
}

8.stat函式和lstat函式

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
    作用:獲取一個檔案的相關資訊
    引數:
        - pathname:需要操作的檔案的路徑
        - statbuf:結構體變數,傳出引數,用於儲存獲取到的檔案的資訊
    返回值:成功返回0,失敗返回-1並設定errno

stat結構體具體構成
在這裡插入圖片描述
注意結構體中的st_mode變數
在這裡插入圖片描述

//示例程式
int main(){
    struct stat statbuf;
    int ret=stat("a.txt",&statbuf);
    if(ret==-1){
        perror("stat");
        return -1;
    }
    printf("size: %ld\n",statbuf.st_size);
    return 0;
}

lstat函式的用法和stat函式幾乎相同,區別在於,lstat函式獲取的是軟連結檔案資訊。
使用下列命令建立一個軟連結檔案b.txt。

$ ln -s a.txt b.txt

在這裡插入圖片描述
這時,如果使用stat函式作用於b.txt,將得到a.txt的檔案資訊,如果使用lstat函式作用於b.txt,將得到b.txt這個軟連結本身的資訊。

9.模擬實現ls -l指令

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>

// 模擬實現 ls -l 指令
// -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt
int main(int argc, char * argv[]) {

    // 判斷輸入的引數是否正確
    if(argc < 2) {
        printf("%s filename\n", argv[0]);
        return -1;
    }

    // 通過stat函式獲取使用者傳入的檔案的資訊
    struct stat st;
    int ret = stat(argv[1], &st);
    if(ret == -1) {
        perror("stat");
        return -1;
    }

    // 獲取檔案型別和檔案許可權
    char perms[11] = {0};   // 用於儲存檔案型別和檔案許可權的字串

    switch(st.st_mode & S_IFMT) {
        case S_IFLNK:
            perms[0] = 'l';
            break;
        case S_IFDIR:
            perms[0] = 'd';
            break;
        case S_IFREG:
            perms[0] = '-';
            break; 
        case S_IFBLK:
            perms[0] = 'b';
            break; 
        case S_IFCHR:
            perms[0] = 'c';
            break; 
        case S_IFSOCK:
            perms[0] = 's';
            break;
        case S_IFIFO:
            perms[0] = 'p';
            break;
        default:
            perms[0] = '?';
            break;
    }

    // 判斷檔案的訪問許可權

    // 檔案所有者
    perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
    perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
    perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';

    // 檔案所在組
    perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
    perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
    perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';

    // 其他人
    perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
    perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
    perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';

    // 硬連線數
    int linkNum = st.st_nlink;

    // 檔案所有者
    char * fileUser = getpwuid(st.st_uid)->pw_name;

    // 檔案所在組
    char * fileGrp = getgrgid(st.st_gid)->gr_name;

    // 檔案大小
    long int fileSize = st.st_size;

    // 獲取修改的時間
    char * time = ctime(&st.st_mtime);

    char mtime[512] = {0};
    strncpy(mtime, time, strlen(time) - 1);

    char buf[1024];
    sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);

    printf("%s\n", buf);

    return 0;
}

10.access函式

#include <unistd.h>
int access(const char *pathname, int mode);
    作用:判斷某個檔案是否有某個許可權,或者判斷檔案是否存在
    引數:
        - pathname:檔案路徑
        - mod:
			R_OK(判斷檔案讀許可權)
			W_OK(寫許可權)
			X_OK(執行許可權)
			F_OK(存在)
    返回值:成功返回0,失敗返回-1
//示例程式
#include <unistd.h>
#include <stdio.h>

int main(){
    int ret=access("a.txt",F_OK);
    if(ret==-1){
        perror("access");
        return -1;
    }
    printf("檔案存在\n");
    return 0;
}

11.chmod函式

#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
    修改檔案許可權
    引數:
        - pathname:檔案路徑
        - mode:巨集值或八進位制的數
    返回值:成功返回0,失敗返回-1
//示例程式
#include <sys/stat.h>
#include <stdio.h>

int main(){
    int ret=chmod("a.txt",0777);
    if(ret==-1){
        perror("chmod");
        return -1;
    }
    return 0;
}

12.chown函式

#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);

命令列檢視使用者id和使用者組的方式
1.$ id 使用者名稱
2.$ vim /etc/password
3.$ vim /etc/group

13.truncate函式

#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
    作用:縮減或者擴充套件檔案的尺寸至指定大小
    引數:
        - path:檔案路徑
        - length:需要最終檔案變成的大小
    返回值:成功返回0,失敗返回-1
//示例程式
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(){
    int ret=truncate("b.txt",20);
    if(ret==-1){
        perror("truncate");
        return -1;
    }
    return 0;
}

14.目錄操作函式

小筆記:對於一個目錄,當前使用者必須具有對其的可執行許可權,才能進入該目錄。

mkdir函式

#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
    作用:建立一個目錄
    引數:
        - pathname:建立的目錄的路徑
        - mode:許可權,巨集值或八進位制數
    返回值:成功返回0,失敗返回-1
//示例程式
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>

int main(){
    int ret=mkdir("aaa",0777);
    if(ret==-1){
        perror("mkdir");
        return -1;
    }
    return 0;
}

rmdir函式

//只有當目錄中沒有檔案時,才能刪除該目錄
#include <unistd.h>
int rmdir(const char *pathname);

rename函式

#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
//示例程式
#include <stdio.h>

int main(){
    int ret=rename("aaa","bbb");
    if(ret==-1){
        perror("rename");
        return -1;
    }
    return 0;
}

chdir函式與getcwd函式

#include <unistd.h>
int chdir(const char *path);
    作用:修改程式的工作目錄
    引數:
        -path:將當前工作路徑修改至path路徑下

#include <unistd.h>
char *getcwd(char *buf, size_t size);
    作用:獲取當前工作目錄
    引數:
        - buf:儲存的路徑,指向一個陣列(傳出引數)
        - size:陣列的大小
    返回值:返回一個指標,指向的就是傳進去的那個陣列
//示例程式
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main(){
    //獲取當前工作目錄
    char buf[128];
    getcwd(buf,sizeof(buf));
    printf("當前工作目錄是:%s\n",buf);

    //修改工作目錄
    int ret=chdir("/home/nowcoder/Linux/lesson13");
    if(ret==-1){
        perror("chdir");
        return -1;
    }

    //建立一個新的檔案
    int fd=open("chdir.txt",O_CREAT | O_RDWR,0664);
    if(fd==-1){
        perror("open");
        return -1;
    }
    close(fd);

    //獲取當前工作目錄
    char buf1[128];
    getcwd(buf1,sizeof(buf1));
    printf("當前工作目錄是:%s\n",buf1);

    return 0;
}

15.目錄遍歷函式

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
    作用:開啟目錄
    引數:
        - name:需要開啟的目錄的名稱
    返回值:成功返回DIR*型別,理解為目錄流資訊;失敗返回NULL

#include <dirent.h>
struct dirent *readdir(DIR *dirp);
    作用:讀取目錄中的資料
    引數:
        - dirp:是opendir返回的結果
    返回值:struct dirent *,代表讀取到的檔案的資訊;讀取到末尾或失敗時,返回NULL

#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);

在這裡插入圖片描述

//示例程式碼,統計某一目錄下所有普通檔案的個數
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getFileNum(const char* path);

int main(int argc,char * argv[]){
    if(argc<2){
        printf("%s path\n",argv[0]);
        return -1;
    }
    int num=getFileNum(argv[1]);
    printf("普通檔案的個數為:%d\n",num);
    return 0;
}

//用於獲取目錄下所有普通檔案的個數
int getFileNum(const char* path){
    //開啟目錄
    DIR* dir=opendir(path);
    if(dir==NULL){
        perror("opendir");
        exit(0);
    }
    //記錄個數
    int total=0;

    struct dirent *ptr;
    while((ptr=readdir(dir))!=NULL){
        //獲取名稱
        char* dname=ptr->d_name;
        //忽略掉.和..
        if(strcmp(dname,".")==0 || strcmp(dname,"..")==0){
            continue;
        }
        //判斷是否是普通檔案
        if(ptr->d_type==DT_DIR){
            //是目錄,需要繼續讀取這個目錄
            char newpath[256];
            sprintf(newpath,"%s/%s",path,dname);
            total+=getFileNum(newpath);
        }
        if(ptr->d_type==DT_REG){
            //普通檔案
            total++;
        }
    }
    //關閉目錄
    closedir(dir);
    return total;
}

16.dup與dup2

dup函式

#include <unistd.h>
int dup(int oldfd);
    作用:The  dup()  system call creates a copy of the 
        file descriptor oldfd, using the lowest-numbered 
        unused file descriptor for the new descriptor.
    返回值:成功返回新的檔案描述符,失敗返回-1
//示例程式
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

int main(){
    int fd=open("a.txt",O_RDWR | O_CREAT,0664);
    int fd1=dup(fd);
    if(fd1==-1){
        perror("dup");
        return -1;
    }
    printf("fd : %d , fd1 : %d\n",fd,fd1);
    close(fd);
    char * str="hello,world";
    int ret=write(fd1,str,strlen(str));
    if(ret==-1){
        perror("write");
        return -1;
    }
    close(fd1);
    return 0;
}

dup2函式

#include <unistd.h>
int dup2(int oldfd, int newfd);
    作用:重定向檔案描述符
        例如:oldfd指向a.txt,newfd指向b.txt
        呼叫函式成功後:newfd對b.txt做close,newfd指向了a.txt
        oldfd必須是一個有效的檔案描述符
        若oldfd和newfd值相同,相當於什麼都沒有做
    返回值:與newfd值相同
//示例程式
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main(){

    int fd=open("1.txt",O_RDWR | O_CREAT,0664);
    if(fd==-1){
        perror("open");
        return -1;
    }

    int fd1=open("2.txt",O_RDWR | O_CREAT,0664);
    if(fd1==-1){
        perror("open");
        return -1;
    }

    printf("fd : %d , fd1 : %d\n",fd,fd1);

    int fd2=dup2(fd,fd1);
    if(fd2==-1){
        perror("dup2");
        return -1;
    }

    //通過fd1去寫資料,實際操作的是1.txt,而不是2.txt
    char * str="hello, dup2";
    int len=write(fd1,str,strlen(str));
    if(len==-1){
        perror("write");
        return -1;
    }

    printf("fd : %d , fd1 : %d, fd2 : %d\n",fd,fd1,fd2);
    close(fd);
    close(fd1);
    return 0;
}

17.fcntl函式

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... );
    引數:
        - fd:表示需要操作的檔案描述符
        - cmd:表示對檔案描述符需要進行何種操作
            1.F_DUPFD:複製檔案描述符,得到新的檔案描述符
            2.F_GETFL:獲取指定的檔案描述符檔案狀態flag(open()函式中的那些flag)
            3.F_SETFL:設定檔案描述符檔案狀態flag
                - O_RDONLY,O_WRONLY,O_RDWR等等,不可以被修改
                - O_APPEND(追加資料),O_NONBLOCK(非阻塞)等等,可以被修改
            4.等等
//示例程式
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main(){
    /*複製檔案描述符
    int fd=open("1.txt",O_RDONLY);
    int ret=fcntl(fd,F_DUPFD);
    */

    int fd=open("1.txt",O_RDWR);
    if(fd==-1){
        perror("open");
        return -1;
    }
    //獲取檔案描述符檔案狀態的flag
    int flag=fcntl(fd,F_GETFL);
    if(flag==-1){
        perror ("fcntl");
        return -1;
    }
    flag |= O_APPEND;

    //修改檔案描述符檔案狀態的flag,給flag加入O_APPEND這個標記
    int ret=fcntl(fd,F_SETFL,flag);
    if(ret==-1){
        perror ("fcntl");
        return -1;
    }
    char * str="大家好啊";
    write(fd,str,strlen(str));
    close(fd);
    return 0;
}

相關文章