当前位置:文档之家› linux高级编程笔计

linux高级编程笔计

linux高级编程笔计
linux高级编程笔计

Linux应用编程

一、IO (2)

标准IO口库: (2)

打开流: (2)

标准IO库读写: (3)

标准I/O口库-定位 (3)

文件IO (4)

一、unix输入输出 (4)

二、文件IO操作 (4)

二、静态库和动态库 (6)

三、进程操作 (8)

系统进程查看: (8)

进程类型: (9)

进程运行状态: (9)

应用程序进程操作 (10)

创建进程: (10)

替换进程: (10)

进程阻塞: (10)

进程结束 (10)

进程的一生 (13)

进程间通信 (13)

网络编程 (19)

UDP (23)

TCP (23)

一、IO

Man: 1 conmmand 2 syscall 3 function

Sorce insight 可以更新符号表,方便编程

Posix 标准接口windows linux 应用层与内核层的标准接口。在posix之上还可能有其它封装如glibc.

标准IO口库:

处理缓存分配、以优化执行I/O,

Strace 加可执行文件可跟踪程序从应用层到硬件的调用过程

Stream流:

文本流:

在流中处理的数据以字符出现。

二进制制流:

流中处理的是二进制的序列。

缓冲文件系统:(高级磁盘IO)

目的:心量减少使用read/write的使用

定义:文件读写都经过内存中介。

分类:全缓存,行缓存,不缓存

非级冲文件系统:(低级磁盘IO)

打开流:

*fopen(const char *path,const char *mode。。。)

释放:fclose

*freopen(const char *restrict pathname,const char restrict type,file * restrict fp)

标准IO库读写:

feof()判断文件是否结束。

EOF/feof() EOF 是glibc中文件结束的返回标志为32位-1。

读一个字符:

Getc(FILE * steam)

Fgetc()

Getchar()

若成功则为下一个字符,若已到文件尾端则为EOF

写一个字符:

Fputc(…a?,*)

每次输入一行:

Char *gets(char *s)

Char *fgets(char *s ,int size . FILE *stream)

Fegets()安全的读取到一个合法的字符串。最多读取到size-1个,s[size-1]一定为0

每次输出一行:

Puts:一定是一行,‘、0’结束符来终址函数,转义成换行字符。

Fputs:

内存搬移:

Fwrite()

Fread()

若文件大于4G 则在编译时打开__USE_FILE_OFFSET64宏

fpos_t

标准I/O口库-定位

ftell

fseek(),SEEK_SET/SEEK_CUR/SEEK_END

rewind(*stream) 设定文件位置为开始

fgetpos(FILE *stream,long offset,int whence)

fsetpos()

临时文件

char *tmpnam(char *s)

FILE *tmpfile(void)

linux 下EOF 为Ctrl + D

文件IO

一、unix输入输出

1、文件描述符

a)顺序分配的非负整数

b)内核用以标识一个特定进程正在访问的文件

c)其它资源的访问标识

2、

3、不用绶存的I/O

a)通过文件描述符进行访问

4、标准IO

二、文件IO操作

open()

在内核中注册一个设备。

int open (const char *pathname,int flags,mode_t mode);

可以打开设备文件,只能创建普通文件

close()

关闭表述符

文件属性获取

fstat/chmod/chown

目录

mkdir/rmdir/chdir/opendir/readdir

目录没有执行权限,我们对该目录不能操作,进入,删除

删阶除,修改

删除文件必须是该文件的所在文件夹具有执行,可写权限。

chown 改变所有者

truncate() 文件截短

opendir() 条开目录文件

readdir()

mkdir()

已知文件名和路径,获取文件大小的方式:

stat(filename,&stat) file_size

open();RET=read()

open ret= lseek(fd,o,SEEK_END)

硬链接软链接区别

硬链接:硬盘中目录对应的内容,其另目录就是硬链接link()unlink()

软链接:相当于windos 的快捷方式symlink() unlink()

二、静态库和动态库

库:(链接权限)

可执行文件:(执行)

都是二进制的bin。

静态库:ar crs *.o

动态库:gcc -shared -fPIC -o *.so *.o

共同点:都是二进制文件的打包

不同点:链接成可执行文件的时候,作用范围不同

静态库:链接阶段,会跟我们其余的*.o文件一起生成可执行文件。gcc -o build *.o *.a.build具备*.a中所有二进制的代码。

动态库:链接阶段,只是起到一个标签的作用,不会跟其余*.o文件共同生成可执行程序build,这个build里没有so文件的二进制代码,在运行期间由操作系统来调度相关的so文件。gcc -o build *.o *.so

默认下GCC是按动态方式编译文件

编译时加–static 即可实现静态库

如何创建静态库:

1、先将*。c转成。o文件。

2、用ar命令,它将很多*。o 的文件生成库

3、ar crs lib.a 1.o 2.o。

系统默认的库目录为/lib /user/lib,把生成的库放在系统默认库中。或者在生成可执行文件时加入–L 文件路径

动态库:

链接阶段,只是起到一个标签的作用,

创建动态库

gcc –fPIC –c *.c –o *.o

gcc –shared –fPIC –o *.so *.o

系统默认的库目录为/lib /user/lib,把生成的库放在系统默认库中。

若系统默认库目录为只读,可以export LD_LIBRARY_PATH=路径名加入新路径,或修改/etc/ld.so.conf 把库所在的路径加到文件末尾,并执行ldconfig 刷新。

gcc –WL, -rpath=/绝对路径–o bulid -*.o。

通过以上在链接时加入路径,则在运行进会在所加的路径下去找动态库。这种情况在上面第一种和第二种情况不能用的情况下用。

生成可执行文件时如何找到库:

gcc -o -L LIB_PATH -lname===$(LIB_PATH)/libname.so a

gcc -o build -L /mnt -lmy 1.o

【默认在程序运行期间,在系统默认目录下去寻找相关的库】gcc -o build 1.o /mnt/libmy.so (不推荐)

【/mnt/libmy.so这个库的运行时查找目录定位为/mnt】

运行期间如何找到库:

静态库不需要在运行期间找。

动态库:系统默认目录:/lib /usr/lib

异常:

当默认目录,无法满足系统运行的情况,即/lib不可写

1、export LD_LIBRARY_PATH=....

2、gcc -Wl,-rpath=/mnt/hgfs/AUP/src/lib/ -o build *.c -l my

-l 表示在lib 和usr/lib 下找lib my.so

三、进程操作

进程一个独立的可调度的任务。进程是运行中的程序。

系统进程查看:

shell 下进程查看ps –aux

/proc 虚拟文件系统 通过文件系统接口实现对内核的访问,它以文件系统的形式,为操作系统本身和应用程序之间的通信提供一个界面, cat /proc/self/status 查看系统当前状态。

MMU

物理内存

进程PID

程序虚拟空间: 系统代码(3~4G ) 栈区 堆区

静态数据区 代码区

进程类型:

交互进程:由shell控制和运行,即可以在前台也可以在后台运行

批处理进程:shell命今集合

守护进程:在后台运行,一般在linux启动时执行,关闭时才结束。

进程运行状态:

运行态(R):

此时进程或都正在运行,或都正准备运行

等待态:此时进程在等待一个事件的发生或某系统资源

可中断(S)

不可中断(D)

停止态(X):

此时进程被中止

死亡态(僵尸态)(Z):

一个已终止的进程,但还在进程向量数组中占有一个task_struct结构

占用PID号,以及返回时的状态,需在父进程中对该进程操作,即回收相应的数据结构。

应用程序进程操作

创建进程:

fock() 创建一个进程,创建时所创建的子进程的fock反的PID为0,父进程的fock()反回的是子进程的PID,用fock创建的进程都是R状态。在程序中killall掉子进程的父进程,则相应子进程则被主进程回收成为保护进程,或精录进程。

Vfork() 相对fork()拷贝后先运行子进程,全局变量共享,局部变量也共享。

替换进程:

exec函数族:

替换调用者进程的所有资源。只保留进程号。

execl

execlp

execv

进程阻塞:

阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。非阻塞的进程在不能进行设备时并不挂起,它或者放弃,或者不停地查询,直到可以操作为至。pid_t wait(int *status)直到任一个子进程结束或者该进程接收到了一个信号为止,如果该进程没有子进程或都子进程结束,

wait会立即返回。

waitpid 可以指定某一进程及阻塞方式。

wait(NULL) = waitpid(-1,NULL,0)

