嵌入式Linux

一、文件 I/O

1.1、文件描述符

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传递给read或write
文件描述符的变化范围是0 ~ OPEN_MAX - 1

注意:
文件描述符 0:标准输入(STDIN_FILENO)
文件描述符 1:标准输出(STDOUT_FILENO)
文件描述符 2:标准错误(STDERR_FILENO)

1.2、函数 open

调用open函数可以打开或创建一个文件

#include
#include
#include

int open(const char *path, int oflag,mode_t mode);

参数:
path: 是要打开或创建文件的名字(包含路径)
oflag:可用来说明此函数的多个选项
       O_RDONLY				只读打开
	   O_WRONLY				只写打开
	   O_RDWR				可读、写打开		
	   O_EXEC				只执行打开
	   O_SEARCH				只搜索打开(应用于目录)
	   
以上5个常量中必须指定一个且只能指定一个,以下常量则是可选

	   O_APPEND				每次写时都追加到文件的尾端
	   O_CLOEXEC			把FD_CLOEXEC常量设置为文件描述符标志
	   O_CREAT				若文件不存在则创建它
	   O_DIRECTORY			如果path引用的不是目录,则出错
	   O_EXCL				如果同时指定了O_CREAT,而文件已经存在,则出错
	   O_NOCTTY				如果path引用的是终端设备,则不将该设备分配作为此进程的控制终端
	   O_NOFOLLON			如果path引用的是一个符号链接,则出错
	   O_NONBLOCK			如果path引用的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的I/O操作设置非阻塞方式
	   O_SYNC				使每次write等待物理I/O操作完成,包括由该write操作引起的文件属性更新所需的I/O
	   O_TRUNC				如果此文件存在,而且为只读或读-写成功打开,则将其长度截断为0
	   O_TTY_INIT			如果打开一个还未打开的终端设备,设置非标准termios参数值,使其符合Single UNLX Specification

mode:记录待创建的文件的访问权限

返回值:若成功,返回文件描述符;若失败返回-1

1.3、 函数 creat

调用creat函数创建一个文件

#include
#include
#include

int creat(const char *pathname, mode_t mode);

参数:
pathname:创建文件的名字(包含路径)
mode:	  记录待创建的文件的访问权限

返回值:若成功,返回为只写打开的文件描述符;若出错,返回-1

注意:此函数等效于:open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
creat 的一个不足之处是它以只写方式打开所创建的文件。在提供open的新版本之前,如果要创建一个临时文件,并要先写该文件,然后又读该文件,则必须先调用creat、close,然后再调用open。现在则可用下列方式调用open实现:

open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);

1.4、函数 close

可调用close函数关闭一个打开文件

#include

int close(int fd);
参数:
fd:是指需要关闭文件的描述符

返回值:若成功,返回0;若出错,返回-1

关闭一个文件时还会释放该进程加在该文件上的所有记录锁

1.5、 函数 lseek

每个打开文件都有一个与其相关联的“当前文件偏移量”(current file offset)。它通常是一个非负整数,用以度量从文件开始处计算的字节数。
通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。按系统默认的情况,当打开一个文件时,除非指定 O_APPEND 选项,否则该偏移量被设置为0

#include
#include

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

参数:
fd:     文件描述符
offset:偏移值
whence:
		若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处 offset 个字节
		若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负
		若whence是SEEK_END,则将该文件的偏移量设置为文件的长度加offset,offset可正可负

返回值:若成功,返回新的文件偏移量;若出错,返回-1

获取当前偏移量的位置

off_t 	  currpos;
currpos = lseek(fd, 0, SEEK_CUR); // 表示从当前位置偏移0个字节,并返回偏移量

这种方法可用来确定所涉及的文件是否可以设置偏移量。如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE

1.6、函数 read

调用read函数从打开文件中读取数据

#include

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

参数:
fd:    文件描述符
buf:  将读取到的数据存入buf缓冲区中
count:读取的字节数

返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1

理论上read的返回值和count的大小一样,但也有读取到的字节数小于count

  • 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前有30字节,而要求读100字节,则read返回30。下次再调用read时,它将返回0(文件尾端)。
  • 当从终端设备读时,通常一次最多读一行。
  • 当从网络读时,网络中的缓冲机制可能造成返回值小于所要求读的字节数
  • 当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数
  • 当从某些面向记录的设备(如磁带)读时,一次最多返回一个记录
  • 当一信号造成中断,而已经读了部分数据量时。

1.7、 函数 write

调用write函数向打开文件写入数据

#include

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

参数:
fd:     文件描述符
buf:   写入缓冲区,将需要写入文件的内容存入buf中
count: 写入的字节数

返回值:若成功,返回已写的字节数;若出错,返回-1

其返回值通常与参数count的值相同,否则表示出错。write出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。
对于普通文件,写操作从文件的当前偏移量处开始。如果在打开该文件时,指定了 O_APPEND 选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。

1.8、函数 fopen

FILE *fopen(char *filename, *type);

