一 linux 命令中重定向,使用>实现
通过 大于号 将前面的内容写入到某一个地方
cat main.c > b.txt //cat main 本身的意思是 显示main.c的值,后面加上 > b.txt 会将显示在屏幕上的字符全部写到b.txt中
例子:将 ls -l 的内容 通过重定向 保存到 list.txt中
hunandede@hunandede-virtual-machine:~$ ls -l > list.txt
hunandede@hunandede-virtual-machine:~$ cat list.txt
total 80
drwxrwxr-x 2 hunandede hunandede 4096 2月 20 18:55 aaa
drwxrwxr-x 4 hunandede hunandede 4096 2月 21 15:16 day02
drwxrwxr-x 11 hunandede hunandede 4096 2月 23 17:47 day03
drwxrwxr-x 2 hunandede hunandede 4096 2月 28 08:19 day04
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Desktop
drwxr-xr-x 3 hunandede hunandede 4096 1月 23 23:28 Documents
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Downloads
-rw-r--r-- 1 hunandede hunandede 8980 1月 23 20:24 examples.desktop
drwxrwxr-x 2 hunandede hunandede 4096 2月 20 17:39 head
-rw-rw-r-- 1 hunandede hunandede 0 2月 28 08:23 list.txt
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Music
drwxrwxr-x 2 hunandede hunandede 4096 1月 23 23:47 mydocument
drwxrwxr-x 3 hunandede hunandede 4096 2月 19 14:05 nginx
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Pictures
drwxrwxr-x 3 hunandede hunandede 4096 2月 23 00:40 projects
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Public
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Templates
drwxr-xr-x 2 hunandede hunandede 4096 1月 23 21:50 Videos
drwxr-xr-x 3 hunandede hunandede 4096 1月 23 21:04 下载
二 dup 和dup2 函数 重点是dup2函数的理解和应用
重定向对象的函数就是 dup 和dup2
dup 函数 dup函数主要是将 参数fd 拷贝一份,有就是让 多个fd 指向同一个文件
函数原型
#include <unistd.h>
int dup(int oldfd);
函数意思
The dup() system call creates a copy of the file descriptor oldfd,
dup函数主要是将 参数fd 拷贝一份,有就是让 多个fd 指向同一个文件
返回值:
成功,返回可以使用的新的值
失败,返回-1,errno被设置
RETURN VALUE
On success, these system calls return the new descriptor. On error, -1
is returned, and errno is set appropriately.

例子:
void duptest() {
const char* filepath = "/home/hunandede/linuxxitongbiancheng/functest/duptest.txt";
int fd = open(filepath, O_RDWR | O_CREAT, 0664);
int newfd = dup(fd);
char* writebuf = "nihao linux dup func test";
int realsize = strlen(writebuf);
cout << " C++ fd = " << fd
<< " newfd = " << newfd
<< " realsize = " << realsize << endl;
printf("C fd =%d ,newfd =%d, realsize=%d \n", fd,newfd,realsize);
write(newfd, writebuf, realsize);
write(newfd, "\n", 1);
write(newfd, "=====\n",6);
write(fd,"abc\n",4);
close(fd);
close(newfd);
}
dup2 函数 重点,让第二个参数也指向第一个参数指向的数据
函数原型
#include <unistd.h>
int dup2(int oldfd, int newfd);
函数参数
oldfd
newfd
函数意义:
使用oldfd 的值 替换 newfd的值
也就是说newfd也会指向oldfd的内容
返回值
成功的话,返回newfd 的值,失败返回-1,errno 被设置。

