linux系统编程(五)针对linux系统中文件的IO操作

2023-11-10

===========================================================================

1.系统调用

什么是系统调用:
由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。是应用程序同系统之间数据交互的桥梁。

C标准函数和系统函数调用关系。一个helloworld如何打印到屏幕。

在这里插入图片描述

2.C标准库文件IO函数

fopen、fclose、fseek、fgets、fputs、fread、fwrite......
	r 只读、 r+读写
w只写并截断为0、 w+读写并截断为0
a追加只写、 a+追加读写

3.open/close函数

3.1 函数原型

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int close(int fd);

3.2 常用参数

O_RDONLY、O_WRONLY、O_RDWR   	
O_APPEND、O_CREAT、O_EXCL、 O_TRUNC、 O_NONBLOCK     
创建文件时,指定文件访问权限。权限同时受umask影响。结论为:
文件权限 = mode & ~umask
都变成二进制,掩码取反,然后与运算.
使用头文件:<fcntl.h>

3.3 open常见错误

1. 打开文件不存在 
2. 以写方式打开只读文件(打开文件没有对应权限)
3. 以只写方式打开目录

4.文件描述符

4.1 PCB进程控制块

PCB进程控制块:本质就是结构体,其中一个成员就是文件描述符表。文件描述符表中的每一个成员都是文件描述符。该表中能用的就是最小的。
0-STDIN_FILENO
1-STDOUT_FILENO
2-STDERR_FILENO

可使用命令locate sched.h查看位置:	
/usr/src/linux-headers-3.16.0-30/include/linux/sched.h	
	
struct task_struct

{

volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */

/*

表示进程的当前状态:

TASK_RUNNING:正在运行或在就绪队列run-queue中准备运行的进程,实际参与进程调度。

TASK_INTERRUPTIBLE:处于等待队列中的进程,待资源有效时唤醒,也可由其它进程通过信号(signal)或定时中断唤醒后进入就绪队列run-queue。

TASK_UNINTERRUPTIBLE:处于等待队列中的进程,待资源有效时唤醒,不可由其它进程通过信号(signal)或定时中断唤醒。

TASK_ZOMBIE:表示进程结束但尚未消亡的一种状态(僵死状态)。此时,进程已经结束运行且释放大部分资源,但尚未释放进程控制块。

TASK_STOPPED:进程被暂停,通过其它进程的信号才能唤醒。导致这种状态的原因有二,或者是对收到SIGSTOP、SIGSTP、SIGTTIN或SIGTTOU信号的反应,或者是受其它进程的ptrace系统调用的控制而暂时将CPU交给控制进程。

TASK_SWAPPING: 进程页面被交换出内存的进程。

*/

unsigned long flags;  //进程标志,与管理有关,在调用fork()时给出

int sigpending;     //进程上是否有待处理的信号

mm_segment_t addr_limit;   //进程地址空间,区分内核进程与普通进程在内存存放的位置不同

/*用户线程空间地址: 0..0xBFFFFFFF。

内核线程空间地址: 0..0xFFFFFFFF */

struct exec_domain *exec_domain;  //进程执行域

volatile long need_resched;     //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度

unsigned long ptrace;

int lock_depth;  //锁深度

long counter;   //进程的基本时间片,在轮转法调度时表示进程当前还可运行多久,在进程开始运行是被赋为priority的值,以后每隔一个tick(时钟中断)递减1,减到0时引起新一轮调 度。重新调度将从run_queue队列选出counter值最大的就绪进程并给予CPU使用权,因此counter起到了进程的动态优先级的作用

long nice;     //静态优先级

unsigned long policy;  //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR,分时进程:SCHED_OTHER

//在Linux 中, 采用按需分页的策略解决进程的内存需求。task_struct的数据成员mm 指向关于存储管理的mm_struct结构。

struct mm_struct *mm;  //进程内存管理信息

int has_cpu, processor;

unsigned long cpus_allowed;

struct list_head run_list;  //指向运行队列的指针

unsigned long sleep_time;   //进程的睡眠时间

//用于将系统中所有的进程连成一个双向循环链表,其根是init_task

//在Linux 中所有进程(以PCB 的形式)组成一个双向链表,next_task和prev_task是链表的前后向指针

struct task_struct *next_task, *prev_task;

struct mm_struct *active_mm; //active_mm 指向活动地址空间。

struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式

int exit_code, exit_signal;

int pdeath_signal;    //父进程终止是向子进程发送的信号

unsigned long personality;

int dumpable:1;

int did_exec:1;

pid_t pid;   //进程标识符,用来代表一个进程

pid_t pgrp;  //进程组标识,表示进程所属的进程组

pid_t tty_old_pgrp;   //进程控制终端所在的组标识

pid_t session;      //进程的会话标识

pid_t tgid;

int leader;        //表示进程是否为会话主管

//指向最原始的进程任务指针,父进程任务指针,子进程任务指针,新兄弟进程任务指针,旧兄弟进程任务指针。

struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;

struct list_head thread_group;   //线程链表

//用于将进程链入HASH表,系统进程除了链入双向链表外,还被加入到hash表中

struct task_struct *pidhash_next;

struct task_struct **pidhash_pprev;

wait_queue_head_t wait_chldexit;   //供wait4()使用

struct semaphore *vfork_sem;     //供vfork()使用

unsigned long rt_priority;      //实时优先级,用它计算实时进程调度时的weight值

//it_real_value,it_real_incr用于REAL定时器,单位为jiffies,系统根据it_real_value

//设置定时器的第一个终止时间.在定时器到期时,向进程发送SIGALRM信号,同时根据

//it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。

//当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送信号SIGPROF,并根据it_prof_incr重置时间.

//it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种

//状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据it_virt_incr重置初值

unsigned long it_real_value, it_prof_value, it_virt_value;

unsigned long it_real_incr, it_prof_incr, it_virt_incr;

struct timer_list real_timer;  //指向实时定时器的指针

struct tms times;         //记录进程消耗的时间

unsigned long start_time;    //进程创建的时间

long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];//记录进程在每个CPU上所消耗的用户态时间和核心态时间

