首页 > 其他分享 >操作系统学习2

操作系统学习2

时间:2023-08-17 21:14:46浏览次数:29  
标签:文件 char const 操作系统 int 学习 信号 进程

分别使用标准IO和系统IO写入一百万个整数到文件,测试谁的时间更短?为什么?

结论:在同等数据的写入下,使用标准IO要比直接使用系统IO更快
原因:
标准IO有缓冲区机制,在执行fwrite写文件时,数据不是直接调用系统IO写入磁盘,而是先存放在
内存的缓冲区中,直到缓冲区满后,才会调用一次系统IO全部写入磁盘,因此对系统IO的调用次数、用户态
内核态的转换次数都大大降低
而直接使用系统IO是没有缓冲区机制,因此耗时增加

解决:如果给系统IO也人为的加上缓冲区机制,那么它的速度要比标准IO更快

随机读写
每个打开的文件都有一个记录读写位置的指针,也称文件位置指针,对文件的读写时该指针会自动往后移动,因此顺序读写时无需操作
当需要去文件的任意位置进行读写时,才需要调整该指针的位置
标准IO:
int fseek(FILE *stream, long offset, int whence);
返回值:成功0 失败-1
系统IO:
off_t lseek(int fd, off_t offset, int whence);
返回值:成功返回调整后位置指针的位置 失败返回-1

系统IO中的文本文件的读写:
在系统IO中没有类似fprintf\fscanf函数,因此没有直接对文本文件读写的操作
但是可以通过把数据转换成字符串进行间接的文本文件读写
写文本文件
任意类型数据对象,sprintf 转换成字符串write写入
读文本文件
把字符串格式读取到内存中,sscanf

文件描述符:
1、非负整数,代表了一个打开的文件
2、通过系统调用(open\creat)返回,该数值时被内核使用的
3、它在内核中对应一个内核对象,因为内核不能暴露它真实的地址,因此不能直接返回真是的文件地址,而是使用文嘉描述符号来表示
4、内核中有一张记录了所有已打开的文件的二维表,文件描述符就是访问该表每行的下标,文件描述符也称为句柄,就是访问文件的凭证
5、内核中有三个默认长期打开的文件描述符
0 标准输入 宏STDIN_FILENO(系统IO) FILE* stdin(标准IO)
1 标准输出 STDOTUT_FILENO stdout
2 标准错误 STDERR_FILENO stderr

文件描述符的重定向:

int dup(int oldfd);
功能:复制一个已经打开的文件描述符
返回值:返回一个当前进程没有使用的最小的文件描述符,该描述符与oldfd对应一个文件

int dup2(int oldfd, int newfd);
功能:复制old文件描述符为newfd
    如果newfd打开了,那么会先关闭newfd,然后在复制

重定向的作用:
把一个已经打开的文件描述符fd,经过dup2指定复制为标准输入0\标准输出1\标准错误2,则会先关闭已经打开的0\1\2,然后0\1\2重
定向指向fd对应的文件
就可以通过printf\scanf函数就可以直接往fd对应的文件进行读写,相当于write\read
但是在当前进程中会导致后续无法使用标注输入、输出、错误文件,因此可以在开始前通过dup先备份这些文件,
结束之后通过dup2重新指向标准文件

文件同步:
1、在写入数据时,内存和磁盘之间有一块缓冲区,目的是为了降低磁盘的读写次数,提高读写效率
2、但是这种机制带来的后果是磁盘中的数据与实际写入的数据可能不符合,系统提供三个函数让缓冲区的数据立即写入磁盘,
称为文件同步
fsync\sync\fdataync

文件属性:
int stat(const char *pathname, struct stat *statbuf);
功能:根据文件路径获取文件的属性

int fstat(int fd, struct stat *statbuf);
功能:根据文件描述符获取文件的属性

int lstat(const char *pathname, struct stat *statbuf);
功能:根据文件路径获取软链接文件的属性