例子验证:
void dup2test2() {
write(STDOUT_FILENO, "before\n", 7);
const char* filepath3 = "/home/hunandede/linuxxitongbiancheng/functest/dup2test3.txt";
const char* filepath4 = "/home/hunandede/linuxxitongbiancheng/functest/dup2test4.txt";
int fd3 = open(filepath3, O_RDWR | O_CREAT, 0664);
int fd4 = open(filepath4, O_RDWR | O_CREAT, 0664);
int ret = dup2(fd3, fd4); //让fd4的指针 和 fd3指向的一样,那么给fd4 写数据,就会写给fd3指向的 文件
ret = dup2(fd3, STDOUT_FILENO);//让STDOUT_FILENO的指针 和fd3 指向的一样,那个给 STDOUT_FILENO写数据,就会写到 fd3上
write(fd3, "aaa", 3);
write(STDOUT_FILENO, "CCC\n", 4);
close(fd3);
close(fd4);
}
dup例子
//dup例子
int main(int argc , char * argv[]) {
int ret = 0;
//int fd = open(argv[1], O_RDWR);
int fd = open("/home/hunandede/projects/linuxcpp/main.cpp", O_RDWR);
//int fd = open("/home/hunandede/projects/linuxcpp/dict.txt", O_RDWR);
if(fd == -1)
{
perror("open file fail \n");
ret = fd;
return ret;
}
int fd2 = dup(fd);
if (fd2==-1) {
perror("dup fd error");
ret = fd2;
return ret;
}
printf("fd = %d,fd2 = %d\n",fd,fd2);
//那么这里可以通过fd2读取fd中的数据吗?测试一下
//测试中,我们发现,第一个bufread 前三个字符是 -17 -69 -65, 这是UTF-8格式的前三位,
char bufread[10] = { 0 };
ret = read(fd2,bufread,10);
if (ret<0) {
perror("read fd2 error\n");
return ret;
}
else {
cout << bufread << endl;
}
bufread[10] = { 0 };
ret = read(fd2, bufread, 10);
if (ret < 0) {
perror("read fd2 error\n");
return ret;
}
else {
cout << bufread << endl;
}
//前面使用fd2都是可以读取到的,且第二次读取会紧挨着第一次读取到的位置
//那么我们接着从fd读,是否可以呢?结果是可以的,那么我们要close 的时候,要close几次呢?
bufread[10] = { 0 };
ret = read(fd, bufread, 10);
if (ret < 0) {
perror("read fd error\n");
return ret;
}
else {
cout << bufread << endl;
}
ret= close(fd);
cout << "start close rer " << ret<< endl;
ret = close(fd2);
cout << "end close ret " << ret <<endl;
return 0;
}
dup2例子
//dup2例子,注意两个问题:
//1.open几个file,就要close几个file
//2.如果使用不同的fd,对同一个文件,进行读写,那么读取的位置不会发生变化,每次都在上一次读取的位置停留
int main() {
int ret = 0;
int fd = open("/home/hunandede/projects/linuxcpp/main.cpp", O_RDWR);
int fd2 = open("/home/hunandede/projects/linuxcpp/dict.txt", O_RDWR);
cout << fd << " " << fd2 << endl;
//fd中的内容替换了fd2中的内容,fd还是3,fd2还是4,
//不过是fd2指向的已经变成fd的指向了
ret = dup2(fd,fd2);
cout << "after " << fd << " " << fd2 << endl;
char readbuf[10] = { 0 };
read(fd2,readbuf,10);
cout << readbuf << endl;
readbuf[10] = { 0 };
read(fd2, readbuf, 10);
cout << readbuf << endl;
readbuf[10] = { 0 };
read(fd, readbuf, 10);
cout << readbuf << endl;
ret = close(fd);
cout << "ret = " << ret << endl;
ret = close(fd2);
cout << "retaaa = " << ret << endl;
return 0;
}
三 fcntl实现dup 和 dup2 知道就行
//fcntl实现dup, 知道有这种写法就行
int main() {
cout << "start " << endl;
int fd = open("/home/hunandede/projects/linuxcpp/main.cpp", O_RDWR);
int fd2 = fcntl(fd,F_DUPFD,0);
cout << fd << " " << fd2 << endl;
//最后一个参数,表示我们想要的fd的值,
但是fd2结果为 4,
这是因为0,已经被占用,
系统默认给一个可以使用的空闲的fd。
int fd3 = fcntl(fd,F_DUPFD,7);//结果为7 这是因为7没有被使用
char readbuf[10] = { 0 };
read(fd2, readbuf, 10);
cout << readbuf << endl;
readbuf[10] = { 0 };
read(fd2, readbuf, 10);
cout << readbuf << endl;
readbuf[10] = { 0 };
read(fd, readbuf, 10);
cout << readbuf << endl;
int ret = close(fd);
cout << "ret = " << ret << endl;
ret = close(fd2);//clost 这个也不会error,成功关闭。
cout << "retaaa = " << ret << endl;
return 0;
}
四,进程和程序,
程序和进程
程序,是指编译好的二进制文件,在磁盘上存放
不占用系统资源(cpu、内存、打开的文件、设备、锁....)
进程,是一个抽象的概念,与操作系统原理联系紧密。进程是活跃的程序,占用系统资源。在内存中执行。(程序运行起来,产生一个进程)
程序 → 剧本(纸) 进程 → 戏(舞台、演员、灯光、道具...)
同一个剧本可以在多个舞台同时上演。同样,同一个程序也可以加载为不同的进程(彼此之间互不影响)
如:同时开两个终端。各自都有一个bash但彼此ID不同。
五 多道程序设计: 了解
我们会一边看小说,一边听歌,这说明计算机 在 同时的进行两个不同的进程,一个QQ小说,一个千千静听
那么计算机是怎么做到的呢?
在单核CPU 的年代,是通过
它们在管理程序控制之下,相互穿插的运行。多道程序设计必须有硬件基础作为保证。
时钟中断即为多道程序设计模型的理论基础。 并发时,任意进程在执行期间都不希望放弃cpu。因此系统需要一种强制让进程让出cpu资源的手段。时钟中断有硬件基础作为保障,对进程而言不可抗拒。 操作系统中的中断处理函数,来负责调度程序执行。
在多道程序设计模型中,多个进程轮流使用CPU (分时复用CPU资源)。而当下常见CPU为纳秒级,1秒可以执行大约10亿条指令。由于人眼的反应速度是毫秒级,所以看似同时在运行。
1s = 1000ms, 1ms = 1000us, 1us = 1000ns 1000000000
实质上,并发是宏观并行,微观串行!
六 虚拟内存和物理内存映射关系
cpu执行代码的大致逻辑如下

