《APUE》chapter 7 Process Environment 学习笔记(加上自己的代码)

Process  Environment

Everything start here -- main Function
int main(int argc ,char *argv []);


We are familiar with this guy... but I would like to repeat and write down my understanding  about this powerful function.
argc  是输入参数的个数。。。argv是指向输入参数的指针数组。

Process  Termination 进程的结束
There are eight  ways  for  a  process  to  terminate. Normal  termination  occurs  in  five ways:
1.  Return from main
2.  Calling exit
3.  Calling _exit or _Exit
4.  Return of the last thread from its start routine (Section 11.5)
5.  Calling pthread_exit from the last thread Abnormal termination occurs in three ways:
6.  Calling abort(Section 10.17)
7.  Receipt of a signal 
8.  Response of the last thread to a cancellation request (Sections 11.5 and 12.7)


Three functions terminate a program normally: _exit and _Exit, which return to the kernel  immediately,and exit, which  performs certain  cleanup  processing and  then returns to the kernel.

 

Exit Functions

 

可以看出,若直接调用_exit或者_Exit来结束当前进程,那么exit handler不会被调用,std 流也不会关闭,所以一般我们应尽量避免直接调用_exit或者_Exit。而应该调用exit或者直接return 0来安全的结束进程

#include<stdlib.h>
void exit(intstatus );
void _Exit(intstatus );
#include<unistd.h>
void _exit(intstatus );
 


_exit 和exit函数的declaration不在同一个头文件里面

 

/* Terminate program execution with the low-order 8 bits of STATUS.  */
extern void _exit (int __status) __attribute__ ((__noreturn__));


/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which theywere registered,
   perform stdio cleanup, and terminate programexecution with STATUS.  */
extern void exit(int __status) __THROW __attribute__ ((__noreturn__));


/* Terminate theprogram with STATUS without calling any of the
   functions registered with `atexit' or`on_exit'.  */
extern void_Exit (int __status) __THROW __attribute__ ((__noreturn__));
 



上面是在linux header file里面找到的相关定义和注释,可以看到_Exit函数是不会调用atexit函数或则on_exit函数来处理未关闭的流

 


exit function  has  always performed  a  clean shutdown  of  the standardI/O  library: the fclose function  is  called for  all  open streams.  This causes all buffered output data to be flushed (written to the file).

exit函数都需要一个整形参数status。我们称作exit status

Exit status是有意义的。

以下三种情况的exit status是未定义的:

1上面这些exit函数被调用的时候,没有参数输入

2 main函数return时候没有任何值或者没有return

3 main函数 没有被声明为int main(例如老谭喜欢的void main或者直接写main 没有返回值的类型declaration)

exit(0) 和 return 0是等价的

 

由下面代码的测试可以看出没有明确返回值类型的时候,返回值是什么


#include<stdio.h>
main()
{
        printf("hello world!\n");
}



root@ubuntu:/Ad_Pro_in_Unix/chapter_7#./a.out

hello world!

root@ubuntu:/Ad_Pro_in_Unix/chapter_7#echo $?

13

root@ubuntu:/Ad_Pro_in_Unix/chapter_7#cc -std=c99 ./pro_7_1.c

./pro_7_1.c:3:1:warning: return type defaults to ‘int’ [enabled by default]

root@ubuntu:/Ad_Pro_in_Unix/chapter_7#./a.out

hello world!

root@ubuntu:/Ad_Pro_in_Unix/chapter_7#echo $?

0

 

echo $? 是打印exit status的命令

在不同的C标准下(默认ANSI C ,–std=c99将编译标准设置为C99),没有明确返回值的类型,执行程序的时候,返回值是不同的


#include"apue.h"
#include"myerr.h"
 
static voidmy_exit1(void);
static voidmy_exit2(void);
 
int main()
{
        if(atexit(my_exit2) != 0)
        {
                err_sys("can't registermy_exit2\n");
        }
 
        if(atexit(my_exit1) != 0)
        {
                err_sys("can't registermy_exit1\n");
        }
 
        if(atexit(my_exit1) != 0)
        {
                err_sys("can't registermy_exit1\n");
        }
 
        printf("main is done\n");
        return 0;
}
 
static voidmy_exit1(void)
{
        printf("first exithandler\n");
}
 
static voidmy_exit2(void)
{
        printf("second exithandler\n");
}



root@ubuntu:/Ad_Pro_in_Unix/chapter_7#./a.out

main is done

first exithandler

first exithandler

second exithandler

 

上面这个demo就显示了atexit调用exit handler函数的情形

 

Command-Line  Arguments 命令行参数

#include<stdio.h>
#include<stdlib.h>
int main(intargc,char* argv[])
{
        int i = 0;
 
        for(i = 0; i<argc ;i++)
        {
               printf("argv[%d]:%s\n",i,argv[i]);
        }
 
        exit(0);
}


root@ubuntu:/Ad_Pro_in_Unix/chapter_7#./b.out arg1 hehe wakaka xixi

argv[0]:./b.out

argv[1]:arg1

argv[2]:hehe

argv[3]:wakaka

argv[4]:xixi

 

另外一种实现方法,由于 ISO C和POSIX.1标准同时要求argv[argc]的值是NULL指针。于是这里对于上面demo的for循环限制条件还可以写作:

for (i = 0;argv[i] != NULL; i++) 


Environment  List 环境表


每个程序都有environment list。就像参数列表。environment list是指向字符的指针数组,而这个指针数组的地址包含在全局变量environ里面

extern char **environ;


历史上unix的main函数参数是有三个的,前面个大家都知道了,和现在一样int argc,char* argv。而第三个参数就是 char *envp []。但是由于ISO C对于C的规范,于是去除了这第三个参数,转而用environ全局变量来代替。

#include <stdio.h>

extern char** environ;

int main()
{
        int temp = 0;

        while(*(environ+temp) != NULL)
        {
                printf("%s\n",*(environ+temp));
                temp++;
        }

        return 0;
}


一下是程序运行的结果,打印environment list

LC_PAPER=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
SSH_AGENT_PID=2051
LC_MONETARY=zh_CN.UTF-8
GPG_AGENT_INFO=/run/user/liuzjian/keyring-zSN3re/gpg:0:1
TERM=xterm
SHELL=/bin/bash
XDG_SESSION_COOKIE=d6b50e179c50c481f0a728c751dd11bd-1395226556.973367-1968843037
WINDOWID=39845894
LC_NUMERIC=zh_CN.UTF-8

。。。。。。。。

LC_NAME=zh_CN.UTF-8
OLDPWD=/Ad_Pro_in_Unix
_=./a.out

中间还有很多,就不全贴出来了


Memory Layout  of  a  C  Program C程序内存布局

Text  segment,  consisting  of  the  machine  instructions  that  the  CPU  executes.,  the  text  segment  is  often  read-only, to prevent  a program from accidentally modifying its instructions

Initialized  data  segment,  usually  called  simply  the  data  segment,  containing variables  that  are specifically  initialized  in  the  program. 

Uninitialized  data  segment,  often  called  the  ‘‘bss’’segment,  named  after  an ancient  assembler  operator  that stood  for  ‘‘block  started  by  symbol.’’Data  in this segment is initialized by the kernel to arithmetic 0 or null pointers before the program starts executing.  

Stack, where automatic variables are stored, along with information that is saved each  time  a  function  is  called.

Heap,  where dynamic  memory  allocation  usually  takes  place. Historically,the heap has been located between the uninitialized data and the stack.




jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_7$ size ./a.out
   text   data   bss   dec   hexfilename
   1400    560    16  1976   7b8./a.out


Shared Libraries 共享库

gcc默认编译方式是使用动态的,gcc加上 -static 是使用静态编译的方式

-static On systems that support dynamic linking, this prevents linking with the shared libraries. On other systems, this option has no effect.
a.out 是用默认动态编译方式生成的,b.out 是用-static静态编译方式生成的
jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_7$ size ./a.out ./b.out text   data    bss    dec    hex filename  797880   6192  11376 815448  c7158 ./a.out  1205    560      8   1773    6ed ./b.out
可以明显看出,动态编译能够很大程度上减少可执行程序的大小 http://blog.csdn.net/cinmyheart/article/details/22167075 如果忘记动态库和静态库的concept可以戳上面的link
Memory Allocation 内存分配