进程结束

exit(0)(清理缓存)_exit(0)(不清理缓存) 退出进程。

守护进程(精灵):大多数服务都是守护进程。

在终端中运行的程序都依赖于该终端,如果终端关闭,则该程序结束。如果要让程序突破这个限制则需要建立守护进程。

守护进程建立方法:

1、创建子进程,父进程退出。

pid = fork();

if(pid>0)

{exit(0);}

2、在子进程中创建新会话,改变工作组,使之自已管理自已(setsid())。

3、改变当前目录为根目录chdir(“/”);。

4、重设文件权限掩码。umask()

5、关闭文件描述符。

守护进程建立方法详细介绍:

1.让init进程成为新产生进程的父进程。

调用fork函数创建子进程后,使父进程立即退出。这样,产生的子进程将变成孤儿进程,并被init进程接管,同时,所产生的新进程将变为在后台运行。

2.调用setsid函数

通过调用setsid函数,使得新创建的进程脱离控制终端,同时创建新的进程组,并成为该进程组的首进程。为了使读者更好地理解这一步骤,下面介绍进程组、会话(session)的基本概念。

在Linux系统中,所有的进程都属于各自的进程组。进程组是一个或多个进程的集合。打个比方,可以认为某个班级是一个进程组,而其中成员就是进程。一个班级至少有一个成员。当一个班级的最后一个成员不存在的时候,这个班级也就不存在了,也就是进程组消亡了。

每个进程组都有类似于进程号的标识,称为进程组ID。进程组ID是由领头进程的进程号决定的,每个进程组都存在一个领头进程。进程组的存在与否与领头进程是否存在没有关系。

会话是一个或多个进程组的集合。与进程组类似,每个会话都存在一个领头进程。Linux是一个多用户的操作系统,在同一时刻系统中会存在属于不同用户的多个进程。如果用户在某个终端上发送了某个信号,例如,按下“Ctrl+C”发送SIGINT信号,如何确保信号被正确地发送到对应的进程,同时不会影响使用其他终端的用户的进程?

会话和进程组是Linux内核用于管理多用户情况下用户进程的方法。每个进程都属于一个进程组,而进程组又属于某个会话。当用户从终端登录系统(不管是终端还是伪终端),系统会创建一个新的会话。在该终端上启动的进程都会被系统划归到会话的进程组中。

会话中的进程通过该会话中的领头进程(常称其为控制进程)与一个终端相连。该终端是会话的控制终端。一个会话只能有一个控制终端,反之一样。如果会话存在一个控制终端,则它必然拥有一个前台进程组。属于该组的进程可以从控制终端获得输入。这时,其他的进程组都为后台进程组。图8.3所示为会话、进程组、进程与控制终端之间的关系。

由于守护进程没有控制终端,而使用fork函数创建的子进程继承了父进程的控制终端、会话和进程组,因此,必须创建新的会话,以脱离父进程的影响。Linux系统提供了setsid函数用于创建新的会话。setsid函数的信息如表8.1所示。

表8.1 setsid函数

头文件

函数形式pid_t setsid(void);

返回值成功失败是否设置errno 调用进程的会话ID ?1 是

setsid 函数将创建新的会话,并使得调用setsid函数的进程成为新会话的领头进程。调用setsid函数的进程是新创建会话中的惟一的进程组,进程组ID为调用进程的进程号。setsid函数产生这一结果还有个条件,即调用进程不为一个进程的领头进程。由于在第一步中调用fork的父进程退出,使得子进程不可能是进程组的领头进程。该会话的领头进程没有控制终端与其相连。至此,满足了守护进程没有控制终端的要求。3.更改当前工作目录

使用fork函数产生的子进程将继承父进程的当前工作目录。当进程没有结束时,其工作目录是不能被卸载的。为了防止这种问题发生,守护进程一般会将其工作目录更改到根目录下(/目录)。更改工作目录使用的函数是chdir。

4.关闭文件描述符,并重定向标准输入、输出和错误输出

新产生的进程从父进程继承了某些打开的文件描述符,如果不使用这些文件描述符,则需要关闭它们。守护进程是运行在系统后台的,不应该在终端有任何的输出信息。可以使用dup函数将标准输入、输出和错误输出重定向到/dev/null设备上(/dev/null是一个空设备,向其写入数据不会有任何输出)。下面给出具体的代码:

int fd;

//将标准输入输出重定向到空设备

fd = open ("/dev/null", O_RDWR, 0);

if (fd != -1)

{

dup2 (fd, STDIN_FILENO);

dup2 (fd, STDOUT_FILENO);

dup2 (fd, STDERR_FILENO);

if (fd > 2)

close (fd);

}

5.设置守护进程的文件权限创建掩码

很多情况下,守护进程会创建一些临时文件。出于安全性的考虑,往往不希望这些文件被别的用户查看。这时,可以使用umask函数修改文件权限,创建掩码的取值,以满足守护进程的要求。

8.2.2 守护进程具体实现

本节给出一个守护进程创建的实例。程序p8.1.c中定义了daemon函数,用于实现对守护进程的创建。其创建思想在8.2.1中有详细的介绍,程序的具体代码如下:

//p8.1.c 守护进程的实现

#include

#include

#include

#include

#include

/* daemon函数用于将调用函数的进程转化为守护进程*/

int

daemon (int nochdir, int noclose)

{

pid_t pid;

pid = fork ();

/*如果创建进程失败*/

if (pid < 0)

{

perror ("fork");

return -1;

}

/* 父进程退出运行*/

if (pid != 0)

exit (0);

/* 成为会话领头进程*/

pid = setsid();

if (pid < -1)

{

perror ("setsid");

return -1;

}

/* 将工作目录修改成根目录*/

if (! nochdir)

chdir ("/");

/* 将标准输入输出重定向到空设备*/

if (! noclose)

{

int fd;

fd = open ("/dev/null", O_RDWR, 0);

if (fd != -1)

{

dup2 (fd, STDIN_FILENO);

dup2 (fd, STDOUT_FILENO);

dup2 (fd, STDERR_FILENO);

if (fd > 2)

close (fd);

}

}

umask (0027);

return 0;

}

int main(void)

{

daemon(0,0);

sleep(1000);

return 0;

}

使用gcc编译p8.1.c,得到名为p8.1的可执行文件。执行该程序,程序将以守护进程的状态运行,如图8.4所示。

进程的一生

下面就让我用一些形象的比喻,来对进程短暂的一生作一个小小的总结:随着一句fork,一个新进程呱呱落地,但它这时只是老进程的一个克隆。然后随着exec,新进程脱胎换骨,离家独立,开始了为人民服务的职业生涯。人有生老病死,进程也一样,它可以是自然死亡,即运行到main函数的最后一个"}",从容地离我们而去;也可以是自杀,自杀有2种方式,一种是调用exit函数,一种是在main函数内使用return,无论哪一种方式,它都可以留下遗书,放在返回值里保留下来;它还甚至能可被谋杀,被其它进程通过另外一些方式结束他的生命。进程死掉以后,会留下一具僵尸,wait和waitpid充当了殓尸工,把僵尸推去火化,使其最终归于无形。这就是进程完整的一生。

进程间通信

所有进程均运行于操作系统之上,所以所有进程间通信都是通过操作系统完成。

linux进程通间方式:

无名管道通信

有名管道

信号

套接字(socket)

无名管道:

在3G到4G之间分配了一个队列,不能进行lseek();不占用磁盘空

间,一定不能在有写端时关闭读。read返回0时表示管道破坏(写端没

了)。一但进程收到SIGPIPE信号时,说明管道已破坏,系统自动kill

该进程。

a、只能用于具有亲缘关系的进程之间.

b、半双工的通信模式.

c、管道可以看成一种特殊的文件,可能进行IO文件操作。

建立方法:

int pipe(int fd[2])在父进程上建立两个文件表述符指向系

统中同时开辟的FIFO buf

close(fd)关闭父进程写子进程读。

管道读写注意事项:

有名管道

为无名管道的一种改进,有名管道依赖于文件系统。

a、可以用于不相关的两个进程

b、该管道可以通过路径名来指出,并在文件系统中是可见,可以按普通

文件进行文件操作

c、FIFO,对管道及FIFO的读总是从开始处返回数据。

创建有名管道:

int mkfifo(const char *filename,mode_t mode);

阻塞非阻塞

写端读端管道数据结果写端读端管道数据结果

