Linux-C起步

名称解释

  1. ANSI C
    C语言标准,为各种操作系统上的C程序提供可移植性的保证
  2. POSIX标准
    定义了兼容操作系统的C语言系统接口以及工具标准
  3. 库函数
    库函数完成常见的特定功能,被应用程序调用
  4. 系统调用
    系统调用函数与操作系统相关,不同的操作系统使用的系统调用可能不同。库函数中也可以使用系统调用

第一个程序

写一个程序first.c

#include <stdio.h>

int main(int argc,char **argv)
{
  printf("this is first proram.\n");
  return 0;
}

使用gcc进行编译,链接

$ gcc first.c

运行一下看结果

$ ./a.out

调用函数

写一个阶乘的函数factorial.c

#include <stdio.h>
#include <stdlib.h>

int factorial(int n)
{
  if(n <= 1) return 1;
  else
    return factorial (n-1) * n;
}

写一个调用文件second.c

#include <stdio.h>
#include <stdlib.h>

int factorial(int n);

int main(int argc, char **argv)
{
  int n;
  if(argc < 2){
    printf("Usage: %s \n", argv[0]);
    return -1;
  }else{
    n = atoi(argv[1]);
    printf("Factorial of %d is %d . \n",n,factorial(n));
    return 0;
  }
}

编译和调用方法

$ gcc -c factorial.c
$ gcc -c second.c
$ gcc -o second factorial.o second.o
$ ./second  6

Make工具的使用

刚才我们看到了,如果我们的程序有多个文件,我们需要编译,链接才能正常调用程序。这个过程是很繁琐的,我们可以使用make工具来解决这个问题。
make工具会调用makefile文件,我们先写一个makefile看看
Makefile

second : second.o factorial.o
        gcc -o second second.o factorial.o
second.o : second.c
        gcc -c second.c
factorial.o : factorial.c
        gcc -c factorial.c
clean :
        rm -f *.o
        rm -f second

如果发现make有如下错误:

Makefile:2: *** 遗漏分隔符 。 停止。

可能是执行文件开头使用了空格,应该使用TAB。

Makefile中的常用变量
$@--目标文件
$^--所有依赖的文件
@<--第一个依赖文件

main: main.o mytool1.o mytool2.o
        cc -o $@ $^
main.o: main.c mytool1.h mytool2.h
        cc -c $<
mytool1.o: mytool1.c mytool1.h
        cc -c $<
mytool2.o: mytool2.c mytool2.h
        cc -c $<

库文件的使用

上面的例子中我们使用了printf函数,这个函数的实现是在库文件中。我们连接自己的应用程序的时候,编译器会查找对应函数的连接位置,运行时在当前系统内存空间中查找该库函数在对应库文件中的位置。
Linux系统下有两种库文件:

  1. 静态库:.a为后缀,应用程序从静态库中复制函数到二进制文件中
  2. 共享库:.so为后缀,应用程序运行时将函数代码从共享库文件中读出,从而间接引用。

系统库的路径:

# 系统必备共享库
/lib
# 标准共享库和静态库
/usr/lib
# 本地函数库
/usr/local/lib

库文件的搜索路径为:

  1. 环境变量:LD_LIBRARY_PATH 所指定的位置
  2. 搜索动态加载器在/etc 目录下的缓存文件 /etc/ld.so.cache

在进行cc编译的时候,会自动链接一些常用的库,这就是为什么printf不需要指定链接库的原因。如果我们需要指定库的路径可以使用如下方法:

cc -o temp temp.c -L/home/hutou/myLib

系统缺省库位置:/lib,/usr/lib,/usr/local/lib

静态库的使用

  1. 创建静态库:主要使用ar命令
    创建hello.c,hello.h 文件
#ifndef _libhello_H_
#define _libhello_H_
void hello(void);

#endif
#include <stdio.h>
#include "hello.h"

void hello(void){
        printf("this is a static lib.\n");
}

编译生成静态库hello.a

$ gcc -c hello.c
$ ar rc hello.a hello.o
  1. 使用静态库
    使用静态库需要两个文件:头文件,静态库文件
    默认库文件如果不在库搜索路径中,需要将此库文件拷贝到当前目录,或者在编译时指定库文件路径
    书写一个调用静态库的文件 libuse.c
#include <stdio.h>
#include "hello.h"

int main(int argc, char **argv){
        hello();
        return 0;
}

编译链接

$ gcc -o useHello libuse.c hello.a 

共享库的使用

共享库的创建基本上与静态库相同,主要的差别体现在编译上

  1. 使用gcc和参数-fPIC将源代码编译成.o的目标代码
  2. 使用-shared来创建共享库

下面我们使用上面的hello.c,编译成共享库

# 生成.o的目标文件
gcc -fPIC -c hello.c
# 编译共享库,指定共享库名称和版本,-lc表示引用c库
 gcc -shared  -o libhello.so.1.0 hello.o -lc
# 创建软连接
ln -sf libhello.so.1.0 libhello.so

下面我们看看怎么使用共享库

# 编译程序,生成目标文件
gcc -c libuse.c -o libuse.o
# 连接程序,-L 表示查找库的路径
gcc -o useDynHello libuse.o -L ./ -lhello
# 通过ldd命令查看引用的动态库
ldd useDynHello
# 调用程序,需要指定共享库的位置
LD_LIBRARY_PATH=$(pwd) ./useDynHello

说明一下,还记得我们说过的库文件的搜索路径。我们在链接程序的时候,指定了 -L 的路径,如果将我们的共享库放置在可以搜索到的路径下,则不用指定此参数。

进程

程序:包含可执行代码的文件,是一个静态文件。
进程:一个开始执行但是还没有结束的程序实例。
程序被系统调入内存,系统给程序分配一定的资源,让程序变成进程,为了区分进程,系统会给每一个进程分配一个ID号。进程有新建,运行,阻塞,就绪,完成五个状态。

  1. 获得进程ID号
#include <unist.h>
pid_t getpid(void);  //  获得进程的ID
pid_t getppid(void);  //  获得父进程的ID

演示用程序

#include <unistd.h>
#include <stdio.h>
int main(int argc,char *argv){
        printf("process id = %d\n",getpid());
        printf("process parent id = %d\n", getppid());
}
  1. 运行中出现:段错误(吐核)
    使用如下的命令进行调试(pid是运行的程序)
strace ./pid
  1. 进程的所有者和执行者
#include <stdio.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
    printf("这是程序的运行者%d\n", getuid());
    printf("这是程序的所有者%d\n", geteuid());
    printf("这是组ID =%s\n", getgid());
    printf("这是组EID =%s\n", getegid());
    return 0;
}
  1. 登录用信息
#include <stdio.h>
#include <pwd.h>
int main(int argc, char const *argv[])
{
        struct passwd* my_info;
        my_info = getpwuid(getuid());

        if(my_info)
        {
                printf("我的登录名:%s\n", my_info->pw_name);
                printf("我的密码:%s\n", my_info->pw_passwd);
                printf("我的ID= %ld\n", my_info->pw_uid);
                printf("我的组ID= %ld\n", my_info->pw_gid );
                printf("我的真实名称:%s\n", my_info->pw_gecos);
                printf("我的Home目录:%s\n", my_info->pw_dir);
                printf("我的Sheel:%s\n", my_info->pw_shell);
        }
        return 0;
}

推荐阅读更多精彩内容