复合类型和结构体

结构体

结构体定义和引用

1
2
3
4
5
6
7
//定义一个二维坐标的结构体
struct complex_struct{
double x,y;
}z1,z2;
//对定义的结构体进行引用
struct complex_struct z3,z4;

定义了结构体后就可以对结构体类型进行引用,也就是可以重新定义类似z1,z2这样的变量。

结构体初始化和赋值

结构体变量可以在定义的时候进行赋值。

1
2
3
4
//直接进行变量的顺序初始化
struct complex_struct z={3.0,4.0}
//在变量后多使用一个,不会产生超出变量数量的影响
struct complex_struct z={.x=3.0,.y=4.0,}

乱序和顺序初始化的结果没有区别,乱序初始化直观,成员可以不按照顺序进行初始化,可以更好的对成员变量进行维护。乱序初始化可以只初始化某一个对象,而不是顺序初始化全部的对象,乱序初始化默认将没有赋值的成员变量进行赋值为0。

顺序初始化
1
2
3
4
struct complex_struct z1={
.x=3.0,
.y=4.0
};
乱序初始化
1
2
3
4
5
6
7
//乱序初始化所有对象
struct complex_struct z2={
.y=4.0,
.x=3.0
};
//乱序初始化部分对象
struct complex_struct z3={.y=4.0};

linux IO

linux文件基本属性

参考:https://www.runoob.com/linux/linux-file-attr-permission.html

linux 权限命令

使用chown(change owner)命令对文件所属的用户和组进行修改
使用chome(change mode)命令对用户的权限进行修改
典型的使用就是chown给一个用户授权,用户使用chome对文件进行权限的修改。

ls -l命令对文件的属性和文件的所属的用户和组进行显示

文件属性

1
2
3
4
[root@www /]# ls -l
total 64
dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin
dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot

bin文件的第一个属性是d,表示该文件是一个目录。
在 Linux 中第一个字符代表这个文件是目录、文件或链接文件等等。
当为 d 则是目录
当为 - 则是文件;
若是 l 则表示为链接文档(link file);
若是 b 则表示为装置文件里面的可供储存的接口设备(可随机存取装置);
若是 c 则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)
Alt text
从左到右九位表示文件的类型,13位表示文件拥有者对这个文件权限,46位表示所属的同组成员对这个文件的权限,7~9位表示对其他用户对这个文件的权限。
r表示可以进行读取,w表示可以进行写入,x表示可以执行文件。

Linux文件属主和属组

对于一个文件,它有一个特定的所有者,对这个文件具有所有权的用户。
同时,在Linux系统中,用户是按组分类的,一个用户属于一个或多个组。文件所有者以外的用户又可以分为文件所属组的同组用户和其他用户。因此,Linux系统按文件所有者、文件所有者同组用户和其他用户来规定了不同的文件访问权限。
上述bin文件目录对于所属用户和同组用户都有执行和读取的权限,包括其他用户也有执行和读取的权限。

修改文件属性

chgrp 修改文件所属的组
chown 修改文件所属的所有者
chmod 修改文件的属性
r表示读取(对应4) w表示写入(对应2) x表示执行(对应1)
7=r+w+x=4+2+1
chmod 文件名 777 是常用于root赋予文件可执行读写权限的命令。

文件操作

文件描述符012

对于linux内核而言,所有打开的文件都可以作为文件描述符进行引用,文件描述符是一个非负整数,当打开一个现有文件或者创建一个文件时,内核向这个进程返回一个文件描述符。
012 作为默认的三个文件描述符:
0 STDIN_FILENO 标准输入
1 STDOUT_FILENO 标准输出
2 STDERR_FILENO 标准错误输出
在程序刚开始运行时,有三个文件被自动打开了,占用了 0 1 2 这三个描述符:
依次打开的三个文件分别是/dev/stdin,/dev/stdout,/dev/stderr
程序开始运行时,默认会调用open(“/dev/stdin”, O_RDONLY)将其打开,返回的文件描述符是0
在打开这个以后,再执行:open(“/dev/stdout”, O_WRONLY); 所以这个文件的返回值自然是1