无有无return 0 无有无return 0

无有有正常读写到数

据空时

return0 无有有

正常读写到

数据空时

return0

有无无SIGPIPE,退出

进程有无无

SIGPIPE,退

出进程

有无有SIGPIPE,退出

进程有无有

SIGPIPE,退

出进程

有有无阻塞有有无读错误:再试一次

有有有正常读写、数据

满则写阻塞有有有

正常读写写

满则出错

信号通信:

信号是异步通信中唯一的进程间能信。

信号是在软件层次上对中断机制的一种模拟,可以做用户空间和内核空

间的交互。

kill –l 可在shell中列出所有系统中的信号。

红帽子5中

1~31 是UNIX兼容的信号,没有队列,如信号同时发生两次,只响应一次。

常用信号:

SIGINT : CTRL+C

SIGKILL:不可忽略信号,杀死一个进程

SIGUSR1,SIGUSR2:空白信号用户自定义实现

信号的捕捉:

kill(pid,信号类型)

raise(信号类型)

以上两名命可以给进程自已发送信号。

alarm:

pause()用于将调用进程挂起直到有信号进程处于 T 状态。

信号的处理:

signal(信号类型,函数接口),若函数接口处写 SIG_IGN 则忽略信号终端敲ctrl+c 实质为 sinal(SIGINT,0)即向所有的进程组发送信号。

IPC对像通信:

共享内存:

进程间可以直接读取内存,共享内存是所有进程间通信的方式。

ipcs 列出所有系统的IPC。

ipcrm -m 在shell中删除共享内存。

1、ftok(*path,id)通过系统中存在的文件,和id值算出key。

2、shmid = int shmget(key_t key,int size ,int mode); 在内核空间申请共

享内存。

void *shmat(int shmid , const void *shmaddr,int shmflg) ;

把内核共享内存映射到用户空间。

shmadd()删除映关系。

int shmctl(int shmid,int cmd,struct shmid_ds *buf)

删除内存。

消息队列:

int msgget(key ,msgflg)

msgsend(msqid,*msgp,size,msgflg)

msgrcv()

msgctl()

信号灯(semaphore):

与线程信号量区别为,线程的线号量是过用户空间的全局变量实现,进程是能过内核空间实现。

1、建立创库

int semget()

2、初始化系统资源

int semop()

3、int semctl()

shmat()

四、线程

引入线程的目的:减少处理机空转时间,支持多处理器,减少上下文切换开销。

线程是微型进程,相当于多个进程共享4G的虚拟空间,但可同时可被系统调用。这种进程便为线程。

线程切换速度远大于进程。

线程通信通过全局变量,而进程需要通过系统。

编译时加–lpthread。

每个线程私有资源:

线程ID

PC 和相关寄存器

堆栈

错误号

。。。。。。

线程的操作:

创建:pthread_create()

线程退出 pthread_exit

查线程号pthread_self ()

线程阻塞pthread_join(id1,NULL); 并回收线程资源。

创建:pthread_create()

线程退出 pthread_exit

查线程号pthread_self ()

线程阻塞pthread_join(id1,NULL); 并回收线程资源。

线程属性:

绑定:邦定后可以提高优先级,让用户空间线程与CPU队列一一对应。

分离:分离后自已管理自已,遇pthread_join() 不阻塞。

pthread_attr_setsetdetachstate()

堆栈地址:

优先级:

默认属性:非绑定、非分离,1M堆,与父进程同优先级。

线程间机制:

线程间同步:

信号量代表一类资源,其值表示系统中该资源的数量

信号量是一个受保护的变量,只能有三种操作:

初始化P操作(申请资源)V操作(释放资源)

无名信号量:

int sem_init()

sem_wait() P

int sem_post() V

int sem_getvalue() 得到信号量值

int sem_trywait() P

int sem_destroy()信号量册除

同步几个进程(线程)用几个信号量

互斥锁:

pthred_mutex_init()。

int pthred_mutex_trylock()。

int pthread_mutex_lock()。

int pthread_mutex_unlock()。

网络编程

最早网络模型:ARPAnet。

TCP 用来检测网络传输中差错的传输控制协议。

IP 负则对不同网络进行互联的协议。

物联网:

相关主题
文本预览
相关文档 最新文档