很熟悉这几个家伙了,不解释。。。
#include <stdlib.h>
void *malloc(size_t size );
void *calloc(size_t nobj ,size_tsize );
void *realloc(void *ptr,size_tnewsize);
All three return: non-null pointer if OK, NULL on error
void free(void *ptr);


 

Although sbrk (a system call) can expand  or  contract the  memory  of  a  process, most  versions  of malloc and free never  decrease their  memory  size. The space  that  we free  is available for a later allocation, but the freed space is not usually returned to the kernel; instead,that space is kept in the malloc pool.

 

Writing past the end or before the beginning of a dynamically allocated buffer can corrupt more than internal record-keeping information.

 

Other possible errors that can be fatal are freeing a block that was already freed and calling free with  a pointer  that  was not obtained  from  one of  the  three alloc functions.

 

Environment Variables 环境变量

The UNIX  kernel  never looks  at  these strings;只是shell 用的而已

We

normally set environment variables in a shell start-up file to control the shell’s actions.

 

#include <stdlib.h>
char *getenv(const char *name);
Returns: pointer to value associated with name,NULL if not found


#include <stdio.h>
#include <stdlib.h>
int main()
{
       printf("getenv returned value:%s\n",getenv("HOME"));
       return 0;
}



Test  result:

root@ubuntu:/Ad_Pro_in_Unix/chapter_7# ./a.out

getenv returned value:/root

 

#include <stdlib.h>
int putenv(char *str);
Returns: 0 if OK, nonzero on error
int setenv(const char * name, const char*value,int rewrite);
int unsetenv(const char *name);
Both return: 0 if OK, −1 on error


•Theputenv function  takes a  string  of  the  form name=value and  places it  in  the

environment list. If namealready exists, its olddefinition is first removed.

•The setenv function  sets name to value.If name already  exists in  the

environment, then (a) if re writeis nonzero, theexisting definition for nameis first

re moved; or  (b)  ifre writeis 0,  an  existing definition  for name is  not removed,

nameis not set to the new value,and no erroroccurs.

•The unsetenv function  removes any  definition  of name.It is not  an error  if

such a definition does not exist.

环境变量我用的很少。。。所以现在就没怎么练习这几个API。。。

 

setjmp and longjmp Functions

挺有意思的函数


#include <setjmp.h>
int setjmp(jmp_buf env );
Returns: 0 if called directly ,nonzero ifreturning from a call to longjmp
void longjmp(jmp_bufenv ,int val);

 

 

test code:

#include "apue.h"
#include <setjmp.h>
 
static void f1(int,int,int,int);
static void f2(void);
 
static jmp_buf jmpbuffer;
static int globval;
 
int main()
{
        int autoval;
       register int regival;
       volatile int volaval;
       static int statval;
 
       globval = 1;
       autoval = 2;
       regival = 3;
       volaval = 4;
       statval = 5;
 
       if(setjmp(jmpbuffer) != 0)
        {
               printf("after longjmp:\n");
               printf("globval = %d,autoval = %d,"\
                       "regival =%d,volaval = %d,"\
                        "statval =%d\n",globval,autoval,regival,volaval,statval);
               exit(0);
        }
 
       globval = 95;
       autoval = 96;
       regival = 97;
       volaval = 98;
       statval = 99;
 
       f1(autoval, regival,volaval,statval);
       return 0;
}


Test result:

root@ubuntu:/Ad_Pro_in_Unix/chapter_7# ./a.out
in f1():
globval = 95,statval = 96,regival = 97,volaval =98, statval = 99
after longjmp:
globval = 95,autoval = 96,regival = 97,volaval =98,statval = 99
root@ubuntu:/Ad_Pro_in_Unix/chapter_7# vim./pro_7_13.c
root@ubuntu:/Ad_Pro_in_Unix/chapter_7# gcc -O ./pro_7_13.c-g -o ./a.out
root@ubuntu:/Ad_Pro_in_Unix/chapter_7# ./a.out
in f1():
globval = 95,statval = 96,regival = 97,volaval =98, statval = 99
after longjmp:
globval = 95,autoval = 2,regival = 3,volaval =98,statval = 99

Without  optimization, all  five  variables are stored  in memory  (the register hint  is ignored  forregival).  When we enable  optimization, both autovaland regivalgo into registers, even though the former wasn’t declared register,and the volatile variable stays in memory.

 

 

 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值