Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

프로세서

http://chortle.ccsu.edu/java5/Notes/chap04/ch04_1.html

지금까지의 문서를 통해서 컴퓨터 시스템을 이루는 주요한 하드웨어 구성요소가 아래와 같다는 것을 배웠다.
  • 프로세서
  • 메인 메모리
  • Secondary 메모리 장치
  • 입출력 장치
이번 장에서는 컴퓨터시스템의 두뇌에 해당되는 프로세서에 대해서 알아보도록 하겠다. 이번장에서 배우고자 하는 것은 컴퓨터의 두뇌가 어떻게 인간인 여러분이 짠 프로그램을 해석해서 작업을 수행하는가에 대한 것이다. 이를 위해서 다음과 같은 큰 주제들을 다루도록 하겠다.
  • 기계 수행 (Machine Operations) 와 기계어(Machine language)
  • 기계어의 예
  • 다양한 종류의 프로세서 칩
  • 고 수준 프로그래밍 언어들
  • 컴파일러(compiler) 언어들
  • interpreter 언어들
문제 프로그램의 수행에 직접적으로 관여하는 컴퓨터 시스템의 구성요소는 무엇인가 ?

프로세스의 전자적 명령 수행 (Electronic Operations of a Processor

http://chortle.ccsu.edu/java5/Notes/chap04/ch04_2.html

컴퓨터 프로세서에 의해서 프로그램이 실행되게 되면, 프로세서는 짧은 시간에 매우 많은 작은 단위의 전자적인 명령을 수행하게 된다. 예를 들어 프로세서에게 메인메모리로 부터 1byte의 데이터를 읽어들이는 일을 맡겨지게 될경우, 각 바이트의 bit값이 1인지를 검사하게 된다. 거의 대부분의 프로세서가 이러한 간단한 일을 일초에 수백만번씩 수행하게 된다.

Intel Pentium 프로세서
보내는 사람 Java

믿음이 가지 않을지 모르겠지만, 이런 단순한 작업들이 컴퓨터가 할 수 있는 모든 것들이다. 이러한 작은 작업들이 모이고 모여서 주어진 명령을 수행하게 되는 것이다. 프로세서가 할 수 있는 기본단위의 일들이 워낙에 작기 때문에 수백만 수천만번 이러한 일을 반복수행해야 그럴듯한 결과를 보여줄 수 있다. 다행히 현대의 컴퓨터들은 초당 수백만번의 이러한 연산을 어려움없이 해낸다.

그러나 우리는 엄청난 수의 작은단위의 일을 제어하는 방법을 알고 있어야 프로그램을 작성할 수 있는건 아니다. 다행스럽게도 우리에게는 Java, C(:12)와 같은 고수준 프로그래밍 언어들을 가지고 있다. 우리는 큰 수준에서의 일을 제어하는 방식을 알고 있으면 된다. 나머지는 프로그램 언어가 알아서 해결해 준다.

문제 웹브라우저를 통해서 하이퍼 링크를 클릭해서 새로운 페이지를 화면에 뿌려주기 위해서 프로세서는 얼마나 많은 전자적 명령을 수행해야 할까요 ?
  • 1
  • 10
  • 100
  • 100,000

기계 명령

http://chortle.ccsu.edu/java5/Notes/chap04/ch04_3.html

자가용을 몰고 서울에서 부산까지 달리기 위해서는 매우 복잡한 과정을 거쳐야 한다. 도로표지판을 읽을 줄 알아야 하고, 신호를 보고 가속, 감속, 정지, 방향전환을 해야 한다. 그러나 이러한 복잡한 일들도 사실상 핸들, accelerate, break을 이용한 회전, 앞뒤 방향이동과 같은 단순한 패턴의 반복으로 이루어진다. 이러한 단순한 일이 수천 수만번 일어나는 것이다. 프로세서도 마찬가지다. 몇개의 명령들의 수많은 반복으로 그림을 보여주거나, 음악을 들려주는 것과 같은 일들을 해낸다.

프로세서에 의해서 수행되는 이러한 각각의 조그마한 전자적 수행을 machine operation 이라고 부른다. 이러한 machine operation은 한번에 하나만 수행될 수 있다. 대신 초당 수백만번 수행이 된다.

기계명령 프로세서가 하나의 machine operation을 수행하기 위해서 읽어들이는 메모리에 저장된 몇개의 바이트의 조합이다. 프로세서는 기계명령을 읽어서 수행하고, 다음에 저장된 기계명령을 읽어서 수행하는 식으로 작동한다. 메모리에 저장된 이러한 기계명령의 조합을 우리는 기계어 프로그램 (machine language program) 혹은 실행가능한 프로그램 (executable program) 이라고 한다.

이해하기 힘들다고 해서 공황상태에 빠질 필요는 없다. 차차 알아나가게 될거다. 문제 프로그램이 실행될 때, 기계어 프로그램과 데이터는 모두 메인 메모리에 위치합니까 ?

기계어의 예제

http://chortle.ccsu.edu/java5/Notes/chap04/ch04_4.html

우리가 사용하는 전동치솔에 프로세서와 메인메모리가 있다고 가정해 보자. 이 프로세서는 칫솔을 좌우로 회전시키고, on/off 스위치를 체크하는 등의 일을 할 수 있어야 한다. 기계명령은 하나의 byte로 이루어지다. 각각의 명령은 machine operations (기계 수행)에 대응된다.

Machine Instruction Machine Operation
0000 0000 Stop
0000 0001 칫솔을 왼쪽으로 회전
0000 0010 칫솔을 오른쪽으로 회전
0000 0100 프로그램의 시작위치로 이동
0000 1000 만약 스위치가 off되면 다음명령을 skip한다.
주소 Machine Instruction
0 ______
1 ______
2 ______
3 ______
스위치를 올리면 바로 On 상태인 것으로 가정하자. 프로세서는 한번에 하나의 명령만을 실행할 수 있을 뿐이다. 처음에는 0번 주소에 있는 명령을 실행하고, 실행이 끝나면 1번 주소에 있는 기계명령을 실행할 것이다. 스위치를 켜면, off 상태가 되기 전까지는 칫솔을 좌우로 움직이는 기계명령을 수행해야 할 것이다.

문제 전동치솔을 제어하기 위한 기계명령을 위의 테이블에 채워넣어라.

기계어 프로그램 (Machine Language Program)

여기에 앞의 전동치솔 문제의 해답이 있다.
0 0000 0001 칫솔을 왼쪽으로 회전한다
1 0000 0010 칫솔을 오른쪽으로 회전한다
2 0000 1000 만약 스위치가 0ff라면 다음을 건너뛴다
3 0000 0100 프로그램의 시작위치로 간다
4 0000 0000 프로그램을 종료한다
위의 전동칫솔 프로그램은 메모리 상에 다음과 같은 비트의 연속으로 저장되어 있을 것이다. 0000 0001 0000 0010 0000 1000 0000 0100 0000 0000

프로세서가 시작되면 각 코드의 명령을 수행하게 된다. 물론 위의 예는 교과서적인 설명을 위한 예제다. 컴퓨터 프로세서로 제어되는 전동치솔은 아직 시중에 판매되고 있지 않다. 또한 실제 컴퓨터에 사용되는 프로세서들은 훨씬 복잡한 작업을 수행한다. 그렇지만 이 예제를 통해서 프로세서가 프로그램을 어떤방식으로 실행하는지에 대한 기본적인 아이디어는 얻었을 것이다.
  • 기계어 프로그램은 메인메모리에 저장되어 있는 연속된 기계어 명령을 실행한다.
  • 기계어 명령은 하나 이상의 바이트의 조합으로 이루어진다.
  • 프로세서는 한번에 하나의 기계 명령만을 수행할 수 있다.
  • 모든 기계들은 그들을 제어하기 위한 machine operation 셋을 가지고 있다.
만약 전동칫솔의 스위치를 켜게 되면 프로그램은 off버튼을 누를때까지, 무한 반복으로 왼쪽 오른쪽으로 움직이는 작업을 수행할 것이다. 이와 마찬가지로 실제 컴퓨터가 다루는 프로그램들도 많은 작은 임무를 수행하는 기계명령을 반복하는 형식으로 주어진 일을 처리하게 된다.

문제 위의 전동치솔에서 off버튼을 누르면 어떤일이 발생하는가 ?

명령의 실행

http://chortle.ccsu.edu/java5/Notes/chap04/ch04_6.html

실행(execute) 라는 말은 보통 instruction(명령)에 의해서 machine operation이 수행되는 과정을 의미한다. 위의 전동치솔을 예로 들어보자면, 0000 0000 명령을 실행해서 칫솔을 멈춘다, 혹은 초당 수백만번의 기계명령이 실행된다로 실행을 묘사할 수 있다.

모든 기계어 프로그램은 명령의 연속적인 실행을 통해서 작동을 한다. 실제 컴퓨터에 있어서는 초당 수백만번의 명령이 실행될 것이다. 컴퓨터가 더 좋으면 좋을 수록 (Hz 숫자가 높을 수록) 더 짧은 시간에 더 많은 명령을 실행할 수 있다.

전동칫솔의 예에서 기계명령은 실행이 된후 종료 명령이 내려지기 전까지 좌우로 회전하는 반복되는 명령의 모음들로 이루어져 있다. 일반 컴퓨터역시 마찬가지이며 이러한 반복적인 실행을 loop라고 부른다.

우리가 흔히 접하는 컴퓨터에 사용되는 프로세서들은수백만개의 트랜지스터들을 가지고 있는데, 이들은 integrated circuit (집적 회로)라고 불리우는 조그만 실리콘 웨이퍼위에 놓여 있다. 전동치솔을 위한 프로세서는 매우 간단해서, 아마도 몇백개 정도의 트랜지스터 정도면 주어진 일을 해내는 프로세서를 만들어 낼 수 있을 것이다. 집적회로는 프로세서뿐만 아니라 메인 메모리의 메모리칩과 같은 컴퓨터의 다른 부분들에도 사용된다.

문제 Apple 컴퓨터와 IBM 호환 컴퓨터는 동일한 프로세서 칩을 사용할까 ?

다른 종류의 프로세서들

컴퓨터 시스템에는 여러가지 종류의 프로세서들이 사용된다. 여러분들은 이미 Intel 회사의 프로세서들에 (486, Pentium, Pentium II ...) 대해서 잘 알고 있을 것이다. 또한 Apple 컴퓨터에 사용되는 프로세서의 이름도 알고 있을 것이다. 컴퓨터는 그 디자인된 목적에 따라서 거기에 맞는 프로세서를 가지게 된다. 오실로스코프와 같은 다른 전자장치들은 거기에 맞는 형태의 프로세서들을 가지고 있다.

애플의 파워 매킨토시와 Dell의 프로세서들은 그 디자인에 있어서 많은 차이를 보여준다. 프로세서가 다르면 서로 다른 machine operation을 가지게 된다. 그러므로 Dell 컴퓨터에서 돌아가는 기계 프로그램은 Apple 컴퓨터에서 실행되지 않는다. 이러한 차이는 동일한 회사에서 만든 컴퓨터에서도 나타나게 된다. 486과 Pentium 은 인텔에서 만든 프로세서이므로 비슷한 machine operation을 가지지만, 약간의 차이가 발생하게 된다. 때문에 486에서는 잘돌아갔던 프로그램이 Pentium에서 돌아가지 않는 경우가 발생한다. 이럴 경우에는 새로운 프로세서를 위해서 작성된 최신 버전의 프로그램을 설치해야 한다.

그러나 어도비사의 포토샵과 같은 프로그램은 Pentium기반의 컴퓨터와 Apple 컴퓨터 모두에서 작동한다. 이 둘은 486과 Pentium에 비견할 수 없을 정도로 많은 간격을 가지고 있는데, 어떻게 실행이 될까 ? 비록 두개의 프로세서간의 많은 차이가 있지만, machine operations의 대부분은 비슷하기 때문에 이러한 일이 가능해진다. 지프와 스포츠카가 매우 다르지만 핸들, 브레이드, 악셀레이터등의 기본적인 구성이 같기 때문에, 약간의 훈련으로 움직일 수 있는 것과 마찬가지다. 물론 그렇다고 하더라도 각각의 프로세서에 맞도록 프로그램이 다시 만들어져야 할 것이다.

프로세서가 어떤 구조 (architecture)를 가질 것인가 하는 것은 어떠한 machine operations를 가지도록 만들어질 것인가에 따라서 선택되어진다. 어떠한 machine operations를 가질 것인가 하는 것은 프로세서가 어떤 용도에 쓰일 것인가에 따라 달라질 것이다. 프로세서의 구조에 관한 내용은 컴퓨터 과학의 또다른 분야이기 때문에 여기에서는 자세히 다루지 않도록 하겠다.

고급 언어

전동치솔의 경우를 봐서 알겠지만 기계 언어를 사용해서 프로그램을 작성하는 것은 인간에게 불편한 작업이다. 그나마 전동치솔은 간단하기라도 했지만, 실행가능한 파일 (직업 수행되는 기계명령어로된 프로그램) 들은 수백에서 수천의 기계 언어로 된 명령으로 이루어져 있다. 기계어로 인간이 원하는 프로그램을 작성하는 건 거의 불가능에 가깝다고 봐야 할 것이다. 0과 1의 비트조합으로 계산기를 만든다고 가정해 보라.

기계어를 써서 프로그램을 작성하는게 어렵기 때문에 대부분의 프로그램들은 C(:12), C++, Java와 같은 high level programming language (고급언어)를 통해서 만들어진다. 이러한 고급언어를 이용하면 프로그래머는 복잡한 일을 수행하는 프로그램을 만들어 낼 수 있다. 나중에 이들 프로그램은 많은 machine operation들로 이루어진 기계어로 변환이 되어서 프로세서가 직접 실행시키게 된다.

다음은 C언어 프로그램의 간단한 예이다. int sum = 0;

machine operation으로 나타내 보자면, 메인 메모리의 특정한 영역에 숫자 0을 저장을 수행하라 라는 명령이 된다. 아주 간단한 일처럼 보이겠지만 기계어를 이용해서 직접 작성할려면 0과 1로 이루어진 알아먹기가 거의 불가능한 수백개의 machine operation을 사용해야 한다. C를 이용하면 단한줄로 이러한 일을 끝낼 수 있다. 문제 프로그래머에게 시간당 50$를 지불하는 회사가 있다고 가정해 보자. 회사는 기계어를 이용해서 프로그램을 작성하는 프로그래머를 원할까. 아니면 고급언어를 사용하는 프로그래머를 원할까.

소스 프로그램

프로그래머는 고급언어를 이용해서 프로그램을 만든다. 고급언어로 만들어진 프로그램은 문서작성기를 통해서 입력된 여러 줄의 문자들로 이루어지며, 파일 형태로 하드디스크에 저장된다. 다음은 C언어로 작성된 완전한 프로그램이다.
#include <stdio.h>
int main()
{
  int sum = 0;
  sum = 2+2;
  printf("%d\n", sum);
}
이러한 내용을 가진 프로그램은 addup.c와 같은 이름으로 하드디스크에 저장된다. 이 파일은 연속된 바이트의 조합으로 이루어져 있으며, 이 바이트들이 문자데이터로 읽혀지게 된다. 여러분은 에디터를 통해서 문자의 형태로 읽어오고, 프린터를 통해서 출력할 수 있을 것이다. 그러나 이들 바이트에는 기계 명령이 포함되어 있지 않다. 이 바이트들이 메인메모리로 복사된다고 하더라도 어떠한 일도 수행할 수 없게 된다.

소스 프로그램 (혹은 소스파일)은 고급언어로 씌여진 명령들로 이루어진 문서 파일을 의미한다. 이 것은 변환 과정이 없이는 실행이 될 수 없다.

소스 프로그램이 실행되기 위해서는 기계어로 번역되는 과정을 필요로 한다. 이렇게 소스파일을 입력받아서 실행가능한 기계어 프로그램으로 바꾸어주는 일을 하는 응용 프로그램을 translater이라고 한다. C 프로그램인 addup.c가 번역되는 과정을 거쳐서 실행 프로그램이 되는 것이다. 이렇게 번역과정을 거친 프로그램은 addup.exe등의 이름을 가진 실행가능한 프로그램의 형태로 하드디스크에 저장이 된다. 이 프로그램은 메인메모리로 저장이 되서 실행될 수 있다.

프로그래머는 보통 소스 프로그램을 번역하는 과정을 translate한다 라고 하는대신에, compile한다라고 말한다. 즉 소스프로그램은 컴파일 과정을 거쳐서 실행 프로그램이 된다라고 바꿔 말할 수 있다.

문제 이상에서 우리는 소스프로그램이 번역과정을 거쳐서 실행된다는 것을 알았다. 그러나 프로그램은 원하지 않는 방향으로 잘못 실행될 수도 있다. 1부터 100까지 더하는 프로그램을 만들었는데, 1부터 99까지 더하는 프로그램을 만들었다면 프로그램을 수정해야 할것이다. 소스 프로그램을 수정해야 하는가. 아니면 실행 프로그램을 수정해야 하는가.

프로그램 번역

다음 그림은 C에 의해서 만들어진 프로그램이 어떻게 실행파일로 만들어지는지를 보여주고 있다. (Java는 다른 과정을 거친다. 이에 대해서는 다음장에서 다루도록 하겠다.

attachment:Figure4-1.gif

  1. 소스파일은 문서작성기를 통해서 만들어진다.
    • 여기에는 고급언어 명령이 포함되어 있다.
    • 문자로된 byte 정보들이 포함되어 있다.
  2. 소스파일은 하드디스크에 저장된다.
  3. 소스파일은 메인 메모리로 올라간다고 하더라도 프로세스에 의해서 실행될 수 없다.
  4. 컴파일러(번역기) 프로그램이 소소 파일을 실행파일로 번역한다.
    • 소스파일은 변경되지 않은체, 새로운 실행파일이 생성된다.
    • 컴파일러 자체도 C 언어와 같은 고급언오로 만들어지며, 목표로 하는 프로세서와 운영체제에서만 작동한다. 영어 번역가는 영문서만 번역할 수 있는 것과 마찬가지다.
  5. 실행파일 역시 하드디스크로 저장된다.
  6. (키보드를 통해서)프로그램을 실행을 명령하면, 운영체제는 실행파일을 메인메모리로 카피하고 실행시킨다.
위의 방식은 Ada, Pascal, C, C++, FORTRAN 등의 많은 언어들이 사용하고 있다. Java는 이들과는 약간 다른 방식을 채택하고 있는데, 다음장에서 자세히 다루도록 하겠다.

문제 C언어로 만들어진 소스코드파일을 팬티엄 컴퓨터에서 애플 매킨토시 컴퓨터로 복사했다. 이들 컴퓨터에서 프로그램을 실행시키려면 어떻게 해야 하는가.

Portability

이론적으로 고급언어를 통해서 만든 프로그램은 다른 여러 종류의 프로세서에서 실행되게 할 수 있다. 퀘이크를 예로 들어보자면, 윈도우즈와 매킨토시를 위한 별도의 소스파일을 만들 필요 없이 하나의 소스파일만 가지고, 양쪽에서 동일하게 실행되는 실행파일을 만들 수 있다. 물론 소스파일을 해당 프로세서에 맞도록 번역할 수 있는 컴파일러 프로그램이 존재한다는 조건이 따른다.

이렇게 하나의 소스파일을 유지하면서도 서로 다른 프로세서에서 실행가능하게 됨으로써, software portability를 높일 수 있다. 단지 고급언어를 사용해서 프로그램을 작성할 수 있기만 하면 된다. 나머지 작업은 컴파일러가 알아서 해준다.

그러나 불행하게도 고급언어를 사용하더라도 서로 다른 프로세서에서 동일하게 작동하는 프로그램을 짜는건 그리 만만한 일이 아니다. 다른 시스템에서 돌아가게 하려면, 운영체제의 특성, 버전등과 같은 상당히 많은 것들에 대해서 신경을 써야한다. 많은 것을 해결해 주기는 하지만 여전히 portability 한 소프트웨어를 만들기 위해서는 상당한 인간의 노력을 필요로 하는 것이다. 자바를 사용하면 portability한 소프트웨어를 만들기 위해서 필요로 하는 상당한 인간의 노력조차 자바에서 해결해 주므로 훨씬 적은 노력으로 portability한 소프트웨어를 만들 수 있다.

문제 프로그래머에게 시간당 50$를 주는 회사가 있다. 이 회사는 애플과 인텔 컴퓨터 모두에서 작동되는 프로그램을 만들려고 한다. 이 회사는 Java와 다른 고급언어 프로그래머들 중 어느쪽을 선호 할 거라 생각되는가

Interpreter

고급언어로 씌여진 프로그램은 절대 프로세서에 의해서 곧바로 실행될 수 없다. 컴파일러를 통해서 소스 프로그램을 실행가능한 (기계어로 된) 프로그램으로 번역해야 한다.

여기에서는 interpreter방식을 사용하는 다른 언어들을 소개하고자 한다.

인터프리터는 그 자체가 프로세서 처럼행동해서, 고급언어로 작성된 프로그램을 직접 실행시킨다.

다음은 인터프리터의 작동방식을 나타내는 그림이다.

attachment:Figure4-2.gif

위의 그림에서 소스프로그램은 program.bas이며, BASIC 프로그래밍 언어로 만들어졌다. 이 프로그램을 BASIC interpreter에 넘기면, 소스프로그램을 넘겨 받은 interpreter가 직접 실행시킨다. BASIC 인터프리터는 소스프로그램을 라인단위로 읽어서 실행을 하는데, 컴파일러가 번역기라면 인터프리터는 동시통역기라고 생각하면 될 것이다.

인터프리터는 많은 곳에서 사용되는데, WOW와 같은 게임에도 사용된다. 이러한 게임은 자체적으로 인터프리터를 가지고 있어서, 캐릭터의 행동방식, 마법데미지, UI등을 결정할 수 있다. 이러한 인터프리터는 자체적인 명령어셋과 체계를 가지고 있는데, 이를 지킨다면 쉽게, UI, 캐릭터행동, 데미지 등의 변경이 가능하다. UI같은 경우에는 프로그래머가 아니더라도 실시간으로 변경이 가능하다. 만약 내부적으로 인터프리터를 사용하고 있지 않다면, UI, 마법데미지, 행동방식의 변경이 있을 때마다 프로그램을 새로 컴파일해서 패치시켜주는 작업이 필요할 것이다.

Virtual Machine (가상기계)

BASIC 소스 프로그램을 인터프리터를 통해서 실행시키면, 인터프리터와 소스프로그램이 모두 메모리에 올라가게 된다. 이 인터프리터는 하드웨어에서 직접실행기 가능한 기계 명령으로 구성되어 있다. BASIC 소스프로그램은 BASIC 인터프리터가 이해할 수 있는 명령어들의 조합으로 이루어져 있다. 달리말하자면 BASIC 인터프리터가 가상의 프로세서인 것처럼 작동을 해서 BASIC 프로그램을 직접 실행시키게 되는 것이다.

attachment:Figure4-3.gif

위의 그림은 인터프리터를 설명했을 때 사용한 그림과 비슷해 보이지만 약간의 차이가 있다. Basic 인터프리터와 프로세서를 하나로 묶고 있는데, 이렇게 베이직 인터프리터가 프로세스를 그 하부에 감추고 있으므로, 베이직 프로그래머는 가상의 기계위에 있는 것처럼 프로그래밍을 할 수 있다. 이런 이유로 우리는 베이직 인터프리터를 베이직 가상기계라고 부르기도 한다. 영화 메트릭스 에서 메트릭스를 통해서 가상의 세계를 구성함으로써, 인간들로 하여금 하나의 세계에 있는 것처럼 느끼는 것과 마찬가지라 할 수 있겠다. 문제 베이직 소스 프로그램을 실행파일로 변경할 수 있을까 ?

속도

컴퓨터 언어는 크게 두 종류로 나눌 수 있음을 알 수 있다. 그럼 인간의 언어 활동과 비교해서 이 둘의 차이점을 설명해 보도록 하겠다.
  • Translator : 책을 번역하는 것과 비슷한 행위다. 영어로 된책은 한글로 완전히 번역이 되어서 출판이 된다.
  • Interpreter : 동시 통역기이다. 한쪽에서 영어로 말을 하면, 통역가가 그 즉시 한글로 통역을 해서 알려준다.
영어로 된 책을 통역가가 번역을 한다음에 읽어주는 것보다는 이미 번역을 끝마친 책을 보는게 훨씬 빠를 것이다. 컴퓨터 언어도 마찬가지다. 인터프리터기는 프로그램을 한줄씩 읽어서 해석하고 기계어로 변경해서 실행을 한다. 때문에 즉시 실행되는 컴파일과정을 거친 프로그램보다 느릴 수 밖에 없다. 문제 프로그램이 빠르게 작동할 수 있어야 한다는 것은 항상 중요한 문제가 되는가 ?