Linux系统编程之学结

上传人:hjk****65 文档编号:178022753 上传时间:2022-12-27 格式:DOC 页数:10 大小:58.50KB
收藏 版权申诉 举报 下载
Linux系统编程之学结_第1页
第1页 / 共10页
Linux系统编程之学结_第2页
第2页 / 共10页
Linux系统编程之学结_第3页
第3页 / 共10页
资源描述:

《Linux系统编程之学结》由会员分享,可在线阅读,更多相关《Linux系统编程之学结(10页珍藏版)》请在装配图网上搜索。

1、一、文件I/O操作部分:C标准I/O库函数 Unbuffered I/O函数 fopen open fdopen creat fclose close fseek lseek fread read fwrite write知识小结:1. C标准I/O库函数1.1 文件的创建,打开与关闭原型为:#include FILE *fopen(const char *pach,const char *mode);FILE *fdopen(int fd,const char *mode);int fclose(FILE *stream);fopen 以 mode 的方式打开或创建文件,如果成功,将返回一个

2、文件指针,失败则返回 NULL.fopen 创建的文件的访问权限将以 0666 与当前的 umask 结合来确定。mode 的可选模式列表模式 读 写 位置 截断原内容 创建rb Y N 文件头 N Nr+b Y Y 文件头 N Nwb N Y 文件头 Y Yw+b Y Y 文件头 Y Yab N Y 文件尾 N Ya+b Y Y 文件尾 N Y在 Linux 系统中,mode 里面的b(二进制)可以去掉,但是为了保持与其他系统的兼容性,建议不要去掉。ab 和 a+b 为追加模式,在此两种模式下,无论文件读写点定位到何处,在写数据时都将是在文件末尾添加,所以比较适合于多进程写同一个文件的情况下

3、保证数据的完整性。fdopen 根据已经打开的文件描述符打开一文件指针,对同一个文件既打开文件描述符又打开文件指针,将容易出现问题,但是在多进程程序中,往往需要传递文件描述符,所以此类混合文件操作必须掌握。1.2 读写文件基于文件指针的数据读写函数较多,可分为如下几组:数据块读写:#include size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);fread 从文件流 stream 中

4、读取 nmemb 个元素,写到 ptr 指向的内存中,每个元素的大小为 size 个字节.fwrite 从 ptr 指向的内存中读取 nmemb 个元素,写到文件中,每个元素 size 个字节。所有的文件读写函数都从文件的当前读写点开始读写,读写完以后,当前读写点自动往后移动 size*nmemb 个字节。1.3 文件定位:文件定位指读取或设置文件当前读写点,所有的通过文件指针读写数据的函数,都是从文件的当前读写点读写数据的。常用的函数有:#include int fseek(FILE *stream, long offset, int whence);long ftell(FILE *str

5、eam);void rewind(FILE *stream);fseek 设置当前读写点到 offset 处,whence 可以是 SEEK_SET,SEEK_CUR,SEEK_END,这些值决定是从文件头、当前点和文件尾计算偏移量 offset.ftell 获取当前的读写点rewind 将文件当前读写点移动到文件头 01.4 目录操作获取目录信息:原型为:#include #include DIR *opendir(const char *name); /打开一个目录struct dirent *readdir(DIR *dir); /读取目录的一项信息,并返回该项信息的结构体指针void

6、rewinddir(DIR *dir); /重新定位到目录文件的头部int closedir(DIR *dir); /关闭目录文件读取目录信息的步骤为:1 用 opendir 函数打开目录;2 使用 readdir 函数迭代读取目录的内容,如果已经读取到目录末尾,又想重新开始读,则可以使用rewinddiw 函数将文件指针重新定位到目录文件的起始位置;3 用 closedir 函数关闭目录1.5. 标准输入/输出流在进程一开始运行,就自动打开了三个对应设备的文件,它们是标准输入、输出、错误流,分别用全局文件指针 stdin、stdout、stderr 表示,stdin 具有可读属性,缺省情况下

7、是指从键盘的读取输入,stdout 和 stderr 具有可写属性,缺省情况下是指向屏幕输入数据。2. Unbuffered I/O函数2.1. 打开、创建和关闭文件open 和 creat 都能打开和创建函数,原型为#include #include #include int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);int creat(const char *pathname, mode_t mode);flags 和 mode 都是一组掩码的合成

