Linux系统编程之我的学习笔记1_linux函数学习心得

2023-08-03 14:55:37    来源:个人图书馆-wuxinit_

gcc(g++)的工作流程: 1 预处理:调用cpp的预处理器,do的工作是:去掉注释,展开头文件,宏替换 gcc -E test.c -o test.i 2 编译:gcc将源代码文件编译为汇编语言代码 gcc -S test.i -o test.s 3 汇编:as,将汇编语言代码编译为了二进制文件(目标代码) gcc -c test.s -o test.o 4 链接:ld,链接test.c代码中所调用到的库函数 gcc -o test.o test

//要是想一步到位,do上面四步的工作的话,就用: gcc test.cpp(源文件名)/.c -o test(可执行文件名) CPP中,库一般都是一些功能相近或者相似的函数和类的集合体 库 分为静态库(static libary) 和 动态(共享)库(shared libary) //linux下,静态库文件用.a 作为后缀名;动态库文件用.so 作为后缀名 //windows下,静态库文件用.lib 作为后缀名,动态库文件用.dll 作为后缀名


(相关资料图)

一份库 制作完成后,如何给用户使用呢? //1-头文件:包含了库函数和类的声明 //2-库文件:包含了库函数和类的代码实现 //注意:库不能单独使用,只能作为其他执行程序的一部分来完成某些功能 //也就是说只能被其他程序调用类才能够使用!!! //你一个公司自己写的库源代码肯定不能给别人!肯定是给一个头文件和加密了的库文件卖给别人去用而已!

静态库(static library) 静态库可以认为是一些目标代码的集合,是在可执行程序运行前就已经加入到执行代码中的了, 已经成为可执行程序的一部分了(因此,如果生成了可执行程序后,你再删除静态库也不会对可执行程序产生任何的影响!) 按照习惯,linux下的静态库一般都是以.a作为文件的后缀名的 静态库的命名一般分为3个部分: 1-前缀:lib 2-库名称:自定义即可,如test 3-后缀:.a 所有最终的静态库名称为:libtest.a

静态库文件的找不到

CC是选定编译器的意思(linux下,C语言程序用gcc,C++语言程序用g++)

CPPFLAGS是选定你的.c/.cpp源代码在do预处理时,进而do展开呢?

CFLAGS是编译时选定,是否需要添加额外的选项(-Wall会输出warning警告信息,-

g使得你的程序可使用gdb调试,-c目前还不清楚可以干嘛~)

LDFLAGS是do链接时,指定你的静态库/动态库的路径以及名字的!(-L+绝对路径/相对路径 来指定库所在的路径!-I+库真正地名字,掐头lib去尾.a/.so就是库真正地名字了)

