Linux系统编程(2)

手动文件锁定

#include 
void flockfile(FILE* stream);

void funlockfile(FILE* stream);

//非阻塞函数
int ftrylockfile(FILE* stream);

不会锁定流的操作

#define _GNU_SOURCE
#include 

int fgetc_unlocked(FILE* stream);
char *fgets_unlocked(char*str, int size, FILE* stream);
size_t fread_unlocked(void* buf, size_t size, size_t nr, FILE* stream);
int fputc_unlocked(int c, FILE* stream);
int fputs_unlocked(const char* str, FILE* stream);
size_t fwrite_unlocked(void * buf, size_t size, size_t nr, FILE* stream);

int fflush_unlocked(FILE* stream);
int feof_unlocked(FILE* stream);
int ferror_unlocked(FILE* stream);
int fileno_unlocked(FILE* stream);
void clearerr_unlocked(FILE* stream);

标准IO最大的问题在于性能受到两次复制的影响,读取数据时,标准IO会对内核进行read()系统调用,从内核复制数据到标准IO缓冲区,如果有一个应用程序接着通过标准IO送出一个读取请求,则数据会被再复制一次,会从标准IO缓冲区复制到所提供的缓冲区中,写入请求则逆向运作,数据从提供的缓冲区被复制到标准IO缓冲区,然后通过write()系统调用从标准IO缓冲区被复制到内核。

readv()和writev()

#include 
ssize_t readv(int fd, const struct iovec *iov, int count);
ssize_t writev(int fd, const struct iovec* iov, int count);

struct iovec {
    void* iov_base;
    size_t iov_len;
};

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    struct iovec iov[3];
    ssize_t nr;
    int fd, i;

    char* buf[] = {"The term buccaneer comes from the word boucan.\n", 
                   "A boucan is a wooden frame used for cooking mear.\n",
                   "Buccaneer is the West Indies name for a pirate.\n"};

    fd = open("buccaneer.txt", O_WRONLY | O_CREAT | O_TRUNC);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 填三个iovec结果
    for (int i = 0; i < 3; i++) {
        iov[i].iov_base = buf[i];
        iov[i].iov_len = strlen(buf[i]);
    }

    // 单词调用将他们写出
    nr = writev(fd, iov, 3);
    if (nr == -1) {
        perror("writev");
        return 1;
    }
    printf("wrote %ld bytes\n", nr);

    if (close(fd)) {
        perror("close");
        return 1;
    }
    return 0;
}
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main() {
    // 下面的长度如果变化,结果会有不同的
    // char foo[47], bar[50], baz[48];
    // char foo[47], bar[51], baz[48];
    char foo[47], bar[50], baz[47];
    struct iovec iov[3];
    ssize_t nr;
    int fd, i;
    fd = open("buccaneer.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    iov[0].iov_base = foo;
    iov[0].iov_len = sizeof(foo);
    iov[1].iov_base = bar;
    iov[1].iov_len = sizeof(bar);
    iov[2].iov_base = baz;
    iov[2].iov_len = sizeof(baz);

    // 单次调用读取
    nr = readv(fd, iov, 3);
    if (nr == -1) {
        perror("readv");
        return 1;
    }

    for (i = 0; i < 3; i++) {
        printf("%d-%ld: %s", i, strlen((char*)iov[i].iov_base), (char*)iov[i].iov_base);
    }
    if (close(fd)) {
        perror("close");
        return 1;
    }
    return 0;

}

获取页大小

long page_size = sysconf(_SC_PAGESIZE);

或者

#include 
int getpagesize(void);

页面大小也被静态存储在PAGE_SIZE(定义于)中,因此获取页面大小的第三种方式为int page_size = PAGE_SIZE;

 mmap函数的用法详解及实例分析-CSDN博客

 Linux C | mmap使用实例_mmap使用示例-CSDN博客

#include 
#include 
#include 
#include 
#include 
#include 


int main() {
    int fd;
    void *mmap_addr;
    struct stat sb;

    fd = open("buccaneer.txt", O_RDONLY);
    fstat(fd, &sb);
    mmap_addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mmap_addr == MAP_FAILED) {
        return -1;
    }
    printf("%s\n", (char*)mmap_addr);
    munmap(mmap_addr, sb.st_size);
    close(fd);
    return 0;
}

