目录
linux下安装gcc和g++
su 到root用户下,输入命令
yum install gcc 即可安装gcc,然后再
yum install gcc-c++ 即可安装g++
第一次在linux下编译运行c++程序:
用vim编辑器新建一个cpp文件:vim helloworld.cpp
在此文件中写好c++程序,然后:wq退出
在终端中输入 g++ -o main helloworld.cpp -lm 即可编译cpp程序,并将生成的程序命名为main
若编译成功,则不会返回任何信息
然后输入./main来调用刚刚生成的文件,效果:
Vi三种模式详解
命令行模式 (command mode/一般模式)
任何时候,不管用户处于何种模式,只要按一下“ESC”键,即可使Vi进入命令行模式;我们在shell环境(提示符为$)下输入启动Vi命令,进入编辑器时,也是处于该模式下。
在该模式下,用户可以输入各种合法的Vi命令,用于管理自己的文档。此时从键盘上输入的任何字符都被当做编辑命令来解释,若输入的字符是合法的Vi命令,则Vi在接受用户命令之后完成相应的动作。但需注意的是,所输入的命令并不在屏幕上显示出来。若输入的字符不是Vi的合法命令,Vi会响铃报警。
文本输入模式 (input mode/编辑模式)
在命令模式下输入插入命令i(insert)、附加命令a (append)、打开命令o(open)、修改命令c(change)、取代命令r或替换命令s都可以进入文本输入模式。在该模式下,用户输入的任何字符都被Vi当做文件内容保存起来,并将其显示在屏幕上。在文本输入过程中,若想回到命令模式下,按"ESC"键即可。
末行模式 (last line mode/指令列命令模式)
末行模式也称ex转义模式。
Vi和Ex编辑器的功能是相同的,二者主要区别是用户界面。在Vi中,命令通常是单个键,例如i、a、o等;而在Ex中,命令是以按回车键结束的正文行。Vi有一个专门的“转义”命令,可访问很多面向行的Ex命令。在命令模式下,用户按“:”键即可进入末行模式下,此时Vi会在显示窗口的最后一行(通常也是屏幕的最后一行)显示一个“:”作为末行模式的提示符,等待用户输入命令。多数文件管理命令都是在此模式下执行的(如把编辑缓冲区的内容写到文件中等)。末行命令执行完后,Vi自动回到命令模式。
dd命令用于复制文件并对原文件的内容进行转换和格式化处理。dd命令功能很强大的,对于一些比较底层的问题,使用dd命令往往可以得到出人意料的效果。用的比较多的还是用dd来备份裸设备。但是不推荐,如果需要备份oracle裸设备,可以使用rman备份,或使用第三方软件备份,使用dd的话,管理起来不太方便。
df命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为KB。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。
top命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过top命令所提供的互动式界面,用热键可以管理。
netstat命令用来打印Linux中网络系统的状态信息,可让你得知整个Linux系统的网络情况。
du查看目录大小,df查看磁盘使用情况。
我常使用的命令(必要时,sudo使用root权限),
1).查看某个目录的大小:du -hs /home/master/documents
查看目录下所有目录的大小并按大小降序排列:sudo du -sm /etc/* | sort -nr | less
2).查看磁盘使用情况(文件系统的使用情况):sudo df -h
df --block-size=GB
-h是使输出结果更易于人类阅读;du -s只展示目录的使用总量(不分别展示各个子目录情况),-m是以
MB为单位展示目录的大小(当然-k/-g就是KB/GB了)。
3,du使用详细案例
a:显示全部目录和其次目录下的每个档案所占的磁盘空间
s:只显示各档案大小的总合
b:大小用bytes来表示
x:跳过在不同文件系统上的目录不予统计
a:递归地显示指定目录中各文件及子孙目录中各文件占用的数据块数
...
进程
父子进程究竟共享啥,复制了啥:
#include <stdio.h>
#include <stdlib.h>
#include < unistd.h>
int global = 1; //初始化的全局变量,存在data段
int main(){
pid_t pid;
int stack =1; //局部变量,存在栈
int *heap; //指向堆变量的指针
heap=(int *)malloc(sizeof(int));
*heap=3;
pid=fork();
if(pid<0){
perror("fail to fork");
exit(-1);
}
else if(pid==0){
//子进程, 改变变量的值
global++;
stack++;
(*heap)++;
printf("in sub-process, global:%d, stack:%d, *heap:%d \n", global,stack,*heap);
exit(0);
}
else{
sleep(2); //休眠2 秒钟,确保子进程已执行完毕, 再执行父进程
printf("in parent-process, global:%d, stack:%d, *heap:%d \n", global,stack,*heap);
}
return 0;
}
执行结果:
事实上,子进程完全复制了父进程的地址空间的内容,包括堆栈段和数据段的内容。但是,子进程并没有复制代码段,而是和父进程共用代码段。这样做是合理的,因为子进程可能执行不同的流程来改变数据段和堆栈段,因此需要分开存储父子进程各自的数据段和堆栈段。但是代码段是只读的,不存在被修改的问题,因此代码段可以让父子进程共享,以节省存储空间。父进程和子进程共享fork之后的程序段、打开的文件、工作目录、共享内存,注意锁不继承。
1.fork()函数
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
printf("xxxxxxxxxxxxxxx\n");
pid = fork();
if(pid == -1){
perror("fork error:");
exit(1);
}
else if(pid==0)
printf("i'm child ,pid = %u,ppid=%u \n",get_pid(),get_ppid());
else{
printf("i'm parent,pid = %u,ppid=%u \n",get_pid(),get_ppid());
sleep(1);
}
printf("yyyyyyyyyyyyyyy\n");
return 0;
}
循环创建n个子进程:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
int i;
pid_t pid;
printf("xxxxxxxxxxxxxxx\n");
for(i=0;i<5;i++){
pid = fork();
if(pid == 0){
break;
}
if(i<5)
printf("i'm child ,pid = %u,ppid=%u \n",get_pid(),get_ppid());
else{
printf("i'm parent,pid = %u,ppid=%u \n",get_pid(),get_ppid());
sleep(i);
}
printf("yyyyyyyyyyyyyyy\n");
return 0;
}
2.fork()、vfork()、clone()的区别
进程的四要素:
(1)有一段程序供其执行(不一定是一个进程所专有的),就像一场戏必须有自己的剧本。
(2)有自己的专用系统堆栈空间(私有财产)
(3)有进程控制块(task_struct)(“有身份证,PID”)
(4)有独立的存储空间。
缺少第四条的称为线程,如果完全没有用户空间称为内核线程,共享用户空间的称为用户线程。
一、fork()
fork()函数调用成功:返回两个值; 父进程:返回子进程的PID;子进程:返回0;
失败:返回-1;
fork 创造的子进程复制了父亲进程的资源(写时复制技术),包括内存的内容task_struct内容(2个进程的pid不同)。这里是资源的复制不是指针的复制。
读时共享写时复制(Copy-On-Write):在fork()之后exec之前两个进程用的是相同的物理空间(内存区),先把页表映射关系建立起来,并不真正将内存拷贝。子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父进程中有更改相应段的行为发生时,如进程写访问,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。fork时子进程获得父进程数据空间、堆和栈的复制所以变量的地址(当然是虚拟地址)是一样的。
二、vfork()
vfork是一个过时的应用,vfork也是创建一个子进程,但是子进程共享父进程的空间。在vfork创建子进程之后,父进程阻塞,直到子进程执行了exec()或者exit()。vfork最初是因为fork没有实现COW机制,而很多情况下fork之后会紧接着exec,而exec的执行相当于之前fork复制的空间全部变成了无用功,所以设计了vfork。而现在fork使用了COW机制,唯一的代价仅仅是复制父进程页表的代价,所以vfork不应该出现在新的代码之中。
3.clone
clone是Linux为创建线程设计的(虽然也可以用clone创建进程)。所以可以说clone是fork的升级版本,不仅可以创建进程或者线程,还可以指定创建新的命名空间(namespace)、有选择的继承父进程的内存、甚至可以将创建出来的进程变成父进程的兄弟进程等等。
clone函数功能强大,带了众多参数,它提供了一个非常灵活自由的常见进程的方法。因此由他创建的进程要比前面2种方法要复杂。clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系。
问题:clone和fork的区别:
(1) clone和fork的调用方式很不相同,clone调用需要传入一个函数,该函数在子进程中执行。
(2)clone和fork最大不同在于clone不再复制父进程的栈空间,而是自己创建一个新的。 (void *child_stack,)也就是第二个参数,需要分配栈指针的空间大小,所以它不再是继承或者复制,而是全新的创造。
fork底层实现
linux通过clone()系统调用实现fork()。
fork(),vfork(),和_clone()库函数都根据各自需要的参数标志去调用clone(),然后由clone()去调用do_fork()。
do_fork()完成了创建中的大部分工作,它定义在kernel/fork.c中。该函数调用copy_process(),copy_process()实现的工作如下
- 1.调用dup_task_struct()为新进程创建一个内核栈,thread_info结构和task_struct,这些值与当前进程的值相同。此时,子进程和父进程的描述符是完全相同的。
- 2.检查新创建的这个子进程后,当前用户所拥有的进程数目没有超出给他分配的资源的限制。
- 3.现在,子进程着手使自己与父进程区别开来。进程描述符内的许多成员都要被清0或设为初始值。进程描述符的成员值并不是继承而来的,而主要是统计信息,进程描述符中大多数的数据都是共享的。
- 4.接下来,子进程的状态被设置为TASK_UNINTERRUPTIBLE(不可中断)以保证它不会投入运行。
- 5.copy_process()调用copy_flags()以更新task_struct的flags成员。表明进程是否拥有超级用户权限的PF_SUPERPRIV标志被清0.表名进程还没有调用exec()函数的PF_FORKNOEXEC标志被设置。
- 6.调用get_pid()为新进程获取一个有效的PID
- 7.根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件,文件系统信息,信号处理函数,进程地址空间和命名空间等。在一般情况下,这些资源会被给定进程的所有线程共享;否则,这些资源对每个进程是不同的,因此被拷贝到这里。
- 8.让父进程和子进程平分剩余的时间片
- 9.最后copy_process()做扫尾工作并返回一个指向子进程的指针
再回到do_fork()函数,如果copy_process函数成功返回,新创建的子进程被唤醒并让其投入运行。内核有意选择子进程首先执行。因为一般子进程都会马上调用exec()函数