open

1
2
3
4
5
6
7
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
返回值:成功返回新分配的文件描述符,出错返回-1并设置errno

open 通过设置flags参数进行操作设置,和fopen函数有以下不同:
以可写的方式fopen一个文件时,如果文件不存在会自动创建,而open一个文件时必须明确指定O_CREAT才会创建文件,否则文件不存在就出错返回。

以w或w+方式fopen一个文件时,如果文件已存在就截断为0字节,而open一个文件时必须明确指定O_TRUNC才会截断文件,否则直接在原来的数据上改写。

close

1
2
3
4
#include <unistd.h>

int close(int fd);
返回值:成功返回0,出错返回-1并设置errno

close函数关闭一个已经打开的文件。fd类似windows中的句柄,在linux中叫做文件描述符。当一个进程终止时,内核对该进程所有没有关闭的文件描述符,使用close进行关闭。

read

1
2
3
4
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0

读常规文件时,在读到count个字节之前已到达文件末尾。例如,距文件末尾还有30个字节而请求读100个字节,则read返回30,下次read将返回0。
从终端设备读,通常以行为单位,读到换行符就返回了。

write

1
2
3
4
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
返回值:成功返回写入的字节数,出错返回-1并设置errno

write函数向打开的设备或文件中写数据。

阻塞和非阻塞

当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。与睡眠状态相对的是运行(Running)状态,在Linux内核中,处于运行状态的进程分为两种情况:
正在被调度执行。CPU处于该进程的上下文环境中,程序计数器(eip)里保存着该进程的指令地址,通用寄存器里保存着该进程运算过程的中间结果,正在执行该进程的指令,正在读写该进程的地址空间。
就绪状态。该进程不需要等待什么事件发生,随时都可以执行,但CPU暂时还在执行另一个进程,所以该进程在一个就绪队列中等待被内核调度。内核的调度算法是基于优先级和时间片的,会对进程的执行优先情况进行控制。

lseek

1
2
3
4
#include <sys/types.h>
#include <unistd.h>

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

若lseek成功执行,则返回读写位置偏移量

fcntl

参考:https://www.cnblogs.com/xuyh/p/3273082.html

1
2
3
4
5
6
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);

ioctl

1
2
3
4
5
6
#include <sys/ioctl.h>

int ioctl(int d, int request, ...);
ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换的精度和工作频率通过ioctl设置。

#include <sys/ioctl.h>

mmap

1
2
3
4
#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);
int munmap(void *addr, size_t len);

mmap函数可以把磁盘文件的一部分直接映射到内存中,这样文件中的位置就会有对应的内存地址,对文件的读写可以使用指针而不是read和write函数。
如果addr参数为NULL,内核会自己在进程地址空间中选择合适的地址建立映射。如果addr不是NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择addr之上的某个合适的地址开始映射。建立映射后,真正的映射首地址通过返回值可以得到。len参数是需要映射的那一部分文件的长度。off参数是从文件的什么位置开始映射,必须是页大小的整数倍(在32位体系统结构上通常是4K)。filedes是代表该文件的描述符。
prot参数有四种取值:

PROT_EXEC表示映射的这一段可执行,例如映射共享库

PROT_READ表示映射的这一段可读

PROT_WRITE表示映射的这一段可写

PROT_NONE表示映射的这一段不可访问

flag参数有很多种取值,这里只讲两种,其它取值可查看mmap(2)

MAP_SHARED多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。

MAP_PRIVATE多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。

如果mmap成功则返回映射首地址,如果出错则返回常数MAP_FAILED。当进程终止时,该进程的映射内存会自动解除,也可以调用munmap解除映射。munmap成功返回0,出错返回-1。