8、值,flags 表示打开或创建的方式,mode 表示文件的访问权限。flags 的可选项有 掩码 含义O_RDONLY 以只读的方式打开O_WRONLY 以只写的方式打开O_RDWR 以读写的方式打开O_CREAT 如果文件不存在,则创建文件O_EXCL 仅与 O_CREAT 连用,如果文件已存在,则强制 open失败O_TRUNC 如果文件存在,将文件的长度截至 0O_APPEND 已追加的方式打开文件,每次调用 write 时,文件指针自动先移到文件尾,用于多进程写同一个文件的情况O_NONBLOCK 非阻塞方式打开O_NODELAY 非阻塞方式打开O_SYNC 只有在数据被真正写入物理设

9、备设备后才返回int creat(const char *pathname,mode_t mode);等价于open(pathname,O_CREAT|O_TRUNC|O_WRONLY,mode);文件使用完毕后,应该调用 close 关闭它,一旦调用 close,则该进程对文件所加的锁全都被释放,并且使文件的打开引用计数减 1,只有文件的打开引用计数变为 0 以后,文件才会被真正的关闭,文件引用计数主要用于多进程之间文件描述符的传递。2.2. 读写文件读写文件的函数原型为:#include ssize_t read(int fd, void *buf, size_t count);ssize

10、_t write(int fd, const void *buf, size_t count);2.3. 文件定位函数 lseek 将文件指针设定到相对于 whence,偏移值为 offset 的位置#include #include off_t lseek(int fildes, off_t offset, int whence);whence 可以是下面三个常量的一个SEEK_SET 从文件头开始计算SEEK_CUR 从当前指针开始计算SEEK_END 从文件尾开始计算2.4. 文件的锁定在多进程对同一个文件进行读写访问时,为了保证数据的完整性,有时需要对文件进行锁定。可以通过 fcntl

11、 对文件进行锁定和解锁。#include #include int fcntl(int fd, int cmd, struct flock *lock);参数 cmd 置为 F_GETLK 或 F_SETLK 可以获取或设置文件锁定信息。参数 struct flock 为文件锁信息。文件锁有两种类型,读取锁(共享锁)和写入锁(互斥锁)。对于已经加读取锁的文件,再加写入锁将会失败,但是允许其它进程继续加读取锁;对于已经加写入锁的文件,再加读取锁和写入锁都将会失败。注意:文件锁只会对其它试图加锁的进程有效,对直接访问文件的进程无效。2.5.文件描述符的复制函数 dup 和 dup2 可以实现文件描

12、述符的复制。原型为:#include int dup(int oldfd);int dup2(int oldfd, int newfd);文件描述符的复制是指用另外一个文件描述符指向同一个打开的文件,它完全不同于直接给文件描述符变量赋值,例如:描述符变量的直接赋值:char szBuf32;int fd=open(“./a.txt”,O_RDONLY);int fd2=fd;close(fd); /导致文件立即关闭printf(“read:%dn”,read(fd2),szBuf,sizeof(szBuf)-1);close(fd2); /读取失败,无意义了在此情况下,两个文件描述符变量的值相

13、同,指向同一个打开的文件,但是内核的文件打开引用计数还是为 1,所以 close(fd)或者 close(fd2)都会导致文件立即关闭掉。描述符的复制:char szBuf32;int fd=open(“./a.txt”,O_RDONLY);int fd2=dup(fd);close(fd); /当前还不会导致文件被关闭,此时通过 fd2 照样可以访问文件printf(“read:%dn”,read(fd2),szBuf,sizeof(szBuf)-1);close(fd2); /内核的引用计数变为 0,文件正式关闭dup2(int fdold,int fdnew)也是进行描述符的复制,只不过

14、采用此种复制,新的描述符由用户用参数 fdnew 显示指定,而不是象 dup 一样由内核帮你选定。对于 dup2,如果 fdnew 已经指向一个已经打开的文件,内核会首先关闭掉fdnew 所指向的原来的文件。如果成功 dup2 的返回值于 fdnew 相同,否则为-1.2.6.标准输入输出文件描述符与标准的输入输出流对应,在更底层的实现是用标准输入、标准输出、标准错误文件描述符表示的。它们分别用STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 三个宏表示,值分别是 0、1、2 三个整型数字。我的小结:在对两类文件操作的学习之后,常用的文件打开,创建,读写和关

15、闭,以及文件指针的定位,文件大小的求取等常用的操作都能掌握,在实际应用的过程出错不多,不过也有,曾经犯过fread读操作的错误,例如,在fread(void *ptr, size_t size, size_t nmemb, FILE *stream)时;有时候一次读size(自定义一个较大的值)个字节,导致读失败,特别是在文件读取最后一次不足size个字节时,现在已经能正常应用,读的时候一个字节一个字节的读,写的时候一次写fread返回值那么多个字节,就能保证文件的读写不会出现两文件或文件和数组里面的内容的个数或大小不相同。二、进程管理部分:知识小结:1. 进程的基本概念: 进程是程序的一次执

16、行,进程是拥有资源的最小单位和调度单位(在引入线程的操作系统中,线程是最小的调度单位); 进程的三种最基本的状态是:运行态(running),就绪态(readying), 阻塞态(block), 进程和程序的主要区别是进程是动态的,程序是静态的。进程时运行中的程序,程序是一些保存在硬盘上的可执行的代码。 1.1.进程的优点和缺点 优点:使多个程序并发执行 缺点:程序并发执行时付出了巨大的时空开销,每个进程在进行切换时身上带了过多的“累赘”导致系统效率降低。 1.2.linux系统的进程间通信有哪几种方式 1管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关

17、系的进程间使用 。进程的亲缘关系通常是指父子进程关系。2有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信 。3信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 4消息队列( message queue ) : 消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。5 信号 ( sin

18、al ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。6 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。7套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 1.3.进程控制 1 linux进程控制包括创建进程,执行进程,退出进程以及改变进程优先级等。 在linu

19、x系统中,用于对进程进行控制的系统调用有: a.fork: 用于创建一个新进程。 b.exit : 用于终止进程 c.exec : 用于执行一个应用程序 d.wait : 将父进程挂起,等待子进程终止 e.getpid : 获取当前进程的进程ID f.nice : 该变进程的优先级 2 getpid 和 getppid 的区别(1)getpid(): getpid函数用来取得目前进程的进程识别码。(2)getppid():getppid函数用来取得目前进程的父进程识别码。3 fork 和 vfork 的区别(1)fork():使用fork()创建一个子进程时,子进程只是完全复制父进程的资源。这

20、样得到的子进程独立于父进程具有良好的并发性,父子进程执行顺序不定。(2)vfork():使用 vfor创建一个子进程时,操作系统并不将父进程的地址空间完全复制到子进程。而是子进程共享父进程的地址空间,即子进程完全运行在父进程的地址空间上,子进程对该地址空间中任何数据的修改同样为父进程所见。同时保证子进程先运行,在它调用exec或exit后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。4 exit()和_exit()的区别: (1) exit函数有参数,正常终止进程 ,exit执行完后把控制权交给系统,exit是在_exit函数之上的一个封装,其会

21、调用_exit,并在调用之前先刷新流。(2) _exit()执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。调用_exit函数时,其会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷新流(stdin, stdout, stderr .)。 5 僵尸进程的避免(1) 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。(2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。(3) 如果父进程不关心子进程什么时候结

22、束,那么可以用signal(SIGCHLD, SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后, 内核会回收, 并不再给父进程发送信号。(4) 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。 2.线程的引入: 人们为了解决这个缺点,想到让进程在并行时不拥有资源-从而引入了线程的概念:即线程本身不拥有资源或者是很少的资源,进程是拥有资源的基本单位,线程是调度的基本单位.在操作系统中引入线程则是为了减少程序并发执行时所付出的时空开

23、销,使操作系统具有更好的并发性。 2.1.线程标识 每个进程内部的不同线程都有自己的唯一标识,线程标识( ID )只在它所属的进程环境中有效,线程标识是 pthread_t 数据类型。2.2.线程创建新创建线程从start_rtn 函数的地址开始运行,不能保证新线程和调用线程的执行顺序。#include int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr,void *(*start_rtn)(void), void *restrict arg);返回:成功返回 0 ,否则返回错误编号2

24、.3.终止方式1 pthread_exit 和 pthread_join的使用:#include void pthread_exit( void *retval )int pthread_join( pthread_t *th , void *thread_return )返回值:成功返回 0 ,否则返回错误编号pthread_exit:retval是pthread_exit 调用者线程的返回值 , 可由其他函数和 pthread_join 来检测获取。线程退出时使用函数 pthread_exit, 是线程的主动行为。由于一个进程中的多个线程共享数据段,因此通常在线程退出后,退出线程所占用的资

25、源并不会随线程结束而释放。所有需要 pthread_join 函数来等待线程结束,类似于 wait 系统调用。pthread_join:等待线程的结束。 th :等待线程的标识符 thread_return :用户定义指针,用来存储被等待线程的返回值。2 pthread_exit 和 pthread_cancel的区别(1)线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。 (2)pthread_cancel是一种让线程可以结束其他线程的机制,一个线程可以对另一个线程发送一个结束的请求。当一

26、个线程最终尊重了取消的请求,他的行为就像执行了pthread_exit函数。3.进程和线程的区别 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,

27、来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.我的小结:通过对进程和线程的学习,分析他们的区别,了接他们各自的主要特点,在应用项目中可以根据自己的项目需

28、求来选择多进程或多线程的使用;在这一块碰到过很多问题,使用进程,碰到像如何处理进程间通讯,还有解决僵尸进程等问题,使用线程上,碰到过像线程同步问题,以及对线程锁的使用。三、网络编程部分:1.TCP协议下的网络编程: 服务器端 客户端socket bind listen socket accept connect read write write read close close 1.1.常用库函数1)服务器和客户机首先要调用socket()建立套接字并指明合适的通讯协议,格式为: int socket(int family, int type, int protocol); socket()打

29、开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对于IPv4,family参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。 int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); 服务器程序

30、所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。bind()成功返回0,失败返回-1。 int listen(int sockfd, int backlog); 典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请

31、求就忽略。listen()成功返回0,失败返回-1。 int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); 三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。cliaddr是一个传出参数,accept()返回时传出客户端的地址和端口号。addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区cliaddr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(

32、有可能没有占满调用者提供的缓冲区)。如果给cliaddr参数传NULL,表示不关心客户端的地址。2)客户端需要调用connect()连接服务器。 int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1。2.UDP协议下的网络编程: 服务器端 客户端 socket bind scoketrecvfrom sendto sendto recvfro