//内存缺页和交换信息:

//min_flt, maj_flt累计进程的次缺页数(Copyon Write页和匿名页)和主缺页数(从映射文件或交换

//设备读入的页面数);nswap记录进程累计换出的页面数,即写到交换设备上的页面数。

//cmin_flt, cmaj_flt,cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。

//在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中

unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;

int swappable:1;   //表示进程的虚拟地址空间是否允许换出

//进程认证信息

//uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid,euid,egid为有效uid,gid

//fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件系统的访问权限时使用他们。

//suid,sgid为备份uid,gid

uid_t uid,euid,suid,fsuid;

gid_t gid,egid,sgid,fsgid;

int ngroups;     //记录进程在多少个用户组中

gid_t groups[NGROUPS];  //记录进程所在的组

kernel_cap_t cap_effective, cap_inheritable, cap_permitted;//进程的权能,分别是有效位集合,继承位集合,允许位集合

int keep_capabilities:1;

struct user_struct *user;  //代表进程所属的用户

struct rlimit rlim[RLIM_NLIMITS];   //与进程相关的资源限制信息

unsigned short used_math;   //是否使用FPU

char comm[16];     //进程正在运行的可执行文件名

//文件系统信息

int link_count;

struct tty_struct *tty;  //进程所在的控制终端,如果不需要控制终端,则该指针为空

unsigned int locks; /* How many file locks are being held */

//进程间通信信息

struct sem_undo *semundo;  //进程在信号量上的所有undo操作

struct sem_queue *semsleeping;  //当进程因为信号量操作而挂起时,他在该队列中记录等待的操作

struct thread_struct thread;   //进程的CPU状态,切换时,要保存到停止进程的task_struct中

struct fs_struct *fs;     //文件系统信息,fs保存了进程本身与VFS(虚拟文件系统)的关系信息

struct files_struct *files; //打开文件信息,指向文件描述符号

//信号处理函数

spinlock_t sigmask_lock; /* Protects signal and blocked */

struct signal_struct *sig; //信号处理函数

sigset_t blocked;      //进程当前要阻塞的信号,每个信号对应一位

struct sigpending pending; //进程上是否有待处理的信号

unsigned long sas_ss_sp;

size_t sas_ss_size;

int (*notifier)(void *priv);

void *notifier_data;

sigset_t *notifier_mask;

/* Thread group tracking */

u32 parent_exec_id;

u32 self_exec_id;

spinlock_t alloc_lock; //用于申请空间时用的自旋锁。自旋锁的主要功能是临界区保护

};

4.2 文件描述图表

结构体PCB 的成员变量file_struct *file 指向文件描述符表。
从应用程序使用角度,该指针可理解记忆成一个字符指针数组,下标0/1/2/3/4…找到文件结构体。
本质是一个键值对0、1、2…都分别对应具体地址。但键值对使用的特性是自动映射,我们只操作键不直接使用值。
新打开文件返回文件描述符表中未使用的最小文件描述符。

STDIN_FILENO  	0
STDOUT_FILENO  	1
STDERR_FILENO	2

在这里插入图片描述

4.3 最大打开文件数

一个进程默认打开文件的个数1024。    	
命令查看ulimit -a 查看open files 对应值。默认为1024    
 可以使用ulimit -n 4096 修改   	
 当然也可以通过修改系统配置文件永久修改该值,但是不建议这样操作。

 cat /proc/sys/fs/file-max可以查看该电脑最大可以打开的文件个数。
受内存大小影响。

4.4 FIFE结构体

主要包含文件描述符、文件读写位置、IO缓冲区三部分内容。 
struct file {
		...
		文件的偏移量;
		文件的访问权限;
		文件的打开标志;
		文件内核缓冲区的首地址;
		struct operations * f_op;
		...		
	};			
查看方法:
	(1) /usr/src/linux-headers-3.16.0-30/include/linux/fs.h 	
	(2) lxr:百度 lxr → lxr.oss.org.cn → 选择内核版本(3.10) → 点击File Search进行搜索 
		→ 关键字:“include/linux/fs.h” → Ctrl+F 查找 “struct file {” 
→ 得到文件内核中结构体定义
		→ “struct file_operations”文件内容操作函数指针 
		→ “struct inode_operations”文件属性操作函数指针

5. read/write 函数

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

read与write函数原型类似。使用时需注意:read/write函数的第三个参数。

练习:编写程序实现简单的cp功能。

程序比较:如果一个只读一个字节实现文件拷贝,使用read、write效率高,还是使用对应的标库函数(fgetc、fputc)效率高呢?

5.1 strace命令

shell中使用strace命令跟踪程序执行,查看调用的系统函数。
在这里插入图片描述

5.2 缓冲区

read、write函数常常被称为Unbuffered I/O。指的是无用户及缓冲区。但不保证不使用内核缓冲区。

5.3 预读入缓输出

在这里插入图片描述

6.错误处理函数

错误号:errno

perror函数:	  	void perror(const char *s); 
strerror函数:	char *strerror(int errnum); 

查看错误号:	
/usr/include/asm-generic/errno-base.h
	    /usr/include/asm-generic/errno.h

#define EPERM 	1 	/* Operation not permitted */
#define ENOENT 	2 	/* No such file or directory */
#define ESRCH		3	/* No such process */
#define EINTR 	4	/* Interrupted system call */
#define EIO 		5 	/* I/O error */
#define ENXIO 	6 	/* No such device or address */
#define E2BIG 	7 	/* Argument list too long */
#define ENOEXEC 	8 	/* Exec format error */
#define EBADF 	9 	/* Bad file number */
#define ECHILD 	10 	/* No child processes */
#define EAGAIN 	11 	/* Try again */
#define ENOMEM 	12 	/* Out of memory */
#define EACCES 	13 	/* Permission denied */
#define EFAULT 	14 	/* Bad address */
#define ENOTBLK 	15 	/* Block device required */
#define EBUSY 	16 	/* Device or resource busy */
#define EEXIST 	17 	/* File exists */
#define EXDEV 	18 	/* Cross-device link */
#define ENODEV 	19	/* No such device */
#define ENOTDIR 	20 	/* Not a directory */
#define EISDIR 	21 	/* Is a directory */
#define EINVAL 	22 	/* Invalid argument */
#define ENFILE 	23 	/* File table overflow */
#define EMFILE 	24 	/* Too many open files */
#define ENOTTY 	25 	/* Not a typewriter */
#define ETXTBSY 	26 	/* Text file busy */
#define EFBIG 	27	/* File too large */
#define ENOSPC 	28 	/* No space left on device */
#define ESPIPE 	29	/* Illegal seek */
#define EROFS 	30 	/* Read-only file system */
#define EMLINK 	31 	/* Too many links */
#define EPIPE 	32 	/* Broken pipe */
#define EDOM 	33 	/* Math argument out of domain of func */
#define ERANGE 	34 	/* Math result not representable */

7.阻塞、非阻塞

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。

现在明确一下阻塞(Block)这个概念。当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。与睡眠状态相对的是运行(Running)状态,在Linux内核中,处于运行状态的进程分为两种情况:

正在被调度执行。CPU处于该进程的上下文环境中,程序计数器(eip)里保存着该进程的指令地址,通用寄存器里保存着该进程运算过程的中间结果,正在执行该进程的指令,正在读写该进程的地址空间。

就绪状态。该进程不需要等待什么事件发生,随时都可以执行,但CPU暂时还在执行另一个进程,所以该进程在一个就绪队列中等待被内核调度。系统中可能同时有多个就绪的进程,那么该调度谁执行呢?内核的调度算法是基于优先级和时间片的,而且会根据每个进程的运行情况动态调整它的优先级和时间片,让每个进程都能比较公平地得到机会执行,同时要兼顾用户体验,不能让和用户交互的进程响应太慢。

阻塞读终端:				【block_readtty.c】

非阻塞读终端				【nonblock_readtty.c】

非阻塞读终端和等待超时		【nonblock_timeout.c】

注意,阻塞与非阻塞是对于文件而言的。而不是read、write等的属性。read终端,默认阻塞读。

总结read 函数返回值:   
1. 返回非零值:  实际read到的字节数
2. 返回-11):errno != EAGAIN (!= EWOULDBLOCK)  read出错
2):errno == EAGAIN (== EWOULDBLOCK)  设置了非阻塞读,并且没有数据到达。
3. 返回0:读到文件末尾

附上测试代码:
阻塞方式打开设备文件:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
        char buf[10];
        int n;

        n=read(STDIN_FILENO,buf,10);
        if(n<0){
                perror("read STDIN_FILENO");
                exit(1);
        }
        write(STDOUT_FILENO,buf,n);
        return 0;
}


非阻塞方式打开设备文件:

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(void)
{
        char buf[10];
        int fd,n;

        fd=open("/dev/tty",O_RDONLY|O_NONBLOCK);
        if(fd<0){
                perror("open /dev/tty");
                exit(1);
        }


tryagain:
        n = read(fd,buf,10);
        if(n<0){
                if(errno != EAGAIN ){ //if(errno!=EWOULDBLOCK)
                perror("read /dev/tty");
                exit(1);
                }else{
                write(STDOUT_FILENO,"try again\n",strlen("try again\n"));
                sleep(2);
                goto tryagain;
                }
        }
        write(STDOUT_FILENO,buf,n);
        close(fd);

        return 0;

}

非阻塞设置超时:


#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "time out\n"



int main(void)
{
        char buf[10];
        int fd,n,i;

        fd=open("/dev/tty",O_RDONLY|O_NONBLOCK);
        if(fd<0){
                perror("open /dev/tty");
                exit(1);
        }
        printf("open /dev/tty ok... %d\n",fd);

        for(i=0;i<5;i++){
                n = read(fd,buf,10);

                if(n<0){
                        if(errno != EAGAIN ){ //if(errno!=EWOULDBLOCK)
                        perror("read /dev/tty");
                        exit(1);
                        }else{
                        write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY));
                        sleep(2);
                }




                }
        }
        if(i==5){
                write(STDOUT_FILENO,MSG_TIMEOUT,strlen(MSG_TIMEOUT));
        }else{
        write(STDOUT_FILENO,buf,n);


        }
        close(fd);
  return 0;

}

8.lseek函数

8.1 文件偏移

Linux中可使用系统函数lseek来修改文件偏移量(读写位置)