test:(makefile的

其中,$< 表示的是规则中的第一个条件,也即依赖项条件的第一个的意思!比如:

test:(makefile的

一般来说,在linux下,大多数的系统函数程序运行成功后,都会返回0!

对于链接文件而言,不论是软连接softlink还是硬链接hardlink,用stat函数最后都会显示出其所链接到的真正的文件之属性!

改进test_codes:

//stat函数测试:获取文件的类型和权限#include#include#include#include//include 各种类型声明 比如size_t#include//这是linux下特有的头文件!#include#includeint main(int argc,char* argv[]){        //int stat(const char *pathname, struct stat *statbuf);        //获取文件的属性        struct stat st;        stat(argv[1],&st);        //获取文件类型        if((st.st_mode & S_IFMT) == S_IFREG)        {                printf("普通文件\n");        }        else if((st.st_mode & S_IFMT) == S_IFDIR)        {                printf("目录文件\n");        }        else if((st.st_mode & S_IFMT) == S_IFLNK)        {                printf("链接文件\n");        }        if(S_ISREG(st.st_mode))        {                printf("普通文件\n");        }        else if(S_ISDIR(st.st_mode))        {                printf("目录文件\n");        }        else if(S_ISLNK(st.st_mode))        {                printf("链接文件\n");        }        //判断文件权限        //因为 权限 是可以有多个的,因此不能用if-else语句来判断,而是挨个if语句来判断!        if(st.st_mode & S_IROTH)        {                printf("---R---");        }        if(st.st_mode & S_IWOTH)        {                printf("---W---");        }        if(st.st_mode & S_IXOTH)        {                printf("---x---");        }        printf("\n");        return 0;}

stat函数

1 对于

,其实很容易理解,

就比如:命令ls -ltr > test.log2,会将ls显示的内容都“输出”到test.log2文件中去,其实就是把ls -ltr命令显示到终端的内容全都定向地输入到test.log2文件中去而已!

(这个命令中,>这个大于号就是重定向的操作符!)

>>符号,可以把字符串。那么此时,再用标准输出函数printf输出内容的话,就不会再向屏幕设备文件输出了,就会自动地把printf的内容输出(打印给)到fd所指向的文件test.log中了!此时就完成了dup2函数的重定向工作!

最后,再介绍一个IO函数,那么linux的IO系统函数就讲完啦!~

fcntl函数:(功能非常之强大!)

从定义式:int fcntl(int fd,int cmd,.../*arg*/);

就可以看出来:这里的...表明这个fcntl函数是一个:这里使用make + 对应源文件名(除去后缀.c/.cpp的名字),就可以对应快速编译某个源文件了!而不用每一次都 gcc/g++ XXX.c/.cpp -o XXX 敲多这么多代码了!

dup和dup2函数:    复制文件描述符----详情看图(我的截图,红色笔记)fcntl(fcontrol)函数:    1 复制文件描述符:int fd = fcntl(oldfd,F_DUPFD,0);    2 获得和设置文件的flag属性:        int flag = fcntl(fd,F_GETFL,0);        flag = flag | O_APPEND;//flag |= O_APPEND;        fcntl(fd,F_SETFL,flag);        int flag = fcntl(fd,F_GETFL,0);        flag = flag | O_NONBLOCK;//flag |= O_NONBLOCK;        fcntl(fd,F_SETFL,flag);注:这些都是固定的写法了,如果不会,直接去linux的终端输入命令man func or man 2 func 查即可!就算在工作中,也不一定说所有的系统函数你都得记住,但最起码你知道怎么去查找!怎么去查如何使用这些函数!知道怎么去查才是王道!

下面学习,

目录操作相关的函数:

test_codes:

//测试目录相关的函数 opendir readdir closedir#include#include#include#include#include#include#includeint checkdir(char* path);int main(int argc,char* argv[]){int n = checkdir(argv[1]);//传递一个路径给main函数!printf("n==[%d]\n",n);}int checkdir(char* path){//打开一个目录//DIR* opendir(const char* name));DIR * pDir = opendir(path);if(pDir == NULL){perror("opendir error!");return -1;}//循环读取目录项//struct dirent* readdir(DIR* dirp);int n = 0;char sFullPath[1024];struct dirent* p = NULL;while((p=readdir(pDir)) != NULL){//过滤掉.和..文件if(strcmp(p->d_name,".") == 0 || strcmp(p->d_name,"..") == 0)continue;printf("文件名:[%s/%s]--->",path,p->d_name);//判断文件所什么类型switch(p->d_type){case DT_DIR:printf("目录文件\n");memset(sFullPath,0x00,sizeof(sFullPath));sprintf(sFullPath,"%s/%s",path,p->d_name);n += checkdir(sFullPath);//自己调用自己!break;case DT_REG:printf("普通文件\n");n++;break;case DT_LNK:printf("链接文件\n");break;}}//关闭目录closedir(pDir);//只要你打开一个目录,就必须要关闭一个目录!这是必须要写的!return n;}

result:

接下来,我们将学习:

了解进程相关的概念

掌握时时,可以共享同一全局变量!此时不论是父还是子进程,它们读取g_var时,都是拿的同一块物理内存上的值!

简记为:对于同一全局变量,父子进程,写时复制(copy这个全局变量),读时共享(这个全局变量)。

test codes:

//fork函数测试代码,测试父子进程是否共享同一个全局变量#include#include#include#include#include//定义一个 全局变量int global_var = 99;int main(){//创建子进程函数原型://pid_t fork(void);pid_t pid = fork();if(pid < 0)//fork失败的case{perror("fork error!");return -1;}else if(pid == 0)//pid == 0 时,则当前进程为子进程{        sleep(1);//为了避免父进程还没执行呢,子进程就执行完成了的case!        //if子进程抢到CPU的时间片先执行了,就让其休眠1s        //确保执行顺序是:父进程执行完成后,再执行子进程printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());printf("child process"s global_var==[%d]\n",global_var);}else//pid > 0 时,则当前进程为父进程{ //pid_t getpid(void);//这个函数会返回调用该函数的进程的ID//(哪个进程调用它,它就返回谁的PID)printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());global_var++;printf("father process"s global_var==[%d]\n",global_var);        //或者让父进程执行后先休眠2s,这样子也可以达到父进程优先执行到效果}return 0;}

result:

子进程先sleep 可以达到让父进程等子进程执行完成的效果!

or

父进程后sleep 可以达到让父进程等子进程执行完成的效果!

从结果我们可以看出来,虽然父进程对全局变量global_var进行++读写入操作,但是子进程的global_var并没有发生变化,也就是说父子进程之间do写操作时不能共享同一全局变量!√

if只是读操作,则可以共享同一全局变量! (把上述的global_var++;的代码注释掉!)

注意:父子进程的全局变量的虚拟地址空间是一样的!

在上述的test codes中的每个if语句后加入代码:

printf("child process"s global_var==[%d],&global_var==[%d]\n",global_var,&global_var);

result:

下面学习ps命令和kill命令:

ps命令:是指,查看进程相关信息的命令。最常用的ps命令是:ps -ef | grep pid这个命令就是用来找进程id==pid的某个进程的!这是 very 常用的!ps aux | grep "xxx"ps ajx | grep "xxx"    -a:(all)当前系统所有用户的进程    -u:查看进程所有者及其他一些信息    -x:显示没有控制终端的进程(所谓的没有控制终端的进程,即:不能与用户进行交互(输入/输出)的进程)    -j:列出与作业控制相关的信息    -A:显示所有进程    -e:等于“-A”    -f:表达程序间的相互关系ps -ef //显示所有进程信息,连同命令行kill命令:是指,杀死(终止)某进程的命令。kill -l 查看系统有哪些信号kill -9 pid 杀死进程id==pid的某个进程

例子:

ps aux:(== ps -aux,不写-也OK)

(带?问号的就是没有控制终端的进程,不能与用户进行交互输入/输出)

补充: 一些英文缩写符号的意思是:

ps ajx:(== ps -ajx,不写-也OK)ajx就能看到更多的信息,比如PPID,表示的是这个进程的父进程id

ps -ef | grep bash:

kill -l(这是-小L即-l,不是-大ai即-I,虽然长得一样,但是得区分清楚!):

kill -9 pid: (在终端开一个sleep 350休眠350s的进程)

再输入 ps ajx命令来查看sleep 350这个进程的PID,然后就可以通过kill -9 pid来杀死对应PID的某个进程了!

下面我们将继续学习:

4exec函数族:

4.1 函数作用和函数介绍

有的时候,我们需要在一个进程中,执行其他的命令或者是运行用户自定义的应用程序,此时就用到了exec函数族中的函数了。

使用方法一般都是在父进程里面调用fork函数创建子进程,然后再在子进程中调用exec函数。

注意:我们这里,只学习exec函数族中,用得最多的2个函数!学会这2个函数即可。万一后续你要用到另外一些函数的话,你大可以用man exec命令来查看对应的函数原型和用法,然后你就无师自通了!

①execl函数:int execl(path,"命令名称","命令对应的参数",NULL));

那么,什么时候这个函数会执行失败呢?答,最直观最简单的case就是:当你要拉起来的这个应用程序根本就不存在时,那肯定是执行失败的!

其次,这里的路径path其实也可以是相对路径,只要你写的路径能找到对应的应用程序or命令即可!

只要你想在一个进程内部,去执行linux系统的命令或者说应用程序的话,应优先想到如下方式:

先fork,然后再在子进程中用execl函数拉起一个可执行程序or命令//codes:pid = fork();if(pid == 0){    execl(...);}

总结:

exec函数是用一个新程序替换了当前进程的代码段,数据段、堆和栈;原有的进程空间并没有发生变化,也并没有创建新的进程,且进程的PID也没有发生变化。

test codes:

//execl函数的测试代码,测试用execl函数拉起一个应用程序or命令#include#include#include#include#includeint main(){//创建子进程函数原型://pid_t fork(void);pid_t pid = fork();if(pid < 0)//fork失败的case{perror("fork error!");return -1;}else if(pid == 0)//pid == 0 时,则当前进程为子进程{printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());//linzhuofan@VM-12-8-ubuntu:~$ which ls///usr/bin/lsexecl("/usr/bin/ls","ls","-ltr",NULL);//if成功拉起这个ls命令的话,则execl函数后序的代码并不会给执行!perror("execl error!\n");}else//pid > 0 时,则当前进程为父进程{ //pid_t getpid(void);//这个函数会返回调用该函数的进程的ID//(哪个进程调用它,它就返回谁的PID)printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());}return 0;}

result:

test codes2:

//execl函数的测试代码,测试用execl函数拉起一个应用程序or命令#include#include#include#include#includeint main(){//创建子进程函数原型://pid_t fork(void);pid_t pid = fork();if(pid < 0)//fork失败的case{perror("fork error!");return -1;}else if(pid == 0)//pid == 0 时,则当前进程为子进程{printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());//linzhuofan@VM-12-8-ubuntu:~$ which ls///usr/bin/ls//execl("/usr/bin/ls","ls","-ltr",NULL);execl("./test","test","hello","world","ni","hao!",NULL);//if成功拉起这个ls命令的话,则execl函数后序的代码并不会给执行!perror("execl error!\n");}else//pid > 0 时,则当前进程为父进程{ //pid_t getpid(void);//这个函数会返回调用该函数的进程的ID//(哪个进程调用它,它就返回谁的PID)printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());}return 0;}test.c:#includeint main(int argc,char* argv[]){int i = 0;for(i=0;i

result2:

先do

后do

②execlp函数:int execl(const char* file,const char* arg,.../*(char *)NULL*/);

(命令名字/可执行程序名字,命令,命令的参数1,命令的参数2,....,NULL)

test codes:

//execlp函数的测试代码,测试用execlp函数拉起一个应用程序or命令#include#include#include#include#includeint main(){//创建子进程函数原型://pid_t fork(void);pid_t pid = fork();if(pid < 0)//fork失败的case{perror("fork error!");return -1;}else if(pid == 0)//pid == 0 时,则当前进程为子进程{printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());execlp("ls","ls","-ltr",NULL);execlp("./test","test","hello","world","ni","hao!",NULL);//if成功拉起这个ls命令的话,则execl函数后序的代码并不会给执行!perror("execl error!\n");}else//pid > 0 时,则当前进程为父进程{ //pid_t getpid(void);//这个函数会返回调用该函数的进程的ID//(哪个进程调用它,它就返回谁的PID)printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());}return 0;}

result:

test codes2:

//execlp函数的测试代码,测试用execlp函数拉起一个应用程序or命令#include#include#include#include#includeint main(){//创建子进程函数原型://pid_t fork(void);pid_t pid = fork();if(pid < 0)//fork失败的case{perror("fork error!");return -1;}else if(pid == 0)//pid == 0 时,则当前进程为子进程{printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());//execlp("ls","ls","-ltr",NULL);execlp("./test","test","hello","world","ni","hao!",NULL);        //==> execlp("test","hello","world","ni","hao!",NULL);//if成功拉起这个ls命令的话,则execl函数后序的代码并不会给执行!perror("execl error!\n");}else//pid > 0 时,则当前进程为父进程{ //pid_t getpid(void);//这个函数会返回调用该函数的进程的ID//(哪个进程调用它,它就返回谁的PID)printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());}return 0;}

result:

test codes3:

//execlp函数的测试代码,测试用execlp函数拉起一个应用程序or命令#include#include#include#include#includeint main(){//创建子进程函数原型://pid_t fork(void);pid_t pid = fork();if(pid < 0)//fork失败的case{perror("fork error!");return -1;}else if(pid == 0)//pid == 0 时,则当前进程为子进程{printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());execlp("test222","hello","world","ni","hao!",NULL);        //==> execlp("test222","hello","world","ni","hao!",NULL);        //注意:test222文件夹我并没有创建!这肯定会执行perror的!//if成功拉起这个ls命令的话,则execl函数后序的代码并不会给执行!perror("execl error!\n");}else//pid > 0 时,则当前进程为父进程{ //pid_t getpid(void);//这个函数会返回调用该函数的进程的ID//(哪个进程调用它,它就返回谁的PID)printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());}return 0;}

result3:

标签:

X 关闭

X 关闭