库和C程序存储分配
在之前先记录一个库文件的制作方法,它基于分文件编程之上,也是分文件,模块化的延展。
关于库文件的博文推荐
库相关的理论性博文
分文件的编程思想:主要逻辑代码main.c 功能实习代码 xxx.c 包含功能函数和相关信息、参数的 xxx.h
main.c 中包含 “xxx.h”
gcc mian.c xxx.c -I ./ -o test
库文件分为静态库(.a文件)和动态共享库(.so文件);
静态库的制作:
首先生成.o文件
gcc xxx.c -c //编译C文件生成OBJ对象文件
ar rcs libxxx.a xxx.o xxx.o//将OBJ文件加载到一起生成静态库.a文件
动态库的制作:
gcc -shared -fpic xxx.c xxx.c -o libxxx.so //将C功能程序集成到动态库.so文件中
库文件的使用:
静态库—gcc main.c -l静态库文件名 -I ./ -L ./ -o test1 //编译包含声明了库函数头文件的主程序,-l链接库名,-I大写的i指定包含的头文件在当前路径./ -L指定的库在当前路径./ -o为生成的可执行程序命名为test1。
动态共享库—gcc main.c -l动态库名 -I ./ -L ./ -o test2 //动态库的使用和静态库一样,区别在静态库在链接的时候已经把功能程序加了进去,而动态共享库的使用虽然链接了库但是功能程序没加进去,它是在执行的过程中实时从动态库中提取功能。所以直接运行使用动态库的可执行程序可能会出现错误。通常需要把相应的动态库文件放到系统指定文件夹mv libxxx.so /usr/lib 或者配置环境变量(库函数名:砍头(去掉lib)去尾(去掉.a或.so的后缀))
动态库环境变量配置
终端输入export列出环境变量
export LD_LIBRARY_PATH="这里放动态库的路径"
将环境变量与shell脚本写在一起
vi test.sh
将上面环境变量的配置和执行加载动态库的执行程序写在一起。线程中有提到
关于分别用动态库和静态库生成的同一程序,适用 du 或者 size 指令即可看出区别
#C程序的存储空间
参考优质博文
下方内容取自 CSDN博主「编码赚钱娶老婆」的原创文章 ,感谢博主的分享
原文链接:https://blog.csdn.net/weixin_39923782/article/details/105487735
正文段(code):这是有CPU执行的机器指令。正文段是可共享的,所以即使所示频繁执行程序(文本编辑器,C编译器、shell等)在存储器也只需要有一个副本,另外正文段常常是只读的,以防止程序出现意外而修改其指令;
初始化数据段(data):通常此段称为数据段,它包含了需明确的赋值的变量像全局变量 int maxcount = 99;那么maxcount 变量就放在数据段
未初始化的数据段(bss):通常称为bss段,像全局变量 long sum[];sum就是存储早bss段
栈 (stack):自动变量以及每次函数调用时所保存的信息都放在此段,像函数的参数,函数体内变量,返回值。栈顶在低地址,栈底在高地址。
堆区(heap):通常在堆中进行动态分配像使用malloc函数分配空间。
fork()创建的父子进程在内存空间上是写时拷贝,vfork()创建的 父子进程中子进程直接使用父进程的内存,不进行拷贝
进程
概念:进程是程序的一次运行活动
补充:Linux下使用ps -aux|grep a.out 查看进程,top相当于任务管理器 kill 进程ID(结束相应的进程)
进程ID中 0为交换进程,1为init系统初始化进程
父进程创建子进程的写实拷贝。进程的退出状态可以被收集并用宏解析出来。
exec族函数借鉴博文
exec族函数同门借鉴
API:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
pid_t pid;//定义一个存放进程ID号的变量
pid_t fork(void);//创建进程1
pid_t vfork(void);//创建进程2,与1的区别在于如果创建多个进程,父进程会等待子进程退出才运行,而fork()创建的父子进程相互争着运行。
pid_t wait(int *wstatus);//进程等待,用于等待子进程退出
pid_t waitpid(pid_t pid, int *wstatus, int options);//可设选项的进程等待
pid_t getpid(void);//获取当前进程的ID进程标识符
pid_t getppid(void);//获取当前进程的父进程ID
exit();//退出进程(标准C库) exit为正常退出
_exit(); _Exit();//(系统调用)
//异常退出
//abort()
//执行系统指令的API(exc族函数)
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
int system(const char *command);//参数为Linux下的指令或可执行文件名
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);//popen执行指令或程序,并返回一个指向存储结果的文件指针,pclose关闭文件指针
int chdir(const char *path);
int fchdir(int fd);//在程序中进入某个文件夹
进程退出及其状态收集
参考博文
在wait函数中传参一个整型指针去接收进程退出的状态值,想要看到正确的状态值需要使用宏解析.
fork()模拟客户端介入服务端的场景
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int passwd;
pid_t pid;
pid=getpid();//获取最初进程的ID
printf("server ID:%d\n",pid);
while(1)
{
passwd=0;
printf("please input passwd :\n");
scanf("%d",&passwd);
getchar();
if(passwd==1)
{
//如果这里调用vfork那么父进程就不在等待按键输入,而阻塞。
//子进程退出,父进程没有wait,子进程变成僵尸进程。
//父进程在子进程结束前先退出,子进程被init进程收留成为孤儿进程。
if(fork()==0)//调用fork会返回两次,父进程那次返回子进程的ID号(非负整数),子进程那次返回0;
//fork创建进程失败返回-1;
{
while(1)
{
sleep(2);
printf("server ID:%d client ID:%d\n",getppid(),getpid());
}
}
}
}
return 0;
}
DIY—FTP服务器程序的指令执行部分
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define ZL 68
#define PT 3000
#define YES 1
#define NO 0
char order[ZL];
char output[PT];
void display()//》》》》》》》》》》》》》显示标题
{
int i;
putchar('\n');
for(i=0;i<5;i++)
{
if(i==2)
{
printf("$*************Welcome To FTP !!!**************$\n");
}else{
printf("$*********************************************$\n");
}
}
}
void mygets(char *p)//>>>>>>>>>>>>>>>>指令输入函数
{
if(p==NULL)return;
while(*p=getchar())
{
if(*p=='\n')break;
else p++;
}
*p='\0';
}
int orderJudge()//>>>>>>>>>>>>>>>>>>>>>>>>>>>>指令鉴别
{
memset(order,0,ZL);
printf("order:");
mygets(order);
if(!strcmp(order,"ps"))return YES;
else if(!strcmp(order,"ls"))return YES;
else if(!strcmp(order,"ls -l"))return YES;
else if(!strcmp(order,"pwd"))return YES;
else if(!strcmp(order,"tree"))return YES;
else if(!strcmp(order,"date"))return YES;
else if(!strcmp(order,"ifconfig"))return YES;
else if(strstr(order,"cd "))return YES;
else if(strstr(order,"du"))return YES;
else if(strstr(order,"cat "))return YES;
else if(!strcmp(order,"quit"))
{
printf("$*******************See You*******************$\n");
exit(1);
}
else return NO;
}
void popenexec()//>>>>>>>>>>>>>>>>>>>>>>>>>>指令执行
{
char *cdorder=NULL;
if(strstr(order,"cd "))
{
cdorder=order+strlen("cd ");
if(chdir(cdorder)==-1)
{
perror("cd");
return;
}
}else{
FILE *fp=NULL;
fp=popen(order,"r");
if(fp==NULL)
{
printf("$**************exec order fail!!**************$\n");
return;
}else{
memset(output,0,PT);
fread(output,sizeof(char),PT,fp);
printf("result:\n");
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
printf("%s",output);
if(!strcmp(order,"ls -l"))putchar('\n');
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
pclose(fp);
}
}
}
int popenOrder()
{
if(orderJudge()==YES)
{
popenexec();
}else{
printf("$*sorry,haven't the order.please input again.*$\n");
}
}
int main(void)
{
display();
while(1)
{
popenOrder();
}
return 0;
}
环境变量的配置
例:将a.out配置为环境变量
1.在a.out文件所在的目录下使用pwd打印出可执行程序的路径
2.使用echo $ PATH 指令输出环境变量
3.export PATH=环境变量(到games:前+a.out的路径,最后回车即可
或者 export PATH=$PATH:+pwd输出的路径
执行到第3步,环境变量在当前控制台有效。如果想配置长期有效则:
将第3步的内容写入~/.bashrc中重启或者source ~/.bashrc即可