每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以O_APPEND方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。lseek和标准I/O库的fseek函数类似,可以移动当前读写位置(或者叫偏移量)。

 回忆fseek的作用及常用参数。 SEEK_SETSEEK_CURSEEK_END
    int fseek(FILE *stream, long offset, int whence);  成功返回0;失败返回-1
    	特别的:超出文件末尾位置返回0;往回超出文件头位置,返回-1

    off_t lseek(int fd, off_t offset, int whence); 失败返回-1;成功:返回的值是较文件起始位置向后的偏移量。
特别的:lseek允许超过文件结尾设置偏移量,文件会因此被拓展。

    注意文件“读”和“写”使用同一偏移位置。					

【lseek.c】

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>


int main(void){
        int fd,n;
        char msg[]="it's a test foe lseek\n";
        char ch;

        fd = open("lseek.txt",O_RDWR|O_CREAT,0644);

        if(fd<0)
        {
                perror("open lseek.txt error");
                exit(1);
        }

        write(fd,msg,strlen(msg));

        lseek(fd,0,SEEK_SET);

        while((n=read(fd,&ch,1))){
                if(n<0){
                        perror("read error");
                        exit(1);
                }
                write(STDOUT_FILENO,&ch,n);
        }

        close(fd);
        return 0;
}
~     

8.2 lseek常用应用

  1. 使用lseek拓展文件:write操作才能实质性的拓展文件。单lseek是不能进行拓展的。   
		一般:write(fd, "a", 1);								
    	od -tcx filename  查看文件的16进制表示形式
    	od -tcd filename  查看文件的10进制表示形式 	
		使用truncate函数,直接拓展文件。

2. 通过lseek获取文件的大小:lseek(fd, 0, SEEK_END);		【lseek_test.c】

    【最后注意】:lseek函数返回的偏移量总是相对于文件头而言。 

【lseek_test.c】

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{

        int fd = open(argv[1],O_RDWR);
        if(fd == -1)
        {
                perror("open error");
                exit(1);
        }

        int lenth = lseek(fd,0,SEEK_END);
        printf("file size:%d\n",lenth);


        return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>


int main(int argc,char *argv[])
{
        //open/lseek(fd,249,SEEK_END)/write(fd,"\0",1);
        int ret =truncate("lseek.txt",250);//in there ,it must own an knowned file.
        printf("ret = %d\n",ret);
        return 0;
}

9.fcntl函数

改变一个【已经打开】的文件的 访问控制属性。
   	重点掌握两个参数的使用,
   	F_GETFL 和 F_SETFL。		

fcntl :
	int flgs =fcntl(fd,F_GETFL);
	获取文件状态:F_GETFL
	设置文件状态:F_SETFL
									

位图:
位图就是使用一个比特位来进行表示数据存在与否的信息。
这里面的flags就是位图

在这里插入图片描述

【fcntl.c】

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

#define MSG_TRY "try again\n"

int main(void)
{
        char buf[10];
        int flags ,n;

        flags = fcntl(STDIN_FILENO,F_GETFL);
        if(flags == -1){
                perror("fcntl error");
                exir(1);
        }
        flags |= O_NONBLOCK;
        int ret = fcntl(STDIN_FILENO,F_SETFL,flags);
        if(ret == -1){
                perror("fcntl error");
                exit(1);
        }
tryagain:
        n=read(STDIN_FILENO,buf,10);
        if(n<0){
                if(errno != EAGAIN){
                        perror("read /dev/tty");
                        exit(1);
                }
        sleep(3);
        write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY));
        goto tryagain;
        }       
        write(STDOUT_FILENO,buf,n);     
        return 0;
}


10.ioctl 函数

对设备的I/O通道进行管理,控制设备特性。(主要应用于设备驱动程序中)。

通常用来获取文件的【物理特性】(该特性,不同文件类型所含有的值各不相同)

11.传入输出参数

11.1传入参数

const 关键字修饰的 指针变量  在函数内部读操作。  char *strcpy(cnost char *src, char *dst);

11.2 传出参数

1. 指针做为函数参数 
2. 函数调用前,指针指向的空间可以无意义,调用后指针指向的空间有意义,且作为函数的返回值传出  
3. 在函数内部写操作。

11.3 传入传出参数


传入参数:
	1.指针作为函数参数
	2.通常const关键字修饰
	3.指针指向有效区域,在函数内部做读操作
