您的位置:首页 > 运维架构 > Linux

Linux进程与线程之一

2015-10-10 10:47 375 查看
                     每日一结

一 动态库和静态库 

1.命名方法 

静态库 : libname.a 

动态库 : libname.so 

注意   :库的名字是name 

 

2.链接动态库和静态库的区别 

(1)链接静态库 

如果链接的是静态库,编译器将程序中调用的库中函数接口的实现代码链接到可执行

文件中,生成可执行文件体积大.程序运行的时候,不需要库 。 

(2)链接动态库 

如果链接的是动态库,编译器将程序中调用的函数所在的库的名字记录在可执行文件中

,生成可执行文件体积小.程序运行的时候,需要加载动态库[OS]。

 

3.制作静态库

(1)将.c文件编译成.o文件  

gcc -c file.c -o file.o 

.... 

 

(2)将.o文件打包成静态库 

ar -cr libname.a  *.o 

(3)关于如何运行的问题:

I.第一种方法是举例如下:

gcc  main.c -o main -L . -ladd

其相关解释如下:main.c 是我们要编译的最终文件。main是我们打算生成的可执行文件。-L . 表示指示我们当前的库的路径是在当前目录下。-ladd表示我们要链接的库的名称是add(注意:l 和 库之间没有空格)

II.第二种方法是:

先将库文件拷贝到/usr/include目录下,然后再执行如下命令:

gcc main.c -o main -ladd

这一步只是比上一步少指定了一个路径

 

Q:为什么我把我制作的库文件拷贝到/usr/include目录下时,再在主函数中声明库函数时,然后用 gcc main.c -o main 执行,为何链接不成功。该满足的条件都满足了,为何不能像C库一样呢,非得要再指定是哪个库。究其原因,老师给的答案是,gcc只能识别它自带的库,希望知道的读者能给个更具体一点的答案或者推荐一下这方面的书籍,谢谢!

 

4.gcc 链接库的参数 

注意:gcc 默认到/usr/lib 和/lib链接库,gcc只能识别它自带的库,第三方库[不是编译器自带库]无法识别 

-L  告诉编译器库的路径 

-l  告诉编译器库的名字 

例如:

gcc main.c -L 库的路径 -l库的命令

 

5.制作动态库 

(1)将.c文件编译成.o文件  

gcc -c file.c -o file.o 

.... 

(2)将.o文件打包成动态库 

gcc -shared *.o -o libname.so 

(3)然后将动态库打包到动态库的搜索路径下

(4)最后用gcc main.c -o main -ladd即可完成程序从编译到运行

注:对于动态库必须先将其打包到动态库的搜索路径下才能成功,可是为什么我用-L指定路径就不可以呢?

关于这个原因至今还没太整明白,望知道的朋友给个答案。

6、动态库的路径问题 为了让执行程序顺利找到动态库,有三种方法:

(1)把库拷贝到/usr/lib和/lib目录下。