struct stat {
    dev_t     st_dev;         //设备ID
    ino_t     st_ino;         //inode节点号
    mode_t    st_mode;        //文件的类型和权限
    nlink_t   st_nlink;       //硬链接数
    uid_t     st_uid;         //用户ID
    gid_t     st_gid;         //组ID
    dev_t     st_rdev;        //特殊设备ID
    off_t     st_size;        //总字节数
    blksize_t st_blksize;     //IO块的总字节
    blkcnt_t  st_blocks;      //占用大小512字节的内存块数量
    struct timespec st_atim;  //最后访问时间
    struct timespec st_mtim;  //最后修改时间
    struct timespec st_ctim;  //最后状态修改时间

    #define st_atime st_atim.tv_sec /* Backward compatibility */
    #define st_mtime st_mtim.tv_sec
    #define st_ctime st_ctim.tv_sec
};

文件的权限:
int access(const char *pathname, int mode);
功能:测试当前用户对文件的权限
pathname:想要测试的文件路径
mode:想要测试的权限
F_OK 测试文件是否存在
R_OK 测试文件是否有读权限
W_OK 测试文件是否有写权限
X_OK 测试文件是否有执行权限

返回值:存在返回0 不存在返回-1

int chmod(const char *pathname, mode_t mode);
功能:根据文件路径修改文件权限
mode:由三个八进制数组成的权限掩码
    0xxx
    0644    0666    普通文件
    0755            可执行文件
int fchmod(int fd, mode_t mode);
功能:根据文件描述符修改文件权限

权限屏蔽码:
    如果想让新创建的文件不具备某些权限,可以通过设置权限屏蔽码进行屏蔽权限
    查看命令 umask 查看当前终端的权限屏蔽码
    注意:可以通过chmod命令、函数可以无视权限屏蔽码

    修改权限屏蔽码:
        命令:umask 0xxx     修改成0xxx
        注意:只是当前终端临时有效,如果想要长期有效需要修改配置文件
        函数:mode_t umask(mode_t mask);
            mask:新的屏蔽码
            返回值:原来的屏蔽码
        注意:该函数只是对当前进程中生效,进程结束后失效

修改文件大小:
int truncate(const char *path, off_t length);
功能:根据文件路径截取文件长度
length:截取后文件的长度(字节)
返回值:成功返回0,失败返回-1;
int ftruncate(int fd, off_t length);
功能:根据文件描述符截取文件长度

文件删除、重命名:
int remove(const char *pathname);
功能:由c标准库提供的删除文件函数

int unlink(const char *pathname);
功能:操作系统提供的删除文件函数
    remove底层调用unlink 、rmdir函数
    1、如果删除的是硬链接文件或者源文件本身且文件是关闭的,则是把该文件
    的硬链接数减1,然后清除该文件的inode信息
    2、当文件的硬链接数减为0时,系统把该文件的block权限释放,可以用于存储其他文件的数据
    3、如果删除软链接文件,则删除的是软链接文件的inode信息,而不会删除源文件的数据,没有
    任何一个函数可以通过软链接文件删除源文件

int rename(const char *oldpath, const char *newpath);
功能:重命名文件

什么是软硬链接文件?
Linux文件系统会把分区分为两大部分:
inode信息块区:
每块默认128B,记录某个文件的文件权限、大小、所有者、修改时间等属性信息,

    block数据块区:
        每块默认4kb,记录了文件的真正内容数据、文件名

    每个文件有且只有一个inode信息块以及若干个block数据块,读取文件file需要借助所在目录文件的block中
    记录的file文件的inode号和文件名,来找到file的inode信息块,从而读取file文件的block数据块

    硬链接文件:
        没有属于自己的inode和block,只是在不同的目录下复制链接的源文件的inode信息块,
        通过该复制的inode信息块访问源文件的block数据块
    软链接文件:
        软链接文件会创建自己的新的inode和block块,在软链接文件的block中不存储源文件的数据,
        而是存储源文件的inode号,从而借助源文件的inode来访问源文件的block
    区别:
        1、删除源文件,只是删除源文件的inode信息块中的数据,对应的block数据不会清理,所以硬链接依然可以访问,
        但是软链接文件就无法访问。
        2、只有当文件的硬链接数删除到0时,文件才算被真正的删除了
        3、修改硬链接文件的内容,源文件也会随之修改
        4、硬链接不能链接目录、软链接可以
        5、硬链接不能跨文件系统使用,而软链接可以