我们在这里是要知道这个 MMU,MMU存在于CPU 的内部,是一个计算机的硬件,
主要功能是:
虚拟地址到物理地址的转换(即虚拟内存管理)、 我们在这里,主要了解这个功能
内存保护、也就是 修改 内存 访问级别。我们在这里,还需要了解这个功能
中央处理器高速缓存的控制 我们在这里,暂时不需要了解这个功能
关于 虚拟内存地址到 物理内存地址的转换
那么这个物理内存和虚拟内存分别指的是什么?他们又是怎么映射的呢?
当每一个程序跑起来的时候,就会给这个程序一个虚拟的内存,如下图左边的部分,
物理内存就是我们买电脑时配置的那块内存,当前假设是512M的金士顿的内存条
物理内存和虚拟内存通过MMU 这个中间翻译官进行映射

好,我们把上图再完善一下,当同时有两个进程 a.out 和 b.out 跑起来的情况

最终图:

对于 a.out的kernel 空间的内存通过MMU 内存映射后在物理内存上的区域 和 b.out 的kernel空间的内存通过 MMU内存映射后在屋里内存上的区域是同一块 区域。。
对于 a.out的user 空间的内存通过MMU 内存映射后在物理内存上的区域 和 b.out 的user 空间的内存通过 MMU内存映射后在屋里内存上的区域 不是同一块 区域。
为什么kernal 的内存就在同一块区域呢?原因是:linux的内核就只有一块共同的区域。因此所有的程序进程的kernel 就只能在这块区域。
也正是不同的进程的kernel中的元素 通过 MMU 最后 映射的物理内存是在 同一片 区域的,因此才有后面学习的进程间通讯的内容,不然怎么通讯呢?
关于 内存保护、也就是 修改 内存 访问级别
从前面的图我们了解到,如果是从user 区域的数据,通过MMU 映射到物理内存
如果是从kernel区域的数据,通过MMU也是映射到物理内存。
那么映射的这个物理内存是怎么区分的呢?
实际上 是MMU做了事情,如果MMU发现你的变量是从user 区域过来的,则在映射成物理内存的时候,会给这个物理内存映射成3级
如果MMU发现你的变量是从kernel 区域过来的,则在映射成物理内存的时候,会给这个物理内存映射成0级
当我们使用write 函数要从user 写到kernel的时候,MMU会将这个3级转化成0级,慢就慢在这里。慢在转化的这个过程。

七 pcb进程块详解
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块在代码上是task_struct结构体。
/usr/src/linux-headers-3.16.0-30/include/linux/sched.h文件中可以查看struct task_struct 结构体定义。其内部成员有很多,我们重点掌握以下部分即可:
* 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
* 进程的状态,有 初始态 就绪、运行、挂起、停止等状态。
* 进程切换时需要保存和恢复的一些CPU寄存器。例如正在计算a.out的一些数据,这时候时钟滴答到了,那么a.out 就要放弃CPU,这时候需要保存a.out 正在计算的状态。
* 描述虚拟地址空间的信息。
* 描述控制终端的信息。
* 当前工作目录(Current Working Directory)。
* umask掩码。
* 文件描述符表,包含很多指向file结构体的指针。
* 和信号相关的信息。
* 用户id和组id。
* 会话(Session)和进程组。
* 进程可以使用的资源上限(Resource Limit)。
进程状态图如下:

八 linux 相关的环境变量
8.0 环境变量的位置

8.1 使用 env命令查看所有的环境变量
常用的环境变量
LD_LIBRARY_PATH: 动态连接器
PATH: 记录可执行文件目录位置的
SHELL: 记录当前使用的解释器是那个
TERM:当前终端类型是啥,也就是那个黑框框类型是啥,在图形界面终端下它的值是xterm,终端类型决定了一些程序的输出显示方式。比如图形界面终端可以显示汉字,而字符终端一般不行。
LANG 语言和local,决定了字符编码以及时间,货币等信息的显示形式
HOME 当前用户主目录的路径,很多程序需要在主目录下保存配置文件,使得每个用户在运行该程序时都有自己的一套配置。
8.2 使用 echo $xxx 查看某一个具体的环境变量
echo 是回显的意思,如下的语句是显示PATH环境变量的值
echo $PATH
8.3 代码获取 环境变量,getenv函数
获取环境变量值
char *getenv(const char *name); 成功:返回环境变量的值;失败:NULL (name不存在)
练习:编程实现getenv函数。 【getenv.c】
8.4 设置 环境变量 ,setenv函数
设置环境变量的值
int setenv(const char *name, const char *value, int overwrite); 成功:0;失败:-1
参数overwrite取值: 1:覆盖原环境变量
0:不覆盖。(该参数常用于设置新环境变量,如:ABC = haha-day-night)
8.5 删除环境变量的定义, unsetenv函数
int unsetenv(const char *name); 成功:0;失败:-1
注意事项:name不存在仍返回0(成功),当name命名为"ABC="时则会出错。

被折叠的 条评论
为什么被折叠?



