文件操作,进程

1.文件操作

因为在Linux系统中一切都可以看作是文件。并且 文件的管理格式 为 EXT4.

在LInux 系统中,只能 由 LInux 内核 操作 文件。所以用户不能直接 操作 内核。可以通过 系统调用访问 LInux内核,再由 LInux内核 去访问 文件。

系统调用 又可以 分为 c语言库函数调用 和  系统调用库函数调用

其中 系统调用库函数 可以直接访问 LInux 内核,而 c语言库函数 可以先 访问 系统调用,在通过系统调用库函数 访问 Linux内核,也可以直接访问 Linux内核。

总结:(应用程序 系统调用 标准款函数 LInux内核 之间的关系)

应用程序 如果 要访问 Linux内核(Linux内核 能 操作文件(注意:在Linux系统中 ,一切皆是文件)),需要 通过 系统调用 或 标准库 间接访问 Linux内核,标准库 有时 也可以调用 系统调用

访问 Linux内核。

补充:typedef  和 define

1.类型替换 ---typedef

格式:typedef  数据类型(要替换)  新名字;

例:

#include<sthio.h>

typedef int s;

int main()
{
    int a=10;
    s b=10;
    if(a==b)
    {
        printf("a  == b");
    }
    return 0;

}

2.宏定义:define

#define 新名字 被替换的内容

注意:这里被替换的内容,可以是常量,也可以是函数,也可以是表示。

例:

#include<stdio.h>

#define a 10//1第一种,替换常量
#define c 10> 20?10:20//第二种替换表达式
#define NX(A,B) A * B 
int main()
{
    int b=a;//等价于 b=10;
    printf("max=%d\n",c);
    //如果不用括号改变运算顺序的话,结果与实际会出现偏差
    //如:NX(10+20,20+30)
    printf("%d",NX(10+20,20+30));//运算顺序:10+20*20+30 =440
    //所以要用括号去改变运行顺序
    //正确的如下:顺序为 (10+20) *(20+30)
    printf("%d",NX((10+20),(20+30)));
}

2.标准IO

 c语言库函数中有关 文件操作的库函数 又 称为 标准IO,主要是对文件进行操作。

1.打开文件

FILE *fopen(const char *pathname,const char *mode)

返回值:如果成功,返回一个指针,表示成功打开文件的地址

参数2:const char *mode

r:读打开。通常从文件开始的第一个位置开始读取

r+:读写打开。读和写的位置是同一个位置。默认从文件开始位置开始读写。

w:写打开,文件不存在,就创建。文件存在,就清空文件里的内容。

w+:读写打开 ,文件不存在,就创建。文件存在,就清空文件里的内容。     

a:追加打开,在文件末尾开始追加新的内容。。如果不存在则创建文件,存在则在文件末尾进行操作(定位到文件末尾)

a+:以追加 和 读的方式打开。如果不存在则创建文件,存在则在文件末尾进行追加,读则在文件开始位置(但是读的位置,可能会因为不同系统,读的位置也会不同)。

特别:

b:以二进制形式操作文件。通常和 mode参数进行组合使用

2.关闭文件

int fclose(FILE *stream)

通过打开文件的文件地址,来关闭打开的文件

注意:在程序执行过程中,系统会默认打开三个文件

1.stdin

2.stdout

3.stderr

3.读写文件

第一组:每次只进行一个字符的读写

读操作:fgetc

int fgetc(FILE *stream)

成功:返回读取到的字符。

写操作:fputc

int putc(in c,FILE * stream)

int c:表示 要读入的 字符

FILE * stream:打开的文件

第二组 :一次操作一行字符串

读操作:fgets

char *fgets(char *s, int size ,FILE *stream);

参数1:表示将 读取内容存放到该字符串地址中去

参数2:每次读取的长度

参数3:要读取的文件

注意点:

1.如果第一行中存在换行符("\n"),并且第一行的内容小于要读取的大小,那么在输出时 换行符也会被读取

2.使用fgets 会在末尾 添加 '\0'

写操作:fputs

int fputs(const char *s ,FILE *stream)

参数1:要读入的字符串

第三组:读写若干个元素

读操作:fread

size_fread(void *ptr,size_t size ,size_t nmemb,FILE * stream)

参数1:将读取的内容存放到该地址里去

参数2:读取的块大小

参数3:读取的块数量

返回值:成功:返回块的数量