参数:
filename: 文件名称
mode:     打开模式:
					r   只读方式打开一个文本文件                           
			        rb  只读方式打开一个二进制文件                         
			        w   只写方式打开一个文本文件                           
			        wb  只写方式打开一个二进制文件                         
			        a   追加方式打开一个文本文件                           
			        ab  追加方式打开一个二进制文件                         
			        r+  可读可写方式打开一个文本文件                       
			        rb+ 可读可写方式打开一个二进制文件                     
			        w+  可读可写方式创建一个文本文件                       
			        wb+ 可读可写方式生成一个二进制文件                     
			        a+  可读可写追加方式打开一个文本文件                   
			        ab+ 可读可写方式追加一个二进制文件    
返回值:返回一个文件指针

open和fopen的区别:

  • 前者属于低级IO,后者是高级IO。
  • 前者返回一个文件描述符,后者返回一个文件指针。
  • 前者无缓冲,后者有缓冲。 前者与 read,write 等配合使用, 后者与 fread, fwrite等配合使用。
  • 后者是在前者的基础上扩充而来的,在大多数情况下,用后者。

1.9、函数 fwrite

例程

1 、打开一个文件,如果文件不存在则创建

#include 
#include 
#include 
#include 

int main()
{
    int fd;

 // 如果file1文件不存在则会创建file1文件,如果存在open函数会返回-1
    fd = open("./file1", O_RDWR | O_CREAT | O_EXCL, 0x600);	//0600 代表可读可写权限
    if(fd == -1)                         // O_EXCL 文件存在则返回-1
        printf("file exist!!!\n");
    else
        printf("file creat succeed!!!\n");
        
    return 0;
}

2、在文件末尾添加数据,不改变原本内容!!!

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

int main()
{
    int fd;
    char *buf = "welcom to Linux !!!";

    /* 
    	O_RDWR    是从文件的开头开始写入,并覆盖原有的内容
		O_APPEND  在文件末尾处写入,不改变原有内容
		O_TRUNC   在清除文件的数据,并头开始写入
	*/

    fd = open("./file1", O_RDWR | O_APPEND); // 可读可写,光标移动至尾端
    printf("open file success!!!\n");

    int n_write = write(fd, buf, strlen(buf));
    
    if(n_write != -1)
        printf("write %d byte to file\n", n_write);
        
    close(fd);
    return 0;
}

3、创建一个文件

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

int main()
{
	int fd;
	char *buf = "Hello Linux World";

	fd = creat("./file2", S_IRWXU);     // 创建文件,权限是可读可写可执行
	
   /************************************** 
    *常见创建模式(mode):                 vi 
	* 宏表示		数字		              
	* S_IRUSR   4        可读                
	* S_IWUSR   2		 可写             
	* S_IXUSR   1        可执行
	* S_IRWXU   7        可读可写可执行
	**/
	
	write(fd, buf, strlen(buf));        // 写入文件

	return 0;
}

4、打开text文件,若没有该文件则创建。 之后在text中写入数据,最后在读取出来显示在屏幕并计算数据大小。

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

int 
main()
{
	int fd;
	char *Write_buf = "Welcom to Linuix\n";
	
	fd = open("./test", O_RDWR | O_APPEND);	// 写入的内容在尾端直接添加
	if(fd == -1)
	{
		printf("open file fiale!!!\n");
		fd = open("./test", O_RDWR | O_CREAT | O_APPEND, 0600);
		if(fd > 0)
			printf("creat file success!!!\n");
		else
		{
			printf("creat file faile!!!\n");
			exit(0);
		}
     }

	int byte = write(fd, Write_buf, strlen(Write_buf)); // 写入的大小,单位字节
	printf("write file %d byte\n", byte);

	char *read_buf = (char *)malloc(sizeof(char) * byte);	// 申请空间
// 由于偏移量在尾端,需要往前移动,具体移动多少?
	lseek(fd, -byte, SEEK_END);		// 在末尾写入多少我们就往前移动多少,最后读取写入的内容

	byte = read(fd, read_buf, strlen(Write_buf));
	
	printf("read:%s\nread Byte:%d\n",read_buf, byte);	// 输出写入的内容

	close(fd);	// 一定要记得关闭
	return 0;
}

5、获取一个文件中内容的字节数

#include 
#include 
#include 
#include 
#include 
#include 
#include 
int
main()
{
	int fd;
	fd=open("test",O_RDWR);
	char *write_buf="hello linux!";
	if(fd==-1)
	{
		printf("no file\n");
		fd=open("test",O_RDWR|O_CREAT,0600);
		if(fd > 0)
			printf("file creat success\n");
		else if(fd == -1)
		{
			printf("file creat failt! \n");
			exit(0);
		}
	}
	printf("fd=%d\n",fd);
	int size=lseek(fd,0,SEEK_END);	// 获取字节数
	printf("file size=%d\n",size);
	close(fd);
	return 0;
}

6、实现复制文件cp指令

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

