Process Environment
int main(int argc ,char *argv []);
argc 是输入参数的个数。。。argv是指向输入参数的指针数组。
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__));
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.