传出参数:
	1.指针作为函数参数
	2.在函数调用之前,指针指向的空间可以无意义,但必须有效
	3.在函数内部,做写操作。
	4.函数调用结束后,充当函数返回值。
传入传出参数:
	1.指针作为函数参数
	2.在函数调用之前,指针有实际意义
	3.在函数内部,先做读操作,后做写操作
	4.函数调用结束后,充当函数返回值。

拓展阅读

关于虚拟4G内存的描述和解析: 一个进程用到的虚拟地址是由内存区域表来管理的,实际用不了4G。而用到的内存区域,会通过页表映射到物理内存。
所以每个进程都可以使用同样的虚拟内存地址而不冲突,因为它们的物理地址实际上是不同的。内核用的是3G以上的1G虚拟内存地址, 其中896M是直接映射到物理地址的,128M按需映射896M以上的所谓高位内存。各进程使用的是同一个内核。

首先要分清“可以寻址”和“实际使用”的区别。
其实我们讲的每个进程都有4G虚拟地址空间,讲的都是“可以寻址”4G,意思是虚拟地址的0-3G对于一个进程的用户态和内核态来说是可以访问的,而3-4G是只有进程的内核态可以访问的。并不是说这个进程会用满这些空间。
其次,所谓“独立拥有的虚拟地址”是指对于每一个进程,都可以访问自己的0-4G的虚拟地址。虚拟地址是“虚拟”的,需要转化为“真实”的物理地址。

好比你有你的地址簿,我有我的地址簿。你和我的地址簿都有1、2、3、4页,但是每页里面的实际内容是不一样的,我的地址簿第1页写着3你的地址簿第1页写着4,对于你、我自己来说都是用第1页(虚拟),实际上用的分别是第3、4页(物理),不冲突。
内核用的896M虚拟地址是直接映射的,意思是只要把虚拟地址减去一个偏移量(3G)就等于物理地址。同样,这里指的还是寻址,实际使用前还是要分配内存。而且896M只是个最大值。如果物理内存小,内核能使用(分配)的可用内存也小。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux系统编程(五)针对linux系统中文件的IO操作 的相关文章