mmap优点

相比较于标准的read()和write()系统调用,经过mmap映射有以下优点:

1、对内存映射文件进行读取和写入操作,可避免使用read和write系统调用时所产生的无关副本,read和write会将数据复制到一个用户空间缓冲区,并从用户缓冲区复制回来。

2、除了可能的页面失误,对内存映射文件的读写操作不会产生任何系统调用或操作环境切换的开销,进行简单的内存操作即可。

3、当有多个进程将同一个对象映射至内存时,数据由这些进程共享,对类型为read-only以及shared的可写入映射而言,所共享的是他们的全部,对类型为private的可写入映射而言,所共享的是他们尚未被“写入时复制”的页面。

4、映射的查找仅涉及很少的指针操作,不需要使用lseek系统调用。

mmap缺点

1、内存映射往往是页面大小的整数倍,可能会造成内存浪费。

2、内存映射必须适合放入进程的地址空间。

3、创建和维护内存映射以及内核内部相关的数据结构是有代价的。

调整映射的大小

#define _GNU_SOURCE
#include 
#include 

void* mremap(void* addr, size_t old_size, size_t new_size, unsigned long flags);

 Linux所特有的mremap系统调用可以用于扩大或者缩小所指定的映射的大小。将位于[addr, addr+old_size)中的内存区间扩大或者缩小为new_size,可能会发生地址移动。

输出特定文件的iNode编号

#include 
#include 
#include 
#include 
#include 

int get_inode(int fd) {
    struct stat buf;
    int ret;

    ret = fstat(fd, &buf);
    if (ret < 0){
        perror("fstat");
        return -1;
    }
    return buf.st_ino;
}