int link(const char *oldpath, const char *newpath);
功能:创建硬链接文件
int symlink(const char *target, const char *linkpath);
功能:创建软链接文件 文件类型为 l

目录操作:
int mkdir(const char *pathname, mode_t mode);
功能:创建空目录
mode:目录权限,必须要有执行权限才能进入目录
返回值:成功0 失败-1

int rmdir(const char *pathname);
功能:删除空目录

int chdir(const char *path);
功能:修改当前的工作路径为path 相当于cd

char *getcwd(char *buf, size_t size);
功能:获取当前进程的工作路径 相当于pwd

DIR *opendir(const char *name);
功能:根据目录路径打开目录
返回值:成功返回一个目录流
DIR *fdopendir(int fd);
功能:根据目录文件描述符打开目录
返回值:成功返回一个目录流

注意:DIR目录流对象中记录了该目录中所有文件的信息

struct dirent *readdir(DIR *dirp);
功能:从目录流dirp中读取一条文件信息并返回

struct dirent {
    ino_t          d_ino;       //inode号
    off_t          d_off;       //距离下一条信息的字节数
    unsigned short d_reclen;    //当前信息的字节数
    unsigned char  d_type;      //文件的类型
    char           d_name[256]; //文件名
};
注意:readdir读取完一条信息之后,会自动的指向下一条信息,只需要继续执行
readdir即可读取下一条,直到返回值为NULL读取完毕

信号管理
一、基本概念
1、中断
当进程接收到消息后中止当前正在进行进程,转而去执行其他任务,等其它任务执行结束后再返回
刚刚中止的位置,可以继续往下运行,这种执行模式叫做中断

中断分为硬件中断、软件中断、硬件中断是由硬件设备引发的、软件中断是执行了中断指令引发

2、信号
信号是一种软件中断,由操作系统发出,进程接收后会执行相应的操作

3、常见的信号
kill -l 显示所有信号
SIGINT(2)   通过 Ctrl+c     终止
SIGQUIT(3)      Ctrl+\     终止+core
SIGFPE(8)       除0        终止+core
SIGKILL(9)      终止信号    终止
SIGSEGV(11)     非法访问内存    终止+core

4、不可靠信号和可靠信号
        建立在早期的信号处理机制上的信号称为不可靠信号(1-31)
    不支持排队,可能会丢失,如果同一个信号连续多次发送,很
    可能进程只接受一次
        建立在新的信号处理机制上的信号称为可靠信号(34-64)
    支持排队,不会丢失
5、信号的来源
    硬件异常:除零、非法访问内存、未定义的指令、总线错误
    软件异常:通过一些命令、函数产生信号
6、信号的处理方式
    1、忽略
    2、终止进程
    3、终止进程并产生core文件
    4、捕获并处理
        在信号来之前先向内核注册一个信号处理函数与要捕获的信号绑定
    当信号发生时进程会中止先转去执行信号处理函数
7、命令发送信号
    kill 信号编号(宏名)进程id(pid)

二、信号的捕获
typedef void (*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);
    功能:向内核注册一个信号处理函数
    signum:要绑定捕获的信号编号
    handler:函数指针 提供需要注册的信号处理函数
        亦可以使用以下
        SIG_IGN 忽略处理
        SIG_DFL    按默认方式处理
    返回值:
        之前的信号处理函数
    注意:只有9和19号信号不能被捕获、也不能被忽略
    注意:有些系统通过signal注册的函数只能有效一次,第一次之后会还原成默认的处理方式,
    可以在信号处理函数的末尾重新注册signal注册
    注意:信号处理函数结束后会返回信号产生时代码处继续执行,如果导致信号产生的错误还在,例如
    段错误、除零等,可能会导致死循环反复调用信号处理函数,正确的处理方式时备份进程数据然后
    结束进程(exit)

    注意:通过fork创建的子进程会继承父进程的信号处理方式,但是通过exec系列函数创建的子进程会恢复
    默认的信号处理方式