33、m close 2.1.常用库函数1)函数原型:int sendto ( int s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ;sendto() 用来将数据由指定的socket传给对方主机。参数s为已建好连线的socket,如果利用UDP协议则不需经过连线操作。参数msg指向欲连线的数据内容,参数flags 一般设0,详细描述请参考send()。参数to用来指定欲传送的网络地址,结构sockaddr请参考bind()。参数tolen为sockaddr的结果

34、长度。 2)函数原型:ssize_t recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from,socket_t *fromlen); 用来接收远程主机经指定的socket传来的数据,并把数据传到由参数buf指向的内存空间,参数len为可接收数据的最大长度.参数flags一般设0,其他数值定义参考recv().参数from用来指定欲传送的网络地址,结构sockaddr请参考bind()函数.参数fromlen为sockaddr的结构长度. 3.TCP协议和UDP协议的区别 TCP 的目的是提供

35、可靠的数据传输,并在相互进行通信的设备或服务之间保持一个虚拟连接。TCP 在数据包接收无序、丢失或在交付期间被破坏时,负责数据恢复。它通过为其发送的每个数据包提供一个序号来完成此恢复。 UDP 与 TCP 的主要区别在于 UDP 不一定提供可靠的数据传输。事实上,该协议不能保证数据准确无误地到达目的地。UDP 在许多方面非常有效。当某个程序的目标是尽快地传输尽可能多的信息时(其中任意给定数据的重要性相对较低),可使用 UDP。ICQ 短消息使用 UDP 协议发送消息。 许多程序将使用单独的 TCP 连接和单独的 UDP 连接。重要的状态信息随可靠的 TCP 连接发送,而主数据流通过 UDP 发送。 我的小结:通过对网络编程这一块的学习,基本了解了TCP和UDP两种协议下的通讯机制,能够有选择的使用这两种网络通讯,同时在做项目中的文件传输过程中,出现过文件缺失,问题出在读写的数据内容量不一致,在TCP协议下做webserver服务器能正常发送网页信息,在UDP协议下的远程拷贝,能实现本地到远程,远程到 本地的拷贝。 /* 文档编辑人:姜维义 文档编辑时间:2012年3月20日 */

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!