Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1378628
  • 博文数量: 370
  • 博客积分: 10654
  • 博客等级: 中将
  • 技术积分: 4396
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-07 15:44
文章分类

全部博文(370)

文章存档

2012年(36)

2011年(195)

2010年(139)

分类: LINUX

2011-08-06 17:51:15

                     西邮Linux兴趣小组10暑期学习周总结

                   第三周(729日至84日)

                                 第四组

组长:李攀

组员:王情杨东明 韩伟 周媛媛 张京鹏

本周任务

     《鸟哥的linux私房菜》:第八章第九章    第八章:在本章节中,熟悉linux文件系统,重点的掌握fdisk命令和mount命令的应用,熟悉ln命令,会区别软链接与硬链接的区别,熟悉启动与载入的过程。在本章中要求熟练掌握/etc/fstab文件的修改。第九章:在本章中主要掌握文件的压缩与解压缩的方法,要求熟练掌握tar的各种参数的应用,了解其他格式包的接压缩方式,如:rarzip

     LinuxC 编程实战》第七章:进程控制    理解进程的概念,看一些操作系统方面的书,这样能有助于理解进程的概念。

                                     计科1005李攀第三周学习报告

学习心得:

        这周主要是对linux系统的进程概念进行理解和运用,对于多用户,多任务的linux系统来说,进程十一个非常重要的功能;在系统编程中对于进程的控制,主要是使用forkvfork函数,这两个函数是非常特殊的,因为这两个函数调用一次就会返回两次,这两次分别是在父进程和子进程返回,其中父进程返回子进程的id,子进程返回0,这样通过这两个不同的返回值,我们就可以实现对父子进程的控制。在这一找个你的学习中,内容开始加深,理解有一定的困难,但是只要并不轻易放弃,多看几遍,多动手写程序,就一定能学好。


学习进度:


linuxc 编程实战》 第七章 进程的控制

《鸟个的私房菜》 第十一章

《数据结构》第三章


遇到的问题:


cons的作用:


   1)可以定义const常量        例如:

             constint Max=100;

             intArray[Max];        

   2)便于进行类型检查           例如:

             voidf(const int i) { .........}

        编译器就会知道i是一个常量,不允许修改;

   3)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。

        还是上面的例子,如果在函数体内修改了i,编译器就会报错;

        例如:

             voidf(const int i) { i=10;//error! }

    (5)为函数重载提供了一个参考。

         classA

         {

           ......

           voidf(int i)       {......} file://一个函数

           voidf(int i) const {......} file://上一个函数的重载

            ......

          };

     (6)可以节省空间,避免不必要的内存分配。

         例如:

              #definePI 3.14159         file://常量宏

              constdoulbe  Pi=3.14159;  file://此时并未将Pi放入ROM

              ......

              doublei=Pi;               file://此时为Pi分配内存,以后不再分配!

              doubleI=PI;               file://编译期间进行宏替换,分配内存

              doublej=Pi;               file://没有内存分配

              doubleJ=PI;               file://再进行宏替换,又一次分配内存!

        const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

     7)提高了效率。

          编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。


使用const:


   1)修饰一般常量,常数组,常对象

  修饰符const可以用在类型说明符前,也可以用在类型说明符后。     例如:   

           intconst x=2;  或  constint x=2;


      intconst a[5]={1, 2, 3, 4, 5};    constint a[5]={1, 2, 3, 4, 5};


           classA;      constA a;  或     Aconst a;

    

   2)修饰指针

        constint *A;   或  intconst *A; //const修饰指向的对象,A可变,A指向的对象不可变

        int*const A;             //const修饰指针A,    A不可变,A指向的对象可变

        constint *const A;      //指针AA指向的对象都不可变

   3)修饰引用

      constdouble & v;      该引用所引用的对象不能被更新

 (4)修饰函数的返回值:

       const修饰符也可以修饰函数的返回值,是返回值不可被改变,格式如下:

            constint Fun1();

            constMyClass Fun2();

   5)修饰类的成员函数:

        const修饰符也可以修饰类的成员函数,格式如下:

            classClassName

     {

             public:

                 intFun() const;

                   .....

             }

        这样,在调用函数Fun时就不能修改类里面的数据

    6)在另一连接文件中引用const常量

         externconst int i;     //正确的引用

         externconst int j=10;  //错误!常量不可以被再次赋值




*******************放在类内部的常量有什么限制?


        classA

        {

         private:

           constint c3 = 7;               // err

           staticint c4 = 7;               // err

           staticconst float c5 = 7;  // err

          ......

  };


初始化类内部的常量


        1初始化列表:

         classA

         {

          public:

                A(inti=0):test(i) {}

          private:

                constint i;

          }

         2外部初始化,例如:

         classA

         {

          public:

                A(){}

          private:

                staticconst int i;  

          }

          constint A::i=3;

僵尸进程的产生:


        一个进程在调用exit命令结束自己的生命的时候,其实linux中的僵尸进程它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用waitwaitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。


传统上,unix操作系统下运行的应用程序、服务器以及其他程序都被称为进程,而linux也继承了来自unix进程的概念。必须要理解下,程序是指的存储在存储设备上(如磁盘)包含了可执行机器指令(二进制代码)和数据的静态实体;而进程可以认为是已经被OS从磁盘加载到内存上的、动态的、可运行的指令与数据的集合,是在运行的动态实体。这里指的指令和数据的集合可以理解为linuxELF文件格式中的.text.data数据段。


进程,进程组和会话:


        那什么是进程组呢?顾名思义,进程组就是一些进程的组合。这些进程并不是孤立的,他们彼此之间或者存在父子、兄弟关系,或者在功能上有相近的联系。

        那为啥linux里要有进程组呢?其实,提供进程组就是为了方便对进程进行管理。假设要完成一个任务,需要同时并发100个进程。当用户处于某种原因要终止这个任务时,要是没有进程组,就需要手动的一个个去杀死这100个进程,并且必须要严格按照进程间父子兄弟关系顺序,否则会扰乱进程树。有了进程组,就可以将这100个进程设置为一个进程组,它们共有1个组号(pgrp),并且有选取一个进程作为组长(通常是“辈分”最高的那个^_^)。现在就可以通过杀死整个进程组,来关闭这100个进程,并且是严格有序的。

        进程必定属于一个进程组,也只能属于一个进程组。一个进程组中可以包含多个进程。进程组的生命周期从被创建开始,到其内所有进程终止或离开该组。

        内核中,sys_getpgrp()系统调用用来获取当前进程所在进程组号;sys_setpgid(intpid, int pgid)调用用来设置置顶进程pid的进程组号为pgid

        再看下会话。由于linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话。每个会话都有一个会话首领(leader),即创建会话的进程

        sys_setsid()调用能个创建一个会话。必须注意的是,只有当前进程不是进程组的组长时,才能创建一个新的会话。


未解决问题:

        linuxc 中,怎么清除键盘缓冲区?

                           计科1004王情第三周学习总结



学习心得:

      通过这一周通过学习进程和线程,我对操作系统有了一定的了解接触了很多新知识,在过程中有许多不理解的地方,通过查资料大部分问题已解决,但仍有知识还是需要再去反复的理解,学习


学习进度:

       LinuxC编程实战》第七章第八章

         《鸟哥的linux私房菜》第八章

遇到的问题:


守护进程:

守护进程简介

  linux或者unix中在系统的引导的时候会开启很多服务,这些服务就叫做守护进程。为了增加灵活性,root可以选择系统开启的模式,这些模式叫做运行级别,每一种运行级别以一定的方式配置系统。守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。守护进程,也就是通常说的Daemon进程,是Linux中的服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的,同时,守护进程还能完成许多系统任务,例如,作业规划进程crond、打印进程lqd等(这里的结尾字母d就是Daemon的意思)。

  由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到整个系统关闭时才退出。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程。
创建守护进程:1.创建子进程,父进程退出

  这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离。

  在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成的子进程。