三、信号的产生
1、键盘
Ctrl+c Ctrl+\ Ctrl+z暂停\挂起 fg继续
2、错误
段错误 除0 硬件故障
3、命令
kill -信号id 进程号
killall -信号id 进程名 向同进程名的所有进程发送信号
4、函数
int kill(pid_t pid, int sig);
功能:向进程号pid的进程发送信号sig
int raise(int sig);
功能:向自己发送信号sig
void abort(void)
功能:向自己发送SIGABRT信号
unsigned int alarm(unsigned int seconds);
功能:让内核在seconds秒后向自己发送SIGALRM信号
返回值:上一次alarm设置的剩余秒数
注意:再次调用alarm函数会重置闹钟时间,不会产生两次信号

四、进程休眠

    unsigned int sleep(unsigned int seconds);
    功能:让调用进程进入指定的休眠秒速,秒数到了会唤醒,并且在休眠
    中遇到了正常的信号都会提前唤醒返回
    返回值:剩余没睡的秒数

    int pause(void)
    功能:让进程进入休眠,直到遇到正常信号才唤醒
    返回值:要么一直休眠不返回,要么返回-1表示信号来了

五、信号集和信号阻塞
信号集:是一种数据类型,定义变量可以存储多个信号
sigset_t 128位二进制数 用每一位代表一个信号
相关的函数:
int sigemptyset(sigset_t *set);
功能:清空信号集set
int sigfillset(sigset_t *set);
功能:填满信号集set
int sigaddset(sigset_t *set, int signum);
功能:向信号集set中添加信号signum
int sigdelset(sigset_t *set, int signum);
功能:从信号集set中删除信号signum
int sigismember(const sigset_t *set, int signum);
功能:测试信号集set中是否存在信号signum
返回值:
1 存在
0 不存在
-1 非法信号

    信号阻塞:
        当进程正在执行一些关键性的特殊操作时,不适合暂停去处理信号,
        此时可以让内核先屏蔽信号,等特殊操作完成之后再继续发送并处理。

        当信号产生时,内核会在其维护的一张信号表中给对应的进程设置
        一个该信号的标记,这个过程称为递送。

        从信号产生到完成递送的过程有一个时间间隔,处于该间隔中的信号
        状态称之为未决

        信号阻塞屏蔽是让信号处于未决状态,暂停递送,当解除阻塞时处于未决
        状态的信号就会继续递送

        每个进程中都有一个信号集用于记录该进程中要屏蔽的信号
    
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    功能:设置进程的信号屏蔽集
    how:设置信号屏蔽的方式
        SIG_BLOCK   把set中的信号添加到本进程的信号屏蔽集中
        SIG_UNBLOCK 从信号屏蔽集中删除set中的信号
        SIG_SETMASK 用set完全替换原来的信号屏蔽集
    set:想要设置的信号集
    oldset:获取设置前信号屏蔽集

六、附带信息的信号处理函数
int sigaction(int signum, const struct sigaction act,struct sigaction oldact);
功能:向内核注册一个信息处理函数
signum:想要绑定的信号
act:要注册的信号处理函数
struct sigaction {
void (
sa_handler)(int);//不带信息的信号处理函数
void (
sa_sigaction)(int, siginfo_t *, void *);//附带额外信息的信号处理函数
sigset_t sa_mask;//信号屏蔽集,如果有想要屏蔽的信号,可以给该成员赋值
int sa_flags;//信号绑定的处理标志,SA_SIGINFO 使用sa_sigaction来绑定; SA_NODEFER 在信号处理过程中绑定的信号不会被屏蔽
// SA_RESETHAND 该信号处理方式执行一次后,还原会默认的信号处理方式
//SA_RESTART 绑定的信号执行完处理函数后,被中断的系统调用会重新启动

        void     (*sa_restorer)(void);  //保留NULL
    };

    int sigqueue(pid_t pid, int sig, const union sigval value);
    功能:向进程pid发送附带额外数据信息的信号sig
    union sigval {
        int   sival_int;    //整数
        void *sival_ptr;    //指针
    };