随机推荐

  • 算法训练营第三十二天(8.16)

    目录 Leecode 435 Non overlapping Intervals Leecode 763 Partition Labels Leecode 56 Merge Intervals Leecode 435 Non overlap
  • pycharm问题求解

    为什么我的pycharm下面会弹出在 init 中找不到某个函数 我不知道在哪里设置了这个就都成这个样子了 重新安装一个模组可以暂时解决这个问题 但是切个屏就又变成这样了 正常的好像是这样的 求解
  • graph 图数据结构

    树 和 图 辨析 1 树的父节点和子节点之间是一条路单向可达 2 图的的节点之间存在多条路可达 基本概念 1 顶点 2 边 3 邻居节点 只有一条边连接的顶点 4 度 degree 一个顶点有几条边 就有几度 图的区分 1 无向图 边没有方
  • 【Shell】expect解决脚本中交互时自动输入的问题

    日常和shell相关的工作中 经常遇到要在脚本中连接其他服务器进行文件传输等操作 这些命令通常会要求和用户交互输入验证 信息 那么在脚本中如何实现自动输入口令之类的信息 这里就要用到expect 以ubuntu20为例 首先要安装这个软件
  • Unity Animancer插件(三)运动

    一 根运动 Animancer的根运动系统与原生的工作原理完全相同 但我们可以通过继承Transition类型或实现ITransition接口 来将额外的数据与动画绑定 从而更方便地控制根运动 在下面这个示例中 我们通过自定义的Transi
  • 从N个整数中判断是否有三个整数能组成三角形

    解决这个问题 可以用斐波那契数列 Fibonacci sequence 原因 斐波那契数列中的数是不可能组成三角形的 而我们只要在这些数列里面加一个数就可以有一个三角形可以组成 有了这个原因我们就可以写一个非常快速就可以判断出结果的函数 如
  • C#:递归汉诺塔

    一 运行效果截图 二 实验要求 如果n 1 直接将金片从A针移到C针上 函数输出 Form A to C 如果n gt 1时 移动过程分解为以下几个步骤 1 将A上的n 1片金片借助C针移到B针上 2 把A针上剩下的一片金片由A针移到C针上
  • stomp.min.js(stomp协议的客户端脚本)、sockjs.min.js(SockJS的客户端脚本)以及jQuery

    stomp min js stomp协议的客户端脚本 sockjs min js SockJS的客户端脚本 以及jQuery 点击打开链接
  • [ C++ ] — 智能指针

    一 三种智能指针 auto ptr unique ptr shared ptr auto ptr是C 98提供的方案 C 11已经将其摒弃 以下只是示例 实际不要使用auto ptr 使用new和delete管理动态内存常出现的问题 1 忘
  • STM32MP1开发环境搭建

    STM32MP1 wiki教程 stm32mpu 按照教程的介绍 开发MPU需要在linux环境下 一般选择在VMware虚拟机环境下安装Ubuntu 安装步骤 1 安装VMware 我安装的是VMware 10 0 0 链接 https
  • jQuery使用手册

    官方网站 http jquery com jQuery是一款优秀js开发库类 特别是对css和XPath的支持 使我们写js变得更加方便 如果你不是个js高手又想写出优 秀的js效果 jQuery可以帮你达到目的 下载地址 Starterk
  • C语言数据结构问题:停车场问题(栈和队列)

    试题描述 设停车场只有一个可停放几辆汽车的狭长通道 只有一个大门可供汽车进出 汽车在停车场内按车辆到达的先后顺序依次排列 若车场内已停满几辆汽车 则后来的汽车只能在门外的便道上等候 一旦停车场内有车辆开走 则排在便道上的第一辆汽车即可进入
  • ARP(地址解析协议)协议和RARP协议(逆地址解析协议)

    ARP协议 地址解析协议 及ARP 是根据IP地址获取物理地址的一个TCP IP协议 主机发送信息是将包含将包含目标IP地址的APR请求广播到局域网络上的所有主机 并接收返回消息 以此确定目标的物理地址 受到返回消息的时候将IP地址和物理地
  • 接口自动化之测试数据动态生成并替换

    一 测试数据 1 随机库random 查看内置random方法 该方法自行学习 不再介绍 show 2 Faker库 pip install faker showHttps github com joke2k faker 3 应用到项目中
  • Java 反射机制 与 工厂设计模式

    什么是反射 Java反射机制是在运行状态中 对于任意类 都能知道这个类的全部属性和方法 对于任意对象 都能够调用它的任何一个方法或属性 这种动态获取的信息以及动态调用对象的方法的功能 称为Java语言的反射机制 Class类 Class 是
  • MPI与main()程序中的其他函数执行次数

    我原先以为只有在MPI代码区域 即MPI Init argc argv 到MPI Finalize 中的代码才会涉及到进程通信的问题 但实际上在MPI区域外的代码依然受到影响 执行的次数与开启的进程数有关 为此可以使用MPI 秩 rank
  • AttributeError: 'Function' object has no attribute 'fn' [in caffe]

    n global pool prob3 L Sigmoid n global pool up3 name global pool prob3 ntop 0 top global pool up3 n att repmat3 L Tile n
  • 智能语音技术栈

    识别原理 硬件数据采集 软件数据处理 目前主流的开源平台包括CMU Sphinx HTK Kaldi Julius iATROS CNTK TensorFlow等 CMU Sphinx是离线的语音识别工具 支持DSP等低功耗的离线应用场景
  • 推荐系统 用户画像 标签聚类 个性化搜索

    最近在做短视频推荐 和别的部门配合着做 我们部门做用户画像这一部分 回头看看 我们部门以前做的用户画像只能称之为 所谓的用户画像 如果一个人不懂用户画像还好指挥来指挥去真的让人无言 不知道其他公司的有没有这样的人儿那 哈哈 扯远了 言归正传
  • linux系统编程(五)针对linux系统中文件的IO操作

    文章目录 1 系统调用 2 C标准库文件IO函数 3 open close函数 3 1 函数原型 3 2 常用参数 3 3 open常见错误 4 文件描述符 4 1 PCB进程控制块 4 2 文件描述图表 4 3 最大打开文件数 4 4 F