2.在子进程中创建新会话:

  这个步骤是创建守护进程中最重要的一步,虽然它的实现非常简单,但它的意义却非常重大。在这里使用的是setsid,在具体介绍setsid之前,首先要了解两个概念:进程组和会话期

  进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。

  会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。

  接下来就可以具体介绍setsid的相关内容:

  setsid函数作用:

  setsid函数用于创建一个新的会话,并担任该会话组的组长。

    调用setsid有下面的3个作用:

  让进程摆脱原会话的控制

  让进程摆脱原进程组的控制

  让进程摆脱原控制终端的控制

  那么,在创建守护进程时为什么要调用setsid函数呢?由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。由于在调用了fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,还还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。
3.改变当前目录为根目录:  这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数式chdir
4.重设文件权限掩码  文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)
5.关闭文件描述符

  同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。

  在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,0123个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。通常按如下方式关闭文件描述符:

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

  for(i=0;i

  close(i);

  ===============================
6.守护进程退出处理

  当用户需要外部停止守护进程运行时,往往会使用kill命令停止该守护进程。所以,守护进程中需要

  编码来实现kill发出的signal信号处理,达到进程的正常退出。

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

  signal(SIGTERM,sigterm_handler);

  voidsigterm_handler(int arg)

  {

  _running= 0;

  }

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

Exec函数族:       简介:exec函数族,顾名思义,就是一簇函数,他把当前进程映像替换成新的程序文件,而且该程序通常从main函数开始执行!

说是exec系统调用,实际上在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是:

#include

externchar **environ;

intexecl(const char *path, const char *arg, ...);

intexeclp(const char *file, const char *arg, ...);

intexecle(const char *path, const char *arg, ..., char * const envp[]);

intexecv(const char *path, char *const argv[]);

intexecvp(const char *file, char *const argv[]);

intexecve(const char *path, char *const argv[], char *const envp[]);

其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件,如果不是可以执行的文件,那么就解释成为一个shell文件,sh**执行!

深入:

       上面6条函数看起来似乎很复杂,但实际上无论是作用还是用法都非常相似,只有很微小的差别。

参数argc指出了运行该程序时命令行参数的个数,数组argv存放了所有的命令行参数,数组envp存放了所有的环境变量。环境变量指的是一组值,从用户登录后就一直存在,很多应用程序需要依靠它来确定系统的一些细节,我们最常见的环境变量是PATH,它指出了应到哪里去搜索应用程序,如/binHOME也是比较常见的环境变量,它指出了我们在系统中的个人目录。环境变量一般以字符串"XXX=xxx"的形式存在,XXX表示变量名,xxx表示变量的值。

值得一提的是,argv数组和envp数组存放的都是指向字符串的指针,这两个数组都以一个NULL元素表示数组的结尾。

现在来看一下exec函数族,先把注意力集中在execve上:

intexecve(const char *path, char *const argv[], char *const envp[]);

execve1个参数path是被执行应用程序的完整路径,第2个参数argv就是传给被执行应用程序的命令行参数,第3个参数envp是传给被执行应用程序的环境变量。

在这里有点要注意,不管是arg0,还是argv[0]都必须是程序的可执行文件的名字,比如:

execl("/bin/echo","echo", "executed by execl", NULL)中的echo

execl("/bin/ls","ls", "/azuo", "-la", (char *)0 )中的ls

execlp("echo","echo", "executed by execlp", NULL)中的echo

留心看一下这6个函数还可以发现,前3个函数都是以execl开头的,后3个都是以execv开头的,它们的区别在于,execv开头的函数是以"char*argv[]"这样的形式传递命令行参数,而execl开头的函数采用了我们更容易习惯的方式,把参数一个一个列出来,然后以一个NULL表示结束。这里的NULL的作用和argv数组里的NULL作用是一样的。

这里建议使用(char*)0 代替NULL

在全部6个函数中,只有execleexecve使用了char*envp[]传递环境变量,其它的4个函数都没有这个参数,这并不意味着它们不传递环境变量,这4个函数将把默认的环境变量不做任何修改地传给被执行的应用程序。而execleexecve会用指定的环境变量去替代默认的那些。

还有2个以p结尾的函数execlpexecvp,咋看起来,它们和execlexecv的差别很小,事实也确是如此,除execlpexecvp之外的4个函数都要求,它们的第1个参数path必须是一个完整的路径,如"/bin/ls";而execlpexecvp的第1个参数file可以简单到仅仅是一个文件名,如"ls",这两个函数可以自动到环境变量PATH制定的目录里去寻找。

  

尚未解决的问题:

        1.《实战》184页,用vfork创建的进程运行结果不知为何不对?只把子进程中的break改成exit(0)后运行正确;只把intvar=1;移到外面也运行正确;而若只把intglobvar=5 移到里面结果就会很奇怪。


           

                     

                               
      • 《实战》195页,为什么将文件的权限改变后用户仍不能将test.c文件成功打开?


             

网络1001杨东明第三周学习总结

心得体会

     在这一周,我主要认识和学习了shell,并且学习了有关进程方面的一些东西。

问题

      1set

      PS1='\[\e]0;\u@\h:\w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '

       怎么解读,尤其是{debian_chroot:+($debian_chroot)}

        还有“+”等一类的特殊符号都什么意思??



     2、鸟哥中谈到“在原本运行的bash下运行子进程时,父进程会sleep”,如果是在多个cpu或者超线程下还是这样的么?

如果不是的话父进程中的环境变量子进程还会继承么?  使用export设置后,父进程的自定义变量还会在子进程中被使用么?

                                  软件1001韩伟第三周学习总结

.学习进度
   
由于这周有点事和内容越来越难理解了,进度慢了点。《C编程实战》第七章,《鸟哥的私房菜》第八章。
.学习中遇到的问题
1.
咋样让一个父进程先于子进程结束让子进程成为一个孤儿进程
exit()吗 为啥break也行  既然break行 为啥之前创建的进程也是用break 此进程就不是孤儿进程呢
2.
定义pid时用pid_tint有啥区别
3.wait(&stat_val)
而且stat_val的值还是由自己去定的 什么用法
4.
调用exec函数族时说argv参数是一个以NULL结尾的字符串数组怎么理解,又有什么涵义
5.
守护进程这块还是很难理解
.学习情况总结
      
这周主要花费了大量时间看进程这张,由于接触的概念多,也不好理解,所以进度慢了点,现在对进程有了了解,但对第七章myshell的编程还是很不懂,下来我会多查资料多询问别人,强加练习这章!《鸟哥》慢慢感受到了man的好处,接下来会强加练习巩固所学的知识!


                                  软件1001 周媛媛 第三周学习总结

1.对于《C编程实战》第七章看了前三章,第四章稍微看了一点。对于《鸟哥的私房菜》看了第九章和第十章,基本上都掌握了。第八章虽然看了,但不咋的懂,所以就跳过了。

2.《鸟哥的私房菜》中vim 的附加功能:块选择有问题。如果你需要选择多条语句,但最长的那个语句不在末尾的话,就不能将所有内容都选上,即只能根据最后一行句子的长度来确定块的长度。3.PIDID一样吗,有什么联系?4.守护进程不理解,有难度,所以就跳过了,只是选择性的看了能理解的东西。进程会话与会话组长不理解。5.通过这几周的学习,我觉得书应该反复的看,看不明白的不宜过度纠结,先跳过,之后再回过头来看。把时间浪费在某一点上而停滞不前很不好。

                              计科1002张京鹏第三周学习总结报告

学习内容:

               1.学习鸟哥的第89章; 

               2.初步了解了shellLinux的账号管理;

               3.学习Linux C实战编程中的第7章; 

               4.学习有关数据结构的有关知识;

遇到的问题:  

               1.在学习图的过程中,不会根据结点关系创建十字链表;

                  2.关于2级指针与字符串数组的运用,以及与整形二维数组中应用时规则的不一致,我认为这和寻址有关系,那么‘[]’这个符号是否就是寻址的呢?具体的寻址规则又是什么呢?                                             

                                小组周总结

组内讨论人员: 李攀王情 杨东明 韩伟 周媛媛 张京鹏

在这一周中,小组成员按照学习计划,基本上都完成了任务,都对Linux操作系统有了进一步的认识,熟悉认识了进程和线程等概念以及了解了磁盘文件系统等知识,收获颇丰。组员们遇到问题大部分都能通过自己以及网络解决,剩下的一些问题通过周末组内讨论,绝大部分也已得到解决。   

                                        


阅读(894) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~