注意:fread 函数 不会添加 ‘\0’

写操作:fwrite

size_fwrite(void *ptr,size_t size ,size_t nmemb,FILE * stream

参数1:将哪个地址中存放的数据读取出来,然后进行写入

参数2:写入块的大小

参数3:读取块的数量

4.补充

1.判断文件是否到末尾

int feof(FILE *stream)

返回值:如果 已经 到达文件末尾 返回 1(真),

              否则 返回 0(假)

2.判读文件是否出错

int ferror(FILE *stream)

返回值: 如果 文件错误 返回 1(真),

             否则 返回 0(假)

3.缓冲区

使用标准IO读写操作,在打开文件时会创建缓冲区,用于读写文件

注意:只有使用标准IO时才会有缓冲区机制

三种缓冲机制:

无缓冲---stderr

行缓冲:只有在缓冲区遇到'\n' 或者 缓冲区满,把缓冲区数据刷新到文件----stdin,stdout

全缓冲“只有缓冲区满,把缓冲区数据刷新到文件中----其他标准IO打开的文件

3.系统IO

系统IO是指计算机系统进行输入和输出操作的过程。

1.打开文件

int open(const char *pathname,int flags); (这个函数只能打开,如果不存在则打开失败)

参数2:flags:文件的打开方式

三种基本方式:(下面三种最多只能同时存在一个,并且必须有下面中的任意一个)

1. O_RDONLY:只读方式打开  

2.O_WRONLY:只写方式打开

3.O_RDWR    :读写方式打开

其他方式:

O_APPEND:以追加方式打开(与只读打开排斥),必须有写

O_CREAT:如果文件不存在,则会创建文件(限制只能适用三个参数的函数 )mode参数 不使用,可以没有第三个参数

O_EXCL:与O_CREAT 一起使用,如果文件存在,则返回错误信息,只用于创建新文件

O_NOCTTY:如果文件是终端文件,终端就不能作为当前打开文件的程序的控制终端

O_TRUNC:如果文件存在,则清空文件

返回值:成功:打开文件的文件描述符。文件描述符作用就是标识文件

三种基本打开方式 和 其他 方式 进行组合。每个 方式之间 用 | 隔开。

注意:

1.如果使用 O_CREAT,必须在后面添加参数3,参数3 表示 文件的权限

文件权限的计算:参数3 & (-umask)

umask的取值:0002---八进制数

2. open 函数返回值 在 LINUX内核中,以下标为 3 开始,因为 在程序开始执行,系统会默认打开三个文件 stdin,stdout,stderr三个文件。

3. O_APPEND:在每次执行写的时候,都会把基准点便宜点文件末尾

2.关闭文件

int close(int fd)

参数:int  fd ;fd表示的就是文件描述符。

3.读文件

ssize_t read (int fd,void *buf,size_t count )

参数1:文件描述符,表示从哪个文件读取内容

参数2:将读取到的内存进行存储

参数3:每次读取的长度

返回值:成功:实际读取的长度

4.写操作

ssize_t write(int fd,const void *buf,size_t count)

参数2:从哪个文件写入

参数3:每次写入的长度

返回值:成功返回 写入的长度大小

5.文件读/写位置 偏移

在读入文件时,很多时候都是从文件开始的位置进行 读/写 ,下一次位置为上一次读/写结束的位置。也可以人为的进行修改。开始位置为0(下标)。

off_t lseek(int fd,off_t offset ,int whence)

参数2:偏移量。基于参数3 把 读/写位置 向前或向后 偏移 的长度。

             负数:表示 向前 偏移  正数:表示向后偏移

参数3: 基准点。有三个固定的位置。

                SEEK_SET 基准点为文件开始位置

                SEEK_CUR 基准点为当前位置

                SEEK_END 基准点为文件结束位置

例:

int fd=open("1.txt",O_WRONLY);
lseek(fd,45,SEEK_SET);//表示从文件开始位置,往后偏移45个字节。

int fd1=open("2.txt",O_RDONLY);
lseek(fd1,-10,SEEK_END);//表示从文件末尾位置,往前偏移10个字节,作为下次读写的开始位置。


int fd2=open("2.txt",O_RDONLY);
lseek(fd2,0,SEEK_END);//这个可以用来求打开文件的大小

补充:创建空洞文件

int fd=open("1.txt",O_RDONLY);
int size=lseek(fd,0,SEEK_END);//获取1.txt的文件大小

int fd2=open("2.txt",O_WRONLY |O_TRUNC |O_CREAT);//表示以写的方式打开,并清空里面的内容,如果文件不存在,就创建

lseek(fd2,sizee-1,SEEK_SET);
write(fd2,"\0",1);//文件2.txt里面的内容全是"\0"

6.文件属性

int stat(const char *pathname ,struct stat *statbuf)

参数1:查看的文件名

参数2:结构体指针,存放文件属性

例:

struct stat s;//创建结构体,用查看文件属性
stat("1.txt",&s);//表示打开文件1.txt,查看1.txt的属性

printf("inode si %d\n",s.st_ino);//查看文件的inode号

//特殊:mode_t    st_mode;//文件类型,文件权限
// 文件类型 12 ~ 15 位
// 文件权限:0~8位

printf("%x\n",s.st_mode & 01700000);//查看文件类型
printf("%x\n",x.st_mode & 0777);//查看文件权限

4.多文件 与 库的制作

1.gcc 编译流程---了解

1.预处理 ---只处理以 # 开头的语句,不进行语法检查

gcc xx.c -E -o xx.i

后缀名:.i,就表示 预处理文件

2.编译---将预处理文件转为汇编代码,会进程语法处理

gcc xxx.i -S -o xxx.s

后缀名:.s  ,就表示 编译文件

3.汇编----将生成的 编译文件(汇编语言) ,转化为 二进制文件

gcc xxx.s -c -o xx.o

后缀名:.o ,就表示 汇编文件

4.链接-----将生成的多个汇编文件(.o文件)或 库文件 合并链接,最终形成一个可执行文件。

gcc xx1.o xx2.o xx3.o……

2.多文件

将一个完整且独立的函数功能,改写成一个头文件。不同的内容,可以写在不同的头文件中。

1.编写头文件--独立出函数功能

#define  N 10
int and(int a,int b,int c)
{
        return a+b+c+N;
}

int sub(int a,int b)
{
        return a-b;
}


//当前文件名:math.c

2.头文件申明

#ifndef  MATH_H
#define  MATH_H

int and(int a,int b,int c);//申明函数
int sub(int a,int b);

#endif

//当前文件名:math.h
3.加载头文件

#include<stdio.h>
#include"math.h"
int main()
{
        printf("sub=%d\n",sub(10,5));//调用函数功能
}

编译:

gcc 文件1.c 文件2.c 文件3.c 文件4.c……

补充:

1 < > 和 “ ”的区别.

< >:表示从库里面去找文件

“ ”:表示不仅要从库里面去找,还要从当前文件下去找

2.ifndef 作用

  1. 防止头文件的重复包含和编译;
  2. 便于程序的调试和移植;

3.库的制作

1.静态库

第1步.把程序文件编译为二进制文件,只编译,不链接

gcc -c xxx.c -o xxx.o

第2步:生成静态库(后缀名:.a)

ar -cr libxxx.a xxx.o xxx.o

//libxxx.a 就是静态库的名字

第3步:在编译万能我们自己的程序时,添加库

gcc xx.c xx2.c…… -L库的路径 -l库名 -I头文件路径

补充:

-L:库的路径

-l(小写L):链接库名,库的名字

-I (大写i):头文件路径(库的头文件)

2.动态库

第1步:gcc -fPIC -shard xxx.c -o libxxx.so (后缀名:.so)

-fPIC:忽略文件位置

-shard :共享库

第2步:gcc xx.c xx2.c…… -L库的路径 -l库名 -I头文件路径

注意:使用动态库的2种方法:

1.将自己的库,拷贝到系统库lib中

cp xx.so  xx1.so……/lib

2.export LD_LIBRARY_PATH = 库的绝对路径

注意:该方法只能临时生效

静态库 和 动态库的 区别:

静态库:编译时,直接把库的内容编译到程序中

共享库:在编译时,只是把符号表添加到程序中

对程序的影响:静态库 比 共享库更大。其次,修改共享库的内容,运行共享库的程序文件中的内容也会发送改变。如果 修改 静态库,运行静态库的程序文件内容不会发生改变。

5.进程与线程

1.进程的概念

进程:简单点说就是程序的动态执行。程序的每一次执行都可以被称为进程

程序:只是一系列动作的指令操作文件

即:进程是动态的,程序是静态的

补充:进程和程序的区别:

进程:有完整执行的过程,包含程序运行起来的申请的空间,进程有PID

程序:没有完整执行的过程,程序只是一段代码,没有空间,没有PID标识

补充:查看进程的三种方法:

1.查看当前进程:ps + 进程格式(主要包括 :axu,bsd)

2.动态显示当前进程的情况:top

3.以Ubuntu系统为例,可以查看根目录下的/proc

2.进程的状态

最基础的三种状态:就绪态,运行态,等待(阻塞)态

就绪态:进程在就绪队列中排队,等待CPU调度

运行态:进程在CPU中执行

等待态:进程在等待某个事件调度,进入就绪态

3.进程的命令操作

1.进程的创建 

pid_t fork(void);

返回值:父进程返回 子进程的 pid;

                子进程返回 0;

父进程:哪个函数调用了fork函数,那么这个函数就被称为父进程

注意:子进程会从fork后的下一条语句开始执行。并且子进程拥有与父进程相同的内容,即大小相同,但是存储空间确实独立的。互不影响。

2.进程的关闭

三种方法:

1.主函数使用return 语句,直接结束进程。

注意:如果是在子函数中使用return语句,结束的是子函数,并不能结束进程

2.kill命令

kill + pid(要关闭的进程) 

3.exit函数

void exit(int status)

参数:int status 

返回值 & 0XFF 作为进程结束状态

void _exit(int status)

参数:int status 

返回值 & 0XFF 作为进程结束状态

总结:两个函数的作用是一样的,都是退出进程

区别:exit函数会刷新缓冲区,_exit函数不会刷新缓冲区。

3.进程的等待

pid_t wait(int *wstatus)

参数:用于接收子进程状态的地址

返回值:返回等待到的子进程的pid

注意:wait只会等待任意一个子进程结束,即有两个wait函数,会等待两个子进程结束

补充:

pid_t waitpid(pid_t pid ,int *wstatus,int options)

与wait函数作用一致。参数1表示哪个进程,参数2与wait函数的参数一样。只是参数3有变化

参数3:0:表示阻塞等待

             WNOHANG:表示非阻塞等待

wait默认就是阻塞等待

进程

4创建守护进程

什么是守护进程:系统启动时,就自动启动,系统关闭,自动关闭

1.创建子进程,退出父进程
int pid=fork();//创建子进程
if(pid > 0)//调用fork函数创建子进程,会有两个返回值,父进程返回子进程的pid,子进程的pid为0
{
    exit(0);//退出父进程
}

2.在子进程中创建会话
setsid();将当前改变为新会话的组长

3.改变子进程的工作目录
//因为子进程创建通常使用和父进程相同的路径
chdir("/etc");//存放到根目录下的etc中

4.修改子进程的文件操作掩码
/补充:默认掩码:0002
umask(0);

5.关闭所有的文件文件描述符
close(0);stdin
close(1);stdou
close(2);stderr

5.进程间的通信

1.管道通信

1.

6.线程

线程的概念:

比进程更小,称为轻量级进程。线程依赖于进程。

线程没有独立的内存空间。线程之间共享进程的共享区域。

1.线程的创建

函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数1:线程id

参数2:创建线程的属性,通常为NULL

参数3:线程执行的任务,是一个函数指针

参数4:线程的参数,通常为NULL

2.线程的关闭

void pthread_exit(void *retval);

参数:表示线程的结束状态;与进程的status类似

3.线程的等待

int pthread_join(pthread_t thread, void **retval)

参数1:等待哪个线程结束

参数2:因为在关闭线程时,使用的一级指针,要想存储一级指针就得使用二级指针。

         存储线程结束状态的地址

4.指定某个线程结束

int pthread_cancel(pthrea_t thread)

参数:要结束的线程

5.互斥锁

有效避免在同一时刻,多个线程竞争一个资源。

原理:通过信号量,信号量的值只能是0或1。当信号量为1时,表示可以访问资源。为0时,则等待资源。

补充:进程和线程区别:

进程:拥有执行的完整独立的资源,进程的通信依靠不同的方法

线程:被称为轻量级进程,有自己的线程id,线程会共用进程打开的文件,线程通信在进程中,依靠共享区域

主线程每秒都在变化,子线程获取主线程变化的值

与线程的区别:线程共享资源

线程 之间是 共享的,进程 之间是独立的。

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值