int main(int argc, char* argv[]) {
    int fd, inode;
    if (argc < 2) {
        fprintf(stderr, "usage: %s \n", argv[0]);
        return 1;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    inode = get_inode(fd);
    printf("%d\n", inode);
    return 0;
}

获取文件的逻辑块

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*
*fd相关的文件,映射至logical_block物理块
*/
int get_block(int fd, int logical_block) {
    int ret;
    ret = ioctl(fd, FIBMAP, &logical_block);
    if (ret < 0) {
        perror("ioctl");
        return -1;
    }
    return logical_block;
}

/*
* get_nr_blocks   返回与fd相关联的文件所耗用的逻辑块的数目
*/
int get_nr_blocks(int fd) {
    struct stat buf;
    int ret;

    ret = fstat(fd, &buf);
    if (ret < 0) {
        perror("fstat");
        return -1;
    }

    return buf.st_blocks;
}

/**
 * @brief 输出(logical blocks, physical block)
 * 
 */
void print_blocks(int fd) {
    int nr_blocks, i;
    nr_blocks = get_nr_blocks(fd);
    if (nr_blocks < 0) {
        fprintf(stderr, "get_nr_blocks failed!\n");
        return;
    }

    if (nr_blocks == 0) {
        printf("no allocated blocks\n");
        return;
    } else if (nr_blocks == 1) {
        printf("1 bllock\n\n");
    } else {
        printf("%d blocks\n\n", nr_blocks);
    }

    for (i = 0; i < nr_blocks; i++) {
        int phys_block;
        phys_block = get_block(fd, i);
        if (phys_block < 0) {
            fprintf(stderr, "get_block failed!\n");
            return;
        }
        if (!phys_block) {
            continue;
        }
        printf("(%u, %u) ", i, phys_block);
    }
    putchar('\n');

}

int main(int argc, char* argv[]) {
    int fd;
    if(argc < 2) {
        fprintf(stderr, "usage: %s\n", argv[0]);
        return 1;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    print_blocks(fd);
    return 0;
}

获取进程号和父进程号

#include 
#include 

pid_t getpid(void);
pid_t getppid(void);

exec系列调用

#include 

int execl(const char* path, const char* arg, ...);
//不定参数必须以NULL结尾
//下面执行会将/bin/vi取代当前所执行的程序

int ret;
ret = execl("/bin/vi", "vi", NULL);
if (ret == -1) 
    perror("execl");
#include 

int execlp(const char* file, const char* arg, ...);
int execle(const char* path, const char* arg, ..., char* const envp[]);
int execv(const char* path, char* const argv[]);
int execvp(const char* file, char* const argv[]);
int execve(const char* filename, char* const argv[], char* const envp[]);

使用wait检测子进程的状态

#include 
#include 
#include 
#include 
#include 

int main() {
    int status;
    pid_t pid;

    if (!fork()) {
        // 子进程
        // return 1;
        abort();
    }
    pid = wait(&status);
    if (pid == -1) 
        perror("wait");
    printf("pid=%d\n", pid);
    
    if (WIFEXITED(status)) {
        printf("Normal termination with exit status=%d\n", WEXITSTATUS(status));
    }

    if (WIFSIGNALED(status)) {
        printf("Killed by signlal=%d%s\n", WTERMSIG(status), WCOREDUMP(status) ? " (dumped core)" : "");
    }

    if (WIFSTOPPED(status)) {
        printf("Stopped by signal=%d\n", WSTOPSIG(status));
    }

    if (WIFCONTINUED(status)) {
        printf("Continued\n");
    }
    return 0;
}

等待特定进程waitpid

#include 
#include 
#include 
#include 

// pid_t waitpid(pid_t pid, int * status, int options)

/**
 * @brief pid参数说明
 * <-1  等待其进程组ID等于此值的绝对值的任何子进程,例如传入-500, 则等待进程组ID为500的任何进程
 * -1   等待任何子进程,此刻的行为如同wait()
 * 0    等待与calling process (进行调用的进程)均属于同一个进程组的任何子进程
 * >0   等待其pid等于所指定值的任何子进程,例如传入500,则表示等待进程号为500的子进程
 * status参数的行为如同wait的参数,可以使用前面的宏来操作它
 * options参数可以是零个或者多个以下选项的OR逻辑运算
 * WNOHANG 非阻塞,如果没有符合的子进程终止,则立即放回
 * WUNTRACED  
 *      如果设定了,则设定WIFSTOPPED,及时calling process 并未追踪子进程
 * WCONTINUED
 *      如果设定了,则设定WIFCONTINUED,即使calling process并为追踪子进程,如同WUNTRACED
 */


int main() {
    pid_t pid;
    int status;

    pid = waitpid(2448, &status, WNOHANG);
    if (pid == -1) {
        perror("waitpid");
    } else {
        printf("pid=%d\n", pid);
        if (WIFEXITED(status)) {
            printf("Normal termination with exit status=%d\n", WEXITSTATUS(status));
        }
        if (WIFSIGNALED(status)) {
            printf("killed by signal=%d%s\n", WTERMSIG(status), WCOREDUMP(status) ? " (dump core)" : "");
        }
    }
    return 0;
}

利用fork(),exec,和waitpid来实现

#include 
#include 
#include 
#include 

int my_system(const char* cmd) {
    int status;
    pid_t pid;

    pid = fork();
    if (pid == -1) {
        perrorr("fork");
        return -1;
    }

    if (pid == 0) {
        const char* argv[4];
        argv[0] = "sh";
        argv[1] = "-c";
        argv[2] = cmd;
        argv[3] = NULL;
        execv("/bin/sh", argv);
        exit(-1);
    }
    if (waitpid(pid, &status, 0) == -1) {
        return -1;
    } else if (WIFEXITED(status)) {
        return WEXITSTATUS(status);
    }
    retrun 1;
}

获取和修改资源限制,setrlimit,getrlimit

#include 
#include 
#include 
#include 

/**
 * @brief 
 * struct rlimit {
 *      rlim_t rlim_cur;   //soft limit
 *      rlim_t rlim_max;   //hard limit
 * };
 * 
 * int getrlimit(int resource, struct rlimit * rlim);
 * int setrlimit(int resource, const struct rlimit* rlim);
 * @return int 
 */

int main() {
    struct rlimit rlim;
    int ret;
    // 取得core文件大小的限制
    ret = getrlimit(RLIMIT_CORE, &rlim);
    if (ret == -1) {
        perror("getrlimit");
        return 1;
    }
    printf("before : RLIMIT_CORE limits: soft=%ld hard=%ld\n", rlim.rlim_cur, rlim.rlim_max);
    // 产生如下输出
    // RLIMIT_CORE limits: soft=0 hard=-1  -1表示无穷大

    rlim.rlim_cur = 32*1024*1024;  //32MB
    rlim.rlim_max = RLIM_INFINITY;
    ret = setrlimit(RLIMIT_CORE, &rlim);
    if (ret == -1) {
        perror("setrlimit");
        return 1;
    }
    printf("setrlimit success!\n");

    ret = getrlimit(RLIMIT_CORE, &rlim);
    if (ret == -1) {
        perror("getrlimit");
        return 1;
    }
    printf("after: RLIMIT_CORE limits: soft=%ld hard=%ld\n", rlim.rlim_cur, rlim.rlim_max);
}

stat函数,获取文件元数据

#include 
#include 
#include 

int stat(const char* path, struct stat* buf);
int fstat(int fd, struct stat* buf);
int lstat(const char* path, struct stat* buf);

Linux系统编程(2)_第1张图片

 Linux系统编程(2)_第2张图片

 Linux系统编程(2)_第3张图片

 使用stat获取指定文件的大小

#include 
#include 
#include 
#include 

int main(int argc, char* argv[]) {
    struct stat sb;
    int ret;
    if (argc < 2) {
        fprintf(stderr, "usage: %s \n", argv[0]);
        return 1;
    }

    ret = stat(argv[1], &sb);
    if (ret == -1) {
        perror("stat error");
        return 1;
    }

    printf("%s is %ld bytes\n", argv[1], sb.st_size);

    return 0;
}

下面这段代码会使用fstat检查一个已经打开的文件是否位于一个物理设备上

#include 
#include 
#include 
#include 
#include 
#include 

/**
 * @brief 如果fd位于一个物理设备上,则返回一个正整数,如果位于一个非物理或者虚拟设备上
 * 则返回0,发生错误,返回-1
 * 
 * @param fd 
 * @return int 
 */
int is_on_physical_device(int fd) {
    struct stat sb;
    int ret;

    ret = fstat(fd, &sb);
    if (ret == -1) {
        perror("fstat error");
        return -1;
    }
    return gnu_dev_major(sb.st_dev);
}

int main(int argc, char* argv[]) {
    struct stat sb;
    int ret;
    if (argc < 2) {
        fprintf(stderr, "usage: %s \n", argv[0]);
        return 1;
    }

    ret = stat(argv[1], &sb);
    if (ret == -1) {
        perror("stat error");
        return 1;
    }

    printf("%s is %ld bytes\n", argv[1], sb.st_size);

    int fd = open("buccaneer.txt", O_RDONLY);
    ret = is_on_physical_device(fd);
    if (ret == -1) {
        perror("is_on_physical_device faile\n");
        return -1;
    }
    if (ret == 0) {
        printf("buccaneer.txt is not on physical device!\n");
    } else {
        printf("buccaneer.txt is on physical device!\n");
    }
    return 0;
}

输出:
buccaneer.txt is 149 bytes
buccaneer.txt is on physical device!

 获取文件使用权限

#include 
#include 

int chmod(const char* path, mode_t mode);
int fchmod(int fd, modt_t mode);

将fd文件拥有者和组设定为root

int make_root_owner(int fd) {
    int ret;
    // root的gid与uid皆为0
    ret = fchown(fd, 0, 0);
    if (ret)
        perror("fchown");
    return ret;
}

你可能感兴趣的:(linux,C++)