七、定时器
int getitimer(int which, struct itimerval *curr_value);
功能:获取当前进程的定时方案
which:
ITIMER_REAL 真实定时器 程序的总运行时间 SIGALRM
ITIMER_VIRTUAL 虚拟定时器 用户态的运行时间 SIGVTALRM
ITIMER_PROF 实际定时器 用户态+内核态的运行时间 SIGPROF
真实定时器 = 实际定时器+切换时间+休眠时间
curr_value:定时方案

struct itimerval {
    struct timeval it_interval; //定时器的间隔时间
    struct timeval it_value;    //第一次启动定时器的时间
};

struct timeval {
    time_t      tv_sec;         //秒
    suseconds_t tv_usec;        //微秒
};

int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
功能:设置定时器
new_value:想要设置的定时方案
old_value:获取旧的定时方案

标签:文件,char,const,操作系统,int,学习,信号,进程
From: https://www.cnblogs.com/c-learnmore/p/17638844.html

相关文章

  • 【算法学习笔记】DFN序求LCA(最近公共祖先)
    前置知识DFN序:对一棵树进行深度优先搜索DFS得到的结点序列,即深度优先搜索DFS的访问顺序。该表述不一定严谨,建议百度ST表(SparseTable,稀疏表)算法概述引理1.1在DFN序中祖先一定出现后代之前。考虑一树上的两个节点\(x\),\(y\)的最近公共祖先\(d\),设\(x\)的DFN序......
  • UEFI学习资料
    书籍《UEFI编程实践》技术博客罗冰的UEFI开发专栏https://www.lab-z.com/iof/UEFI入门必读的12本书......
  • Spring源码学习笔记13——总结篇, 从IOC到AOP
    系列文章目录和关于我零丶序言在《Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点》中,我们总结了SpringIOC部分的知识,为了更好的给群里的伙伴们分享SpringAOP的知识,遂有了这篇文章,这篇文章将从IOC聊到AOP,其中IOC不会那么细致,重点还是在AOP。一丶引入1.AOP概述......
  • 《Java编程思想第四版》学习笔记16
    学习了多形性的知识后,由于多形性是如此“聪明”的一种工具,所以看起来似乎所有东西都应该继承。但假如过度使用继承技术,也会使自己的设计变得不必要地复杂起来。事实上,当我们以一个现成类为基础建立一个新类时,如首先选择继承,会使情况变得异常复杂。一个更好的思路是首先选择“合成”......
  • openGauss学习笔记-42 openGauss 高级数据管理-触发器
    openGauss学习笔记-42openGauss高级数据管理-触发器触发器会在指定的数据库事件发生时自动执行函数。42.1语法格式创建触发器CREATETRIGGERtrigger_name{BEFORE|AFTER|INSTEADOF}{event[OR...]}ONtable_name[FOR[EACH]{ROW|STATEMENT......
  • C学习6
    1、通过自定义函数做二分查找#include<stdio.h>intbinary_search(intarr[],intx,intsz){intleft=0;intright=sz-1;while(left<=right){intmid=(left+right)/2;if(arr[mid]<x){lef......
  • Python学习日记 2023年8月17日
    今天有点懒啊,做的东西少了点importosimportjiebaimportwordcloudimportimageio#pho=imageio.imread('7848.jpg')f=open('口红.txt')txt=f.read()txt_list=jieba.lcut(txt)string=''.join(txt_list)wc=wordcloud.WordCloud(......
  • 《Java编程思想第四版》学习笔记14
    //:Frog.java//TestingfinalizewithinheritanceclassDoBaseFinalization{publicstaticbooleanflag=false;}classCharacteristic{Strings;Characteristic(Stringc){s=c;out.println("Creating......
  • 《Java编程思想第四版》学习笔记15
    初始化的实际过程是这样的:(1)在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。(2)就象前面叙述的那样,调用基础类构建器。此时,被覆盖的draw()方法会得到调用(的确是在RoundGlyph构建器调用之前),此时会发现radius的值为0,这是由于步骤(1)造成的。(3)按照原先声明的......
  • 【狂神说Java】Java零基础学习笔记-JavaSE总结
    【狂神说Java】Java零基础学习笔记-JavaSE总结JavaSE总结:......