int main(int argc, char **argv)
{
    int fdSrc;
    int fdDes;
    char *readBuf = NULL;	// 定义野指针

    if(argc != 3)
    {
        printf("pararn error!!!\n");
        exit(-1);
    }
    fdSrc = open(argv[1], O_RDWR);
    int size = lseek(fdSrc, 0, SEEK_END);   // 计算字节大小
    lseek(fdSrc, 0, SEEK_SET);              // 把光标移动至开头
    // 申请空间内存
    readBuf = (char *)malloc(sizeof(char) * size + 8);   // 加上8个字节为了防止溢出的可能
    int n_read = read(fdSrc, readBuf, size);	// 有多少字节读取多少字节
    // O_TRUNC 的存在使得先清空再写入数据
    fdDes = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0600);
    int n_write = write(fdDes, readBuf, strlen(readBuf));

    close(fdSrc);
    close(fdDes);
    return 0;
}

7、从键盘上获取数据,并输出数据

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

int main()
{
    int fd;
    char readBuf[128];
    
/*
	0	标准输入
	1	标准输出
	2	标准错误

*/
	// 读取键盘的输入
    int n_read = read(0, readBuf, 10);		// 标准输入
    int n_write = write(1, readBuf, strlen(readBuf));
    return 0;
}

8、向file1文件中添加整型数,并输出写入文件中的数字

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

int main()
{
    int fd;
    int data = 100;
    int data2 = 0;

    fd = open("./file1", O_RDWR);
    int n_write = write(fd, &data, sizeof(int));
    lseek(fd, 0, SEEK_SET);
    int n_read = read(fd, &data2, sizeof(int));

    printf("read %d \n", data2);
    close(fd);
    
    return 0;
}

9、向文件中写入一个结构体

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

typedef struct
{
    int a;
    char c;
}Data;

int main()
{
    int fd;
    /*定义结构体*/
    Data data = {100, 'c'};
    Data data2;   
    
    fd = open("./file1", O_RDWR);
    // 将data数据写入file1文件中
    int n_write = write(fd, &data, sizeof(Data));
    // 光标回到开头
    lseek(fd, 0, SEEK_SET);
    // 将file1中的数据读取到data2中
    int n_read = read(fd, &data2, sizeof(Data));

    // 输出data2的数据
    printf("read %d,%c \n", data2.a, data2.c);
    close(fd);
   
    return 0;
}

10、fopen函数的用法

#include 
#include 
#include 
int main()
{
    FILE *fp;
    char *str = "welcom to LINUX!!!";
    char *readBuf = NULL;	// 定义野指针
    fp = fopen("./jian.txt","w+");

//  size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    /**
     * 参数:
     * *ptr    代表 缓冲区地址
     * size    代表 写入长度
     * nmemb   代表 写入字节的长度
     * stream  代表 哪个文件
     */
    fwrite(str, sizeof(char), strlen(str), fp);		// 写入数据(光标在末尾)
//  fwrite(str, sizeof(char)*strlen(str), 1, fp);
    int size = fseek(fp, 0, SEEK_END);			// 获取长度
    fseek(fp, 0, SEEK_SET);				// 移动光标至开头
    readBuf = (char *)malloc(sizeof(char)*size + 8);    // 申请空间内存
//  size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    fread(readBuf, sizeof(char), strlen(str), fp);	// 读取写入的数据

    printf("read data: %s\n", readBuf);			// 输出读取到的数据

    return 0;
}

二、进程环境

2.1、 main 函数

C程序总是从main函数开始执行。
main函数的原型是:

int main(int argc, char *argv[]);int main(int argc, char **argv);

其中,argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组。

2.2、进程终止

有8种方式使进程停止(termination),其中5种为正常终止

  1. 从main返回
  2. 调用exit
  3. 调用 _exit 或 _Exit
  4. 最后一个线程从启动例程返回
  5. 从最后一个线程调用 pthread_exit

异常终止:

  1. 调用abort
  2. 接到一个信号
  3. 最后一个线程对取消请求做出响应

2.2、退出函数

3个函数用于正常终止一个程序: _exit 和 _Exit 立即进入内核,exit 则先执行一些清理处理,然后返回内核

#include "stdlin.h"

void exit(int status);
void _Exit(int status);

#include "unistd.h"
void _exit(int status);

exit()用来正常终结目前进程的执行,并把参数status返回给父进程,
而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。

mian函数返回一个整型值与该值调用exit是等价的

exit(0);
等价于
return 0

2.3、C程序的存储空间布局

嵌入式Linux_第1张图片

  1. 正文段。这是由CPU执行的机器指令部分。通常,正文段是可共享,所以即使是频繁执行的程序在存储器中也只需有一个副本,另外,正文段常常是只读,以防止程序由于意外而修改其指令。
  2. 初始化数据段。通常将此段称为数据段,它包含了程序中需明确地赋初值的变量。例如, int max = 100;
  3. 未初始化数据段。通常将此段称为bss段,在程序开始执行之前,内核将此段中的数据初始化为0或空指针。例如,int max[100];
  4. 栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中,通常函数中的局部变量存放在栈中。函数中的形参是不会分配具体的空间,当函数被调用的时候,具体给出的参数,也就是实参,其实是一份拷贝,它的内存空间在该函数的堆中进行分配。
  5. 堆。通常在堆中进行动态存储分配,通常在堆中进行动态存储分配,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减)

你可能感兴趣的:(Linux,嵌入式,linux)