(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。例如动态库libhello.so在/home/ting/lib目录下,以bash为例,使用命令: $export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ting/lib

(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾,并执行ldconfig刷新。这样,加入的目录下的所有库文件都可见。

------------------------------------------------------------------------------------------------------------------

查看可执行文件所依赖的动态库:

readelf -a  可执行文件名 | grep "Shared"

 

ldd命令也可以查看一个可执行文件所依赖的动态库:

例如# ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld- linux.so.2 (0×40000000)

可以看到ln命令依赖于libc库和ld-linux库

 

nm:

有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印库中的涉及到的所有的符号。库既可以是静态的也可以是动态的。nm列出的符号很多,常见的有三种:

一种是在库中被调用,但并没有在库中定义(表明需要其他库的支持),用U表示。

一种是库中定义的函数,用T表示,这是最常见的;

一种是所谓的“弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

----------------------------------------------------------------------------------------------------------------

7.当静态库和动态库同时存在,编译器默认链接的是动态库 。 

  如果想强制链接静态库,需要加上 -static 参数  



 

8.给库加上版本号 

libtest.so.0.1 , libtest.so.0.2 

让编译器链接库成功,必须创建创建一个软链接 

ln -s libtest.so.0.2  libtest.so 

(注:给它加个软链接的办法是可以避免当库升级时,多个同名的库使)

 

9.运行进程需要那些资源? 

CPU资源 ,内存资源,时间片资源 

 

10. 进程与程序 

程序:一个存放在磁盘上的可执行文件 

进程:是程序的一次执行过程,伴随着资源的分配和释放。相对于OS而言。

 

 

11. 进程的组成

栈 ,堆,ro data,data段,bss段,代码段,一组寄存器值[pc]

 

12. Linux 对进程描述 

PID  :标识一个进程

PPID :标识父进程 

进程内存空间 

进程状态 : R(运行态),T(停止态),S(等待态), Z(僵尸态)

....

Linux 用task_struct 结构体描述一个进程 

 

13.进程相关命令

(1)查看PID和PPID 

ps -ef | grep 进程名或进程号 

(2)查看进程的状态 

ps aux  | grep 进程名或进程号 

D : 不可中断等待态 

S : 可中断的等待态 

T : 停止态 

Z : 僵尸态 

R : 运行态 

+:表示在前台运行,没有+号表示其在后台运行

(3)给进程发信号 

kill -信号的序号  PID 

查看信号的序号:

kill -l

或 

killall -信号的序号  进程名  

 

(4)运行程序的时候,指定进程的优先级 

nice  -值  可执行文件 

nice值 [-20-19]

例如:

nice  -10   ./while 

nice --10   ./while 

 

(5)更改已经运的进程nice值 

renice  nice值  PID 

例如:

renice   10  1235 

renice  -10  1235 

(6)top查看当前进程的优先级,它是每秒刷新一次(其中PR是代表优先级,NI代表nice)

(7)例如我有个可执行程序while

那么 ./while可以让其在前台运行;

./while&可以让其在后台运行。

 

14. 创建子进程 

pid_t fork(void);

功能:创建一个子进程 

返回值:

成功给父进程返回子进程的PID,给子进程返回0,失败返回-1 

 

创建子进程过程:复制父进程来创建子进程 [复制内容:代码段共享,其他都复制] 

 

注:从fork()函数之后,父进程和子进程的运行顺序是不确定的,因为进程和进程之间是相互独立的

 

思考:创建完子进程后,父进程从fork的下一条语句执行,子进程为什么也是从fork的下一条语句执行?

创建子进程的时候拷贝父进程的PC寄存器值 

具体示例如下:



上述代码分析如下:

用fork()函数创建了父子进程的时候,子进程与父进程的拷贝情况如下:



注:其中代码段(txt段)是共享的

注:从上述代码中,注意去理解子进程和父进程的关系

 

 

 

深入理解fork()函数的另一段代码:

(关于其解释,看注释)









 

-----------------------------------------------------------

僵尸态进程:进程结束了,没有进行收尸处理

孤儿进程  :父进程结束了,子进程会变成孤儿进程,会自动被init进程收养 

-----------------------------------------------------------

 

pid_t vfork(void);

功能:创建一个子进程 

返回值:

成功给父进程返回子进程的PID,给子进程返回0,失败返回-1 



注:注意其中的一个全局变量global_var和局部变量local_var的变化,以理解vfork()执行过程中, 子进程和父进程的先后顺序

vfork 与 fork区别:

(1)fork创建的子进程后,父子进程执行的顺序不确定并且父子进程的地址空间是独立的

(2)vfork创建完子进程后,保证子进程先执行,父进程在子进程结束或子进程调用了exec函数族后,才开始运行。

   并且父子进程共享同一个地址空间[如果子进程调用了exec函数族,则子进程会独立出来]

 

15. exec函数族

功能:在一个进程中执行另外一个程序

 

思考:exec函数族如何执行另外一个程序?

用新程序的代码段,数据段,堆栈替换原程序,只保留原进程的PID,其他全部替换

 

I:参数以列表的形式传递

 

int execl(const char *path, const char *arg, ...);

参数:

@path 可执行程序的路径 

@arg  可执行程序的名字

@arg1 给可执行程序传递的第一个参数 

@arg2 给可执行程序传递的第二个参数 

...

@最后一个参数写成NULL 

返回值:

成功返回0,失败返回-1 

例如:执行/bin/ls ,传递的参数-l 

execl("/bin/ls","ls","-l",NULL);



注:由于执行execl()函数,它就代替了当前的那个进程,所以后面的最后那个printf()就不会执行。

 

v:参数以指针数组的形式传递 

int execv(const char *path, char *const argv[]);

char *p_arry[] = {"ls","-l",NULL};

execv("/bin/ls",p_arry);

p:执行的可执行程序从PATH环境变量中搜索

int execlp(const char *file, const char *arg, ...);

execlp("ls","ls","-l",NULL);

-----------------------------------------------------------

int execvp(const char *file, char *const argv[]);

char *p_arry[] = {"ls","-l",NULL};

execvp("ls",p_arry);

 

任务:

I.多进程拷贝 

<1>父进程拷贝文件的前一半 

<2>子进程拷贝文件的后一半

 

思考:

<1>.获取文件大小(stat)

<2>.子进程中需要重新打开文件,定位到一半的位置

代码示例如下:









注:待整理

-----------------------------------------------------------

 

II.实现myshell

 

<1>让父进程用fgets读取用户的输入,解析出可执行文件名和传递的参数 

<2>创建子进程,在子进程中调用execvp执行可执行文件 

代码示例如下:



注:有空对照一下自己写的代码

字符串分割函数 

char *strtok(char *str, const char *delim);

功能:根据分隔符号来分割字符串 

参数:

@str   第一次:字符串首地址    后面传递:NULL [告诉strtok函数接着上一次后面操作]

@delim 分割字符串 

返回值:

成功返回子串的首地址,结束返回NULL  

 

关注微信公众号获取更多资讯



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: